<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	
	xmlns:georss="http://www.georss.org/georss"
	xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
	>

<channel>
	<title>Das Blinken Lichten</title>
	<atom:link href="http://www.dasblinkenlichten.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.dasblinkenlichten.com</link>
	<description>Networking, Virtualization, Docker, Kubernetes, DevOps, Automation, etc, etc, etc</description>
	<lastBuildDate>Thu, 07 Nov 2024 04:13:39 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.6.5</generator>

<image>
	<url>http://www.dasblinkenlichten.com/wp-content/uploads/2019/02/cropped-img_59ee34d204572-60x60.png</url>
	<title>Das Blinken Lichten</title>
	<link>http://www.dasblinkenlichten.com</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">10916793</site>	<item>
		<title>Understanding BGP &#8211; Establishing peering</title>
		<link>http://www.dasblinkenlichten.com/understanding-bgp-establishing-peering/</link>
					<comments>http://www.dasblinkenlichten.com/understanding-bgp-establishing-peering/#respond</comments>
		
		<dc:creator><![CDATA[Jon Langemak]]></dc:creator>
		<pubDate>Thu, 07 Nov 2024 04:13:37 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://www.dasblinkenlichten.com/?p=23454</guid>

					<description><![CDATA[After some reflection I&#8217;ve realized that while I&#8217;ve spent a lot of time talking about BGP in it&#8217;s many forms I haven&#8217;t really ever done a deep dive on it.... <a href="http://www.dasblinkenlichten.com/understanding-bgp-establishing-peering/">Read more &#187;</a>]]></description>
										<content:encoded><![CDATA[
<p>After some reflection I&#8217;ve realized that while I&#8217;ve spent a lot of time talking about BGP in it&#8217;s many forms I haven&#8217;t really ever done a deep dive on it. To be clear &#8211; Im not aiming to talk about how to configure BGP , or how path selection works, or even how to troubleshoot BGP. What I want to examine is what BGP is doing on the wire. How it communicates with peers, when it sends updates, and what kind of things are in the updates. Im hoping to write several blogs starting with the basics and then diving deeper as we go. That said, let&#8217;s get started!</p>



<p>To start things out with &#8211; I think it makes sense to start with a simple lab consisting of two BGP nodes that are peering together.  Something like this&#8230;</p>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="1124" height="620" src="https://www.dasblinkenlichten.com/wp-content/uploads/2023/08/image.png" alt="" class="wp-image-23473" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2023/08/image.png 1124w, http://www.dasblinkenlichten.com/wp-content/uploads/2023/08/image-300x165.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2023/08/image-200x110.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2023/08/image-768x424.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2023/08/image-624x344.png 624w" sizes="(max-width: 1124px) 100vw, 1124px" /></figure>



<p>I don&#8217;t want to spend a lot of time focusing on the configuration syntax and basic configuration parameters so let&#8217;s just run <a href="https://bird.network.cz/">BIRD</a> on both of the nodes so we can get off the ground with minimal effort. Let&#8217;s assume that both BGP Peers shown above are just normal Ubuntu VMs and both have a single interface on common 169.254.10.0/31 network.</p>



<p><em>Note: All of the links will come out of the /24 and be /31 IPv4 networks.</em></p>



<p>To install BIRD we&#8217;ll simply&#8230;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
apt install bird2
</pre></div>


<p>Once that completes we should be up and running with a default BIRD configuration.  You can validate this by simply typing <code>birdc show proto</code>&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm0:~# birdc show proto
BIRD 2.0.7 ready.
Name       Proto      Table      State  Since         Info
device1    Device     ---        up     18:12:43.178  
direct1    Direct     ---        down   18:12:43.178  
kernel1    Kernel     master4    up     18:12:43.178  
kernel2    Kernel     master6    up     18:12:43.178  
static1    Static     master4    up     18:12:43.178  
root@vm0:~# 
</pre></div>


<p>Perfect!  Now the default configuration that comes with BIRD is meant to be more of an example of what&#8217;s possible rather than a meaningful configuration.  That said &#8211; let&#8217;s add our own basic configuration to the BIRD-0 host.  You can simply overwrite the configuration in <code>/etc/bird/bird.conf</code> with this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
log syslog all;

router id 169.254.10.0;

debug protocols {states,interfaces,events};

protocol bgp bird_1_iBGP_peering {
    local as 65000;
    neighbor 169.254.10.1 as 65000;
    ipv4 {
        import all;
        export all;
    };
}
</pre></div>


<p>And then restart the bird service (<code>systemctl restart bird</code>). Now let&#8217;s do something similar on the BIRD-1 host&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
log syslog all;

router id 169.254.10.1;

debug protocols {states,interfaces,events};

protocol bgp bird_0_iBGP_peering {
    local as 65000;
    neighbor 169.254.10.0 as 65000;
    ipv4 {
        import all;
        export all;
    };
}
</pre></div>


<p>After you have this configuration in place on both nodes, and have restarted the BIRD service, you should be able to run the <code>show proto</code> command again and see something like this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm0:~# birdc show proto
BIRD 2.0.7 ready.
Name       Proto      Table      State  Since         Info
bird_1_iBGP_peering BGP        ---        up     18:53:51.344  Established   
root@vm0:~# 
</pre></div>


<p>Wohoo! That might be the quickest Zero to BGP peering we&#8217;ve ever done here! Now at this point &#8211; all we have is a BGP session. But that by itself required a lot of communication to happen. I&#8217;d like to spend the rest of this post talking about what actually happened for both of these BGP peers to actually decide it was OK for them to form a peering relationship. So let&#8217;s kill the BIRD service on the BIRD-0 host, start a packet capture on the wire, and then restart the service so we can see the communication that happens when the hosts try to BGP peer&#8230;</p>



<figure class="wp-block-image size-full"><img decoding="async" width="1447" height="310" src="https://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image.png" alt="" class="wp-image-23475" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image.png 1447w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-300x64.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-200x43.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-768x165.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-624x134.png 624w" sizes="(max-width: 1447px) 100vw, 1447px" /></figure>



<p>Now this is hard to see but above is the total output of our BGP session startup.  So let&#8217;s walk through these packets one at a time to make sure we know what&#8217;s going on&#8230;</p>



<figure class="wp-block-image size-full"><img decoding="async" width="857" height="441" src="https://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-1.png" alt="" class="wp-image-23476" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-1.png 857w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-1-300x154.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-1-200x103.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-1-768x395.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-1-624x321.png 624w" sizes="(max-width: 857px) 100vw, 857px" /></figure>



<p><strong>Packet 1</strong> &#8211; Packet one shows the host BIRD-1 (169.254.10.1) attempting to setup a TCP session to the host BIRD-0 (169.254.10.0) by sending a SYN packet.  This makes sense as we only stopped the BGP service on BIRD-0 so BIRD-1 will happily try to kick things off. </p>



<figure class="wp-block-image size-full is-resized"><img loading="lazy" decoding="async" width="808" height="463" src="https://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-2.png" alt="" class="wp-image-23477" style="width:798px;height:auto" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-2.png 808w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-2-300x172.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-2-200x115.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-2-768x440.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-2-624x358.png 624w" sizes="(max-width: 808px) 100vw, 808px" /></figure>



<p><strong>Packet 2</strong> &#8211; The host BIRD-0 sends a reset to BIRD-1. It does this because it&#8217;s not actually listening on the TCP 179 for BGP yet as the service is still down.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="864" height="443" src="https://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-3.png" alt="" class="wp-image-23478" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-3.png 864w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-3-300x154.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-3-200x103.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-3-768x394.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-3-624x320.png 624w" sizes="(max-width: 864px) 100vw, 864px" /></figure>



<p><strong>Packet 3</strong> &#8211; We restart the BIRD service on the host BIRD-0 and it immediately attempts to setup a TCP connection to the host BIRD-1 by sending a SYN packet. Let&#8217;s use this TCP session setup as a means to remind ourselves how the TCP three way handshake works. You&#8217;ll note that the sequence number in this SYN packet is 2120355052 which is a random 32 bit number chosen by the client. This initial sequence value is sometimes referred to as an &#8220;Initial Sequence Number&#8221; or ISN for short. Also worth noting is that the acknowledgement number in this packet is set to 0 which it should always be in the initial setup of a TCP session.</p>



<p><em>Note: Keep in mind that Wireshark is trying to be helpful which is why the &#8220;Sequence number&#8221; field is listed as 0.  It&#8217;s 0 relative to the first TCP segment seen.  To see the real sequence number you should be looking at the &#8220;raw&#8221; version of this field.  </em></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="850" height="460" src="https://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-4.png" alt="" class="wp-image-23479" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-4.png 850w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-4-300x162.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-4-200x108.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-4-768x416.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-4-624x338.png 624w" sizes="(max-width: 850px) 100vw, 850px" /></figure>



<p><strong>Packet 4</strong> &#8211; In packet 4 we see BIRD-1&#8217;s response to the SYN sent by BIRD-0.  Note that both the SYN and ACK flags are set and that the acknowledgement number is 2120355053 which is the sequence number received in the SYN packet from BIRD-0 (2120355052) plus 1.  In addition BIRD-1 has sent it&#8217;s own ISN of 147274070 in the reply to BIRD-0.  </p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="810" height="478" src="https://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-5.png" alt="" class="wp-image-23480" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-5.png 810w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-5-300x177.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-5-200x118.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-5-768x453.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-5-624x368.png 624w" sizes="(max-width: 810px) 100vw, 810px" /></figure>



<p><strong>Packet 5</strong> &#8211; In packet 5 we see the final ACK of the TCP session setup.  Note that the raw sequence number is the ISN show in packet 3 incremented by 1.  Also the acknowledgment number is the ISN sent in packet 4 incremented by 1.  In terms of TCP options we see that the ACK flag is also set.  While there&#8217;s a lot more going on at the TCP level (windowing etc) that we arent covering here we now have a setup TCP session.  </p>



<p><em>Note: It&#8217;s worth mentioning here that there are generally 6 phases of BGP session establishment. While checking on the status of a BGP peer you might see either Idle, Connect, Active, Open Sent, Open Confirm, or Established. The first 3 typically refer to the TCP session setup while the last 3 refer to the BGP session setup. I&#8217;ll note here that the &#8220;Active&#8221; state has led to more than one conversation around &#8220;Well I don&#8217;t know why you&#8217;re end is down &#8211; my end says it&#8217;s active&#8221; when turning up BGP peerings.</em></p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="800" height="425" src="https://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-6.png" alt="" class="wp-image-23481" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-6.png 800w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-6-300x159.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-6-200x106.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-6-768x408.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-6-624x332.png 624w" sizes="(max-width: 800px) 100vw, 800px" /></figure>



<p><strong>Packet 6</strong> &#8211; Our first BGP protocol packet! In this packet we see host BIRD-0&#8217;s attempt to start a BGP session. To do this the host originates a BGP open message. The BGP open message includes a surprising amount of information about the peers BGP configuration.  Let&#8217;s talk through it line by line&#8230;</p>



<p><strong>Marker</strong> &#8211; This one deserves a paragraph on it&#8217;s own which we&#8217;ll cover in a later post. For now just know that in a standard BGP open message this field is just filled with all 1s.</p>



<p><strong>Length</strong>: The Length of the BGP options which is 53 bytes. In other words the TCP payload of this frame is 53 bytes.</p>



<p><strong>Type</strong>: There are <a href="https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-1">defined types of BGP message types</a>. 1 happens to be the BGP Open message.</p>



<p><strong>Version</strong>: The BGP version being used. While many folks assume that version 4 implies that this is an IPv4 peering &#8211; that&#8217;s not actually true. It&#8217;s the actual version of the BGP protocol which means the previous version was in fact BGP-3. I would be stretching my memory if I took a guess at when BGP-4 became the defacto standard but Im thinking it was in the early 2000s.</p>



<p><strong>My AS</strong> &#8211; Just what it sounds like.  The Autonomous system this BGP speaker lives in.  </p>



<p><strong>Hold time </strong>&#8211; This describes the hold time for the BGP session. There is often confusion around this with BGP. There&#8217;s always a misconception around hold time and keep alive timers and if they need to match. The reality is that they dont need to match and the only thing that&#8217;s negotiated is the hold time. The hold time negotiation is really just the two peers picking the lower of the two values in the exchanged open messages. The keepalive is not negotiated and is assumed to be 1/3 of the hold time by default. That said &#8211; the keepalive can be manually set on each peer as well although I&#8217;d argue you&#8217;d have to have a very good reason to manually change the keep-alive timers. But it does mean that you can configure one peer to send keepalives every second and let the other use the default keepalive (which in BIRD is a hold time of 240 seconds yielding a keepalive of 80 seconds). The summary here being that hold times are negotiated and keepalives are assumed based on the hold time by default. They can be overriden in the peer configuration so each peer sends them at different rates.</p>



<p><strong>BGP Identifier</strong> &#8211; This is the peers router-ID. There is also general confusion around this. In most cases &#8211; a router-ID is configured to align with the peering IP address. This makes things easy to comprehend. The reality though is that a router-ID can be pretty much anything you want. Some operating systems mandate that the router-ID is one of the defined IP addresses on the routing system. They even go so far as to have logic that dynamically picks a router-ID based on the available IPs. That should be your first hint that there isn&#8217;t a strong binding between the peering relationship and the router-ID. They are only mandating that you have it configured as &#8220;a&#8221; IP address on the system. That could be anything! Now you might be wondering &#8211; what happens if the IPs overlap between peers? Turns out so long as it&#8217;s an eBGP peering that works! The only rule is that a router-ID can not overlap inside of a given AS. At the end of the day &#8211; the best approach is to align the router-ID with something that&#8217;s significant. For iBGP peerings likely the source transport loopback &#8211; for eBGP the peering interface IP address in the case of a single peer.</p>



<p><strong>Optional Parameters</strong> &#8211; These define the optional parameters which are typically a list of capabilities the peer can support. There can be other things than capabilities here &#8211; but the most common type of optional parameter is <a href="https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-11">type 2 (capabilities)</a>. We won&#8217;t dive into each capability listed in this open (there&#8217;s a complete reference <a href="https://www.iana.org/assignments/capability-codes/capability-codes.xhtml">here</a>) &#8211; but it&#8217;s important to know that peers need to align on the available capabilities. That is &#8211; if one peer supports something the other peer doesnt &#8211; that capability wont work across the peering. The capability negotiation (I use that term very very loosely here) is actually sort of a mess. Since the exchange of capabilities only occurs as part of the BGP open some BGP implementations will forcibly restart a session if you change your local capabilities. Others might expect you to know that you have to restart the session and will happily keep running without the new capability until you manually restart the session. <a href="https://blog.ipspace.net/2021/11/bgp-dynamic-capability/">Ivan Pepelnjak wrote an interesting post</a> about this awhile back and mentioned the draft RFC for capability exchange which appears to have still gone nowhere. Since we&#8217;re using BIRD I&#8217;ll note that if you change the capabilities and ask BIRD to refresh the configuration (<code>birdc configure</code>) the host will actually send a <a href="https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-1">BGP notification message (type 3)</a> <a href="https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-3">major code 6</a> <a href="https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-8">minor code 6</a>. Which if we reference our type codes from the links you&#8217;ll see it&#8217;s a BGP notification message for Cease because of configuration change. Put another way &#8211; BIRD will forcibly restart the session if the peer capabilities change. That said if there&#8217;s not consensus on a capability it&#8217;s just not used.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="808" height="478" src="https://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-7.png" alt="" class="wp-image-23483" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-7.png 808w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-7-300x177.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-7-200x118.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-7-768x454.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-7-624x369.png 624w" sizes="(max-width: 808px) 100vw, 808px" /></figure>



<p><strong>Packet 7</strong> &#8211; Packet 7 is simply host BIRD-1 ACKing the BGP open packet it got from BIRD-0. If you&#8217;re still playing the TCP game with me you&#8217;ll notice that the ACK number is the length of the open packet TCP segment (53) plus the initial 1 from the TCP session setup.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="843" height="424" src="https://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-8.png" alt="" class="wp-image-23484" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-8.png 843w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-8-300x151.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-8-200x101.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-8-768x386.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-8-624x314.png 624w" sizes="(max-width: 843px) 100vw, 843px" /></figure>



<p><strong>Packet 8</strong> &#8211; Now it&#8217;s host BIRD-1&#8217;s turn to send it&#8217;s BGP open message to BIRD-0.  These packets should look shockingly similar as they share almost identical configuration.  </p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="801" height="476" src="https://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-9.png" alt="" class="wp-image-23485" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-9.png 801w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-9-300x178.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-9-200x119.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-9-768x456.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-9-624x371.png 624w" sizes="(max-width: 801px) 100vw, 801px" /></figure>



<p><strong>Packet 9</strong> &#8211; Host BIRD-0 ACKing the open from BIRD-1. Again you can follow the TCP seq/ack numbers if you&#8217;re interested. A fun exercise if you&#8217;re so inclined on these smaller more manageable captures.</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="805" height="154" src="https://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-10.png" alt="" class="wp-image-23486" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-10.png 805w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-10-300x57.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-10-200x38.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-10-768x147.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-10-624x119.png 624w" sizes="(max-width: 805px) 100vw, 805px" /></figure>



<p><strong>Packet 10</strong> &#8211; BIRD-1 sends BIRD-0 a keepalive message.  While keep alive messages have a distinct use case in terms of keeping a BGP session alive this one is actually super important.  It signals to BIRD-0 that BIRD-1 has agreed to the BGP session (agreed to the terms in the open message) and acts as a sort of final ACK that we&#8217;re good to establigh the BGP peering.  After this initial keepalive future keepalive messages are used to keep the session open.   </p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="802" height="152" src="https://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-11.png" alt="" class="wp-image-23487" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-11.png 802w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-11-300x57.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-11-200x38.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-11-768x146.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-11-624x118.png 624w" sizes="(max-width: 802px) 100vw, 802px" /></figure>



<p><strong>Packet 11 </strong>&#8211; BIRD-0 sends BIRD-1 the keepalive ACK</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="813" height="484" src="https://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-12.png" alt="" class="wp-image-23488" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-12.png 813w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-12-300x179.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-12-200x119.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-12-768x457.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-12-624x371.png 624w" sizes="(max-width: 813px) 100vw, 813px" /></figure>



<p><strong>Packet 12</strong> &#8211; BIRD-0 ACKing the keepalive from BIRD-1</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="816" height="474" src="https://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-13.png" alt="" class="wp-image-23489" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-13.png 816w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-13-300x174.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-13-200x116.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-13-768x446.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-13-624x362.png 624w" sizes="(max-width: 816px) 100vw, 816px" /></figure>



<p><strong>Packet 13</strong> &#8211; BIRD-1 ACKing the keepalive from BIRD-0. The BGP session is now fully established!</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="808" height="189" src="https://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-14.png" alt="" class="wp-image-23490" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-14.png 808w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-14-300x70.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-14-200x47.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-14-768x180.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-14-624x146.png 624w" sizes="(max-width: 808px) 100vw, 808px" /></figure>



<p><strong>Packet 14</strong> &#8211; The next thing we see right after the session comes up is what I would describe as an empty update packet. We can see it&#8217;s a type <a href="https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-parameters-1">2 BGP message</a> which is an update. But it looks sort of desperately empty. If you&#8217;re thinking this makes sense because we aren&#8217;t advertising any routes yet you&#8217;d be mostly correct. But the reality is this is a very special kind of BGP update message that&#8217;s referred to as an &#8220;End of RIB marker&#8221;. While initially defined for use as part of the <a href="https://datatracker.ietf.org/doc/html/rfc4724">BGP graceful restart capabilit</a>y (we&#8217;ll talk about that in an upcoming post because it is some truly interesting signaling that takes place) it was proposed to be used as part of general<a href="https://tools.ietf.org/html/draft-dong-idr-end-of-rib-use-extension-00"> BGP convergence</a> and used in some other capabilities such route target constraints (another interesting use case we&#8217;ll eventually get to). In either case &#8211; you might have noticed that one of the default capabilities our peers have is &#8211; you guessed it &#8211; graceful restart! Since we negotiated that capability we use it which means that following the RFC which says&#8230;.</p>



<pre class="wp-block-preformatted">The Receiving Speaker MUST send the End-of-RIB marker once it
   completes the initial update for an address family (including the
   case that it has no routes to send) to the peer.</pre>



<p>So in our case we see in both packet 14 and 16 that each BGP speaker sends this End of RIB marker to other peer to indicate that it has no more updates to send.  But what makes this an End of RIB marker?  Specifically the fact that it&#8217;s an update that has 0 withdrawn paths and 0 path attribute length.  </p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="817" height="485" src="https://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-15.png" alt="" class="wp-image-23491" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-15.png 817w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-15-300x178.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-15-200x119.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-15-768x456.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-15-624x370.png 624w" sizes="(max-width: 817px) 100vw, 817px" /></figure>



<p><strong>Packet 15</strong> &#8211; ACKing the End of RIB marker from BIRD-0</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="818" height="190" src="https://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-16.png" alt="" class="wp-image-23492" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-16.png 818w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-16-300x70.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-16-200x46.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-16-768x178.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-16-624x145.png 624w" sizes="(max-width: 818px) 100vw, 818px" /></figure>



<p><strong>Packet 16</strong> &#8211; End of RIB marker from BIRD-1</p>



<figure class="wp-block-image size-full"><img loading="lazy" decoding="async" width="806" height="481" src="https://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-17.png" alt="" class="wp-image-23493" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-17.png 806w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-17-300x179.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-17-200x119.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-17-768x458.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2024/11/image-17-624x372.png 624w" sizes="(max-width: 806px) 100vw, 806px" /></figure>



<p><strong>Packet 17</strong> &#8211; ACKing the End of RIB marker from BIRD-1</p>



<p>So there you have it &#8211; a walk through of basic BGP session establishment.  In the next post we&#8217;re going to cover some other use cases around this basic BGP Peering before we move into the exciting world of actually exchanging routing information!</p>
]]></content:encoded>
					
					<wfw:commentRss>http://www.dasblinkenlichten.com/understanding-bgp-establishing-peering/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">23454</post-id>	</item>
		<item>
		<title>Packet Actions &#8211; Python and Scapy</title>
		<link>http://www.dasblinkenlichten.com/packet-actions-python-and-scapy/</link>
					<comments>http://www.dasblinkenlichten.com/packet-actions-python-and-scapy/#comments</comments>
		
		<dc:creator><![CDATA[Jon Langemak]]></dc:creator>
		<pubDate>Tue, 01 Jun 2021 12:58:28 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<guid isPermaLink="false">http://www.dasblinkenlichten.com/?p=23415</guid>

					<description><![CDATA[Hello and welcome to the &#8220;Packet Actions&#8221; series of blog posts. I&#8217;d like to spend a few posts talking through how you can programmatically integrate with a network dataplane. I... <a href="http://www.dasblinkenlichten.com/packet-actions-python-and-scapy/">Read more &#187;</a>]]></description>
										<content:encoded><![CDATA[
<p>Hello and welcome to the &#8220;Packet Actions&#8221; series of blog posts.  I&#8217;d like to spend a few posts talking through how you can programmatically integrate with a network dataplane.  I had thrown around the idea of calling this series &#8220;Doing things with packets&#8221; but that seemed a bit long and also could mean just about anything.  So what does Packet Actions mean?  Well &#8211; its the shortest way I could come up with to say &#8220;Looking at packets on the wire and doing things based on what you see in the packet&#8221;.  To discuss this further I&#8217;d like to talk about the often made analogy of network engineers being plumbers &#8211; an analogy that makes fairly good sense in most cases.  For instance, network engineers create the paths for data to flow &#8211; plumbers make paths for water to flow.  Additionally both need to make sure that there are no blockages or issues with handling the amount of data or water that needs to flow through the pipes.  Going a step further &#8211; plumbers might use a diagnostic tool like a scope to physically look inside the pipes if theres a blockage or issue so they can see what&#8217;s going on.  One could quite easily translate this to a network engineer using a network diagnostic tool such as TCPDump to inspect the flow of bits to see if we can spot an issue.  So all in all &#8211; the analogy makes pretty good sense.  </p>



<p>In this series of posts &#8211; I&#8217;d like to take things one further step and not only look at the packets &#8211; but also take actions based on them.  Im sure at this point someone will comment &#8220;We already do this!&#8221;.  Certainly what Im referring to is heading in the direction of network applications and that is a space that has seen an explosion in recent years.  But let&#8217;s back up a second &#8211; a network engineer certainly already does do this &#8211; after all we manage network applications like routing protocols, link management protocols, traffic monitoring tooling, and the list goes on.  We even have applications that look deeply into the packets and can do a myriad of things for us based on that information.  The problem is that this has largely been a feature of vendor provided products and tooling.  There&#8217;s nothing wrong with that &#8211; I have been happily watching these tools and products mature over the last few years.  But at the same time &#8211; there has been an explosion of functionality that is very (very, very, very) accessible to you as well.  What was once something that lived in the realm of vendors who did magical things in secret ASICs (or at least that&#8217;s what we were told) is now largely there for you to do yourself.  And that&#8217;s the point of this series &#8211; to show you how you can do some interesting things based on real packets on a real wire.  In each post in this series we&#8217;ll use different tools to build the same application in the hopes of exploring all of our options for interacting with real packets.</p>



<p>But before we get into the weeds &#8211; I&#8217;ll take a moment to pause here and say two more things.  First &#8211; just because you can do something doesn&#8217;t mean you should.  There are a couple of &#8220;packet actions&#8221; I will discuss that are really neat &#8211; but won&#8217;t scale to the point you need them to.  Are there ways to make them scale?  Sure &#8211; but that&#8217;s sort of the fun part about all of this &#8211; figuring out how things work and how you can apply them to real world problems.  The point of these posts is to just show you what&#8217;s possible.  The application we build will be largely useless but should give you and idea of what&#8217;s possible and what level of effort is required to make something that can scale and be resilient.  Second &#8211; Some of this is going to be totally new to me so you&#8217;ll need to bear with me.  As we progress through the posts I&#8217;ll be learning some of this as we go.  This includes new programming languages and new Linux kernel functions that I have only been playing with recently (sometimes because the functionality is so recent too!).  As always &#8211; if you spot something that&#8217;s wrong or could be better just let me know!  Feedback is much appreciated.</p>



<p>So now that we got the intro out of the way &#8211; let&#8217;s talk about what our packet action is going to be.  In this series we&#8217;re going to be building an application Im going to called &#8220;ping_return&#8221;.  What ping return does is simple.  If you ping an IP &#8211; it will do what&#8217;s required to facilitate that ping to work.  Something that looks like this&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="http://www.dasblinkenlichten.com/wp-content/uploads/2021/05/image.png" alt="" class="wp-image-23417" width="798" height="478" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2021/05/image.png 739w, http://www.dasblinkenlichten.com/wp-content/uploads/2021/05/image-300x180.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2021/05/image-200x120.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2021/05/image-624x374.png 624w" sizes="(max-width: 798px) 100vw, 798px" /></figure></div>



<p></p>



<p>That seems like an entirely useless application, which it is, but again the point here is to look at different ways in which we can look at the real packets on the wire and do &#8220;something&#8221; based on them.  So in our case &#8211; we want this flow to happen&#8230;.</p>



<ul class="wp-block-list"><li>Client has a default route pointed at the server address.</li><li>Client pings a random IP address which matches the default route</li><li>The server receives these ICMP request packets</li><li>Our application on the server see these ICMP request packets and adds an IP address matching the destination to a local interface</li><li>The client gets a ICMP return and things work!</li></ul>



<p>Should be easy right?  So let&#8217;s take a look at how to do this.  First thing first &#8211; let&#8217;s look at what our lab is going to look like for this&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter size-large is-resized"><img loading="lazy" decoding="async" src="http://www.dasblinkenlichten.com/wp-content/uploads/2021/05/image-3.png" alt="" class="wp-image-23423" width="828" height="171" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2021/05/image-3.png 739w, http://www.dasblinkenlichten.com/wp-content/uploads/2021/05/image-3-300x62.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2021/05/image-3-200x41.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2021/05/image-3-624x129.png 624w" sizes="(max-width: 828px) 100vw, 828px" /></figure></div>



<p><br>Perhaps the easiest lab we&#8217;ve ever had, but that&#8217;s OK &#8211; we want to focus on the packet actions not the lab topology.  The two servers we&#8217;re going to use here are straight Ubuntu 20 VMs.  Base installs &#8211; nothing fancy &#8211; we&#8217;ll work through the dependancies as we come across them.  So let&#8217;s get right into it!</p>



<p>As the title suggest, we&#8217;re going to use Scapy to accomplish our goals in this post.  Many of you may be familiar with <a href="https://scapy.net/">Scapy</a>, but for those who aren&#8217;t, it&#8217;s an open source Python library that lets you do all sorts of interesting things with packets.  While Im not an everyday user, I suspect that the majority of the use cases for Scapy are more around packet creation.  It seems to be a pretty popular tool to use in the security space for this very reason.  In any case, it has a rather unique function called <code>sniff</code> which can be used much like TCPDump in order to see real traffic on the wire.  But the first thing we need to do is to install it.  Since this is a blank fresh install of Ubuntu 20 &#8211; the first thing we need to do is to install PIP3 so we can install Python packages&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
apt-get update
apt-get install python3-pip
</pre></div>


<p>Now that we have PIP installed we can install the scapy module as follows&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
pip3 install scapy
</pre></div>


<p>Awesome, with the prereqs out of the way we can write a super simple script that shows us how the sniffing capability can work.  Create a new file called <code>ping_return.py</code> and put this code in it&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
import scapy.all as scapy
ens6_traffic = scapy.sniff(iface=&quot;ens6&quot;, count=5)
ens6_traffic.nsummary()
</pre></div>


<p>Now on your client box &#8211; start a ping to the server IP address.  Something like <code>ping 10.10.10.1</code> and just leave it running.  Now go back on the server and run the script you just created (<code>python3 ping_return.py1</code>) and wait a few seconds&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@server:~# python3 ping_return.py 
0000 Ether / IP / ICMP 10.10.10.0 &gt; 10.10.10.1 echo-request 0 / Raw
0001 Ether / IP / ICMP 10.10.10.1 &gt; 10.10.10.0 echo-reply 0 / Raw
0002 Ether / IP / ICMP 10.10.10.0 &gt; 10.10.10.1 echo-request 0 / Raw
0003 Ether / IP / ICMP 10.10.10.1 &gt; 10.10.10.0 echo-reply 0 / Raw
0004 Ether / IP / ICMP 10.10.10.0 &gt; 10.10.10.1 echo-request 0 / Raw
root@server:~# 
</pre></div>


<p>So that was easy!  Our second line of code simply told it to sniff on interface <code>ens6</code> and that it should only sniff 5 packets.  Then the third line of code said to simply print out a summary of those 5 packets.  Pretty cool huh?  Let&#8217;s look at a couple of other ways we can look at this data though before we move on.  To do that we&#8217;re going to use the <code>prn</code> function of the sniff to call a function for each packet the sniffer receives.  So let&#8217;s modify our script to be something like this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
import scapy.all as scapy

def process_packet(packet):
    packet.show()

ens6_traffic = scapy.sniff(iface=&quot;ens6&quot;, prn=process_packet, count=1)
</pre></div>


<p>So what&#8217;s changed here is that we&#8217;ve lowered our packet count to 1 (<code>count=1</code>) and we&#8217;ve also added a <code>prn</code> flag which references a function (<code>prn=process_packet</code>).  </p>



<p><em>Note:  If you&#8217;re like me and you&#8217;re wondering what the heck <code>prn</code> stands for there&#8217;s <a href="https://stackoverflow.com/questions/47577805/prn-parameter-in-scapy">an actual StackOverFlow question about it</a>.  At least Im not the only one who was wondering&#8230;</em></p>



<p>The <code>prn</code> flag tells the sniffer to call the defined function every time it receives a packet.  This may seem sort of counterintuitive considering we&#8217;ve also set a the count equal to 1 but all this does is tells Scapy to just take in one packet and then end.  So let&#8217;s give it a run&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@server:~# python3 ping_return.py 
###&#x5B; Ethernet ]### 
  dst       = 52:ab:54:cd:02:01
  src       = 52:ab:54:cd:01:01
  type      = IPv4
###&#x5B; IP ]### 
     version   = 4
     ihl       = 5
     tos       = 0x0
     len       = 84
     id        = 48642
     flags     = DF
     frag      = 0
     ttl       = 64
     proto     = icmp
     chksum    = 0x5492
     src       = 10.10.10.0
     dst       = 10.10.10.1
     \options   \
###&#x5B; ICMP ]### 
        type      = echo-request
        code      = 0
        chksum    = 0xdaae
        id        = 0x2
        seq       = 0x613b
        unused    = &#039;&#039;
###&#x5B; Raw ]### 
           load      = &#039;м\\xaf`\x00\x00\x00\x00o#\x0e\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !&quot;#$%&amp;\&#039;()*+,-./01234567&#039;

root@server:~# 

</pre></div>


<p>Very cool!  So we got a packet and we printed it out.  Looking at the packet we can see it is clearly split into 4 sections.  Ethernet, IP, ICMP, and RAW.  The relevant fields for all of these sections are also called out.  What&#8217;s cool about this is that we can actually reference all of these fields in our code!  There are two ways to do this.  First, we can do it by index.  For instance, say that we wanted to print out the destination ethernet address and destination IP address we could change our code to look like this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [5,6,7]; title: ; notranslate">
import scapy.all as scapy

def process_packet(packet):
    packet.show()
    destination_mac_address = packet&#x5B;0]&#x5B;0].dst
    destination_ip_address = packet&#x5B;0]&#x5B;1].dst
    print(&quot;Received a packet with a destination MAC address of %s and a destination IP address of %s&quot; % (destination_mac_address,destination_ip_address))

ens6_traffic = scapy.sniff(iface=&quot;ens6&quot;, prn=process_packet, count=1)
</pre></div>


<p>Notice that in addition to showing the packet summary, we are now also defining variables for the destination ethernet and IP address as well.  What&#8217;s interesting is how we&#8217;re pulling those values out of the packet variable.  Sort of looks like we&#8217;re referring to list indexes or something right?  What we&#8217;re actually doing here is referencing the header we want to look at.  The syntax <code>packet[0][0]</code> means look at the first packet (index 0) and the first header (in our case Ethernet).  </p>



<p><em>Note: You could for instance sniff 10 packets and then assign them to a variable.  In that case, you could reference that set of packets by index 0-9.  In our case since the <code>prn</code> function gets called each time we get a packet we&#8217;ll always be looking at index 0.  </em></p>



<p>That said &#8211; our packet summary looks a bit like this&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="731" height="669" src="http://www.dasblinkenlichten.com/wp-content/uploads/2021/05/image-4.png" alt="" class="wp-image-23426" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2021/05/image-4.png 731w, http://www.dasblinkenlichten.com/wp-content/uploads/2021/05/image-4-300x275.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2021/05/image-4-200x183.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2021/05/image-4-624x571.png 624w" sizes="(max-width: 731px) 100vw, 731px" /></figure></div>



<p><br>That makes pretty good sense &#8211; but it turns out you can also reference the sections by name.  For instance we could change the program to look like this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
import scapy.all as scapy

def process_packet(packet):
    packet.show()
    destination_mac_address = packet&#x5B;0]&#x5B;scapy.Ether].dst
    destination_ip_address = packet&#x5B;0]&#x5B;scapy.IP].dst
    print(&quot;Received a packet with a destination MAC address of %s and a destination IP address of %s&quot; % (destination_mac_address,destination_ip_address))

ens6_traffic = scapy.sniff(iface=&quot;ens6&quot;, prn=process_packet, count=1)
</pre></div>


<p>Notice how we&#8217;re now able to refer to the fields by name instead of by index?  Running this version of the program would provide the same results.  I myself dislike this approach for two reasons.  First, the variable names aren&#8217;t the same that as what shows up in the packet display.  AKA &#8211; Ether is not Ethernet.  Second &#8211; I like to import Scapy a certain way.  Notice how I do an <code>import scapy.all as scapy</code>?  If you look at lots of other examples you&#8217;ll see that it&#8217;s common to just do <code>from scapy.all import *</code>.  This allows you to just reference the different Scapy types just by name.  I tend to prefer to be rather explicit in the code so I can tell what comes from where.  This is truly a matter of &#8220;to each their own&#8221; so you&#8217;ll need to figure out what works best for you.  So for now, we&#8217;ll revert to using the index based approach.</p>



<p>So now we know how to get traffic off the wire and look at the values from each packet.  Now let&#8217;s talk about how to put some of this together to work toward our goal of ping return.  The first thing we need to do is figure out what kind of datagram we have coming in.  Let me show you what I mean.  Let&#8217;s update our program to look like this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
import scapy.all as scapy

def process_packet(packet):
    packet.show()
    destination_mac_address = packet&#x5B;0]&#x5B;0].dst
    destination_ip_address = packet&#x5B;0]&#x5B;1].dst
    icmp_type_code = packet&#x5B;0]&#x5B;2].type
    print(&quot;Received a packet with a destination MAC address of %s and a destination IP address of %s and ICMP type code of %s&quot; % (destination_mac_address,destination_ip_address, icmp_type_code))

ens6_traffic = scapy.sniff(iface=&quot;ens6&quot;, prn=process_packet)
</pre></div>


<p>We&#8217;ve made two big changes.  First, we removed the <code>count</code> command from the sniff instantiation.  This just means that our <code>prn</code> function will get every packet it picks up with no limit.  Second we added a new field to grab the ICMP type code from the packet.  Let&#8217;s give that a run&#8230;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101]; title: ; notranslate">
root@server:~# python3 ping_return.py 
###&#x5B; Ethernet ]### 
  dst       = 52:ab:54:cd:02:01
  src       = 52:ab:54:cd:01:01
  type      = IPv4
###&#x5B; IP ]### 
     version   = 4
     ihl       = 5
     tos       = 0x0
     len       = 84
     id        = 22559
     flags     = DF
     frag      = 0
     ttl       = 64
     proto     = icmp
     chksum    = 0xba75
     src       = 10.10.10.0
     dst       = 10.10.10.1
     \options   \
###&#x5B; ICMP ]### 
        type      = echo-request
        code      = 0
        chksum    = 0x6124
        id        = 0x2
        seq       = 0x6881
        unused    = &#039;&#039;
###&#x5B; Raw ]### 
           load      = &#039;Cį`\x00\x00\x00\x00s`\t\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !&quot;#$%&amp;\&#039;()*+,-./01234567&#039;

Received a packet with a destination MAC address of 52:ab:54:cd:02:01 and a destination IP address of 10.10.10.1 and ICMP type code of 8
###&#x5B; Ethernet ]### 
  dst       = 52:ab:54:cd:01:01
  src       = 52:ab:54:cd:02:01
  type      = IPv4
###&#x5B; IP ]### 
     version   = 4
     ihl       = 5
     tos       = 0x0
     len       = 84
     id        = 15055
     flags     = 
     frag      = 0
     ttl       = 64
     proto     = icmp
     chksum    = 0x17c6
     src       = 10.10.10.1
     dst       = 10.10.10.0
     \options   \
###&#x5B; ICMP ]### 
        type      = echo-reply
        code      = 0
        chksum    = 0x6924
        id        = 0x2
        seq       = 0x6881
        unused    = &#039;&#039;
###&#x5B; Raw ]### 
           load      = &#039;Cį`\x00\x00\x00\x00s`\t\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !&quot;#$%&amp;\&#039;()*+,-./01234567&#039;

Received a packet with a destination MAC address of 52:ab:54:cd:01:01 and a destination IP address of 10.10.10.0 and ICMP type code of 0
###&#x5B; Ethernet ]### 
  dst       = 52:ab:54:cd:01:01
  src       = 52:ab:54:cd:02:01
  type      = ARP
###&#x5B; ARP ]### 
     hwtype    = 0x1
     ptype     = IPv4
     hwlen     = 6
     plen      = 4
     op        = who-has
     hwsrc     = 52:ab:54:cd:02:01
     psrc      = 10.10.10.1
     hwdst     = 00:00:00:00:00:00
     pdst      = 10.10.10.0

Traceback (most recent call last):
  File &quot;/usr/local/lib/python3.8/dist-packages/scapy/packet.py&quot;, line 428, in __getattr__
    fld, v = self.getfield_and_val(attr)
  File &quot;/usr/local/lib/python3.8/dist-packages/scapy/packet.py&quot;, line 423, in getfield_and_val
    raise ValueError
ValueError

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File &quot;ping_return.py&quot;, line 10, in &lt;module&gt;
    ens6_traffic = scapy.sniff(iface=&quot;ens6&quot;, prn=process_packet)
  File &quot;/usr/local/lib/python3.8/dist-packages/scapy/sendrecv.py&quot;, line 1263, in sniff
    sniffer._run(*args, **kwargs)
  File &quot;/usr/local/lib/python3.8/dist-packages/scapy/sendrecv.py&quot;, line 1210, in _run
    session.on_packet_received(p)
  File &quot;/usr/local/lib/python3.8/dist-packages/scapy/sessions.py&quot;, line 108, in on_packet_received
    result = self.prn(pkt)
  File &quot;ping_return.py&quot;, line 6, in process_packet
    destination_ip_address = packet&#x5B;0]&#x5B;1].dst
  File &quot;/usr/local/lib/python3.8/dist-packages/scapy/packet.py&quot;, line 430, in __getattr__
    return self.payload.__getattr__(attr)
  File &quot;/usr/local/lib/python3.8/dist-packages/scapy/packet.py&quot;, line 428, in __getattr__
    fld, v = self.getfield_and_val(attr)
  File &quot;/usr/local/lib/python3.8/dist-packages/scapy/packet.py&quot;, line 1828, in getfield_and_val
    raise AttributeError(attr)
AttributeError: dst
root@server:~# 
</pre></div>


<p>Aha!  Can you sort out what happened?  We were assuming that every packet would have an ICMP header and we ended up capturing an ARP request.  The packet summary displayed fine, but when we tried to look at <code>packet[0][2].type</code> it bombed because in an ARP frame the 3rd header section doesn&#8217;t exist.  So we can fix this a couple of ways.  Let&#8217;s explore both of them.  First, we can add a filter as part of the sniff instantiation.  For instance, we could change our program to look like this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [10]; title: ; notranslate">
import scapy.all as scapy

def process_packet(packet):
    packet.show()
    destination_mac_address = packet&#x5B;0]&#x5B;0].dst
    destination_ip_address = packet&#x5B;0]&#x5B;1].dst
    icmp_type_code = packet&#x5B;0]&#x5B;2].type
    print(&quot;Received a packet with a destination MAC address of %s and a destination IP address of %s and ICMP type code of %s&quot; % (destination_mac_address,destination_ip_address, icmp_type_code))

ens6_traffic = scapy.sniff(iface=&quot;ens6&quot;, prn=process_packet, filter=&quot;icmp&quot;)
</pre></div>


<p>All we did was add on a <code>filter</code> variable and pass in <code>icmp</code>.  For those of you that are familiar with TCPDump filtering &#8211; this syntax will be largely the same.  This means that we&#8217;ll only be sniffing for ICMP packets which means our <code>prn</code> function will never receive any ARP or non-ICMP packets to start with.  While this is OK (it works), I sort of want to see the other packets we&#8217;re getting too so let&#8217;s try another approach&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [4,9,10]; title: ; notranslate">
import scapy.all as scapy

def process_packet(packet):
    if (packet.haslayer(scapy.ICMP)):
        destination_mac_address = packet&#x5B;0]&#x5B;0].dst
        destination_ip_address = packet&#x5B;0]&#x5B;1].dst
        icmp_type_code = packet&#x5B;0]&#x5B;2].type
        print(&quot;Received a packet with a destination MAC address of %s and a destination IP address of %s and ICMP type code of %s&quot; % (destination_mac_address,destination_ip_address, icmp_type_code))
    else:
        print(&quot;We received a non-ICMP packet&quot;)

ens6_traffic = scapy.sniff(iface=&quot;ens6&quot;, prn=process_packet)
</pre></div>


<p>Here we check the packet for a certain header (or as Scapy refers to them &#8220;layer&#8221;).  We check to see if the packet has an ICMP layer and if it does we run our logic.  If it does not &#8211; we just print a message saying we don&#8217;t have it.  Our output would look like this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@server:~# python3 ping_return.py 
Received a packet with a destination MAC address of 52:ab:54:cd:02:01 and a destination IP address of 10.10.10.1 and ICMP type code of 8
Received a packet with a destination MAC address of 52:ab:54:cd:01:01 and a destination IP address of 10.10.10.0 and ICMP type code of 0
Received a packet with a destination MAC address of 52:ab:54:cd:02:01 and a destination IP address of 10.10.10.1 and ICMP type code of 8
Received a packet with a destination MAC address of 52:ab:54:cd:01:01 and a destination IP address of 10.10.10.0 and ICMP type code of 0
Received a packet with a destination MAC address of 52:ab:54:cd:02:01 and a destination IP address of 10.10.10.1 and ICMP type code of 8
Received a packet with a destination MAC address of 52:ab:54:cd:01:01 and a destination IP address of 10.10.10.0 and ICMP type code of 0
Received a packet with a destination MAC address of 52:ab:54:cd:02:01 and a destination IP address of 10.10.10.1 and ICMP type code of 8
Received a packet with a destination MAC address of 52:ab:54:cd:01:01 and a destination IP address of 10.10.10.0 and ICMP type code of 0
Received a packet with a destination MAC address of 52:ab:54:cd:02:01 and a destination IP address of 10.10.10.1 and ICMP type code of 8
Received a packet with a destination MAC address of 52:ab:54:cd:01:01 and a destination IP address of 10.10.10.0 and ICMP type code of 0
Received a packet with a destination MAC address of 52:ab:54:cd:02:01 and a destination IP address of 10.10.10.1 and ICMP type code of 8
Received a packet with a destination MAC address of 52:ab:54:cd:01:01 and a destination IP address of 10.10.10.0 and ICMP type code of 0
We received a non-ICMP packet
We received a non-ICMP packet
Received a packet with a destination MAC address of 52:ab:54:cd:02:01 and a destination IP address of 10.10.10.1 and ICMP type code of 8
Received a packet with a destination MAC address of 52:ab:54:cd:01:01 and a destination IP address of 10.10.10.0 and ICMP type code of 0
Received a packet with a destination MAC address of 52:ab:54:cd:02:01 and a destination IP address of 10.10.10.1 and ICMP type code of 8
Received a packet with a destination MAC address of 52:ab:54:cd:01:01 and a destination IP address of 10.10.10.0 and ICMP type code of 0
Received a packet with a destination MAC address of 52:ab:54:cd:02:01 and a destination IP address of 10.10.10.1 and ICMP type code of 8
</pre></div>


<p>Cool!  But Im not satisfied yet.  I want to see what that packet was that wasn&#8217;t ICMP&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [11]; title: ; notranslate">
import scapy.all as scapy

def process_packet(packet):
    if (packet.haslayer(scapy.ICMP)):
        destination_mac_address = packet&#x5B;0]&#x5B;0].dst
        destination_ip_address = packet&#x5B;0]&#x5B;1].dst
        icmp_type_code = packet&#x5B;0]&#x5B;2].type
        print(&quot;Received a packet with a destination MAC address of %s and a destination IP address of %s and ICMP type code of %s&quot; % (destination_mac_address,destination_ip_address, icmp_type_code))
    else:
        print(&quot;We received a non-ICMP packet&quot;)
        print(packet.summary())

ens6_traffic = scapy.sniff(iface=&quot;ens6&quot;, prn=process_packet)
</pre></div>


<p>With this slight change we can get a quick view into the packet that wasn&#8217;t ICMP&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
^Croot@server:~# python3 ping_return.py 
Received a packet with a destination MAC address of 52:ab:54:cd:02:01 and a destination IP address of 10.10.10.1 and ICMP type code of 8
Received a packet with a destination MAC address of 52:ab:54:cd:01:01 and a destination IP address of 10.10.10.0 and ICMP type code of 0
We received a non-ICMP packet
Ether / ARP who has 10.10.10.0 says 10.10.10.1
We received a non-ICMP packet
Ether / ARP is at 52:ab:54:cd:01:01 says 10.10.10.0
Received a packet with a destination MAC address of 52:ab:54:cd:02:01 and a destination IP address of 10.10.10.1 and ICMP type code of 8
Received a packet with a destination MAC address of 52:ab:54:cd:01:01 and a destination IP address of 10.10.10.0 and ICMP type code of 0
Received a packet with a destination MAC address of 52:ab:54:cd:02:01 and a destination IP address of 10.10.10.1 and ICMP type code of 8
Received a packet with a destination MAC address of 52:ab:54:cd:01:01 and a destination IP address of 10.10.10.0 and ICMP type code of 0
Received a packet with a destination MAC address of 52:ab:54:cd:02:01 and a destination IP address of 10.10.10.1 and ICMP type code of 8
Received a packet with a destination MAC address of 52:ab:54:cd:01:01 and a destination IP address of 10.10.10.0 and ICMP type code of 0
Received a packet with a destination MAC address of 52:ab:54:cd:02:01 and a destination IP address of 10.10.10.1 and ICMP type code of 8
Received a packet with a destination MAC address of 52:ab:54:cd:01:01 and a destination IP address of 10.10.10.0 and ICMP type code of 0
</pre></div>


<p>And as expected &#8211; the non-ICMP packet was indeed an ARP request.   As one last tweak, what if we only want to look at ICMP echo requests?  That is, we don&#8217;t want to process the ICMP echo reply as well.  Simple, just check the type code!  We are already looking at it &#8211; so let&#8217;s add some logic to only look for <a href="https://en.wikipedia.org/wiki/Internet_Control_Message_Protocol">ICMP type code</a> of 8&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [6]; title: ; notranslate">
import scapy.all as scapy

def process_packet(packet):
    if (packet.haslayer(scapy.ICMP)):
        icmp_type_code = packet&#x5B;0]&#x5B;2].type
        if icmp_type_code == 8:
            src_ip_address = packet&#x5B;0]&#x5B;1].src
            dst_ip_address = packet&#x5B;0]&#x5B;1].dst
            print(&quot;Received an ICMP echo request from source %s with a destination of %s&quot; % (src_ip_address, dst_ip_address))
    else:
        print(&quot;We received a non-ICMP packet&quot;)
        print(packet.summary())

ens6_traffic = scapy.sniff(iface=&quot;ens6&quot;, prn=process_packet)
</pre></div>


<p>Quite a few changes here &#8211; but we&#8217;ve already talked about all of them.  We are checking to make sure the packet is ICMP, checking that the ICMP type is an echo request, and then grabbing the source and destination IP of out the IP layer (1).  Pretty straight forward right?  There actually isnt much left to do here in order to make this a ping return program.  All we need to do is configure the IP address that the client is trying to ping on an interface on the server.  To do that we&#8217;ll use <code>pyroute2</code> so let&#8217;s get that installed&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
pip3 install pyroute2
</pre></div>


<p>Now we can add a new function that will add an IP address to an interface&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [2,4,6,7,8,17]; title: ; notranslate">
import scapy.all as scapy
import pyroute2

ipr = pyroute2.IPRoute()

def add_interface_ip(interface_name, ip_address):
    interface_index_id = ipr.link_lookup(ifname=interface_name)&#x5B;0]
    ipr.addr(&quot;add&quot;, index=interface_index_id, address=ip_address, prefixlen=32)

def process_packet(packet):
    if (packet.haslayer(scapy.ICMP)):
        icmp_type_code = packet&#x5B;0]&#x5B;2].type
        if icmp_type_code == 8:
            src_ip_address = packet&#x5B;0]&#x5B;1].src
            dst_ip_address = packet&#x5B;0]&#x5B;1].dst
            print(&quot;Received an ICMP echo request from source %s with a destination of %s&quot; % (src_ip_address, dst_ip_address))
            add_interface_ip(&quot;ens6&quot;, dst_ip_address)
    else:
        print(&quot;We received a non-ICMP packet&quot;)
        print(packet.summary())

ens6_traffic = scapy.sniff(iface=&quot;ens6&quot;, prn=process_packet)
</pre></div>


<p>The biggest change in the above is our new function <code>add_interface_ip</code>.  Here we&#8217;re using <code>pyroute2</code> to send netlink commands to the kernel to configure a specific IP address on a specific interface.  Note that in the function we first need to get the link index of the interface we wish to add the IP address to.  Also note that the function returns a list so we really just want to grab the first (and only) item in that list.  This is then used as the <code>index</code> in the next command in which we add the IP address to the interface that matches the index.  Since these IP&#8217;s will all be individual I can safety hard code the mask to a <code>/32</code>.  So let&#8217;s run this new version on the server and see what happens&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [26]; title: ; notranslate">
root@server:~# python3 ping_return.py 
Received an ICMP echo request from source 10.10.10.0 with a destination of 10.10.10.1
Traceback (most recent call last):
  File &quot;ping_return.py&quot;, line 22, in &lt;module&gt;
    ens6_traffic = scapy.sniff(iface=&quot;ens6&quot;, prn=process_packet)
  File &quot;/usr/local/lib/python3.8/dist-packages/scapy/sendrecv.py&quot;, line 1263, in sniff
    sniffer._run(*args, **kwargs)
  File &quot;/usr/local/lib/python3.8/dist-packages/scapy/sendrecv.py&quot;, line 1210, in _run
    session.on_packet_received(p)
  File &quot;/usr/local/lib/python3.8/dist-packages/scapy/sessions.py&quot;, line 108, in on_packet_received
    result = self.prn(pkt)
  File &quot;ping_return.py&quot;, line 17, in process_packet
    add_interface_ip(&quot;ens6&quot;, dst_ip_address)
  File &quot;ping_return.py&quot;, line 8, in add_interface_ip
    ipr.addr(&quot;add&quot;, index=interface_index_id, address=ip_address, prefixlen=32)
  File &quot;/usr/local/lib/python3.8/dist-packages/pr2modules/iproute/linux.py&quot;, line 1518, in addr
    ret = self.nlm_request(msg,
  File &quot;/usr/local/lib/python3.8/dist-packages/pr2modules/netlink/nlsocket.py&quot;, line 391, in nlm_request
    return tuple(self._genlm_request(*argv, **kwarg))
  File &quot;/usr/local/lib/python3.8/dist-packages/pr2modules/netlink/nlsocket.py&quot;, line 882, in nlm_request
    for msg in self.get(msg_seq=msg_seq,
  File &quot;/usr/local/lib/python3.8/dist-packages/pr2modules/netlink/nlsocket.py&quot;, line 394, in get
    return tuple(self._genlm_get(*argv, **kwarg))
  File &quot;/usr/local/lib/python3.8/dist-packages/pr2modules/netlink/nlsocket.py&quot;, line 719, in get
    raise msg&#x5B;&#039;header&#039;]&#x5B;&#039;error&#039;]
pr2modules.netlink.exceptions.NetlinkError: (17, &#039;File exists&#039;)
root@server:~# 

</pre></div>


<p>Aha!  If you had left your client ping running &#8211; you should immediately see this error message.  The kernel doesnt want to let you add the same IP address to the same interface since it&#8217;s already there.  So how should we fix this?  The easiest thing to do would be to just not ping that IP.  So let&#8217;s stop the ping on the client and run the script again on the server.  Now on the client &#8211; try and ping a new IP &#8211; let&#8217;s say <code>1.1.1.1</code>&#8230;</p>



<p><em>Note: I assume that the client server has a default route pointing at the server</em></p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@client:~# ping 1.1.1.1 -c 5
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=2 ttl=64 time=0.509 ms
64 bytes from 1.1.1.1: icmp_seq=3 ttl=64 time=0.299 ms
64 bytes from 1.1.1.1: icmp_seq=4 ttl=64 time=0.180 ms
64 bytes from 1.1.1.1: icmp_seq=5 ttl=64 time=0.252 ms

--- 1.1.1.1 ping statistics ---
5 packets transmitted, 4 received, 20% packet loss, time 4102ms
rtt min/avg/max/mdev = 0.180/0.310/0.509/0.122 ms
root@client:~# 
</pre></div>


<p>Woohhoo it worked!  Note that we missed the first sequence number.  That&#8217;s to be expected in this case as the server needs to receive the first ICMP echo request and then add the IP address to it&#8217;s interface.  Unfortunately, if we look at the server we see we crashed again&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [27]; title: ; notranslate">
root@server:~# python3 ping_return.py 
Received an ICMP echo request from source 10.10.10.0 with a destination of 1.1.1.1
Received an ICMP echo request from source 10.10.10.0 with a destination of 1.1.1.1
Traceback (most recent call last):
  File &quot;ping_return.py&quot;, line 22, in &lt;module&gt;
    ens6_traffic = scapy.sniff(iface=&quot;ens6&quot;, prn=process_packet)
  File &quot;/usr/local/lib/python3.8/dist-packages/scapy/sendrecv.py&quot;, line 1263, in sniff
    sniffer._run(*args, **kwargs)
  File &quot;/usr/local/lib/python3.8/dist-packages/scapy/sendrecv.py&quot;, line 1210, in _run
    session.on_packet_received(p)
  File &quot;/usr/local/lib/python3.8/dist-packages/scapy/sessions.py&quot;, line 108, in on_packet_received
    result = self.prn(pkt)
  File &quot;ping_return.py&quot;, line 17, in process_packet
    add_interface_ip(&quot;ens6&quot;, dst_ip_address)
  File &quot;ping_return.py&quot;, line 8, in add_interface_ip
    ipr.addr(&quot;add&quot;, index=interface_index_id, address=ip_address, prefixlen=32)
  File &quot;/usr/local/lib/python3.8/dist-packages/pr2modules/iproute/linux.py&quot;, line 1518, in addr
    ret = self.nlm_request(msg,
  File &quot;/usr/local/lib/python3.8/dist-packages/pr2modules/netlink/nlsocket.py&quot;, line 391, in nlm_request
    return tuple(self._genlm_request(*argv, **kwarg))
  File &quot;/usr/local/lib/python3.8/dist-packages/pr2modules/netlink/nlsocket.py&quot;, line 882, in nlm_request
    for msg in self.get(msg_seq=msg_seq,
  File &quot;/usr/local/lib/python3.8/dist-packages/pr2modules/netlink/nlsocket.py&quot;, line 394, in get
    return tuple(self._genlm_get(*argv, **kwarg))
  File &quot;/usr/local/lib/python3.8/dist-packages/pr2modules/netlink/nlsocket.py&quot;, line 719, in get
    raise msg&#x5B;&#039;header&#039;]&#x5B;&#039;error&#039;]
pr2modules.netlink.exceptions.NetlinkError: (17, &#039;File exists&#039;)
root@server:~# 

</pre></div>


<p>Uggh.  Well at least it&#8217;s for the same reason!  We can see that when the first request came in all seemed to be well.  However when the second request came in we ran into the same issue.  So now that we know that the base premise works &#8211; let&#8217;s think about a better way of doing this.  </p>



<p>The base problem can be addressed by having the server keep track of what IPs it has already configured.  That seems easy enough so let&#8217;s tackle that first&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [4,18,19]; title: ; notranslate">
import scapy.all as scapy
import pyroute2

configured_ips = &#x5B;]
ipr = pyroute2.IPRoute()

def add_interface_ip(interface_name, ip_address):
    interface_index_id = ipr.link_lookup(ifname=interface_name)&#x5B;0]
    ipr.addr(&quot;add&quot;, index=interface_index_id, address=ip_address, prefixlen=32)

def process_packet(packet):
    if (packet.haslayer(scapy.ICMP)):
        icmp_type_code = packet&#x5B;0]&#x5B;2].type
        if icmp_type_code == 8:
            src_ip_address = packet&#x5B;0]&#x5B;1].src
            dst_ip_address = packet&#x5B;0]&#x5B;1].dst
            print(&quot;Received an ICMP echo request from source %s with a destination of %s&quot; % (src_ip_address, dst_ip_address))
            if dst_ip_address not in configured_ips:
                configured_ips.append(dst_ip_address)
                add_interface_ip(&quot;ens6&quot;, dst_ip_address)
    else:
        print(&quot;We received a non-ICMP packet&quot;)
        print(packet.summary())

ens6_traffic = scapy.sniff(iface=&quot;ens6&quot;, prn=process_packet)
</pre></div>


<p>To fix that we simply add a list of IPs we&#8217;ve configured and make sure that we don&#8217;t process the same IP twice.  Easy!  However the problem still remains even with this code that the IPs we configured in previous runs will still be there and cause the same problem for us.  We should probably write a clean up routine that checks for extra IPs and cleans them up before we start sniffing for traffic.  As part of this, I&#8217;d suggest we also move the configuration of the IPs we sniff to the loopback interface rather than the physical interface.  This will keep things much cleaner and not pollute the actual dataplane interfaces with extraneous IPs.  So let&#8217;s start with a cleanup routine.  We&#8217;ll create a function like this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
def cleanup_ips(interface_name):
    interface_index_id = ipr.link_lookup(ifname=interface_name)&#x5B;0]
    interface_addresses = ipr.get_addr(index=interface_index_id, family=2)
    for address in interface_addresses:
        ip = address.get_attrs(&quot;IFA_ADDRESS&quot;)&#x5B;0]
        if ip != &quot;127.0.0.1&quot;:
            print(&quot;Cleanup - Deleting IP address %s from interface %s&quot; % (ip, interface_name))
            delete_interface_ip(interface_name, ip)
</pre></div>


<p>So all we&#8217;re doing here is taking in an interface name and then using <code>pyroute2</code> to grab all of the IP addresses from that interface.  As before, we need to do this by link index so we have to grab that first.  Also by specifying <code>family=2</code> as part of the call we tell it to only return IPv4 addresses.  </p>



<p><em>Note: If you&#8217;re wondering how <code>2</code> equals the IPv4 family you need to look a little deeper at Linux.  The best place I can find this defined is <a href="https://students.mimuw.edu.pl/SO/Linux/Kod/include/linux/socket.h.html">here</a>.  Here you can see that <code>AF_INET</code> which is the IPv4 address family matches to a type of 2.  </em></p>



<p>The <code>pyroute2</code> call will return a list of IP addresses.  We then need to iterate through them and delete the ones that we no longer want.  Specifically we want to avoid deleting <code>127.0.0.1</code>.  The line  <code>ip = address.get_attrs("IFA_ADDRESS")[0]</code> might be slightly confusing.  If it is &#8211; it might help to look at the data that is returned from <code>pyroute2</code>.  You can see this yourself by simply adding a print statement for <code>address</code> at the beginning of the for loop.  If we did that &#8211; we might see this in the output&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
{&#039;family&#039;: 2, &#039;prefixlen&#039;: 32, &#039;flags&#039;: 128, &#039;scope&#039;: 0, &#039;index&#039;: 1, &#039;attrs&#039;: &#x5B;(&#039;IFA_ADDRESS&#039;, &#039;1.2.3.4&#039;), (&#039;IFA_LOCAL&#039;, &#039;1.2.3.4&#039;), (&#039;IFA_LABEL&#039;, &#039;lo&#039;), (&#039;IFA_FLAGS&#039;, 128), (&#039;IFA_CACHEINFO&#039;, {&#039;ifa_preferred&#039;: 4294967295, &#039;ifa_valid&#039;: 4294967295, &#039;cstamp&#039;: 23959062, &#039;tstamp&#039;: 23959062})], &#039;header&#039;: {&#039;length&#039;: 76, &#039;type&#039;: 20, &#039;flags&#039;: 2, &#039;sequence_number&#039;: 256, &#039;pid&#039;: 3992, &#039;error&#039;: None, &#039;target&#039;: &#039;localhost&#039;, &#039;stats&#039;: Stats(qsize=0, delta=0, delay=0)}, &#039;event&#039;: &#039;RTM_NEWADDR&#039;}
</pre></div>


<p>And now formatted more nicely&#8230;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
{
    &quot;family&quot;: 2,
    &quot;prefixlen&quot;: 32,
    &quot;flags&quot;: 128,
    &quot;scope&quot;: 0,
    &quot;index&quot;: 1,
    &quot;attrs&quot;: &#x5B;
        (&quot;IFA_ADDRESS&quot;, &quot;1.2.3.4&quot;),
        (&quot;IFA_LOCAL&quot;, &quot;1.2.3.4&quot;),
        (&quot;IFA_LABEL&quot;, &quot;lo&quot;),
        (&quot;IFA_FLAGS&quot;, 128),
        (
            &quot;IFA_CACHEINFO&quot;,
            {
                &quot;ifa_preferred&quot;: 4294967295,
                &quot;ifa_valid&quot;: 4294967295,
                &quot;cstamp&quot;: 23959062,
                &quot;tstamp&quot;: 23959062,
            },
        ),
    ],
    &quot;header&quot;: {
        &quot;length&quot;: 76,
        &quot;type&quot;: 20,
        &quot;flags&quot;: 2,
        &quot;sequence_number&quot;: 256,
        &quot;pid&quot;: 3992,
        &quot;error&quot;: None,
        &quot;target&quot;: &quot;localhost&quot;,
        &quot;stats&quot;: Stats(qsize=0, delta=0, delay=0),
    },
    &quot;event&quot;: &quot;RTM_NEWADDR&quot;,
}

</pre></div>


<p>You can see that there&#8217;s a lot of information here.  <code>pyroute2 </code>has these handy functions such as <code>get_attr</code> to help you grab some of this data.  So in our case &#8211; when we say <code>address.get_attrs("IFA_ADDRESS")[0]</code> we&#8217;re saying that we want to parse through the attributes for the base item (this address) and return the value for the key <code>"IFLA_ADDRESS"</code>.  And while it doesn&#8217;t look like it &#8211; that function will actually return a list hence the need to say <code>[0]</code> at the end.  It&#8217;s interesting to note that if you want the full address, that is the IP and the netmask, you need to grab the netmask from elsewhere.  The netmask is called <code>prefixlen</code> and is in the base object and not in the attribute section.  </p>



<p>Once we have that info &#8211; we call a function called <code>delete_interface_ip</code> which looks suspicously like the <code>add_interface_ip</code> function we already wrote&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
def delete_interface_ip(interface_name, ip_address):
    interface_index_id = ipr.link_lookup(ifname=interface_name)&#x5B;0]
    ipr.addr(&quot;delete&quot;, index=interface_index_id, address=ip_address, prefixlen=32)
</pre></div>


<p>Now if we put this all together our final script will look like this&#8230;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [39]; title: ; notranslate">
import scapy.all as scapy
import pyroute2

configured_ips = &#x5B;]
ipr = pyroute2.IPRoute()

def add_interface_ip(interface_name, ip_address):
    interface_index_id = ipr.link_lookup(ifname=interface_name)&#x5B;0]
    ipr.addr(&quot;add&quot;, index=interface_index_id, address=ip_address, prefixlen=32)

def delete_interface_ip(interface_name, ip_address):
    interface_index_id = ipr.link_lookup(ifname=interface_name)&#x5B;0]
    ipr.addr(&quot;delete&quot;, index=interface_index_id, address=ip_address, prefixlen=32)

def cleanup_ips(interface_name):
    interface_index_id = ipr.link_lookup(ifname=interface_name)&#x5B;0]
    interface_addresses = ipr.get_addr(index=interface_index_id, family=2)
    for address in interface_addresses:
        ip = address.get_attrs(&quot;IFA_ADDRESS&quot;)&#x5B;0]
        if ip != &quot;127.0.0.1&quot;:
            print(&quot;Cleanup - Deleting IP address %s from interface %s&quot; % (ip, interface_name))
            delete_interface_ip(interface_name, ip)

def process_packet(packet):
    if (packet.haslayer(scapy.ICMP)):
        icmp_type_code = packet&#x5B;0]&#x5B;2].type
        if icmp_type_code == 8:
            src_ip_address = packet&#x5B;0]&#x5B;1].src
            dst_ip_address = packet&#x5B;0]&#x5B;1].dst
            if dst_ip_address not in configured_ips:
                configured_ips.append(dst_ip_address)
                print(&quot;Received an ICMP echo request from source %s with a destination of %s&quot; % (src_ip_address, dst_ip_address))
                add_interface_ip(&quot;lo&quot;, dst_ip_address)
    else:
        print(&quot;We received a non-ICMP packet&quot;)
        print(packet.summary())


cleanup_ips(&quot;lo&quot;)
ens6_traffic = scapy.sniff(iface=&quot;ens6&quot;, prn=process_packet)
</pre></div>


<p>Beyond the check to make sure we don&#8217;t process an IP twice the only other new piece is calling our cleanup function before we start sniffing.  So let&#8217;s give this a try&#8230;</p>



<p><em>Note: Make sure to go and manually clean up any IPs you have on <code>ens6</code> from earlier testing</em></p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@client:~# ping 1.1.1.1 -c 5
PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data.
64 bytes from 1.1.1.1: icmp_seq=2 ttl=64 time=0.550 ms
64 bytes from 1.1.1.1: icmp_seq=3 ttl=64 time=0.254 ms
64 bytes from 1.1.1.1: icmp_seq=4 ttl=64 time=0.239 ms
64 bytes from 1.1.1.1: icmp_seq=5 ttl=64 time=0.277 ms

--- 1.1.1.1 ping statistics ---
5 packets transmitted, 4 received, 20% packet loss, time 4082ms
rtt min/avg/max/mdev = 0.239/0.330/0.550/0.127 ms
root@client:~# ping 2.2.2.2 -c 5
PING 2.2.2.2 (2.2.2.2) 56(84) bytes of data.
64 bytes from 2.2.2.2: icmp_seq=2 ttl=64 time=0.320 ms
64 bytes from 2.2.2.2: icmp_seq=3 ttl=64 time=0.256 ms
64 bytes from 2.2.2.2: icmp_seq=4 ttl=64 time=0.271 ms
64 bytes from 2.2.2.2: icmp_seq=5 ttl=64 time=0.241 ms

--- 2.2.2.2 ping statistics ---
5 packets transmitted, 4 received, 20% packet loss, time 4081ms
rtt min/avg/max/mdev = 0.241/0.272/0.320/0.029 ms
root@client:~# ping 3.3.3.3 -c 5
PING 3.3.3.3 (3.3.3.3) 56(84) bytes of data.
64 bytes from 3.3.3.3: icmp_seq=2 ttl=64 time=0.309 ms
64 bytes from 3.3.3.3: icmp_seq=3 ttl=64 time=0.256 ms
64 bytes from 3.3.3.3: icmp_seq=4 ttl=64 time=0.258 ms
64 bytes from 3.3.3.3: icmp_seq=5 ttl=64 time=0.316 ms

--- 3.3.3.3 ping statistics ---
5 packets transmitted, 4 received, 20% packet loss, time 4089ms
rtt min/avg/max/mdev = 0.256/0.284/0.316/0.027 ms
root@client:~# 
</pre></div>


<p>The client looks happy!  It seems it can ping any IP address it wants.  On the server side we see the log messages for things the sniffer picked up&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
Received an ICMP echo request from source 10.10.10.0 with a destination of 1.1.1.1
We received a non-ICMP packet
Ether / ARP who has 10.10.10.1 says 10.10.10.0
We received a non-ICMP packet
Ether / ARP is at 52:ab:54:cd:02:01 says 10.10.10.1
We received a non-ICMP packet
Ether / ARP who has 10.10.10.0 says 10.10.10.1
We received a non-ICMP packet
Ether / ARP is at 52:ab:54:cd:01:01 says 10.10.10.0
Received an ICMP echo request from source 10.10.10.0 with a destination of 2.2.2.2
Received an ICMP echo request from source 10.10.10.0 with a destination of 3.3.3.3
</pre></div>


<p>And now if we restart the server we should see it clean up those IPs as well&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@server:~# python3 ping_return.py 
Cleanup - Deleting IP address 1.1.1.1 from interface lo
Cleanup - Deleting IP address 2.2.2.2 from interface lo
Cleanup - Deleting IP address 3.3.3.3 from interface lo
</pre></div>


<p>Nice!  Now this still really isnt a great solution.  The only way this is working right now is because the source IP address of the ping is on a directly connected route for the server.  A better solution would be to add a static route pointing back out the sniffer interface for each source IP we see.  This would mean that the client could be many hops away and it should still work.  I&#8217;m not going to write that code in this blog,  but I can assure you it&#8217;s easy to do using <code>pyroute2</code> (wow &#8211; that rhymed.  That should be their slogan or something).  </p>



<p>So I hope you can see that performing actions based on packets on the wire isn&#8217;t hard to do.  This script is less than 40 lines of Python and we were already able to fulfill our requirements for the ping return application.  In the next post we&#8217;ll attempt the same thing but in a different programming language!  Stay tuned!</p>
]]></content:encoded>
					
					<wfw:commentRss>http://www.dasblinkenlichten.com/packet-actions-python-and-scapy/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">23415</post-id>	</item>
		<item>
		<title>Working with Linux VRFs</title>
		<link>http://www.dasblinkenlichten.com/working-with-linux-vrfs/</link>
					<comments>http://www.dasblinkenlichten.com/working-with-linux-vrfs/#comments</comments>
		
		<dc:creator><![CDATA[Jon Langemak]]></dc:creator>
		<pubDate>Wed, 28 Apr 2021 02:02:50 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<guid isPermaLink="false">http://www.dasblinkenlichten.com/?p=23391</guid>

					<description><![CDATA[The concept of VRFs is likely one that you&#8217;re familiar with. They are the de facto standard when we talk about isolating layer 3 networks. As we&#8217;ve talked about previously,... <a href="http://www.dasblinkenlichten.com/working-with-linux-vrfs/">Read more &#187;</a>]]></description>
										<content:encoded><![CDATA[
<p>The concept of VRFs is likely one that you&#8217;re familiar with.  They are the de facto standard when we talk about isolating layer 3 networks.  As <a href="http://www.dasblinkenlichten.com/mpls-labs-vrfs-and-mp-bgp/">we&#8217;ve talked about previously</a>, they are used extensively in applications such as MPLS VPNs and really provide the foundation for layer 3 network isolation.  They do this by allowing the creation of multiple routing tables.  Any layer 3 construct can then be mapped into the VRF.  For instance, I could assign an IP address to an interface and then map that interface into the VRF.  Likewise, I could configure a static route and specify that the route is part of a given VRF.  Going one step further I could establish a BGP session off of one of the VRF interfaces and receive remote BGP routes into the VRF.  VRFs are to layer 3 like VLANs are to layer 2.  </p>



<p>So while we&#8217;ve talked about how they are typically used and implemented on networking hardware like routers and switches &#8211; we haven&#8217;t talked about how they&#8217;re implemented in Linux.  Actually &#8211; they&#8217;re fairly new to the Linux space.  The functionality was actually written by Cumulus Networks and then contributed to the Linux kernel (kudos to them for doing that).  VRFs were introduced in kernel version 4.3 which I believe came to be around 2015.  That said &#8211; they were, in my opinion, a long time coming.  So what did folks do before VRFs were introduced?  Let&#8217;s talk about that for a moment.  </p>



<p>There were really 2 options available to you to create a VRF like construct in Linux prior to VRFs landing.  One option was to leverage multiple routing tables and policy routing to make something that looked VRF like.  I tried this once &#8211; I wouldn&#8217;t advise it &#8211; the shortcomings far outweighed the advantages and I&#8217;d argue you&#8217;d have a heck of a hard time getting that solution past any sort of audit around network isolation.  The far more popular option was to leverage network namespaces.  This become incredibly popular when containers came on the scene and began using network namespaces to manage container isolation.  But while network namespaces certainly work well &#8211; they are overkill for what we want.  Cumulus talks about this at length in <a href="https://cumulusnetworks.com/blog/vrf-for-linux/">their article about VRFs</a> so I won&#8217;t reiterate their points.  However it is worth calling out the major drawback at least once.  Network namespaces provide total and complete isolation for &#8220;all the things&#8221;.  In our case, &#8220;all the things&#8221; includes the entire network stack &#8211; devices, interfaces, ARP tables, route tables, etc.  Those of you familiar with how VRFs work know that&#8217;s not what we normally get with VRF functionality in a &#8220;normal&#8221; (I guess that&#8217;s what Im calling vendor provided) router.  This is obvious to see if we look at an example VRF configuration on a normal router.  For instance let&#8217;s look at a setup that looks like this&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter size-full is-resized"><img loading="lazy" decoding="async" src="http://www.dasblinkenlichten.com/wp-content/uploads/2021/04/image-11.png" alt="" class="wp-image-23406" width="794" height="379" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2021/04/image-11.png 591w, http://www.dasblinkenlichten.com/wp-content/uploads/2021/04/image-11-300x143.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2021/04/image-11-200x95.png 200w" sizes="(max-width: 794px) 100vw, 794px" /></figure></div>



<p><br>Above we have a single router that has two interfaces.  Interface <code>ge-0/0/0</code> is a member of vrf <code>vrf-1</code> while interface <code>ge-0/0/1</code> is a member of <code>vrf-2</code>.  We also have some static routes defined in each VRF.  Pretty straight forward right?  The configuration on the router might look something like this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 192.168.10.1/24
set interfaces ge-0/0/1 unit 0 family inet address 192.168.10.1/24

set routing-instances vrf-1 instance-type virtual-router
set routing-instances vrf-1 interface ge-0/0/0.0
set routing-instances vrf-1 routing-options static route 0.0.0.0/0 next-hop 192.168.10.254
set routing-instances vrf-1 routing-options static route 172.64.32.0/24 next-hop 192.168.10.101

set routing-instances vrf-2 instance-type virtual-router
set routing-instances vrf-2 interface ge-0/0/1.0
set routing-instances vrf-2 routing-options static route 0.0.0.0/0 next-hop 192.168.10.10
set routing-instances vrf-2 routing-options static route 192.168.128.0/24 next-hop 192.168.10.20
</pre></div>


<p><em>Note: In JunOS parlance a VRF that is local (Cisco used to call this VRF-lite) AKA one that is not used as part of a MPLS VPN and does not have a RD/RT assigned is of type &#8220;virtual-router&#8221; instead of type &#8220;vrf&#8221;.</em></p>



<p>Again &#8211; nothing crazy here in the configuration.  But let&#8217;s take a look at the routing tables etc.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vmx1.lab&gt; show route table vrf-1.inet.0    

vrf-1.inet.0: 4 destinations, 4 routes (4 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

0.0.0.0/0          *&#x5B;Static/5] 00:00:02
                    &gt; to 192.168.10.254 via ge-0/0/0.0
172.64.32.0/24     *&#x5B;Static/5] 00:00:02
                    &gt; to 192.168.10.101 via ge-0/0/0.0
192.168.10.0/24    *&#x5B;Direct/0] 00:01:10
                    &gt; via ge-0/0/0.0
192.168.10.1/32    *&#x5B;Local/0] 00:01:10
                      Local via ge-0/0/0.0

root@vmx1.lab&gt; show route table vrf-2.inet.0    

vrf-2.inet.0: 4 destinations, 4 routes (4 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

0.0.0.0/0          *&#x5B;Static/5] 00:00:06
                    &gt; to 192.168.10.10 via ge-0/0/1.0
192.168.10.0/24    *&#x5B;Direct/0] 00:01:14
                    &gt; via ge-0/0/1.0
192.168.10.1/32    *&#x5B;Local/0] 00:01:14
                      Local via ge-0/0/1.0
192.168.128.0/24   *&#x5B;Static/5] 00:00:06
                    &gt; to 192.168.10.20 via ge-0/0/1.0

root@vmx1.lab&gt; 
</pre></div>


<p>The important thing to call out here is that I need to tell the router I want to look at a specific VRFs routing table.  But what I don&#8217;t need to do is look inside the VRF context to see things that are common to the platform.  The most obvious case of this is the configuration I pasted above.  I have one global configuration that is used for all the VRFs.  Sure, I map things into specific VRFs but I have a single pane of glass view into all of that.  Even though my interfaces are now mapping into a VRF &#8211; I can still see them on the platform without any specific command to look inside the routing instance&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vmx1.lab&gt; show interfaces terse | grep ge-0/0/ 
ge-0/0/0                up    up
ge-0/0/0.0              up    up   inet     192.168.10.1/24 
ge-0/0/1                up    up
ge-0/0/1.0              up    up   inet     192.168.10.1/24 
ge-0/0/2                up    down
ge-0/0/3                up    down
ge-0/0/4                up    down
ge-0/0/5                up    down
ge-0/0/6                up    down
ge-0/0/7                up    down
ge-0/0/8                up    down
ge-0/0/9                up    down

root@vmx1.lab&gt; 
</pre></div>


<p>And perhaps more importantly &#8211; services that run on the router that aren&#8217;t L3 aware can just run once on the platform as a whole.  That is, I don&#8217;t need to run all of these services in all of the VRFs.  A great example of this is something like LLDP.  I&#8217;ve mapped my two &#8216;up&#8217; interfaces into VRFs, but LLDP is still just running on the platform and I can still see all of the LLDP neighbors etc without having to run the software or command in a given VRF&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vmx1.lab&gt; show lldp neighbors 
Local Interface    Parent Interface    Chassis Id          Port info          System Name
ge-0/0/1           -                   2c:6b:f5:4a:23:c0   ge-0/0/0           vmx3.lab            
ge-0/0/0           -                   2c:6b:f5:f9:08:c0   ge-0/0/1           vmx2.lab            

root@vmx1.lab&gt; 
</pre></div>


<p>If this still comes across as a weak argument to you, let&#8217;s consider the same scenario if we were to use network namespaces on Linux services to provide L3 isolation.  Let&#8217;s say we have a Linux server that looks like this..</p>



<div class="wp-block-image"><figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="591" height="282" src="http://www.dasblinkenlichten.com/wp-content/uploads/2021/04/image-12.png" alt="" class="wp-image-23407" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2021/04/image-12.png 591w, http://www.dasblinkenlichten.com/wp-content/uploads/2021/04/image-12-300x143.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2021/04/image-12-200x95.png 200w" sizes="(max-width: 591px) 100vw, 591px" /></figure></div>



<p>Configuring something like this might look like this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
ip netns add namespace-1
ip link set dev ens6 netns namespace-1
ip netns exec namespace-1 ip link set dev ens6 up
ip netns exec namespace-1 ip address add 192.168.10.1/24 dev ens6
ip netns exec namespace-1 ip route add 0.0.0.0/0 via 192.168.10.254
ip netns exec namespace-1 ip route add 172.64.32.0/24 via 192.168.10.101

ip netns add namespace-2
ip link set dev ens7 netns namespace-2
ip netns exec namespace-2 ip link set dev ens7 up
ip netns exec namespace-2 ip address add 192.168.10.1/24 dev ens7
ip netns exec namespace-2 ip route add 0.0.0.0/0 via 192.168.10.10
ip netns exec namespace-2 ip route add 192.168.128.0/24 via 192.168.10.20
</pre></div>


<p>Not too far off from where we were with the router.  We configure the namespace, add an interface to it, and then add other routes inside the namespace.  And we end up with something that looks pretty close to what we had before&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ip netns exec namespace-1 ip route show
default via 192.168.10.254 dev ens6 
172.64.32.0/24 via 192.168.10.101 dev ens6 
192.168.10.0/24 dev ens6 proto kernel scope link src 192.168.10.1 
root@vm1:~# ip netns exec namespace-2 ip route show
default via 192.168.10.10 dev ens7 
192.168.10.0/24 dev ens7 proto kernel scope link src 192.168.10.1 
192.168.128.0/24 via 192.168.10.20 dev ens7 
root@vm1:~# 
</pre></div>


<p>Now here&#8217;s where things begin to diverge quickly.  If I wanted to run something like LLDP on this server &#8211; how would I do that?  Let&#8217;s start by installing it and seeing what it sees&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# apt -y install lldpd
Reading package lists... Done
Building dependency tree       
Reading state information... Done
Suggested packages:
  snmpd
The following NEW packages will be installed:
  lldpd
0 upgraded, 1 newly installed, 0 to remove and 126 not upgraded.
Need to get 0 B/154 kB of archives.
After this operation, 511 kB of additional disk space will be used.
Selecting previously unselected package lldpd.
(Reading database ... 107391 files and directories currently installed.)
Preparing to unpack .../lldpd_1.0.4-1build2_amd64.deb ...
Unpacking lldpd (1.0.4-1build2) ...
Setting up lldpd (1.0.4-1build2) ...
Processing triggers for libc-bin (2.31-0ubuntu9.1) ...
Processing triggers for systemd (245.4-4ubuntu3.3) ...
Processing triggers for man-db (2.9.1-1) ...
root@vm1:~# 
root@vm1:~# lldpcli show interfaces
-------------------------------------------------------------------------------
LLDP interfaces:
-------------------------------------------------------------------------------
Interface:    ens3, via: unknown, Time: 0 day, 00:19:09
  Chassis:     
    ChassisID:    mac 52:ab:54:ab:01:01
    SysName:      vm1
    SysDescr:     Ubuntu 20.04.1 LTS Linux 5.9.4-050904-generic #202011042130 SMP Wed Nov 4 21:36:10 UTC 2020 x86_64
    MgmtIP:       192.168.127.1
    MgmtIP:       fe80::50ab:54ff:feab:101
    Capability:   Bridge, off
    Capability:   Router, off
    Capability:   Wlan, off
    Capability:   Station, on
  Port:        
    PortID:       mac 52:ab:54:ab:01:01
    PortDescr:    ens3
  TTL:          120
-------------------------------------------------------------------------------
root@vm1:~# lldpcli show neighbors
-------------------------------------------------------------------------------
LLDP neighbors:
-------------------------------------------------------------------------------
root@vm1:~# 
</pre></div>


<p>Notice that it doesn&#8217;t detect <code>ens6</code> or <code>ens7</code>, the two interfaces we moved into the namespaces.  But this makes total sense.  The interfaces are no longer in the global namespaces and simply can&#8217;t be seen.  Again &#8211; compare this to our router where we could still see the interfaces &#8211; we had just isolated the L3 constructs.  To get this to work, we&#8217;d actually need to run <code>lldpd</code> in each namespace.  Since by default <code>lldpd</code> uses the same socket we&#8217;d need to create unique sockets for each instance.  Something like this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ip netns exec namespace-1 /usr/sbin/lldpd -u /var/run/lldpd-namespace-1.socket
root@vm1:~# ip netns exec namespace-1 lldpcli -u /var/run/lldpd-namespace-1.socket show interfaces
-------------------------------------------------------------------------------
LLDP interfaces:
-------------------------------------------------------------------------------
Interface:    ens6, via: unknown, Time: 18739 days, 20:45:55
  Chassis:     
    ChassisID:    mac 52:ab:54:cd:01:01
    SysName:      vm1
    SysDescr:     Ubuntu 20.04.1 LTS Linux 5.9.4-050904-generic #202011042130 SMP Wed Nov 4 21:36:10 UTC 2020 x86_64
    MgmtIP:       192.168.10.1
    MgmtIP:       fe80::50ab:54ff:fecd:101
    Capability:   Bridge, off
    Capability:   Router, off
    Capability:   Wlan, off
    Capability:   Station, on
  Port:        
    PortID:       mac 52:ab:54:cd:01:01
    PortDescr:    ens6
  TTL:          120
-------------------------------------------------------------------------------
root@vm1:~# ip netns exec namespace-1 lldpcli -u /var/run/lldpd-namespace-1.socket show neighbors
-------------------------------------------------------------------------------
LLDP neighbors:
-------------------------------------------------------------------------------
Interface:    ens6, via: LLDP, RID: 1, Time: 0 day, 00:00:39
  Chassis:     
    ChassisID:    mac 52:ab:54:ab:02:01
    SysName:      vm2
    SysDescr:     Ubuntu 20.04.1 LTS Linux 5.9.4-050904-generic #202011042130 SMP Wed Nov 4 21:36:10 UTC 2020 x86_64
    MgmtIP:       192.168.127.2
    MgmtIP:       fe80::50ab:54ff:feab:201
    Capability:   Bridge, off
    Capability:   Router, off
    Capability:   Wlan, off
    Capability:   Station, on
  Port:        
    PortID:       mac 52:ab:54:cd:02:02
    PortDescr:    ens7
    TTL:          120
-------------------------------------------------------------------------------
root@vm1:~# 
</pre></div>


<p>So we can run another instance of <code>lldpd</code> in the namespace which allows us to see the local namespace interfaces and neighbors that are available out of those interfaces.  The same could be done for <code>namespace-2</code>&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ip netns exec namespace-2 /usr/sbin/lldpd -u /var/run/lldpd-namespace-2.socket
root@vm1:~# ip netns exec namespace-2 lldpcli -u /var/run/lldpd-namespace-2.socket show interfaces
-------------------------------------------------------------------------------
LLDP interfaces:
-------------------------------------------------------------------------------
Interface:    ens7, via: unknown, Time: 18739 days, 20:47:14
  Chassis:     
    ChassisID:    mac 52:ab:54:cd:01:02
    SysName:      vm1
    SysDescr:     Ubuntu 20.04.1 LTS Linux 5.9.4-050904-generic #202011042130 SMP Wed Nov 4 21:36:10 UTC 2020 x86_64
    MgmtIP:       192.168.10.1
    MgmtIP:       fe80::50ab:54ff:fecd:102
    Capability:   Bridge, off
    Capability:   Router, off
    Capability:   Wlan, off
    Capability:   Station, on
  Port:        
    PortID:       mac 52:ab:54:cd:01:02
    PortDescr:    ens7
  TTL:          120
-------------------------------------------------------------------------------
root@vm1:~# ip netns exec namespace-2 lldpcli -u /var/run/lldpd-namespace-2.socket show neighbors
-------------------------------------------------------------------------------
LLDP neighbors:
-------------------------------------------------------------------------------
Interface:    ens7, via: LLDP, RID: 1, Time: 0 day, 00:00:05
  Chassis:     
    ChassisID:    mac 52:ab:54:ab:03:01
    SysName:      vm3
    SysDescr:     Ubuntu 20.04.1 LTS Linux 5.9.4-050904-generic #202011042130 SMP Wed Nov 4 21:36:10 UTC 2020 x86_64
    MgmtIP:       192.168.127.3
    MgmtIP:       fe80::50ab:54ff:feab:301
    Capability:   Bridge, off
    Capability:   Router, off
    Capability:   Wlan, off
    Capability:   Station, on
  Port:        
    PortID:       mac 52:ab:54:cd:03:02
    PortDescr:    ens7
    TTL:          120
-------------------------------------------------------------------------------
root@vm1:~# 
</pre></div>


<p>Ok &#8211; but now we have 3 <code>lldpd</code> processes running&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ps -fC lldpd
UID          PID    PPID  C STIME TTY          TIME CMD
_lldpd      2304       1  0 12:53 ?        00:00:00 lldpd: monitor. 
_lldpd      2306    2304  0 12:53 ?        00:00:00 lldpd: no neighbor.
_lldpd      2501       1  0 12:53 ?        00:00:00 lldpd: monitor. 
_lldpd      2503    2501  0 12:53 ?        00:00:00 lldpd: connected to vm2.
_lldpd      2511       1  0 12:56 ?        00:00:00 lldpd: monitor. 
_lldpd      2513    2511  0 12:56 ?        00:00:00 lldpd: connected to vm3.
root@vm1:~# 
</pre></div>


<p>There&#8217;s totally no need to isolate things that don&#8217;t need to be isolated &#8211; the only thing we gain by doing that is creating more overhead on the router.  That said &#8211; I hope this shows that while namespaces do provide the necessary isolation &#8211; they are overkill for what we&#8217;re trying to achieve.  As Cumulus said <a href="https://cumulusnetworks.com/blog/vrf-for-linux/">in their article</a> &#8220;Network Namespace as a VRF? Just say No&#8221;.</p>



<p>So now that we&#8217;ve looked at the other possible options for solving this problem &#8211; let&#8217;s talk about using actual VRFs in Linux.  So same setup we&#8217;ve done above &#8211; but now with VRFs!</p>



<div class="wp-block-image"><figure class="aligncenter size-full"><img loading="lazy" decoding="async" width="591" height="282" src="http://www.dasblinkenlichten.com/wp-content/uploads/2021/04/image-13.png" alt="" class="wp-image-23408" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2021/04/image-13.png 591w, http://www.dasblinkenlichten.com/wp-content/uploads/2021/04/image-13-300x143.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2021/04/image-13-200x95.png 200w" sizes="(max-width: 591px) 100vw, 591px" /></figure></div>



<p><br>The initial implementation of VRFs might initially feel a little different than what you&#8217;re used to &#8211; but once you start using it I&#8217;d argue it makes good sense.  The nice thing about the VRF implementation in Linux is that it&#8217;s all done through existing tooling.  That&#8217;s huge!  So all we need is the existing <code>iproute2</code> packages which in most cases should already be on your system.  Once there &#8211; all you need to do to create a VRF is this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ip link add vrf-1 type vrf table 1
root@vm1:~# ip link set dev vrf-1 up
root@vm1:~# ip vrf
Name              Table
-----------------------
vrf-1                1
root@vm1:~# 
</pre></div>


<p>Easy!  So now we have a VRF &#8211; what do with it?  Well &#8211; we can add interfaces to the VRF quite easily&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ip link set dev ens6 master vrf-1
root@vm1:~# ip addr add 192.168.10.1/24 dev ens6
root@vm1:~# 
root@vm1:~# ip route show vrf vrf-1
192.168.127.0/24 dev ens6 proto kernel scope link src 192.168.127.1 
root@vm1:~# 
</pre></div>


<p>Above we add <code>ens6</code> to the VRF <code>vrf-1</code> and then we add an IP address to it.  Then we can look at the <code> vrf-1</code> routing table using the <code>ip route show vrf</code> syntax.  I&#8217;ll also point out that since the interface <code>ens6</code> is now in the VRF it does not mean it&#8217;s gone from the global namespace as was the case with namespaces&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ip -br link
lo               UNKNOWN        00:00:00:00:00:00 &lt;LOOPBACK,UP,LOWER_UP&gt; 
ens3             UP             52:ab:54:ab:01:01 &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; 
ens6             UP             52:ab:54:cd:01:01 &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; 
ens7             UP             52:ab:54:cd:01:02 &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; 
vrf-1            UP             4e:19:81:5c:ad:ee &lt;NOARP,MASTER,UP,LOWER_UP&gt; 
root@vm1:~# 
</pre></div>


<p>So now that we have an interface in the VRF &#8211; we can add static routes&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ip route add 0.0.0.0/0 via 192.168.127.254 vrf vrf-1
root@vm1:~# ip route add 172.64.32.0/24 via 192.168.127.101 vrf vrf-1
root@vm1:~# ip route show vrf vrf-1
default via 192.168.127.254 dev ens6 
172.64.32.0/24 via 192.168.127.101 dev ens6 
192.168.127.0/24 dev ens6 proto kernel scope link src 192.168.127.1 
root@vm1:~# 

</pre></div>


<p>Let&#8217;s now configure <code>vrf-2</code> all in one shot&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
ip link add vrf-2 type vrf table 2
ip link set dev vrf-2 up
ip link set dev ens7 master vrf-2
ip addr add 192.168.127.1/24 dev ens7
ip route add 0.0.0.0/0 via 192.168.127.10 vrf vrf-2
ip route add 192.168.128.0/24 via 192.168.127.20 vrf vrf-2
</pre></div>


<p>So now that we have the full configuration &#8211; lets back up for a second.  When we created the VRF &#8211; it looked a lot like we were creating another network device.  In fact we were and the device that was created is sometimes called a &#8220;layer 3 master device&#8221; (l3mdev for short).  This device acts like a sort of central point that you can connect all of the VRF interfaces to.  The process of adding interfaces to the VRF might have looked familiar to you if you&#8217;ve worked with Linux bridges before.  If so &#8211; it&#8217;s because it&#8217;s done in the exact same way!  When they designed the VRF functionality they modeled it in the same way that you add devices to a bridge domain.  The mapping even looks the same if we look at the interfaces&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ip -d link show dev ens6
3: ens6: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc mq master vrf-1 state UP mode DEFAULT group default qlen 1000
    link/ether 52:ab:54:cd:01:01 brd ff:ff:ff:ff:ff:ff promiscuity 0 minmtu 68 maxmtu 65535 
    vrf_slave table 1 addrgenmode eui64 numtxqueues 2 numrxqueues 2 gso_max_size 65536 gso_max_segs 65535 
    altname enp0s6
root@vm1:~# 
</pre></div>


<p>See how it shows that the master is <code>vrf-1</code> and in the details we can the VRF table number as well?  Not only does this make it feel more natural to folks already familiar with the concepts &#8211; but it also means that you can do some interesting things with the VRF device.  For instance, what if you want to see traffic in your VRF?  No problem &#8211; just do a TCPDUMP on the VRF interface&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# tcpdump -nnel -i vrf-1
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on vrf-1, link-type EN10MB (Ethernet), capture size 262144 bytes
21:04:59.140107 42:f6:86:4a:a6:17 &gt; 00:00:00:00:00:00, ethertype IPv4 (0x0800), length 98: 192.168.10.1 &gt; 192.168.10.254: ICMP echo request, id 2, seq 466, length 64
21:04:59.140575 52:ab:54:cd:02:02 &gt; 52:ab:54:cd:01:01, ethertype IPv4 (0x0800), length 98: 192.168.10.254 &gt; 192.168.10.1: ICMP echo reply, id 2, seq 466, length 64
21:05:00.164085 42:f6:86:4a:a6:17 &gt; 00:00:00:00:00:00, ethertype IPv4 (0x0800), length 98: 192.168.10.1 &gt; 192.168.10.254: ICMP echo request, id 2, seq 467, length 64
21:05:00.164390 52:ab:54:cd:02:02 &gt; 52:ab:54:cd:01:01, ethertype IPv4 (0x0800), length 98: 192.168.10.254 &gt; 192.168.10.1: ICMP echo reply, id 2, seq 467, length 64
21:05:01.188086 42:f6:86:4a:a6:17 &gt; 00:00:00:00:00:00, ethertype IPv4 (0x0800), length 98: 192.168.10.1 &gt; 192.168.10.254: ICMP echo request, id 2, seq 468, length 64
21:05:01.188320 52:ab:54:cd:02:02 &gt; 52:ab:54:cd:01:01, ethertype IPv4 (0x0800), length 98: 192.168.10.254 &gt; 192.168.10.1: ICMP echo reply, id 2, seq 468, length 64
</pre></div>


<p>Same thing works for ping &#8211; we can just set the source interface to the VRF device name and&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ping -I vrf-1 192.168.10.254
ping: Warning: source address might be selected on device other than: vrf-1
PING 192.168.10.254 (192.168.10.254) from 192.168.10.1 vrf-1: 56(84) bytes of data.
64 bytes from 192.168.10.254: icmp_seq=1 ttl=64 time=0.250 ms
64 bytes from 192.168.10.254: icmp_seq=2 ttl=64 time=0.253 ms
64 bytes from 192.168.10.254: icmp_seq=3 ttl=64 time=0.444 ms
</pre></div>


<p>We can also add IP addresses to the VRF interface itself&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ip addr add 1.1.1.1/32 dev vrf-1
root@vm1:~# ip route get vrf vrf-1 1.1.1.1
local 1.1.1.1 dev vrf-1 table 1 src 1.1.1.1 uid 0 
    cache &lt;local&gt; 
root@vm1:~# 
</pre></div>


<p>This IP can be used as you would a loopback interface in the VRF.  </p>



<p>So at this point we&#8217;ve shown how you can create and use VRFs &#8211; but we haven&#8217;t really talked about how we actually land traffic into the VRF.  We know that when we created the VRF we had to assign it a routing table number.  When dealing with routing tables we generally need to make some rule sets to handle them so let&#8217;s take a look at the existing IP rules&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ip rule show
0:	from all lookup local
1000:	from all lookup &#x5B;l3mdev-table]
32766:	from all lookup main
32767:	from all lookup default
root@vm1:~# 
</pre></div>


<p>Notice rule 1000.  Now look at a different box that you haven&#8217;t configured any VRFs on&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm2:~# ip rule show
0:	from all lookup local
32766:	from all lookup main
32767:	from all lookup default
root@vm2:~# 
</pre></div>


<p>Notice that rule 1000 isn&#8217;t present in the box with no VRFs defined.  Rule 1000 is created when a VRF is configured and it&#8217;s what allows the VRF lookups to magically work.  But let&#8217;s dig in here for a minute.  What does the IP rule set on <code>vm1</code> actually do?  More importantly, what does rule 0 do that is listed before our magic VRF lookup rule?</p>



<p>Rule 0 exists by default everywhere and provides local lookups.  So what does that mean?</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ip route show table local
broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1 
local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1 
local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1 
broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1 
broadcast 192.168.127.0 dev ens3 proto kernel scope link src 192.168.127.1 
local 192.168.127.1 dev ens3 proto kernel scope host src 192.168.127.1 
broadcast 192.168.127.255 dev ens3 proto kernel scope link src 192.168.127.1 
root@vm1:~# 
</pre></div>


<p>This table includes all of the local lookups.  That is &#8211; routes for directly connected or local interfaces.  You&#8217;ll note a lack of any routes for prefixes or interfaces that exist in either of our VRFs.  Makes sense &#8211; VRFs are a layer 3 construct so we wouldn&#8217;t want routes , even local ones, for the VRF in a table they don&#8217;t belong.  The catch here though is how this rule processing happens.  We have a total of 4 rules&#8230;</p>



<ul class="wp-block-list"><li>Rule 0 &#8211; Local Lookups</li><li>Rule 1000 &#8211; L3MDEV (Our magic VRF lookup rule)</li><li>Rule 32766 &#8211; The main table lookup (default table)</li><li>Rule 32767 &#8211; The &#8220;default&#8221; table lookup</li></ul>



<p>Rules are processed in order lower to higher.  So any time we need to do a route lookup &#8211; this processing happens until we find a match regardless of where the lookup is happening.  This is why it&#8217;s important that the kernel creates rule 1000 to do VRF lookups before we do a lookup in the main table.  Some of you might already be seeing a problem with this &#8211; but let&#8217;s table that for now while we talk about how rule 1000 works.  </p>



<p>So what would happen if rule 1000 wasn&#8217;t there?  Currently if we do a ping in our VRF out of one our VRF interfaces this works&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ping -I vrf-1 192.168.10.254
ping: Warning: source address might be selected on device other than: vrf-1
PING 192.168.10.254 (192.168.10.254) from 192.168.10.1 vrf-1: 56(84) bytes of data.
64 bytes from 192.168.10.254: icmp_seq=1 ttl=64 time=0.437 ms
64 bytes from 192.168.10.254: icmp_seq=2 ttl=64 time=0.260 ms
64 bytes from 192.168.10.254: icmp_seq=3 ttl=64 time=0.186 ms
</pre></div>


<p>Now let&#8217;s delete rule 1000 and try again&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ip rule del pref 1000
root@vm1:~# ping -I vrf-1 192.168.10.254 -c 2
ping: Warning: source address might be selected on device other than: vrf-1
PING 192.168.10.254 (192.168.10.254) from 192.168.127.1 vrf-1: 56(84) bytes of data.

--- 192.168.10.254 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1017ms

root@vm1:~# 
</pre></div>


<p>No dice.  So let&#8217;s do a route lookup in the VRF and see what&#8217;s happening&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ip route get vrf vrf-1 192.168.10.254
192.168.10.254 via 192.168.127.100 dev ens3 src 192.168.127.1 uid 0 
    cache 
root@vm1:~# ip route
default via 192.168.127.100 dev ens3 proto static 
10.20.30.0/24 via 192.168.127.100 dev ens3 proto static 
192.168.127.0/24 dev ens3 proto kernel scope link src 192.168.127.1 
root@vm1:~# 
</pre></div>


<p>So as expected &#8211; the route lookup is falling into the main table and coming back with a route that isn&#8217;t in our VRF.  Rule 1000, the l3mdev lookup rule, solved this for us by telling the kernel to always do VRF route table lookups for us.  However &#8211; rule 1000 is strictly a convenience that covers all of the VRF table lookups for us.  If we wanted to we could manually add the rules for a given VRF as well&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ip rule add oif vrf-1 table 1
root@vm1:~# ip rule add iif vrf-1 table 1
root@vm1:~# ip route get vrf vrf-1 192.168.10.254
192.168.10.254 dev ens6 table 1 src 192.168.10.1 uid 0 
    cache 
root@vm1:~# ping -I vrf-1 192.168.10.254 -c 2
ping: Warning: source address might be selected on device other than: vrf-1
PING 192.168.10.254 (192.168.10.254) from 192.168.10.1 vrf-1: 56(84) bytes of data.
64 bytes from 192.168.10.254: icmp_seq=1 ttl=64 time=0.447 ms
64 bytes from 192.168.10.254: icmp_seq=2 ttl=64 time=0.275 ms

--- 192.168.10.254 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1005ms
rtt min/avg/max/mdev = 0.275/0.361/0.447/0.086 ms
root@vm1:~# 
</pre></div>


<p>Above we add two rules that say &#8220;if out of interface <code>vrf1</code> then lookup in table 1&#8243; as well as &#8220;if in interface <code>vrf-1</code> then lookup in table 1&#8243;.  So while you could certainly add these rules for each VRF you create it&#8217;s easier to just let the default l3mdev rule take care of this for you.  When you have a rule that points the lookup to <code>l3mdev-table</code> it covers the bases for all of the l3mdev (VRF interfaces).  So let&#8217;s go ahead and put that rule back and clean up our other two rules&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ip rule show
0:	from all lookup local
32764:	from all iif vrf-1 lookup 1
32765:	from all oif vrf-1 lookup 1
32766:	from all lookup main
32767:	from all lookup default
root@vm1:~# ip rule del pref 32764
root@vm1:~# ip rule del pref 32765
root@vm1:~# ip rule add l3mdev pref 1000
root@vm1:~# ip rule show
0:	from all lookup local
1000:	from all lookup &#x5B;l3mdev-table]
32766:	from all lookup main
32767:	from all lookup default
root@vm1:~# 
</pre></div>


<p>Now this all seems to be working as expected &#8211; but let&#8217;s get back to the possible issues I hinted at earlier.  As I mentioned, the rule lookup happens from lower to higher preference.  So the first rule that will be evaluated will be rule 0.  Remember &#8211; rule 0 is our table for all of the local or directly connected interfaces.  So what happens when our VRF has a route it&#8217;s trying to reach which is also directly connected say in the global table?&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ping -I vrf-1 192.168.10.254 -c 2
ping: Warning: source address might be selected on device other than: vrf-1
PING 192.168.10.254 (192.168.10.254) from 192.168.10.1 vrf-1: 56(84) bytes of data.
64 bytes from 192.168.10.254: icmp_seq=1 ttl=64 time=0.368 ms
64 bytes from 192.168.10.254: icmp_seq=2 ttl=64 time=0.322 ms

--- 192.168.10.254 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1031ms
rtt min/avg/max/mdev = 0.322/0.345/0.368/0.023 ms
root@vm1:~# ip addr add 192.168.10.254/32 dev lo
root@vm1:~# ping -I vrf-1 192.168.10.254 -c 2
ping: Warning: source address might be selected on device other than: vrf-1
PING 192.168.10.254 (192.168.10.254) from 192.168.10.254 vrf-1: 56(84) bytes of data.

--- 192.168.10.254 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1030ms
root@vm1:~# 
</pre></div>


<p>No worky.  And if we check the routes&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ip route get vrf vrf-1 192.168.10.254
local 192.168.10.254 dev lo table local src 192.168.10.254 uid 0 
    cache &lt;local&gt; 
root@vm1:~# 
</pre></div>


<p>As expected, the route lookup is getting caught in the table local lookup and not making it&#8217;s way to the l3mdev rule.  To fix this &#8211; it&#8217;s usually recommended to reorder the rules like so&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ip -4 rule add pref 32765 table local
root@vm1:~# ip -4 rule del pref 0
root@vm1:~# ip rule show
1000:	from all lookup &#x5B;l3mdev-table]
32765:	from all lookup local
32766:	from all lookup main
32767:	from all lookup default
root@vm1:~# 
root@vm1:~# ping -I vrf-1 192.168.10.254 -c 2
ping: Warning: source address might be selected on device other than: vrf-1
PING 192.168.10.254 (192.168.10.254) from 192.168.10.1 vrf-1: 56(84) bytes of data.
64 bytes from 192.168.10.254: icmp_seq=1 ttl=64 time=0.564 ms
64 bytes from 192.168.10.254: icmp_seq=2 ttl=64 time=0.293 ms

--- 192.168.10.254 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1030ms
rtt min/avg/max/mdev = 0.293/0.428/0.564/0.135 ms
root@vm1:~# 
</pre></div>


<p>This moves the local lookup to after rule 1000 so the VRF lookups can always occur first.  Problem solved!</p>



<p>Now that we&#8217;ve gone through how they work, let&#8217;s talk about a couple of extra sort of handy commands to use when working with VRFs&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ip -br addr show master vrf-1
ens6             UP             192.168.10.1/24 fe80::50ab:54ff:fecd:101/64 
root@vm1:~# 

</pre></div>


<p>If you want to see all of the IP interfaces in a given VRF you can use the <code>show master</code> version of the <code>ip addr</code> command to just list the addresses for a given VRF.  Likewise to see all of the links in a given VRF you can use the same syntax with the <code>ip link</code> command&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@vm1:~# ip link show master vrf-1
3: ens6: &lt;BROADCAST,MULTICAST,UP,LOWER_UP&gt; mtu 1500 qdisc mq master vrf-1 state UP mode DEFAULT group default qlen 1000
    link/ether 52:ab:54:cd:01:01 brd ff:ff:ff:ff:ff:ff
    altname enp0s6
root@vm1:~# 
</pre></div>


<p>This should give you a good idea on how to get up and running with Linux VRFs.  If nothing else, it should be a good reminder as to why VRFs are valuable.  They provide a super lightweight construct for L3 isolation while still allowing commons services to run at other layers which helps keep the overhead down.  </p>
]]></content:encoded>
					
					<wfw:commentRss>http://www.dasblinkenlichten.com/working-with-linux-vrfs/feed/</wfw:commentRss>
			<slash:comments>9</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">23391</post-id>	</item>
		<item>
		<title>Python Pieces: Decorators</title>
		<link>http://www.dasblinkenlichten.com/python-pieces-decorators/</link>
					<comments>http://www.dasblinkenlichten.com/python-pieces-decorators/#comments</comments>
		
		<dc:creator><![CDATA[Jon Langemak]]></dc:creator>
		<pubDate>Sun, 01 Nov 2020 15:11:29 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">http://www.dasblinkenlichten.com/?p=23380</guid>

					<description><![CDATA[As some of you know &#8211; Im a big believer that we all learn differently. You may read something the first time and immediately grasp the topic whereas I may... <a href="http://www.dasblinkenlichten.com/python-pieces-decorators/">Read more &#187;</a>]]></description>
										<content:encoded><![CDATA[
<p>As some of you know &#8211; Im a big believer that we all learn differently.  You may read something the first time and immediately grasp the topic whereas I may read it and miss the point entirely.  For me, decorators have been one of those things that I felt like I was always close to understanding but still not quite getting it.  Sure &#8211; some of the examples I read made sense but then I&#8217;d find another one that didn&#8217;t.  In my quest to understand them, I spent a lot of time reviewing a lot of examples and asking a lot of very patient friends for help.  At this point, I feel like I know enough to try and explain the topic in a manner that might hopefully help someone else who was having a hard time with the concept.  With my learning philosophy out of the way, let&#8217;s jump right in&#8230;.</p>



<p>I want to jump right into a real (albeit not super useful) example of decorators using the full decorator (or shorthand) syntax.  Let&#8217;s start with this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [1,5]; title: ; notranslate">
def a_decorator(a_function):
    print(&quot;You&#039;ve been decorated!&quot;)
    return a_function

@a_decorator
def print_name_string(your_name):
    name_string = &quot;Your name is: &quot; + your_name
    return name_string

print(print_name_string(&quot;Jon&quot;))
</pre></div>


<p>The code has two functions &#8211; <code>a_decorator</code> and <code>print_name_string</code>.  The <code>@a_decorator</code> syntax directly above the <code>print_name_string</code> definition decorates that function with the function <code>a_decorator</code> (doing it this way is what I was referring to as the shorthand syntax).  Lastly &#8211; we call our decorated function.  Before we look at the output &#8211; let&#8217;s define what a decorator is.  From all my reading the definition I like the most comes from <a rel="noreferrer noopener" href="https://www.thecodeship.com/patterns/guide-to-python-function-decorators/" data-type="URL" data-id="https://www.thecodeship.com/patterns/guide-to-python-function-decorators/" target="_blank">this article</a> where they define a decorator as&#8230;</p>



<p><em>A function that takes another function as an argument, generates a new function, augmenting the work of the original function, and returning the generated function so we can use it anywhere</em></p>



<p>With that said &#8211; and knowing that we are decorating the <code>print_name_string</code> function &#8211; any guesses as to what the output might be?</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
You&#039;ve been decorated!
Your name is: Jon
</pre></div>


<p>That perhaps wasn&#8217;t hard to guess.  If we apply our definition to this exact code example it might say&#8230;</p>



<p><em><strong><code>a_decorator</code> </strong>takes the <code><strong>print_name_string</strong></code> function as an argument, generates a new function, augmenting the work of <em><code><strong>print_name_string</strong></code></em>, and returns the generated function so we can use it anywhere</em></p>



<p>That said, you may look at the code above and think to yourself that when we call <code>print_name_string</code> Python sees the decorator and then runs the decorator function. The output we&#8217;re seeing certainly supports this.  But now &#8211; let&#8217;s remove the print statement and try again&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [10]; title: ; notranslate">
def a_decorator(a_function):
    print(&quot;You&#039;ve been decorated!&quot;)
    return a_function

@a_decorator
def print_name_string(your_name):
    name_string = &quot;Your name is: &quot; + your_name
    return name_string

#print(print_name_string(&quot;Jon&quot;))
</pre></div>


<p>Our output is now&#8230;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
You&#039;ve been decorated!
</pre></div>


<p>Well &#8211; that&#8217;s discouraging.  Why is the decorator function being called when we aren&#8217;t calling a function that&#8217;s been decorated with it?  The answer is that decorators are rendered at runtime.  The instant that the Python interpreter reads lines 5-8 above it runs them.  So what does &#8220;run&#8221; mean in this context?  I can boil it down to say that the new function based on the decorator is being created the instant the code is processed.  This is not unlike any other function we create.  If you&#8217;re in the Python CLI you have to create a function before you can call it.  When the Python file is processed by the interpreter it&#8217;s done so top down.  In our case, the interpreter sees the function is decorated and does the work to create a decorated function at that time so later in the code we can call it.  So let&#8217;s look a little harder at what the decorator does&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
def a_decorator(a_function):
    print(&quot;You&#039;ve been decorated!&quot;)
    return a_function
</pre></div>


<p>It looks like it takes in a variable, does a print, and then returns the same variable.  Based on the naming, we can discern that the variable being taken in, as well as returned, is a function.  In this case <code>a_function</code> is the function that was being decorated (<code>print_name_string</code>).  All that our decorator is doing is returning the same function.  Seems like a waste returning the same function doesn&#8217;t it?  That said &#8211; the decorator does need to return a function otherwise this will all fall apart.  For instance&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [3]; title: ; notranslate">
def a_decorator(a_function):
    print(&quot;You&#039;ve been decorated!&quot;)
    #return a_function

@a_decorator
def print_name_string(your_name):
    name_string = &quot;Your name is: &quot; + your_name
    return name_string

print(print_name_string(&quot;Jon&quot;))
</pre></div>


<p>Commenting out the decorator function return gives us this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [5]; title: ; notranslate">
You&#039;ve been decorated!
Traceback (most recent call last):
  File &quot;test.py&quot;, line 10, in &lt;module&gt;
    print(print_name_string(&quot;Jon&quot;))
TypeError: &#039;NoneType&#039; object is not callable
</pre></div>


<p>The message is pretty clear &#8211; but since our decorator is returning nothing (<code>None</code>) we can&#8217;t actually call the function since <code>None</code> is not callable.  Again &#8211; it&#8217;s important to think of the decorator as replacing or augmenting the decorated function.  Having the decorator not return a usable function is sort of like taking your car for an oil change and finding out afterwards your car is gone (Im surprised that&#8217;s the best example I can come up with).  We pass the decorator a function, allow it to do some work, but in the end we expect it to return us a usable function.  So it seems we have a hard rule defined here &#8211; decorators must return a function.  But what function can they return if it&#8217;s not the one it is inherently passed as part of the decoration?  Well we can create sub functions of our decorator function.  A simple example might look like this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [2,3,4]; title: ; notranslate">
def a_decorator(a_function):
    def add_some_flair(a_name):
        return &quot;******* &quot; + a_name + &quot; *******&quot;
    return add_some_flair

@a_decorator
def print_name_string(your_name):
    name_string = &quot;Your name is: &quot; + your_name
    return name_string

print(print_name_string(&quot;Jon&quot;))
</pre></div>


<p>Here we add a sub-function to <code>a_decorator</code> called <code>add_some_flair</code>.  This function adds a series of asterisks around the variable it receives and returns that string.  Any ideas what the output of this might look like?</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
******* Jon *******
</pre></div>


<p>This is a good example of a decorator replacing the functionality of an existing function.  Notice that our output doesn&#8217;t list the &#8220;Your name is: Jon&#8221; line.  In this case, we decorated the function <code>print_name_string</code> but the decorator itself decided to return a function that does something entirely different.  Again &#8211; this seems like it has little use if we are just fully overriding the functions that we are decorating.  But it does go to show you that the decorator has the ability to completely override the initial function call if it wants to.  We can prove that we can still get to the decorated function just by calling it as part of the <code>add_some_flair</code> function&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [3]; title: ; notranslate">
def a_decorator(a_function):
    def add_some_flair(a_name):
        print(a_function(a_name))
        return &quot;******* &quot; + a_name + &quot; *******&quot;
    return add_some_flair

@a_decorator
def print_name_string(your_name):
    name_string = &quot;Your name is: &quot; + your_name
    return name_string

print(print_name_string(&quot;Jon&quot;))
</pre></div>


<p>Now our output would look like&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
Your name is: Jon
******* Jon *******
</pre></div>


<p>You may have noticed that our function <code>add_some_flair</code> is taking in a single variable called <code>a_name</code>.  This is not optional.  Since we are decorating a function that takes in a single variable (<code>your_name</code>) our decorator return function must also take in a single variable.  For instance, let&#8217;s try adding another variable to our <code>print_string_name</code> function&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [8]; title: ; notranslate">
def a_decorator(a_function):
    def add_some_flair(a_name):
        print(a_function(a_name))
        return &quot;******* &quot; + a_name + &quot; *******&quot;
    return add_some_flair

@a_decorator
def print_name_string(your_name, your_age):
    name_string = &quot;Your name is: &quot; + your_name
    return name_string

print(print_name_string(&quot;Jon&quot;))
</pre></div>


<p>Now when we run this we get&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [6]; title: ; notranslate">
Traceback (most recent call last):
  File &quot;test.py&quot;, line 12, in &lt;module&gt;
    print(print_name_string(&quot;Jon&quot;))
  File &quot;test.py&quot;, line 3, in add_some_flair
    print(a_function(a_name))
TypeError: print_name_string() missing 1 required positional argument: &#039;your_age&#039;
</pre></div>


<p>The traceback points out our problem.  We are trying to call the function <code>print_your_name</code> but aren&#8217;t giving it the right amount of variables.  This is another case that we have to step back and think about this for a moment.  When we call <code>print_name_string</code> what are we actually doing?  Since it&#8217;s decorated with <code>a_decorator</code> which returns <code>add_some_flair</code> we are really calling <code>add_some_flair</code>.  In doing so, we are passing it a variable <code>a_name</code>.  If we wish to call the original function <code>print_string_name</code> and wish for it to have access to both <code>your_name</code> and <code>your_age</code> then we need to pass those to <code>print_name_string</code> which is really calling <code>add_some_flair</code>.  You still with me?  It&#8217;s important to think of the decorator being the main function being called.  If you wish to call <code>print_name_string</code> again from within the decorator, you need to have the variables available to do so which means passing them into the decorator.  Now &#8211; since we aren&#8217;t currently doing that, we can certainly make up a variable to meet the requirement&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [3]; title: ; notranslate">
def a_decorator(a_function):
    def add_some_flair(a_name):
        print(a_function(a_name,&quot;bah&quot;))
        return &quot;******* &quot; + a_name + &quot; *******&quot;
    return add_some_flair

@a_decorator
def print_name_string(your_name, your_age):
    name_string = &quot;Your name is: &quot; + your_name + &quot; Your age is: &quot; + your_age
    return name_string

print(print_name_string(&quot;Jon&quot;))
</pre></div>


<p>In this case we are still only passing <code>a_name</code> to the <code>add_some_flair</code> function and when we go to call <code>print_name_string</code>(<code>a_function</code>) we simply pass it &#8220;bah&#8221; as the <code>your_age</code> parameter&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
Your name is: Jon Your age is: bah
******* Jon *******
</pre></div>


<p>The right thing to do here though is to pass the variable through.  If we just start passing a second variable on line 12 let&#8217;s see what happens&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [12]; title: ; notranslate">
def a_decorator(a_function):
    def add_some_flair(a_name):
        print(a_function(a_name,&quot;bah&quot;))
        return &quot;******* &quot; + a_name + &quot; *******&quot;
    return add_some_flair

@a_decorator
def print_name_string(your_name, your_age):
    name_string = &quot;Your name is: &quot; + your_name + &quot; Your age is: &quot; + your_age
    return name_string

print(print_name_string(&quot;Jon&quot;,&quot;99&quot;))
</pre></div>


<p>Gives us&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [4]; title: ; notranslate">
Traceback (most recent call last):
  File &quot;test.py&quot;, line 12, in &lt;module&gt;
    print(print_name_string(&quot;Jon&quot;,&quot;99&quot;))
TypeError: add_some_flair() takes 1 positional argument but 2 were given
</pre></div>


<p>This output should help solidify the point I was trying to make above.  We are now passing two variables to <code>print_name_string</code> and if we look at that function we see that it certainly does take two variables on line 8.  However, since our decorator is replacing <code>print_name_string</code> with <code>add_some_flair</code> which does not take two arguments, the interpreter doesn&#8217;t know what to do.  To fix this, we need to accept both arguments in the decorator return function <code>add_some_flair</code>.  Once we do that we can start consuming it&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [2,3]; title: ; notranslate">
def a_decorator(a_function):
    def add_some_flair(a_name, a_age):
        print(a_function(a_name, a_age))
        return &quot;******* &quot; + a_name + &quot; *******&quot;
    return add_some_flair

@a_decorator
def print_name_string(your_name, your_age):
    name_string = &quot;Your name is: &quot; + your_name + &quot; Your age is: &quot; + your_age
    return name_string

print(print_name_string(&quot;Jon&quot;,&quot;99&quot;))
</pre></div>


<p>Which gives us the output&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
Your name is: Jon Your age is: 99
******* Jon *******
</pre></div>


<p>Now that we&#8217;ve talked about the basics &#8211; lets talk about the long hand way of decorating a function.  We&#8217;ve already said that a decorator is a function that takes another function as an argument.  That being said, instead of using the decorator shorthand syntax we could probably just do something like this&#8230; </p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [12,13]; title: ; notranslate">
def a_decorator(a_function):
    def add_some_flair(a_name, a_age):
        print(a_function(a_name, a_age))
        return &quot;******* &quot; + a_name + &quot; *******&quot;
    return add_some_flair

#@a_decorator
def print_name_string(your_name, your_age):
    name_string = &quot;Your name is: &quot; + your_name + &quot; Your age is: &quot; + your_age
    return name_string

decorated_print_name_string = a_decorator(print_name_string)
print(decorated_print_name_string(&quot;Jon&quot;,&quot;99&quot;))
</pre></div>


<p>You can see on line 12 that we are creating a decorated function called <code>decorated_print_name_string</code>.  So it&#8217;s a function that takes another function as an argument, possibly does some modification to it, and then returns the new function.  This will work just like the shorthand syntax did&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
Your name is: Jon Your age is: 99
******* Jon *******
</pre></div>


<p>Now &#8211; you might be wondering if we could shorten this up like this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [12]; title: ; notranslate">
def a_decorator(a_function):
    def add_some_flair(a_name, a_age):
        print(a_function(a_name, a_age))
        return &quot;******* &quot; + a_name + &quot; *******&quot;
    return add_some_flair

#@a_decorator
def print_name_string(your_name, your_age):
    name_string = &quot;Your name is: &quot; + your_name + &quot; Your age is: &quot; + your_age
    return name_string

print(a_decorator(print_name_string(&quot;Jon&quot;,&quot;99&quot;)))
</pre></div>


<p>Unfortunately, running this provides us this output&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
&lt;function a_decorator.&lt;locals&gt;.add_some_flair at 0x102268430&gt;
</pre></div>


<p>Why is that?  By calling the function including the <code>()</code> we&#8217;re telling Python to execute that function.  So in this case, we&#8217;re passing the return value of <code>print_name_string("Jon")</code> to the function <code>a_decorator</code>.  We can see this by just making the same call ourselves&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [12,13]; title: ; notranslate">
def a_decorator(a_function):
    def add_some_flair(a_name, a_age):
        print(a_function(a_name, a_age))
        return &quot;******* &quot; + a_name + &quot; *******&quot;
    return add_some_flair

#@a_decorator
def print_name_string(your_name, your_age):
    name_string = &quot;Your name is: &quot; + your_name + &quot; Your age is: &quot; + your_age
    return name_string

print(a_decorator(print_name_string(&quot;Jon&quot;,&quot;99&quot;)))
print(a_decorator(&quot;Your name is: Jon Your age is: 99&quot;))
</pre></div>


<p>The output is now&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
&lt;function a_decorator.&lt;locals&gt;.add_some_flair at 0x103f8a430&gt;
&lt;function a_decorator.&lt;locals&gt;.add_some_flair at 0x103f8a430&gt;
</pre></div>


<p>The key here is that we aren&#8217;t so much worried about the function input at this point.  In fact, we&#8217;re not interested in it at all when we create decorated functions.  The goal of decorating a function is to create a new function.  This is something that&#8217;s done totally outside of the realm of consuming the function (aka, passing variables to it and expecting a result) and is done only once when Python imports the script.  </p>



<p>It probably makes sense at this point to provide a couple of examples of how decorators might be used in real life.  Here&#8217;s a quick example of one you might use to time a given function call&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
import time

def timing(a_function):
    def time_func_call(x_counter):
        print(&quot;DECORATOR -&gt; Doing a loop for &quot; + str(x_counter) + &quot; iterations&quot;)
        start_time = time.perf_counter()
        a_function(x_counter)
        end_time = time.perf_counter()
        total_run_time = end_time - start_time
        print(&quot;DECORATOR -&gt; Total time was: &quot; + str(total_run_time))
    return time_func_call

@timing
def print_name_string(x_counter):
    print(&quot;The function beginith....&quot;)
    while x_counter &gt; 0:
    	time.sleep(.1)
    	x_counter = x_counter - 1
    print(&quot;The function endith....&quot;)

print_name_string(10)
</pre></div>


<p>The output would look something like this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
DECORATOR -&gt; Doing a loop for 10 iterations
The function beginith....
The function endith....
DECORATOR -&gt; Total time was: 1.034948312
</pre></div>


<p>Ideally you&#8217;d use something besides print statements &#8211; but I think it&#8217;s enough to make the point.  Decorators can be extremely useful and there are lots of Python projects that make great use of them.  There are also lots of other problems to solve in regards to decorators but I&#8217;ll save those for another blog post.  Im hoping this is enough to at least get folks to a base level understanding of how they work.  As always &#8211; comments are welcome and appreciated!</p>
]]></content:encoded>
					
					<wfw:commentRss>http://www.dasblinkenlichten.com/python-pieces-decorators/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">23380</post-id>	</item>
		<item>
		<title>Python Pieces: PyEnv and Venvs</title>
		<link>http://www.dasblinkenlichten.com/python-pieces-pyenv-and-venvs/</link>
					<comments>http://www.dasblinkenlichten.com/python-pieces-pyenv-and-venvs/#comments</comments>
		
		<dc:creator><![CDATA[Jon Langemak]]></dc:creator>
		<pubDate>Wed, 30 Sep 2020 13:03:35 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">http://www.dasblinkenlichten.com/?p=23362</guid>

					<description><![CDATA[In my last post, we talked about PyEnv and how it can help manage your local Python environments. As it turns out it can also help you manage virtual environments... <a href="http://www.dasblinkenlichten.com/python-pieces-pyenv-and-venvs/">Read more &#187;</a>]]></description>
										<content:encoded><![CDATA[
<p>In <a href="http://www.dasblinkenlichten.com/python-pieces-using-pyenv/">my last post</a>, we talked about PyEnv and how it can help manage your local Python environments.  As it turns out it can also help you manage virtual environments as well!  However &#8211; pursuing this functionality took me down a rabbit hole that was a bit deeper than expected.  The way that PyEnv works causes some behaviors (and on my end assumptions) to change which made me start questioning some of the things that I&#8217;ve always just taken for granted.  In other words &#8211; prepare yourself to go down the rabbit hole with me.  </p>



<p>At first glance PyEnv promised the same sort of awesome automagically context switching craziness that we saw previously work with Python versions.  However &#8211; the virtual environment management implementation with PyEnv felt rather foreign (and maybe a little clunky?) to me.  Most notably, as I pointed out in my <a href="http://www.dasblinkenlichten.com/python-pieces-using-pyenv/">last post</a>, the <code>.zshrc</code> alias provided to make the auto activation piece work slows down my terminal immensely which is why I omitted using it.  A slow terminal is about the worst thing I can think of&#8230;</p>



<p>That said &#8211; I still think it&#8217;s worth reviewing what it can offer so you can make your own decision.  But before we dive into that &#8211; I want to point out something about PyEnv that I should have mentioned in my last post.  As some folks have pointed out, PyEnv is just creating another layer of misdirection on your laptop. Admittedly it took me sometime to fully grasp the &#8220;shims&#8221; concept and understand what was actually going on.  However &#8211; at least for me &#8211; I was happy to trade this misdirection for what looks like a very clean Python installation and management approach.  Instead of having Python versions thrown about my filesystem and trying to figure out if I had the right <code>$PATH</code> to actually use them &#8211; PyEnv neatly installs all of the versions I want to use in one place.  Discussing this now actually dovetails quite nicely into how PyEnv handles virtual environments so let me back track for a second and let&#8217;s look at what PyEnv is doing with Python versions&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [2,3,4,5,8,10]; title: ; notranslate">
user@users-MacBook-Pro ~ % pyenv versions                              
  system
  2.7.18
  3.5.1
* 3.8.5 (set by /Users/user/.pyenv/version)
user@users-MacBook-Pro ~ % 
user@users-MacBook-Pro ~ % which python
/Users/user/.pyenv/shims/python
user@users-MacBook-Pro ~ % pyenv which python
/Users/user/.pyenv/versions/3.8.5/bin/python
user@users-MacBook-Pro ~ % 
</pre></div>


<p>As we can see above &#8211; I have my system Python installed as well as 3 unique Python versions installed using PyEnv.  If we use the standard <code>which</code> command we can see the shim as expected, but by using the <code>pyenv which</code> command we can see the real Python being used.  Notice the file path &#8211; let&#8217;s take a look there&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [3]; title: ; notranslate">
user@users-MacBook-Pro ~ % cd /Users/user/.pyenv/versions
user@users-MacBook-Pro versions % ls
2.7.18	3.5.1	3.8.5
user@users-MacBook-Pro versions % 
</pre></div>


<p>Let&#8217;s say we&#8217;ve finally decided we&#8217;re done with that <code>2.7.18</code> version and just want it off our laptop.  How do we do that?  Well PyEnv does have a handy <code>uninstall</code> command &#8211; but to prove my point let&#8217;s just delete that folder manually&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [3,4,5]; title: ; notranslate">
user@users-MacBook-Pro versions % rm -rf 2.7.18 
user@users-MacBook-Pro versions % pyenv versions
  system
  3.5.1
* 3.8.5 (set by /Users/user/.pyenv/version)
user@users-MacBook-Pro versions % 
</pre></div>


<p>Gonesies.  This is perhaps one of the key features of PyEnv for me.  It puts all of the things in one place.  I no longer have to worry about lingering Python pieces laying around my filesystem causing issues.  We can also prove the <code>uninstall</code> command works as well&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [5,6]; title: ; notranslate">
user@users-MacBook-Pro versions % pyenv uninstall 3.5.1
pyenv: remove /Users/user/.pyenv/versions/3.5.1? &#x5B;y|N]y
pyenv: 3.5.1 uninstalled
user@users-MacBook-Pro versions % pyenv versions       
  system
* 3.8.5 (set by /Users/user/.pyenv/version)
user@users-MacBook-Pro versions % 
</pre></div>


<p>Alright &#8211; so now that we&#8217;ve shown how and where the PyEnv based Python installs actually live (and why I think that&#8217;s awesome) let&#8217;s talk about how PyEnv handles venvs.  </p>



<p><em>Note1: As we start talking about PyEnv and virtual environments do keep in mind what we just saw and how PyEnv handles installs and versions.  It will come in handy during the rest of this blog post. </em>  </p>



<p><em>Note2: I apparently can&#8217;t stop calling them different things so please keep in mind that a venv, a virtualenv, and a virtual environment are the same thing.  Not familiar with what they are and why to use them?  Check <a href="https://docs.python.org/3/tutorial/venv.html">this</a> out!</em></p>



<p>PyEnv has handy built in functions to create venvs but starting to use them felt, at least for me, a little bit weird.  After understanding how PyEnv was handling the venvs the model did make more sense to me &#8211; but if you&#8217;re used to working with venvs in a more traditional sense you might find this approach clunky or at the very least uncomfortable.  That said let&#8217;s dive right into creating one.  First Im going to reinstall some Python versions so we have some stuff to play with&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
pyenv install 2.7.18
pyenv install 3.5.1
</pre></div>


<p>Cool.  Now let&#8217;s make some folders to house our potential projects&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
user@users-MacBook-Pro ~ % mkdir venv_projects
user@users-MacBook-Pro ~ % cd venv_projects 
user@users-MacBook-Pro venv_projects % mkdir test_project_1
user@users-MacBook-Pro venv_projects % mkdir test_project_2
</pre></div>


<p>So let&#8217;s make a venv in test_project_1 with PyEnv&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [3]; title: ; notranslate">
user@users-MacBook-Pro venv_projects % cd test_project_1
user@users-MacBook-Pro test_project_1 % 
user@users-MacBook-Pro test_project_1 % pyenv virtualenv 2.7.18 test_project_1
DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won&#039;t be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Collecting virtualenv
  Using cached https://files.pythonhosted.org/packages/12/51/36c685ff2c1b2f7b4b5db29f3153159102ae0e0adaff3a26fd1448232e06/virtualenv-20.0.31-py2.py3-none-any.whl
Collecting appdirs&lt;2,&gt;=1.4.3 (from virtualenv)
  Using cached https://files.pythonhosted.org/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl
Collecting importlib-metadata&lt;2,&gt;=0.12; python_version &lt; &quot;3.8&quot; (from virtualenv)
  Using cached https://files.pythonhosted.org/packages/8e/58/cdea07eb51fc2b906db0968a94700866fc46249bdc75cac23f9d13168929/importlib_metadata-1.7.0-py2.py3-none-any.whl
Collecting six&lt;2,&gt;=1.9.0 (from virtualenv)
  Using cached https://files.pythonhosted.org/packages/ee/ff/48bde5c0f013094d729fe4b0316ba2a24774b3ff1c52d924a8a4cb04078a/six-1.15.0-py2.py3-none-any.whl
Collecting filelock&lt;4,&gt;=3.0.0 (from virtualenv)
  Using cached https://files.pythonhosted.org/packages/14/ec/6ee2168387ce0154632f856d5cc5592328e9cf93127c5c9aeca92c8c16cb/filelock-3.0.12.tar.gz
Collecting importlib-resources&gt;=1.0; python_version &lt; &quot;3.7&quot; (from virtualenv)
  Using cached https://files.pythonhosted.org/packages/ba/03/0f9595c0c2ef12590877f3c47e5f579759ce5caf817f8256d5dcbd8a1177/importlib_resources-3.0.0-py2.py3-none-any.whl
Collecting distlib&lt;1,&gt;=0.3.1 (from virtualenv)
  Using cached https://files.pythonhosted.org/packages/f5/0a/490fa011d699bb5a5f3a0cf57de82237f52a6db9d40f33c53b2736c9a1f9/distlib-0.3.1-py2.py3-none-any.whl
Collecting pathlib2&lt;3,&gt;=2.3.3; python_version &lt; &quot;3.4&quot; and sys_platform != &quot;win32&quot; (from virtualenv)
  Using cached https://files.pythonhosted.org/packages/e9/45/9c82d3666af4ef9f221cbb954e1d77ddbb513faf552aea6df5f37f1a4859/pathlib2-2.3.5-py2.py3-none-any.whl
Collecting configparser&gt;=3.5; python_version &lt; &quot;3&quot; (from importlib-metadata&lt;2,&gt;=0.12; python_version &lt; &quot;3.8&quot;-&gt;virtualenv)
  Using cached https://files.pythonhosted.org/packages/7a/2a/95ed0501cf5d8709490b1d3a3f9b5cf340da6c433f896bbe9ce08dbe6785/configparser-4.0.2-py2.py3-none-any.whl
Collecting contextlib2; python_version &lt; &quot;3&quot; (from importlib-metadata&lt;2,&gt;=0.12; python_version &lt; &quot;3.8&quot;-&gt;virtualenv)
  Using cached https://files.pythonhosted.org/packages/85/60/370352f7ef6aa96c52fb001831622f50f923c1d575427d021b8ab3311236/contextlib2-0.6.0.post1-py2.py3-none-any.whl
Collecting zipp&gt;=0.5 (from importlib-metadata&lt;2,&gt;=0.12; python_version &lt; &quot;3.8&quot;-&gt;virtualenv)
  Using cached https://files.pythonhosted.org/packages/96/0a/67556e9b7782df7118c1f49bdc494da5e5e429c93aa77965f33e81287c8c/zipp-1.2.0-py2.py3-none-any.whl
Collecting typing; python_version &lt; &quot;3.5&quot; (from importlib-resources&gt;=1.0; python_version &lt; &quot;3.7&quot;-&gt;virtualenv)
  Using cached https://files.pythonhosted.org/packages/3b/c0/e44213fcb799eac02881e2485724ba5b0914600bc9df6ed922e364fdc059/typing-3.7.4.3-py2-none-any.whl
Collecting singledispatch; python_version &lt; &quot;3.4&quot; (from importlib-resources&gt;=1.0; python_version &lt; &quot;3.7&quot;-&gt;virtualenv)
  Using cached https://files.pythonhosted.org/packages/c5/10/369f50bcd4621b263927b0a1519987a04383d4a98fb10438042ad410cf88/singledispatch-3.4.0.3-py2.py3-none-any.whl
Collecting scandir; python_version &lt; &quot;3.5&quot; (from pathlib2&lt;3,&gt;=2.3.3; python_version &lt; &quot;3.4&quot; and sys_platform != &quot;win32&quot;-&gt;virtualenv)
  Using cached https://files.pythonhosted.org/packages/df/f5/9c052db7bd54d0cbf1bc0bb6554362bba1012d03e5888950a4f5c5dadc4e/scandir-1.10.0.tar.gz
Installing collected packages: appdirs, configparser, contextlib2, scandir, six, pathlib2, zipp, importlib-metadata, filelock, typing, singledispatch, importlib-resources, distlib, virtualenv
  Running setup.py install for scandir ... done
  Running setup.py install for filelock ... done
Successfully installed appdirs-1.4.4 configparser-4.0.2 contextlib2-0.6.0.post1 distlib-0.3.1 filelock-3.0.12 importlib-metadata-1.7.0 importlib-resources-3.0.0 pathlib2-2.3.5 scandir-1.10.0 singledispatch-3.4.0.3 six-1.15.0 typing-3.7.4.3 virtualenv-20.0.31 zipp-1.2.0
WARNING: You are using pip version 19.2.3, however version 20.2.3 is available.
You should consider upgrading via the &#039;pip install --upgrade pip&#039; command.
created virtual environment CPython2.7.18.final.0-64 in 592ms
  creator CPython2Posix(dest=/Users/user/.pyenv/versions/2.7.18/envs/test_project_1, clear=False, global=False)
  seeder FromAppData(download=False, pip=bundle, wheel=bundle, setuptools=bundle, via=copy, app_data_dir=/Users/user/Library/Application Support/virtualenv)
    added seed packages: pip==20.2.2, setuptools==44.1.1, wheel==0.35.1
  activators PythonActivator,CShellActivator,FishActivator,PowerShellActivator,BashActivator
Looking in links: /var/folders/sb/1xk4f6tj1wq20ngdjw1cwdwm0000gn/T/tmpBBtVKE
Requirement already satisfied: setuptools in /Users/user/.pyenv/versions/2.7.18/envs/test_project_1/lib/python2.7/site-packages (44.1.1)
Requirement already satisfied: pip in /Users/user/.pyenv/versions/2.7.18/envs/test_project_1/lib/python2.7/site-packages (20.2.2)
user@users-MacBook-Pro test_project_1 % 
</pre></div>


<p>Alright &#8211; that was a lot of output &#8211; but it sure looks like it installed all the things we need.  For those of you familiar with working with venvs more traditionally, you might think to yourself that now is the time to do a <code>source test_project_1/bin/activate</code>.  So let&#8217;s see if thats possible&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
user@users-MacBook-Pro test_project_1 % ls -al
total 0
drwxr-xr-x  2 user  staff   64 Sep 23 08:47 .
drwxr-xr-x  4 user  staff  128 Sep 23 08:48 ..
user@users-MacBook-Pro test_project_1 %
</pre></div>


<p>Hrmmm.  Ok &#8211; well maybe there&#8217;s some other magic going on let&#8217;s see what Python we&#8217;re using&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [2]; title: ; notranslate">
pyenv which python
/Users/user/.pyenv/versions/3.8.5/bin/python
user@users-MacBook-Pro test_project_1 % 
</pre></div>


<p>More hrmmm.  So it&#8217;s still using our global version.  So what&#8217;s going on here?  This is where things get a little different.  PyEnv seems to treat venvs as just another version of Python.  And when you step back and think about how they&#8217;re accomplishing this &#8211; it makes sense given the model they&#8217;ve defined.  All the same, it feels different and can definitely make you think you&#8217;re doing it wrong if you don&#8217;t understand how PyEnv works.  So what is going on?  Let&#8217;s go and take a look at our versions directory again&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [3]; title: ; notranslate">
user@users-MacBook-Pro test_project_1 % cd /Users/user/.pyenv/versions
user@users-MacBook-Pro versions % ls
2.7.18		3.5.1		3.8.5		test_project_1
user@users-MacBook-Pro versions %  
</pre></div>


<p>Aha!  That looks familiar. But why is that there?  Sure looks to me like the virtual environment is being created as another PyEnv version.  But if we look closer&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [8]; title: ; notranslate">
user@users-MacBook-Pro versions % ls -al    
total 0
drwxr-xr-x   6 user  staff  192 Sep 23 08:49 .
drwxr-xr-x  26 user  staff  832 Sep 22 10:20 ..
drwxr-xr-x   7 user  staff  224 Sep 23 08:49 2.7.18
drwxr-xr-x   6 user  staff  192 Sep 23 08:40 3.5.1
drwxr-xr-x   7 user  staff  224 Sep 22 10:20 3.8.5
lrwxr-xr-x   1 user  staff   54 Sep 23 08:49 test_project_1 -&gt; /Users/user/.pyenv/versions/2.7.18/envs/test_project_1
user@users-MacBook-Pro versions % 
</pre></div>


<p>AHA!  Symlinks!  So that file is just a symlink pointing to <code>/Users/user/.pyenv/versions/2.7.18/envs/test_project_1</code>.  Let&#8217;s look in there&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [3,6,9]; title: ; notranslate">
user@users-MacBook-Pro versions % cd 2.7.18 
user@users-MacBook-Pro 2.7.18 % ls
bin	envs	include	lib	share
user@users-MacBook-Pro 2.7.18 % cd envs
user@users-MacBook-Pro envs % ls
test_project_1
user@users-MacBook-Pro envs % cd test_project_1 
user@users-MacBook-Pro test_project_1 % ls
bin		include		lib		pyvenv.cfg
user@users-MacBook-Pro test_project_1 % 
</pre></div>


<p>Interesting.  So each venv looks like a discrete version of Python but is really created underneath the base version you specify.  So in our case, since the venv we created uses <code>2.7.18</code> the files exist under <code>/Users/user/.pyenv/versions/2.7.18/envs/</code>.  This is starting to look more like the setup of a &#8220;normal&#8221; virtualenv.  Let&#8217;s dive into the bin directory and see what is there&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [15,20]; title: ; notranslate">
user@users-MacBook-Pro test_project_1 % cd bin
user@users-MacBook-Pro bin % ls -al
total 4248
drwxr-xr-x  23 user  staff      736 Sep 23 08:49 .
drwxr-xr-x   7 user  staff      224 Sep 23 08:49 ..
-rw-r--r--   1 user  staff     2243 Sep 23 08:49 activate
-rw-r--r--   1 user  staff     1462 Sep 23 08:49 activate.csh
-rw-r--r--   1 user  staff     3093 Sep 23 08:49 activate.fish
-rw-r--r--   1 user  staff     1751 Sep 23 08:49 activate.ps1
-rw-r--r--   1 user  staff     1199 Sep 23 08:49 activate_this.py
-rwxr-xr-x   1 user  staff      279 Sep 23 08:49 easy_install
-rwxr-xr-x   1 user  staff      279 Sep 23 08:49 easy_install-2.7
-rwxr-xr-x   1 user  staff      279 Sep 23 08:49 easy_install2
-rwxr-xr-x   1 user  staff      279 Sep 23 08:49 easy_install2.7
-rwxr-xr-x   1 user  staff      270 Sep 23 08:49 pip
-rwxr-xr-x   1 user  staff      270 Sep 23 08:49 pip-2.7
-rwxr-xr-x   1 user  staff      270 Sep 23 08:49 pip2
-rwxr-xr-x   1 user  staff      270 Sep 23 08:49 pip2.7
-rwxr-xr-x   1 user  staff      126 Sep 23 08:49 pydoc
-rwxr-xr-x   1 user  staff  2100208 Sep 23 08:49 python
lrwxr-xr-x   1 user  staff        6 Sep 23 08:49 python2 -&gt; python
lrwxr-xr-x   1 user  staff        6 Sep 23 08:49 python2.7 -&gt; python
-rwxr-xr-x   1 user  staff      257 Sep 23 08:49 wheel
-rwxr-xr-x   1 user  staff      257 Sep 23 08:49 wheel-2.7
-rwxr-xr-x   1 user  staff      257 Sep 23 08:49 wheel2
-rwxr-xr-x   1 user  staff      257 Sep 23 08:49 wheel2.7
user@users-MacBook-Pro bin % 
</pre></div>


<p>Here we can see all of the python executables we&#8217;d expect to see for a given environment.  So if we step back and think about the folder structure, and how PyEnv works, any guesses as to how we can leverage this venv?  To do that, let&#8217;s go back to project directory and run this command&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [1,8]; title: ; notranslate">
user@users-MacBook-Pro test_project_1 % pyenv local test_project_1
user@users-MacBook-Pro test_project_1 % ls -al
total 8
drwxr-xr-x  3 user  staff   96 Sep 23 10:39 .
drwxr-xr-x  4 user  staff  128 Sep 23 08:48 ..
-rw-r--r--  1 user  staff   15 Sep 23 10:39 .python-version
user@users-MacBook-Pro test_project_1 % more .python-version 
test_project_1
user@users-MacBook-Pro test_project_1 % 
</pre></div>


<p>The first thing we need to do is set the local Python version.  To do that we just say that the &#8220;local&#8221; Python version is the one in our venv.  So let&#8217;s see how this looks&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [2]; title: ; notranslate">
user@users-MacBook-Pro test_project_1 % pyenv which python
/Users/user/.pyenv/versions/test_project_1/bin/python
user@users-MacBook-Pro test_project_1 % 
</pre></div>


<p>Nice!  So if you followed the setup from our <a href="http://www.dasblinkenlichten.com/python-pieces-using-pyenv/">last post</a> and have put the following lines in your <code>.zshrc</code> file&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
export PATH=&quot;/Users/user/.pyenv/bin:$PATH&quot;
eval &quot;$(pyenv init - zsh)&quot;
</pre></div>


<p><em>Note: We are specifically omitting the <code>eval "$(pyenv virtualenv-init - zsh)"</code> line.  We do NOT need that. </em></p>



<p>You should now be automagically using the correct version of Python&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [2,4]; title: ; notranslate">
user@users-MacBook-Pro test_project_1 % pyenv which python
/Users/user/.pyenv/versions/test_project_1/bin/python
user@users-MacBook-Pro test_project_1 % python 
Python 2.7.18 (default, Sep 23 2020, 08:43:08) 
&#x5B;GCC 4.2.1 Compatible Apple LLVM 11.0.3 (clang-1103.0.32.62)] on darwin
Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.
&gt;&gt;&gt; 

</pre></div>


<p>We should now be seeing the same automagic behavior we saw in the <a href="http://www.dasblinkenlichten.com/python-pieces-using-pyenv/">last post</a>.  Move out of this directory and we&#8217;ll be back to the global version&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [3]; title: ; notranslate">
user@users-MacBook-Pro test_project_1 % cd ..
user@users-MacBook-Pro venv_projects % pyenv which python
/Users/user/.pyenv/versions/3.8.5/bin/python
user@users-MacBook-Pro venv_projects % 
</pre></div>


<p>Nice.  But here is where I started questioning myself.  By using the base functionality we established in the <a href="http://www.dasblinkenlichten.com/python-pieces-using-pyenv/">last post</a>, we are already getting automatic switching to the version of Python that we want.  If we think about virtual environments at a high level I think of them as providing two big things.  The first is a specific version of Python and the second is a discrete set of Python packages managed typically by <code>pip</code>.  It appears we&#8217;re already getting the first and we haven&#8217;t even had to &#8220;activate&#8221; our venv.  Let&#8217;s test out the second&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [12,24,29,30]; title: ; notranslate">
user@users-MacBook-Pro test_project_1 % pip list
DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained. pip 21.0 will drop support for Python 2.7 in January 2021. More details about Python 2 support in pip can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Package    Version
---------- -------
pip        20.2.2
setuptools 44.1.1
wheel      0.35.1
WARNING: You are using pip version 20.2.2; however, version 20.2.3 is available.
You should consider upgrading via the &#039;/Users/user/.pyenv/versions/2.7.18/envs/test_project_1/bin/python -m pip install --upgrade pip&#039; command.
user@users-MacBook-Pro test_project_1 % 
user@users-MacBook-Pro test_project_1 % 
user@users-MacBook-Pro test_project_1 % pip install pyyaml
DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained. pip 21.0 will drop support for Python 2.7 in January 2021. More details about Python 2 support in pip can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Processing /Users/user/Library/Caches/pip/wheels/d1/d5/a0/3c27cdc8b0209c5fc1385afeee936cf8a71e13d885388b4be2/PyYAML-5.3.1-cp27-cp27m-macosx_10_15_x86_64.whl
Installing collected packages: pyyaml
Successfully installed pyyaml-5.3.1
WARNING: You are using pip version 20.2.2; however, version 20.2.3 is available.
You should consider upgrading via the &#039;/Users/user/.pyenv/versions/2.7.18/envs/test_project_1/bin/python -m pip install --upgrade pip&#039; command.
user@users-MacBook-Pro test_project_1 % pip list
DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained. pip 21.0 will drop support for Python 2.7 in January 2021. More details about Python 2 support in pip can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Package    Version
---------- -------
pip        20.2.2
PyYAML     5.3.1
setuptools 44.1.1
wheel      0.35.1
WARNING: You are using pip version 20.2.2; however, version 20.2.3 is available.
You should consider upgrading via the &#039;/Users/user/.pyenv/versions/2.7.18/envs/test_project_1/bin/python -m pip install --upgrade pip&#039; command.
user@users-MacBook-Pro test_project_1 % cd ..
user@users-MacBook-Pro venv_projects % pip list
Package    Version
---------- ---------
certifi    2020.6.20
chardet    3.0.4
idna       2.10
pip        20.1.1
requests   2.24.0
setuptools 47.1.0
urllib3    1.25.10
WARNING: You are using pip version 20.1.1; however, version 20.2.3 is available.
You should consider upgrading via the &#039;/Users/user/.pyenv/versions/3.8.5/bin/python3.8 -m pip install --upgrade pip&#039; command.
user@users-MacBook-Pro venv_projects % 

</pre></div>


<p>There&#8217;s a lot of output above &#8211; but the basic gist of what Im doing is installing <code>pyyaml</code> using <code>pip</code> from the <code>test_project_1</code> directory.  I then back up one directory and validate that I no longer see that package because I no longer have a <code>local</code> version of Python and am instead defaulting to the global Python (in other words I just switched versions of Python using PyEnv).  To say this slightly differently, package management with PyEnv appears to be per &#8220;version&#8221;.  Make sense?  We saw some of this behavior previously.  Recall at the end of the <a href="http://www.dasblinkenlichten.com/python-pieces-using-pyenv/">last post </a>if we had two project folders both of which used the same <code>local</code> version of Python we inherited the packages between the two.  Since my &#8220;global&#8221; version is different from my &#8220;local&#8221; version I no longer see the package I just installed.   Now throw venvs on top of this with the understanding that each venv is also treated as a PyEnv &#8220;version&#8221;.  If we create another virtual environment with PyEnv using the same version and set it as the <code>local</code> Python version we do NOT inherit the packages.  For instance&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [2,11,12]; title: ; notranslate">
user@users-MacBook-Pro venv_projects % cd test_project_2
user@users-MacBook-Pro test_project_2 % pyenv virtualenv 2.7.18 test_project_2
created virtual environment CPython2.7.18.final.0-64 in 389ms
  creator CPython2Posix(dest=/Users/user/.pyenv/versions/2.7.18/envs/test_project_2, clear=False, global=False)
  seeder FromAppData(download=False, pip=bundle, wheel=bundle, setuptools=bundle, via=copy, app_data_dir=/Users/user/Library/Application Support/virtualenv)
    added seed packages: pip==20.2.2, setuptools==44.1.1, wheel==0.35.1
  activators PythonActivator,CShellActivator,FishActivator,PowerShellActivator,BashActivator
Looking in links: /var/folders/sb/1xk4f6tj1wq20ngdjw1cwdwm0000gn/T/tmpredLoM
Requirement already satisfied: setuptools in /Users/user/.pyenv/versions/2.7.18/envs/test_project_2/lib/python2.7/site-packages (44.1.1)
Requirement already satisfied: pip in /Users/user/.pyenv/versions/2.7.18/envs/test_project_2/lib/python2.7/site-packages (20.2.2)
user@users-MacBook-Pro test_project_2 % pyenv local test_project_2            
user@users-MacBook-Pro test_project_2 % pip list
DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained. pip 21.0 will drop support for Python 2.7 in January 2021. More details about Python 2 support in pip can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Package    Version
---------- -------
pip        20.2.2
setuptools 44.1.1
wheel      0.35.1
WARNING: You are using pip version 20.2.2; however, version 20.2.3 is available.
You should consider upgrading via the &#039;/Users/user/.pyenv/versions/2.7.18/envs/test_project_2/bin/python -m pip install --upgrade pip&#039; command.
user@users-MacBook-Pro test_project_2 % 
</pre></div>


<p>So at this point &#8211; I started questioning things.  When would I ever need to actually &#8220;activate&#8221; any of these venvs?  Once you grasp how PyEnv does the version switching, how versions are handled, and how PyEnv treats virtual environments as versions, you can start to see why you might not need to &#8220;activate&#8221; a virtual environment to get the same kind of behavior.  If you&#8217;re still struggling with what Im getting at &#8211; this helped me visualize things (note some folders are removed to make this not huge)&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [14,23,50,67,96,142]; title: ; notranslate">
user@users-MacBook-Pro versions % tree -L 6 -d
.
├── 2.7.18
│   ├── bin
│   ├── envs
│   │   ├── test_project_1
│   │   │   ├── bin
│   │   │   ├── include
│   │   │   │   └── python2.7 -&gt; /Users/user/.pyenv/versions/2.7.18/include/python2.7
│   │   │   └── lib
│   │   │       └── python2.7
│   │   │           ├── config
│   │   │           ├── lib-dynload -&gt; /Users/user/.pyenv/versions/2.7.18/lib/python2.7/lib-dynload
│   │   │           └── site-packages
│   │   └── test_project_2
│   │       ├── bin
│   │       ├── include
│   │       │   └── python2.7 -&gt; /Users/user/.pyenv/versions/2.7.18/include/python2.7
│   │       └── lib
│   │           └── python2.7
│   │               ├── config
│   │               ├── lib-dynload -&gt; /Users/user/.pyenv/versions/2.7.18/lib/python2.7/lib-dynload
│   │               └── site-packages
│   ├── include
│   │   └── python2.7
│   ├── lib
│   │   ├── pkgconfig
│   │   └── python2.7
│   │       ├── bsddb
│   │       ├── compiler
│   │       ├── config
│   │       ├── ctypes
│   │       ├── curses
│   │       ├── distutils
│   │       ├── email
│   │       ├── encodings
│   │       ├── ensurepip
│   │       ├── hotshot
│   │       ├── idlelib
│   │       ├── importlib
│   │       ├── json
│   │       ├── lib-dynload
│   │       ├── lib-tk
│   │       ├── lib2to3
│   │       ├── logging
│   │       ├── multiprocessing
│   │       ├── plat-darwin
│   │       ├── plat-mac
│   │       ├── pydoc_data
│   │       ├── site-packages
│   │       ├── sqlite3
│   │       ├── test
│   │       ├── unittest
│   │       ├── wsgiref
│   │       └── xml
│   └── share
│       └── man
│           └── man1
├── 3.5.1
│   ├── bin
│   ├── envs
│   │   └── test_project_3
│   │       ├── bin
│   │       ├── include
│   │       └── lib
│   │           └── python3.5
│   │               └── site-packages
│   ├── include
│   │   └── python3.5m
│   ├── lib
│   │   ├── pkgconfig
│   │   └── python3.5
│   │       ├── __pycache__
│   │       ├── asyncio
│   │       ├── collections
│   │       ├── concurrent
│   │       ├── config-3.5m
│   │       ├── ctypes
│   │       ├── curses
│   │       ├── dbm
│   │       ├── distutils
│   │       ├── email
│   │       ├── encodings
│   │       ├── ensurepip
│   │       ├── html
│   │       ├── http
│   │       ├── idlelib
│   │       ├── importlib
│   │       ├── json
│   │       ├── lib-dynload
│   │       ├── lib2to3
│   │       ├── logging
│   │       ├── multiprocessing
│   │       ├── plat-darwin
│   │       ├── pydoc_data
│   │       ├── site-packages
│   │       ├── sqlite3
│   │       ├── test
│   │       ├── tkinter
│   │       ├── turtledemo
│   │       ├── unittest
│   │       ├── urllib
│   │       ├── venv
│   │       ├── wsgiref
│   │       ├── xml
│   │       └── xmlrpc
│   └── share
│       └── man
│           └── man1
├── 3.8.5
│   ├── bin
│   ├── envs
│   ├── include
│   │   └── python3.8
│   │       ├── cpython
│   │       └── internal
│   ├── lib
│   │   ├── pkgconfig
│   │   └── python3.8
│   │       ├── __pycache__
│   │       ├── asyncio
│   │       ├── collections
│   │       ├── concurrent
│   │       ├── config-3.8-darwin
│   │       ├── ctypes
│   │       ├── curses
│   │       ├── dbm
│   │       ├── distutils
│   │       ├── email
│   │       ├── encodings
│   │       ├── ensurepip
│   │       ├── html
│   │       ├── http
│   │       ├── idlelib
│   │       ├── importlib
│   │       ├── json
│   │       ├── lib-dynload
│   │       ├── lib2to3
│   │       ├── logging
│   │       ├── multiprocessing
│   │       ├── pydoc_data
│   │       ├── site-packages
│   │       ├── sqlite3
│   │       ├── test
│   │       ├── tkinter
│   │       ├── turtledemo
│   │       ├── unittest
│   │       ├── urllib
│   │       ├── venv
│   │       ├── wsgiref
│   │       ├── xml
│   │       └── xmlrpc
│   └── share
│       └── man
│           └── man1
├── test_project_1 -&gt; /Users/user/.pyenv/versions/2.7.18/envs/test_project_1
├── test_project_2 -&gt; /Users/user/.pyenv/versions/2.7.18/envs/test_project_2
└── test_project_3 -&gt; /Users/user/.pyenv/versions/3.5.1/envs/test_project_3

555 directories
user@users-MacBook-Pro versions % 

</pre></div>


<p>As you can see &#8211; each &#8220;version&#8221; has it&#8217;s own site-packages directory.  At this point, it seems clear to me that each &#8220;version&#8221; (I keep putting it in quotes since venvs are clearly treated as versions) has it&#8217;s own package directory.  So it seems pretty clear to me that without doing an activation I am getting a virtual environment like experience.  </p>



<p><em>I&#8217;ll remind folks at this point that I am NOT using the automatic venv activation scripts that PyEnv suggests you use (<code>$(pyenv virtualenv-init - zsh)</code>).</em>  </p>



<p>What&#8217;s left at this point is to figure out how each version of Python is being instructed to use it&#8217;s local <code>site-packages</code> directory.  After much digging, I found that it really came down to the <code>site</code> Python module.  Turns out you can take a look at what&#8217;s there pretty easily&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [3,12]; title: ; notranslate">
user@users-MacBook-Pro test_project_1 % python -m site 
sys.path = &#x5B;
    &#039;/Users/user/venv_projects/test_project_1&#039;,
    &#039;/Users/user/.pyenv/versions/2.7.18/lib/python27.zip&#039;,
    &#039;/Users/user/.pyenv/versions/2.7.18/lib/python2.7&#039;,
    &#039;/Users/user/.pyenv/versions/2.7.18/lib/python2.7/plat-darwin&#039;,
    &#039;/Users/user/.pyenv/versions/2.7.18/lib/python2.7/plat-mac&#039;,
    &#039;/Users/user/.pyenv/versions/2.7.18/lib/python2.7/plat-mac/lib-scriptpackages&#039;,
    &#039;/Users/user/.pyenv/versions/2.7.18/lib/python2.7/lib-tk&#039;,
    &#039;/Users/user/.pyenv/versions/2.7.18/lib/python2.7/lib-old&#039;,
    &#039;/Users/user/.pyenv/versions/2.7.18/lib/python2.7/lib-dynload&#039;,
    &#039;/Users/user/.pyenv/versions/test_project_1/lib/python2.7/site-packages&#039;,
]
USER_BASE: &#039;/Users/user/.local&#039; (doesn&#039;t exist)
USER_SITE: &#039;/Users/user/.local/lib/python2.7/site-packages&#039; (doesn&#039;t exist)
ENABLE_USER_SITE: False
user@users-MacBook-Pro test_project_1 % 
</pre></div>


<p>The really interesting output is the <code>sys.path</code> list.  This list is used to search for Python packages and modules and is evaluated in order.  Looking at the first entry we can see this makes sense.  If you have other Python packages locally it will first search there.  Then we have lines 4-11 which from what I can discern are created by the sites module based on platform and other information.  But it&#8217;s the last line that really interested me.  This line specifies the &#8220;global&#8221; site packages directory. Which you can find yourself by running this command&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
python -c &quot;from distutils.sysconfig import get_python_lib; print(get_python_lib())&quot;
</pre></div>


<p>which I found <a href="https://stackoverflow.com/questions/122327/how-do-i-find-the-location-of-my-python-site-packages-directory">here</a>.  If we run that in our project folder we&#8217;ll see that we get that last line there&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
user@users-MacBook-Pro test_project_1 % python -c &quot;from distutils.sysconfig import get_python_lib; print(get_python_lib())&quot;
/Users/user/.pyenv/versions/test_project_1/lib/python2.7/site-packages
user@users-MacBook-Pro test_project_1 % 
</pre></div>


<p>So this &#8220;global sites-packages&#8221; folder is where our Python packages are for a given project.  Notice that this value changes as we switch versions of Python&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [3,8,15]; title: ; notranslate">
user@users-MacBook-Pro test_project_1 % cd ..
user@users-MacBook-Pro venv_projects % python -c &quot;from distutils.sysconfig import get_python_lib; print(get_python_lib())&quot;
/Users/user/.pyenv/versions/3.8.5/lib/python3.8/site-packages
user@users-MacBook-Pro venv_projects % mkdir test_project_3
user@users-MacBook-Pro venv_projects % cd test_project_3
user@users-MacBook-Pro test_project_3 % pyenv local 3.5.1
user@users-MacBook-Pro test_project_3 % python -c &quot;from distutils.sysconfig import get_python_lib; print(get_python_lib())&quot;
/Users/user/.pyenv/versions/3.5.1/lib/python3.5/site-packages
user@users-MacBook-Pro test_project_3 % pyenv virtualenv 3.5.1 test_project_3
Ignoring indexes: https://pypi.python.org/simple
Requirement already satisfied (use --upgrade to upgrade): setuptools in /Users/user/.pyenv/versions/3.5.1/envs/test_project_3/lib/python3.5/site-packages
Requirement already satisfied (use --upgrade to upgrade): pip in /Users/user/.pyenv/versions/3.5.1/envs/test_project_3/lib/python3.5/site-packages
user@users-MacBook-Pro test_project_3 % pyenv local test_project_3
user@users-MacBook-Pro test_project_3 % python -c &quot;from distutils.sysconfig import get_python_lib; print(get_python_lib())&quot;
/Users/user/.pyenv/versions/test_project_3/lib/python3.5/site-packages
user@users-MacBook-Pro test_project_3 % 
</pre></div>


<p>On line 3 we see that since we&#8217;re out of the project directory we are once again using the global version we have set of <code>3.8.5</code>.  Now we make a new project directory called <code>test_project_3</code> and inside of it we set the local version to <code>3.5.1</code>.  When we check again on line 8 &#8211; we see that we&#8217;re using the site packages folder that&#8217;s part of the base <code>3.5.1</code> version directory.  Then we create a venv with PyEnv and set that as the local Python &#8220;version&#8221;.  In doing so we can see on line 15, we are now using that site-packages folder.</p>



<p>So at this point &#8211; I think we have close to a full picture of how this is working.  The bulk of the magic is in how PyEnv manages versions and the actual package management piece is a function of Python and just works the way that it does because of the PyEnv folder structure.  My hypothesis is that by using PyEnv version management (and the shim model) you&#8217;re getting &#8220;virtual environment like&#8221; behavior without having to actually &#8220;activate&#8221; a virtual environment.  </p>



<p>Now that said &#8211; there are some limitations to this.  While discussing this on the Network to Code slack Jacob McGill (thank you for the comments!) pointed out one limitation I hadn&#8217;t considered. My model of laying out projects fit wells into PyEnv since I am typically working on files for a given project in that given project folder.  But without &#8220;activating&#8221; the virtual environment, you HAVE to be in the project directory in order for any of this to work.  In other words, once I leave the project folder (for instance <code>test_project_1</code>) I lose all of this.  Now in my current work flow, this isn&#8217;t a problem since I tightly couple the project folder to the work Im doing.  But I can see this being a drawback for others.  As an example&#8230;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [1,10,21]; title: ; notranslate">
user@users-MacBook-Pro test_project_1 % pyenv activate test_project_1
pyenv-virtualenv: prompt changing will be removed from future release. configure `export PYENV_VIRTUALENV_DISABLE_PROMPT=1&#039; to simulate the behavior.
(test_project_1) user@users-MacBook-Pro test_project_1 % 
(test_project_1) user@users-MacBook-Pro test_project_1 % cd ..
(test_project_1) user@users-MacBook-Pro venv_projects % pip list
DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained. pip 21.0 will drop support for Python 2.7 in January 2021. More details about Python 2 support in pip can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Package    Version
---------- -------
pip        20.2.2
PyYAML     5.3.1
setuptools 44.1.1
wheel      0.35.1
WARNING: You are using pip version 20.2.2; however, version 20.2.3 is available.
You should consider upgrading via the &#039;/Users/user/.pyenv/versions/2.7.18/envs/test_project_1/bin/python -m pip install --upgrade pip&#039; command. 
(test_project_1) user@users-MacBook-Pro venv_projects % cd test_project_3
(test_project_1) user@users-MacBook-Pro test_project_3 % pip list
DEPRECATION: Python 2.7 reached the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 is no longer maintained. pip 21.0 will drop support for Python 2.7 in January 2021. More details about Python 2 support in pip can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Package    Version
---------- -------
pip        20.2.2
PyYAML     5.3.1
setuptools 44.1.1
wheel      0.35.1
WARNING: You are using pip version 20.2.2; however, version 20.2.3 is available.
You should consider upgrading via the &#039;/Users/user/.pyenv/versions/2.7.18/envs/test_project_1/bin/python -m pip install --upgrade pip&#039; command.
(test_project_1) user@users-MacBook-Pro test_project_3 % 
</pre></div>


<p>You can see that if I do &#8220;activate&#8221; the venv then I can move out of the project folder and still have my Python version and packages that I expect.  I can even move into another project folder and I&#8217;m still using the activated virtual environment.  </p>



<p>This all said &#8211; I would love to hear from Python/PyEnv experts out there on if my hypothesis is true.  Please comment and let me know your thoughts!  At this point, if I do end up using PyEnv for virtual environment management, I don&#8217;t think I&#8217;ll be doing any virtual environment activations (unless someone can point out a good reason to do so!).</p>
]]></content:encoded>
					
					<wfw:commentRss>http://www.dasblinkenlichten.com/python-pieces-pyenv-and-venvs/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">23362</post-id>	</item>
		<item>
		<title>Python Pieces: Using PyEnv</title>
		<link>http://www.dasblinkenlichten.com/python-pieces-using-pyenv/</link>
					<comments>http://www.dasblinkenlichten.com/python-pieces-using-pyenv/#respond</comments>
		
		<dc:creator><![CDATA[Jon Langemak]]></dc:creator>
		<pubDate>Sat, 12 Sep 2020 14:49:31 +0000</pubDate>
				<category><![CDATA[Python]]></category>
		<guid isPermaLink="false">http://www.dasblinkenlichten.com/?p=23353</guid>

					<description><![CDATA[If you&#8217;re like me &#8211; one of the most frustrating things about Python is version management. You get a new Mac, the system default is 2.x something, you need 3.x... <a href="http://www.dasblinkenlichten.com/python-pieces-using-pyenv/">Read more &#187;</a>]]></description>
										<content:encoded><![CDATA[
<p>If you&#8217;re like me &#8211; one of the most frustrating things about Python is version management.  You get a new Mac, the system default is 2.x something, you need 3.x something, and you&#8217;re wondering what the best (right) way to get the version you want installed.  You install Python 3 but the default Python version stays the same until you do some symlink hack thing that you know is just creating a mess.  So for awhile you just call <code>python3</code> explicitly but then you realize that all of the packages you installed using <code>pip</code> are no longer available and you need to install them again using <code>pip3</code>.  </p>



<p>Sound familiar?  Maybe I&#8217;m the only one that struggles with this &#8211; but I tend to muddle my way through just making things work while in the back of my head I know that Im creating a complete disaster of the local Python installation.  I shall muddle no longer thanks to PyEnv.  I was recently introduced to the tool and it&#8217;s a total game changer.  It allows you to seemlessly manage your local Python install, easily install different versions, easily switch versions, and even has the capability of automgically switching versions for you based on the project you&#8217;re currently working on.  So &#8211; let&#8217;s dive right into how we make this all work.</p>



<p>First things first &#8211; we need to install PyEnv.  To do that &#8211; hit up the <a href="https://github.com/pyenv/pyenv-installer">PyEnv-Installer page</a>.  You can also clone the PyEnv repo and do things that way if you&#8217;re uncomfortable with piping randomness into <code>bash</code>.  For the sake of simplicity though, we&#8217;ll use the installer version which has you run this command&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
curl https://pyenv.run | bash
</pre></div>


<p>The install should be pretty straight forward but you&#8217;ll likely hit this warning message at the end of the install&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
WARNING: seems you still have not added &#039;pyenv&#039; to the load path.

# Load pyenv automatically by adding
# the following to ~/.bashrc:

export PATH=&quot;/Users/user/.pyenv/bin:$PATH&quot;
eval &quot;$(pyenv init -)&quot;
eval &quot;$(pyenv virtualenv-init -)&quot;
```
</pre></div>


<p><em>Note: I&#8217;ve run into issues with my terminal responding super slowly if I include the last line in my <code>.zshrc</code>.  For now, I&#8217;ve omitted <code>eval "$(pyenv virtualenv-init -)"</code> from the <code>.zshrc</code>.  We&#8217;ll talk more about that later in my next post where we talk about PyEnv and virtual environments.    It&#8217;s not needed for this post.</em></p>



<p>As the message describes &#8211;  we need to do add PyEnv to the load path.  We&#8217;ll see why we need to do this shortly but for now let&#8217;s just focus on getting that done.  Check and see what shell you&#8217;re using&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
users-MacBook-Pro:~ user$ echo $SHELL
/bin/zsh
users-MacBook-Pro:~ user$ 
</pre></div>


<p>In my case Im using <code>zsh</code> so I need to add that stuff to <code>.zshrc</code>.  If you&#8217;re using <code>bash</code> you&#8217;ll need to to add it to <code>.bashrc</code>.  Once done, restart your terminal (or source the requisite file).  Now we should have access to the <code>pyenv</code> CLI tool.  Take a look at what versions PyEnv knows about&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [2]; title: ; notranslate">
user@users-MacBook-Pro ~ % pyenv versions
* system (set by /Users/user/.pyenv/version)
user@users-MacBook-Pro ~ % 
</pre></div>


<p>So right now &#8211; it only knows about the system default version.  Let&#8217;s see what that is&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [2,4]; title: ; notranslate">
user@users-MacBook-Pro ~ % which python
/usr/bin/python
user@users-MacBook-Pro ~ % python --version
Python 2.7.16
user@users-MacBook-Pro ~ % 
</pre></div>


<p>Nice &#8211; Python 2.7.16.  Almost certainly not the version you want to use.  So let&#8217;s take a look at what versions PyEnv knows about that are available for install.  Specifically, let&#8217;s look for versions in 3.8&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [1]; title: ; notranslate">
user@users-MacBook-Pro ~ % pyenv install --list | grep &quot;3.8&quot;   
  3.8.0
  3.8-dev
  3.8.1
  3.8.2
  3.8.3
  3.8.4
  3.8.5
  miniconda-3.8.3
  miniconda3-3.8.3
user@users-MacBook-Pro ~ % 
</pre></div>


<p>If you omit the <code>grep</code> you&#8217;ll see an outstanding list of Python versions you can install.  For right now &#8211; let&#8217;s focus on installing <code>3.8.5</code>.  To do that we simply do this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [1]; title: ; notranslate">
user@users-MacBook-Pro ~ % pyenv install 3.8.5
python-build: use openssl from homebrew
python-build: use readline from homebrew
Downloading Python-3.8.5.tar.xz...
-&gt; https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tar.xz
Installing Python-3.8.5...
python-build: use readline from homebrew
python-build: use zlib from xcode sdk
Installed Python-3.8.5 to /Users/user/.pyenv/versions/3.8.5

user@users-MacBook-Pro ~ % 
</pre></div>


<p>Note that this will take some time as it&#8217;s building Python from scratch.  Once installed, we should see it in our versions&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [2,3]; title: ; notranslate">
user@users-MacBook-Pro ~ % pyenv versions
* system (set by /Users/user/.pyenv/version)
  3.8.5
user@users-MacBook-Pro ~ % 
</pre></div>


<p>Note that the star is still next to the <code>system</code> row meaning that we&#8217;re still currently using the system default.  If we wish to switch our new global version to <code>3.8.5</code> we simply say&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [1,4,6]; title: ; notranslate">
user@users-MacBook-Pro ~ % pyenv global 3.8.5
user@users-MacBook-Pro ~ % 
user@users-MacBook-Pro ~ % which python
/Users/user/.pyenv/shims/python
user@users-MacBook-Pro ~ % python --version
Python 3.8.5
user@users-MacBook-Pro ~ % 
</pre></div>


<p>Nice!  So now our global version of Python will be <code>3.8.5</code>.  Now that we switched Python versions for global use, let&#8217;s talk about local use.  This is where PyEnv really starts to shine.  Let&#8217;s install a couple more versions of Python so we can play around&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [1,11]; title: ; notranslate">
user@users-MacBook-Pro ~ % pyenv install 2.7.18
python-build: use openssl from homebrew
python-build: use readline from homebrew
Downloading Python-2.7.18.tar.xz...
-&gt; https://www.python.org/ftp/python/2.7.18/Python-2.7.18.tar.xz
Installing Python-2.7.18...
python-build: use readline from homebrew
python-build: use zlib from xcode sdk
Installed Python-2.7.18 to /Users/user/.pyenv/versions/2.7.18

user@users-MacBook-Pro ~ % pyenv install 3.5.1 
python-build: use openssl from homebrew
python-build: use readline from homebrew
Downloading Python-3.5.1.tar.xz...
-&gt; https://www.python.org/ftp/python/3.5.1/Python-3.5.1.tar.xz
Installing Python-3.5.1...
python-build: use readline from homebrew
python-build: use zlib from xcode sdk
Installed Python-3.5.1 to /Users/user/.pyenv/versions/3.5.1

user@users-MacBook-Pro ~ % 
</pre></div>


<p>Ok &#8211; so we should now see those as options&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [3,4,5]; title: ; notranslate">
user@users-MacBook-Pro ~ % pyenv versions
  system
  2.7.18
  3.5.1
* 3.8.5 (set by /Users/user/.pyenv/version)
user@users-MacBook-Pro ~ % 
</pre></div>


<p>Awesome!  Now let&#8217;s create a set of test projects and associated folders&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
user@users-MacBook-Pro ~ % cd test_projects   
user@users-MacBook-Pro test_projects % mkdir project_1
user@users-MacBook-Pro test_projects % mkdir project_2
user@users-MacBook-Pro test_projects % mkdir project_3
</pre></div>


<p>Alright &#8211; so now let&#8217;s start in project_1 and setup PyEnv&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [2,4]; title: ; notranslate">
user@users-MacBook-Pro test_projects % cd project_1
user@users-MacBook-Pro project_1 % pyenv local 2.7.18
user@users-MacBook-Pro project_1 % python --version
Python 2.7.18
user@users-MacBook-Pro project_1 % 
</pre></div>


<p>Notice how we use the command <code>pyenv local</code> rather than <code>pyenv global</code>.  Using the local syntax sets the Python distro you wish to use in this local directory.  So now let&#8217;s go to project_2&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [2,4]; title: ; notranslate">
user@users-MacBook-Pro test_projects % cd project_2
user@users-MacBook-Pro project_2 % pyenv local 3.5.1
user@users-MacBook-Pro project_2 % python --version
Python 3.5.1
user@users-MacBook-Pro project_2 % 
</pre></div>


<p>Nothing too surprising here.  So now let&#8217;s go to project_3 and just see what the default is&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [4]; title: ; notranslate">
user@users-MacBook-Pro project_2 % cd ..
user@users-MacBook-Pro test_projects % cd project_3
user@users-MacBook-Pro project_3 % python --version
Python 3.8.5
user@users-MacBook-Pro project_3 % 
</pre></div>


<p>So we&#8217;re back to 3.8.5 which makes sense given that <code>3.8.5</code> is our global version and we haven&#8217;t set a local version here.  Ready for the cool part?  Go back to project_1 or project_2 and check again&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [4,8]; title: ; notranslate">
user@users-MacBook-Pro project_3 % cd ..
user@users-MacBook-Pro test_projects % cd project_1
user@users-MacBook-Pro project_1 % python --version
Python 2.7.18
user@users-MacBook-Pro project_1 % cd ..
user@users-MacBook-Pro test_projects % cd project_2    
user@users-MacBook-Pro project_2 % python --version
Python 3.5.1
user@users-MacBook-Pro project_2 % 
</pre></div>


<p>It automagically switches to the correct version based on what directory we&#8217;re in!  How cool is that?!  How does it do that?  Well when we set the local version it creates a local file <code>.python-version</code> that holds the version number&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7]; title: ; notranslate">
user@users-MacBook-Pro project_2 % ls -al
total 8
drwxr-xr-x  3 user  staff   96 Sep 11 20:10 .
drwxr-xr-x  6 user  staff  192 Sep 11 20:07 ..
-rw-r--r--  1 user  staff    6 Sep 11 20:10 .python-version
user@users-MacBook-Pro project_2 % more .python-version 
3.5.1
user@users-MacBook-Pro project_2 % 
</pre></div>


<p>Now you might be wondering how this all works though.  The fundamental thing that makes PyEnv work are &#8220;shims&#8221;.  They&#8217;re basically scripts that redirect commands to PyEnv.  We enabled this by making those initial changes to our <code>.zshrc</code> file above.  If we look at our <code>$PATH</code> variable now we&#8217;ll see it has changed&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [2]; title: ; notranslate">
user@users-MacBook-Pro ~ % echo $PATH
/Users/user/.pyenv/shims:/Users/user/.pyenv/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
user@users-MacBook-Pro ~ % 
</pre></div>


<p>Notice how the PyEnv directories are now at the front of the path?  And if we look in that directory we&#8217;ll see &#8220;shims&#8221; for all of the relevant Python commands&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
user@users-MacBook-Pro shims % ls    
2to3			easy_install-3.8	pip2.7			pydoc3.8		python2.7-gdb.py	python3.5m-config
2to3-3.5		idle			pip3			python			python3			python3.8
2to3-3.8		idle3			pip3.5			python-config		python3-config		python3.8-config
chardetect		idle3.5			pip3.8			python2			python3.5		python3.8-gdb.py
easy_install		idle3.8			pydoc			python2-config		python3.5-config	pyvenv
easy_install-2.7	pip			pydoc3			python2.7		python3.5-gdb.py	pyvenv-3.5
easy_install-3.5	pip2			pydoc3.5		python2.7-config	python3.5m		smtpd.py
user@users-MacBook-Pro shims % 
</pre></div>


<p>In fact &#8211; we can even see this if we just look at what Python version we&#8217;re using right now&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
user@users-MacBook-Pro shims % which python
/Users/user/.pyenv/shims/python
user@users-MacBook-Pro shims % 
</pre></div>


<p>So just by calling <code>python</code> a search in <code>PATH</code> catches the PyEnv shim first and returns that rather than the system default Python.   And if we look at this file we&#8217;ll see it&#8217;s a script that just puts PyEnv in the way of our command&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
user@users-MacBook-Pro shims % more /Users/user/.pyenv/shims/python
#!/usr/bin/env bash
set -e
&#x5B; -n &quot;$PYENV_DEBUG&quot; ] &amp;&amp; set -x

program=&quot;${0##*/}&quot;
if &#x5B;&#x5B; &quot;$program&quot; = &quot;python&quot;* ]]; then
  for arg; do
    case &quot;$arg&quot; in
    -c* | -- ) break ;;
    */* )
      if &#x5B; -f &quot;$arg&quot; ]; then
        export PYENV_FILE_ARG=&quot;$arg&quot;
        break
      fi
      ;;
    esac
  done
fi

export PYENV_ROOT=&quot;/Users/user/.pyenv&quot;
exec &quot;/Users/user/.pyenv/libexec/pyenv&quot; exec &quot;$program&quot; &quot;$@&quot;
user@users-MacBook-Pro shims % 
</pre></div>


<p>Pretty cool huh?  If you want to see the actual Python you&#8217;re using you can call the <code>pyenv which</code> command to see the actual path&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [2,4]; title: ; notranslate">
user@users-MacBook-Pro project_1 % pyenv which python
/Users/user/.pyenv/versions/2.7.18/bin/python
user@users-MacBook-Pro project_1 % pyenv which pip   
/Users/user/.pyenv/versions/2.7.18/bin/pip
user@users-MacBook-Pro project_1 % 
</pre></div>


<p>Same goes for PIP and any other Python tools that have shim files.  Speaking of PIP, how does that work with PyEnv?  Let&#8217;s take a look&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [2,7,8,9,10,11,12]; title: ; notranslate">
user@users-MacBook-Pro project_1 % pyenv which python
/Users/user/.pyenv/versions/2.7.18/bin/python
user@users-MacBook-Pro project_1 % pip list
DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won&#039;t be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Package    Version  
---------- ---------
certifi    2020.6.20
chardet    3.0.4    
idna       2.10     
pip        19.2.3   
setuptools 41.2.0   
urllib3    1.25.10  
WARNING: You are using pip version 19.2.3, however version 20.2.3 is available.
You should consider upgrading via the &#039;pip install --upgrade pip&#039; command.
user@users-MacBook-Pro project_1 % 
</pre></div>


<p>Lets say we want to use <code>pyyaml</code>.  Let&#8217;s install that quick&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [1,18]; title: ; notranslate">
user@users-MacBook-Pro project_1 % pip install pyyaml
DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won&#039;t be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Collecting pyyaml
  Using cached https://files.pythonhosted.org/packages/64/c2/b80047c7ac2478f9501676c988a5411ed5572f35d1beff9cae07d321512c/PyYAML-5.3.1.tar.gz
Installing collected packages: pyyaml
  Running setup.py install for pyyaml ... done
Successfully installed pyyaml-5.3.1
WARNING: You are using pip version 19.2.3, however version 20.2.3 is available.
You should consider upgrading via the &#039;pip install --upgrade pip&#039; command.
user@users-MacBook-Pro project_1 % pip list
DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won&#039;t be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Package    Version  
---------- ---------
certifi    2020.6.20
chardet    3.0.4    
idna       2.10     
pip        19.2.3   
PyYAML     5.3.1    
setuptools 41.2.0   
urllib3    1.25.10  
WARNING: You are using pip version 19.2.3, however version 20.2.3 is available.
You should consider upgrading via the &#039;pip install --upgrade pip&#039; command.
user@users-MacBook-Pro project_1 % 
</pre></div>


<p>Cool &#8211; now let&#8217;s go and take a look at one of our other Python installs&#8230;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [4,6,7]; title: ; notranslate">
user@users-MacBook-Pro project_1 % cd ..
user@users-MacBook-Pro test_projects % cd project_2
user@users-MacBook-Pro project_2 % pyenv which python
/Users/user/.pyenv/versions/3.5.1/bin/python
user@users-MacBook-Pro project_2 % pip list
pip (7.1.2)
setuptools (18.2)
You are using pip version 7.1.2, however version 20.2.3 is available.
You should consider upgrading via the &#039;pip install --upgrade pip&#039; command.
user@users-MacBook-Pro project_2 % 
</pre></div>


<p>No <code>pyyaml</code>.  So it seems to be that the PIP installs are per &#8220;environment&#8221; or version.  Let&#8217;s test this out by setting a local Python version for project_3 that matches project_1&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [6,10,11,12,13,14,15,16,19,28]; title: ; notranslate">
user@users-MacBook-Pro project_3 % cd ..
user@users-MacBook-Pro test_projects % cd project_2
user@users-MacBook-Pro project_2 % cd ..
user@users-MacBook-Pro test_projects % cd project_3
user@users-MacBook-Pro project_3 % pyenv which python
/Users/user/.pyenv/versions/3.8.5/bin/python
user@users-MacBook-Pro project_3 % pip list
Package    Version
---------- ---------
certifi    2020.6.20
chardet    3.0.4
idna       2.10
pip        20.1.1
requests   2.24.0
setuptools 47.1.0
urllib3    1.25.10
WARNING: You are using pip version 20.1.1; however, version 20.2.3 is available.
You should consider upgrading via the &#039;/Users/user/.pyenv/versions/3.8.5/bin/python3.8 -m pip install --upgrade pip&#039; command.
user@users-MacBook-Pro project_3 % pyenv local 2.7.18
user@users-MacBook-Pro project_3 % pip list
DEPRECATION: Python 2.7 will reach the end of its life on January 1st, 2020. Please upgrade your Python as Python 2.7 won&#039;t be maintained after that date. A future version of pip will drop support for Python 2.7. More details about Python 2 support in pip, can be found at https://pip.pypa.io/en/latest/development/release-process/#python-2-support
Package    Version  
---------- ---------
certifi    2020.6.20
chardet    3.0.4    
idna       2.10     
pip        19.2.3   
PyYAML     5.3.1    
setuptools 41.2.0   
urllib3    1.25.10  
WARNING: You are using pip version 19.2.3, however version 20.2.3 is available.
You should consider upgrading via the &#039;pip install --upgrade pip&#039; command.
user@users-MacBook-Pro project_3 % 
</pre></div>


<p>We can see that in the global version (<code>3.8.5</code>) we didn&#8217;t have the <code>pyyaml</code> library but the instant we switched to version <code>2.7.18</code> we inherited all of the packages that came with that version.  Pretty cool right?  So while we have package management by version we still don&#8217;t have package management by project which is what we really want.  The solution for that?  Virtual environments of course! Luckily PyEnv works well with those as well and we&#8217;ll start exploring those in our next post.  Stay tuned!</p>
]]></content:encoded>
					
					<wfw:commentRss>http://www.dasblinkenlichten.com/python-pieces-using-pyenv/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">23353</post-id>	</item>
		<item>
		<title>Working with TC on Linux systems</title>
		<link>http://www.dasblinkenlichten.com/working-with-tc-on-linux-systems/</link>
					<comments>http://www.dasblinkenlichten.com/working-with-tc-on-linux-systems/#comments</comments>
		
		<dc:creator><![CDATA[Jon Langemak]]></dc:creator>
		<pubDate>Wed, 01 Jul 2020 16:55:38 +0000</pubDate>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[Ubuntu]]></category>
		<guid isPermaLink="false">http://www.dasblinkenlichten.com/?p=23337</guid>

					<description><![CDATA[Hi folks! Long time no talk : ) Life has been incredibly busy for me over the last few months so I&#8217;ll apologize in advance for the lack of posts.... <a href="http://www.dasblinkenlichten.com/working-with-tc-on-linux-systems/">Read more &#187;</a>]]></description>
										<content:encoded><![CDATA[
<p>Hi folks!  Long time no talk : ) Life has been incredibly busy for me over the last few months so I&#8217;ll apologize in advance for the lack of posts.  However &#8211; I&#8217;m aiming to get back on the horse so please stay tuned!</p>



<p>With that out of the way &#8211; I wanted to spend some time in this post talking about the command line tool found on Linux systems called <code>tc</code>.  We&#8217;ve talked about <code>tc</code> <a href="https://www.dasblinkenlichten.com/simulating-latency-and-packet-loss-on-a-linux-host/">before </a>when we discussed creating some network/traffic simulated topologies and it worked awesome for that use case.  If you recall from that earlier post <code>tc</code> is short for Traffic Control and allows users to configure <code>qdiscs</code>.  A <code>qdisc</code> is short for Queuing Discipline.  I like to think of it as manipulating the Linux kernels packet scheduler.</p>



<p><em>Note: <code>tc</code> is traditionally part of the <code>iproute2</code> toolset which Im pretty sure (but not positive) is included in most base Linux distros these days. </em></p>



<p>When <code>tc</code> comes up &#8211; it&#8217;s easy to immediately start thinking about QOS, queuing, and packet(traffic) control.  And while some of the actions available to you when using <code>tc</code> seem obvious, or at least fit within the mindset of queue disciplines (the drop action comes to mind here), you might be surprised to learn that <code>tc</code> can actually do much more.  For instance, it can do things like perform encapsulation and modify packet/frame headers.  But &#8211; rather than keep rambling, let&#8217;s jump into a basic example.  Per usual &#8211; let&#8217;s start with what our lab will look like&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="281" height="345" src="https://www.dasblinkenlichten.com/wp-content/uploads/2020/06/image-2.png" alt="" class="wp-image-23345" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2020/06/image-2.png 281w, http://www.dasblinkenlichten.com/wp-content/uploads/2020/06/image-2-244x300.png 244w, http://www.dasblinkenlichten.com/wp-content/uploads/2020/06/image-2-163x200.png 163w" sizes="(max-width: 281px) 100vw, 281px" /></figure></div>



<p>To start with, we&#8217;ll begin with a single host (it&#8217;s really just a VM), test_host_1.  The host is supporting two different network namespaces called <code>NS1</code> and <code>NS2</code>.  They are connected back to the main or default network namespace through the use of VETH pairs.  One end of the VETH pair resides in the network namespace while the other end resides in default or main namespace of the host.  In addition, the host has a single NIC <code>ens6</code> which we wont be using much in this post at all.  </p>



<p><em>Note: I&#8217;m assuming any and all test hosts used in this lab are newly hosts (Im using Ubuntu 18). with no underlying network configuration besides that of ens6</em>.</p>



<p>So let&#8217;s go ahead and do the base namespace configuration&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
ip netns add ns1
ip link add ns1_veth_ns type veth peer name ns1_veth_float
ip link set ns1_veth_ns netns ns1
ip link set dev ns1_veth_float up
ip netns exec ns1 ip addr add 192.168.10.1/24 dev ns1_veth_ns
ip netns exec ns1 ip link set ns1_veth_ns address 14:ec:d4:01:f1:2b
ip netns exec ns1 ip link set ns1_veth_ns up
ip netns exec ns1 ip link set lo up

ip netns add ns2
ip link add ns2_veth_ns type veth peer name ns2_veth_float
ip link set ns2_veth_ns netns ns2
ip link set dev ns2_veth_float up
ip netns exec ns2 ip addr add 192.168.10.2/24 dev ns2_veth_ns
ip netns exec ns2 ip link set ns2_veth_ns address 84:12:5d:2f:d2:4c
ip netns exec ns2 ip link set ns2_veth_ns up
ip netns exec ns2 ip link set lo up
</pre></div>


<p>So at this point, each NS is up and alive and should have the correct VETH pair configuration.  But if you&#8217;ve noticed, they aren&#8217;t able to talk to each other.  Why is this?..</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@test_host_1:~# ip netns exec ns1 ping 192.168.10.2 -c 3
PING 192.168.10.2 (192.168.10.2) 56(84) bytes of data.
From 192.168.10.1 icmp_seq=1 Destination Host Unreachable
From 192.168.10.1 icmp_seq=2 Destination Host Unreachable
From 192.168.10.1 icmp_seq=3 Destination Host Unreachable

--- 192.168.10.2 ping statistics ---
3 packets transmitted, 0 received, +3 errors, 100% packet loss, time 2038ms
pipe 3
root@test_host_1:~# 
</pre></div>


<p>The answer is pretty simple &#8211; we haven&#8217;t done anything with the other end of the VETH pairs that live in the default network namespace.  They are as I like to say &#8220;floating&#8221; at this point.  Typically, in the case of Docker or any other containerization kind of technology these ends that are currently floating would plug into a bridge.  The bridge would allow all the things we need to occur (MAC learning, ARP, etc) to happen so that the two NS could talk.  The problem right now is that the ARP request packets are showing up on the floating side of the VETH pair and just getting ditched because there&#8217;s no where else for them to go.  L2 broadcast frames like this would typically be flooded within a given broadcast domain but the problem is that this broadcast domain begins and ends on each side of the VETH pair.  If the floating side were to be plugged into a bridge that bridge would propagate the ARP broadcasts to other VETH pairs and everything would work out just fine.  </p>



<p>So how do we fix this?  Well &#8211; we can actually write some <code>tc</code> rules that make things appear to be in the same broadcast domain.  To do that, we first need to enable a <code>qdisc</code> or queuing discipline on the interfaces in question.  In our case &#8211; that would be the floating sides of the VETH pairs.  Let&#8217;s do that now on both of the interfaces just to get it out of the way&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
tc qdisc add dev ns1_veth_float ingress
tc qdisc add dev ns2_veth_float ingress
</pre></div>


<p>This syntax is pretty straightforward &#8211; we&#8217;re adding an <code>ingress</code> queuing discipline to both of the VETH interfaces floating sides that are still in the default name space.  Now comes the interesting part &#8211; <code>tc</code> has a classifier referred to as Flower.  From my understanding there was already something similiar named flow so they just called this one flower but it allows you to select traffic quite easily based on well know header fields such as IP, Mac address, VLAN, etc.  So let&#8217;s take a look at writing some rules that do this.  What&#8217;s the first thing that our two namespace need to do in order to talk?  ARP!  So let&#8217;s write some rules that let that happen&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
tc filter add dev ns1_veth_float protocol arp parent ffff: prio 1 \
flower \
dst_mac ff:ff:ff:ff:ff:ff \
src_mac 14:ec:d4:01:f1:2b \
action mirred egress redirect dev ns2_veth_float
</pre></div>


<p>Once again, I think this is fairly easy to read but let&#8217;s talk through it for the sake of making sure we&#8217;re on the same page.  Here&#8217;s how the above breaks down&#8230;.</p>



<p><strong>Line 1 </strong>&#8211; Add a filter to the floating side of the NS1 VETH pair interface that matches protocol ARP.  Assign it to the ingress queue of the interface (<code>ffff</code> is the static reference to this) and make this filter rule the 1st priority.</p>



<p><strong>Line 2 </strong>&#8211; For filter matching rules we&#8217;ll be using the flower classifier</p>



<p><strong>Line 3</strong> &#8211; Match on a broadcast destination MAC</p>



<p><strong>Line 4</strong> &#8211; Match on a source Mac address of the NS side of NS1 VETH pair (now you know why we set static MAC addresses when we built those).</p>



<p><strong>Line 5 </strong>&#8211; If all of the above matches, on egress redirect the traffic to the NS2 VETH pair floating interface. </p>



<p>So let&#8217;s put this rule in and see what happens! </p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@test_host_1:~# tc filter add dev ns1_veth_float protocol arp parent ffff: prio 1 \
&gt; flower \
&gt; dst_mac ff:ff:ff:ff:ff:ff \
&gt; src_mac 14:ec:d4:01:f1:2b \
&gt; action mirred egress redirect dev ns2_veth_float
root@test_host_1:~#
</pre></div>


<p>If the rule goes in with no feedback &#8211; you&#8217;re off to the races.  If you get the dreaded&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
RTNETLINK answers: Invalid argument
We have an error talking to the kernel
</pre></div>


<p>It means something went wrong.  Make sure you added the qdisc to the interface you&#8217;re working with as that&#8217;s a requirement before writing filter rules.  Then double check your syntax and try whittling the rule down until you find the problem statement.  In our case though it worked!  We can see the rule is applied by using this command&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [1]; title: ; notranslate">
root@test_host_1:~# tc -s filter show dev ns1_veth_float parent ffff:
filter protocol arp pref 1 flower chain 0 
filter protocol arp pref 1 flower chain 0 handle 0x1 
  dst_mac ff:ff:ff:ff:ff:ff
  src_mac 14:ec:d4:01:f1:2b
  eth_type arp
  not_in_hw
	action order 1: mirred (Egress Redirect to device ns2_veth_float) stolen
 	index 1 ref 1 bind 1 installed 6 sec used 6 sec
 	Action statistics:
	Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) 
	backlog 0b 0p requeues 0

root@test_host_1:~# 
</pre></div>


<p>Awesome &#8211; looks good.  So now try a ping from NS1 to NS2 and see what we get&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@test_host_1:~# ip netns exec ns1 ping 192.168.10.2
PING 192.168.10.2 (192.168.10.2) 56(84) bytes of data.
</pre></div>


<p>No response, but if we look at the rule we should see hits&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [11]; title: ; notranslate">
root@test_host_1:~# tc -s filter show dev ns1_veth_float parent ffff:
filter protocol arp pref 1 flower chain 0 
filter protocol arp pref 1 flower chain 0 handle 0x1 
  dst_mac ff:ff:ff:ff:ff:ff
  src_mac 14:ec:d4:01:f1:2b
  eth_type arp
  not_in_hw
	action order 1: mirred (Egress Redirect to device ns2_veth_float) stolen
 	index 1 ref 1 bind 1 installed 205 sec used 191 sec
 	Action statistics:
	Sent 84 bytes 3 pkt (dropped 0, overlimits 0 requeues 0) 
	backlog 0b 0p requeues 0

root@test_host_1:~# 
</pre></div>


<p>And if you were doing a tcpdump on <code>ns2_veth_float</code> you would have seen not only the traffic arrive, but also a reply from NS2.  NS2 doesn&#8217;t need any rules to generate a reply back over it&#8217;s VETH pair&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@test_host_1:~# tcpdump -nne -i ns2_veth_float
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ns2_veth_float, link-type EN10MB (Ethernet), capture size 262144 bytes
01:45:58.442778 14:ec:d4:01:f1:2b &gt; ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 192.168.10.2 tell 192.168.10.1, length 28
01:45:58.442798 84:12:5d:2f:d2:4c &gt; 14:ec:d4:01:f1:2b, ethertype ARP (0x0806), length 42: Reply 192.168.10.2 is-at 84:12:5d:2f:d2:4c, length 28
01:45:59.473754 14:ec:d4:01:f1:2b &gt; ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 192.168.10.2 tell 192.168.10.1, length 28
01:45:59.473763 84:12:5d:2f:d2:4c &gt; 14:ec:d4:01:f1:2b, ethertype ARP (0x0806), length 42: Reply 192.168.10.2 is-at 84:12:5d:2f:d2:4c, length 28
01:46:00.497742 14:ec:d4:01:f1:2b &gt; ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 192.168.10.2 tell 192.168.10.1, length 28
01:46:00.497750 84:12:5d:2f:d2:4c &gt; 14:ec:d4:01:f1:2b, ethertype ARP (0x0806), length 42: Reply 192.168.10.2 is-at 84:12:5d:2f:d2:4c, length 28
</pre></div>


<p>Alright &#8211; we&#8217;re doing great!  But we need some more rules for this to work end to end at layer 3. Let&#8217;s add the same rule for the ARP broadcast on the other interface just in case the initial request comes from NS2 (remember that for every interface you have to define the <code>qdisc</code> the first time!)&#8230;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
tc qdisc add dev ns2_veth_float ingress
tc filter add dev ns2_veth_float protocol arp parent ffff: prio 1 \
flower \
dst_mac ff:ff:ff:ff:ff:ff \
src_mac 84:12:5d:2f:d2:4c \
action mirred egress redirect dev ns1_veth_float
</pre></div>


<p> Now &#8211; this hasn&#8217;t gotten us any further down the road to L3 connectivity, so what rule do we need to tackle next?  Well &#8211; when NS1 pings NS2 we see that NS2 is generating the ARP reply, but if look at the interface <code>ns1_veth_float</code> we see that the reply is never getting there&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@test_host_1:~# tcpdump -nne -i ns1_veth_float
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ns1_veth_float, link-type EN10MB (Ethernet), capture size 262144 bytes
02:13:49.329808 14:ec:d4:01:f1:2b &gt; ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 192.168.10.2 tell 192.168.10.1, length 28
02:13:50.353738 14:ec:d4:01:f1:2b &gt; ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 192.168.10.2 tell 192.168.10.1, length 28
02:13:51.377736 14:ec:d4:01:f1:2b &gt; ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 192.168.10.2 tell 192.168.10.1, length 28
02:13:52.401797 14:ec:d4:01:f1:2b &gt; ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 192.168.10.2 tell 192.168.10.1, length 28
</pre></div>


<p>That&#8217;s because we have no matching <code>tc</code> rule!  And also because we&#8217;re doing our own L2 semantics here to make this work.  So we need to add rules to make the ARP reply work as well.  Those might look like this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
tc filter add dev ns2_veth_float protocol arp parent ffff: prio 2 \
flower \
dst_mac 14:ec:d4:01:f1:2b  \
src_mac 84:12:5d:2f:d2:4c \
action mirred egress redirect dev ns1_veth_float

tc filter add dev ns1_veth_float protocol arp parent ffff: prio 2 \
flower \
dst_mac 84:12:5d:2f:d2:4c \
src_mac 14:ec:d4:01:f1:2b \
action mirred egress redirect dev ns2_veth_float
</pre></div>


<p>The above rules look a lot like the first two rules we entered in, but they&#8217;re not looking for broadcasts.  Rather &#8211; they&#8217;re looking to match an ARP reply which would be direct MAC to MAC communication.  If we put those two rules in &#8211; we ought to see a full ARP conversation take place in our captures&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [4,5,6]; title: ; notranslate">
root@test_host_1:~# tcpdump -nne -i ns1_veth_float
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ns1_veth_float, link-type EN10MB (Ethernet), capture size 262144 bytes
02:16:56.062897 14:ec:d4:01:f1:2b &gt; ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 192.168.10.2 tell 192.168.10.1, length 28
02:16:56.062913 84:12:5d:2f:d2:4c &gt; 14:ec:d4:01:f1:2b, ethertype ARP (0x0806), length 42: Reply 192.168.10.2 is-at 84:12:5d:2f:d2:4c, length 28
02:16:56.062916 14:ec:d4:01:f1:2b &gt; 84:12:5d:2f:d2:4c, ethertype IPv4 (0x0800), length 98: 192.168.10.1 &gt; 192.168.10.2: ICMP echo request, id 2872, seq 1, length 64
</pre></div>


<p>Awesome!  So on the first higlighted line we see the ARP request, followed by the ARP reply, and then the actual ICMP echo request since NS1 now has a valid ARP entry for NS2.  But alas, ping is still not working.  So we need to add one more rule to allow that&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
tc filter add dev ns2_veth_float protocol ip parent ffff: prio 3 \
flower \
dst_mac 14:ec:d4:01:f1:2b \
src_mac 84:12:5d:2f:d2:4c \
action mirred egress redirect dev ns1_veth_float

tc filter add dev ns1_veth_float protocol ip parent ffff: prio 3 \
flower \
dst_mac 84:12:5d:2f:d2:4c \
src_mac 14:ec:d4:01:f1:2b \
action mirred egress redirect dev ns2_veth_float
</pre></div>


<p>The above rules should look awfully familiar as the only thing that&#8217;s changed is the protocol type from <code>arp</code> to <code>ip</code>.  But that&#8217;s all we need for this to work.  So let&#8217;s see a full capture&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@test_host_1:~# tcpdump -nne -i ns1_veth_float
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on ns1_veth_float, link-type EN10MB (Ethernet), capture size 262144 bytes
02:20:09.759492 14:ec:d4:01:f1:2b &gt; ff:ff:ff:ff:ff:ff, ethertype ARP (0x0806), length 42: Request who-has 192.168.10.2 tell 192.168.10.1, length 28
02:20:09.759594 84:12:5d:2f:d2:4c &gt; 14:ec:d4:01:f1:2b, ethertype ARP (0x0806), length 42: Reply 192.168.10.2 is-at 84:12:5d:2f:d2:4c, length 28
02:20:09.759599 14:ec:d4:01:f1:2b &gt; 84:12:5d:2f:d2:4c, ethertype IPv4 (0x0800), length 98: 192.168.10.1 &gt; 192.168.10.2: ICMP echo request, id 2894, seq 1, length 64
02:20:09.759631 84:12:5d:2f:d2:4c &gt; 14:ec:d4:01:f1:2b, ethertype IPv4 (0x0800), length 98: 192.168.10.2 &gt; 192.168.10.1: ICMP echo reply, id 2894, seq 1, length 64
02:20:10.769757 14:ec:d4:01:f1:2b &gt; 84:12:5d:2f:d2:4c, ethertype IPv4 (0x0800), length 98: 192.168.10.1 &gt; 192.168.10.2: ICMP echo request, id 2894, seq 2, length 64
02:20:10.769773 84:12:5d:2f:d2:4c &gt; 14:ec:d4:01:f1:2b, ethertype IPv4 (0x0800), length 98: 192.168.10.2 &gt; 192.168.10.1: ICMP echo reply, id 2894, seq 2, length 64
</pre></div>


<p>And there you have it!  A full L3 conversation done entirely with <code>tc</code> rules!  While this isn&#8217;t a super useful example, I hope it delivers the point that <code>tc</code> is capable of a lot!  In the next post, we&#8217;ll look at some more useful <code>tc</code> actions and extend our lab a little bit.  Stay tuned!</p>
]]></content:encoded>
					
					<wfw:commentRss>http://www.dasblinkenlichten.com/working-with-tc-on-linux-systems/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">23337</post-id>	</item>
		<item>
		<title>Carrier supporting Carrier with BGP-LU</title>
		<link>http://www.dasblinkenlichten.com/carrier-supporting-carrier-with-bgp-lu/</link>
					<comments>http://www.dasblinkenlichten.com/carrier-supporting-carrier-with-bgp-lu/#respond</comments>
		
		<dc:creator><![CDATA[Jon Langemak]]></dc:creator>
		<pubDate>Sat, 16 Nov 2019 01:29:47 +0000</pubDate>
				<category><![CDATA[Juniper]]></category>
		<category><![CDATA[MPLS]]></category>
		<guid isPermaLink="false">http://www.dasblinkenlichten.com/?p=23320</guid>

					<description><![CDATA[In our last post we talked about the less used method of deploying CsC where we ran OSPF and LDP inside the CSC-PE routing-instance. Note: I can&#8217;t help myself apparently... <a href="http://www.dasblinkenlichten.com/carrier-supporting-carrier-with-bgp-lu/">Read more &#187;</a>]]></description>
										<content:encoded><![CDATA[
<p>In <a href="http://www.dasblinkenlichten.com/carrier-supporting-carrier-with-ospf-and-ldp/">our last post</a> we talked about the less used method of deploying CsC where we ran OSPF and LDP inside the CSC-PE routing-instance.  </p>



<p><em>Note: I can&#8217;t help myself apparently so be aware that Carrier of Carriers (CoC) is the same as Carrier supporting Carrier (CsC)</em></p>



<p>This required some changes to be made to our default LDP export policy as well as how we moved routes between the <code>inet.3</code> and <code>inet.0</code> tables.  That being said, if you&#8217;re a single org it might make good sense to run things that way.  I liked how you were able to see all of the remote LDP domain loopbacks in your local <code>inet.3</code> table which in my mind made it easier to imagine the LSP paths.  </p>



<p>That being said, it is clearly not the preferred deployment methodology.  Most examples you&#8217;ll find leverage BGP (BGP-LU specifically) for the CSC-CE to CSC-PE connections as well as within the local label domains.  So in this example, we&#8217;ll do just that.  Larges chunks of the base configuration will be the same as they were in the previous post but for the sake of clarity I&#8217;ll post our starting post the starting configurations and diagrams here once again.  Here is our base lab topology&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter size-full"><a href="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-20.png"><img loading="lazy" decoding="async" width="764" height="483" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-20.png" alt="" class="wp-image-23286" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-20.png 764w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-20-200x126.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-20-300x190.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-20-624x394.png 624w" sizes="(max-width: 764px) 100vw, 764px" /></a></figure></div>



<p>And once again we&#8217;ll split the lab into the orange carrier, who has a network on the left and the right, and the green carrier, who has the top network&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><a href="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-25.png"><img loading="lazy" decoding="async" width="886" height="508" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-25.png" alt="" class="wp-image-23292" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-25.png 886w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-25-200x115.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-25-300x172.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-25-768x440.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-25-624x358.png 624w" sizes="(max-width: 886px) 100vw, 886px" /></a></figure></div>



<p>As before, our goal is to make the orange carrier whole by leveraging connectivity through the green carrier so that the orange carrier can support a VPN service for its customer CE routers.  The interesting bit about this is that the connectivity we leverage in the green carrier is itself a VPN hence the terminology of Carrier supporting Carrier.  So let&#8217;s take a look at our base configuration for each router once again&#8230;</p>



<p><em>Note: Im not going to talk through all the specifics of the base configuration in this post.  If you&#8217;re curious about it &#8211; take a look at the walk through of the base configuration <a href="http://www.dasblinkenlichten.com/carrier-supporting-carrier-with-ospf-and-ldp/">in the last post</a>.</em></p>



<h2 class="wp-block-heading">vMX1</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 10.10.10.1/24
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.0/31
set routing-options autonomous-system 65001
set protocols bgp group to_provider type external
set protocols bgp group to_provider export customer_routes
set protocols bgp group to_provider neighbor 169.254.10.1 peer-as 65002
set policy-options prefix-list customer_routes 10.10.10.0/24
set policy-options policy-statement customer_routes term direct from protocol direct
set policy-options policy-statement customer_routes term direct from prefix-list customer_routes
set policy-options policy-statement customer_routes term direct then accept
set policy-options policy-statement customer_routes then reject

</pre></div>


<h2 class="wp-block-heading">vMX2</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.1/31
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.2/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 2.2.2.2/32 primary
set routing-options router-id 2.2.2.2
set protocols mpls interface ge-0/0/1.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
set protocols ldp interface ge-0/0/1.0
set routing-instances customer instance-type vrf
set routing-instances customer interface ge-0/0/0.0
set routing-instances customer route-distinguisher 100:100
set routing-instances customer vrf-target target:100:100
set routing-instances customer vrf-table-label
set routing-instances customer routing-options autonomous-system 65002
set routing-instances customer protocols bgp group customer1 type external
set routing-instances customer protocols bgp group customer1 neighbor 169.254.10.0 peer-as 65001
</pre></div>


<h2 class="wp-block-heading">vMX3</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.3/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.4/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 3.3.3.3/32 primary
set routing-options router-id 3.3.3.3
set protocols mpls interface ge-0/0/0.0
set protocols mpls interface ge-0/0/1.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
set protocols ldp interface ge-0/0/0.0
set protocols ldp interface ge-0/0/1.0
</pre></div>


<h2 class="wp-block-heading">vMX4</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.5/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.6/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 4.4.4.4/32 primary
set routing-options router-id 4.4.4.4
set protocols mpls interface ge-0/0/0.0
set protocols mpls interface ge-0/0/1.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
set protocols ldp interface ge-0/0/0.0
set protocols ldp interface ge-0/0/1.0
</pre></div>


<h2 class="wp-block-heading">vMX5</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.7/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.8/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 5.5.5.5/32 primary
set routing-options router-id 5.5.5.5
set protocols mpls interface ge-0/0/0.0
set protocols mpls interface ge-0/0/1.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
set protocols ldp interface ge-0/0/0.0
set protocols ldp interface ge-0/0/1.0
</pre></div>


<h2 class="wp-block-heading">vMX6</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.9/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.10/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 6.6.1.1/32
set routing-options router-id 6.6.1.1
set protocols rsvp interface ge-0/0/1.0
set protocols mpls label-switched-path to-vmx9 to 9.9.1.1
set protocols mpls interface ge-0/0/1.0
set protocols ospf traffic-engineering
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
</pre></div>


<h2 class="wp-block-heading">vMX7</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.11/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.12/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 7.7.1.1/32 primary
set routing-options router-id 7.7.1.1
set protocols rsvp interface ge-0/0/1.0
set protocols rsvp interface ge-0/0/0.0
set protocols mpls interface ge-0/0/1.0
set protocols mpls interface ge-0/0/0.0
set protocols ospf traffic-engineering
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
</pre></div>


<h2 class="wp-block-heading">vMX8</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.13/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.14/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 8.8.1.1/32 primary
set routing-options router-id 8.8.1.1
set protocols rsvp interface ge-0/0/1.0
set protocols rsvp interface ge-0/0/0.0
set protocols mpls interface ge-0/0/1.0
set protocols mpls interface ge-0/0/0.0
set protocols ospf traffic-engineering
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
</pre></div>


<h2 class="wp-block-heading">vMX9</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.15/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.16/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 9.9.1.1/32
set routing-options router-id 9.9.1.1
set routing-options autonomous-system 65000
set protocols rsvp interface ge-0/0/0.0
set protocols mpls label-switched-path to-vmx6 to 6.6.1.1
set protocols mpls interface ge-0/0/0.0
set protocols ospf traffic-engineering
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
</pre></div>


<h2 class="wp-block-heading">vMX10</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.17/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.18/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 10.10.10.10/32 primary
set routing-options router-id 10.10.10.10
set protocols mpls interface ge-0/0/0.0
set protocols mpls interface ge-0/0/1.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
set protocols ldp interface ge-0/0/0.0
set protocols ldp interface ge-0/0/1.0
</pre></div>


<h2 class="wp-block-heading">vMX11</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.19/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.20/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 11.11.11.11/32 primary
set routing-options router-id 11.11.11.11
set protocols mpls interface ge-0/0/0.0
set protocols mpls interface ge-0/0/1.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
set protocols ldp interface ge-0/0/0.0
set protocols ldp interface ge-0/0/1.0
</pre></div>


<h2 class="wp-block-heading">vMX12</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.21/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.22/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces fxp0 unit 0 family inet address 192.168.127.12/24
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 12.12.12.12/32 primary
set routing-options static route 10.171.200.0/22 next-hop 192.168.127.100
set routing-options router-id 12.12.12.12
set protocols mpls interface ge-0/0/0.0
set protocols mpls interface ge-0/0/1.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
set protocols ldp interface ge-0/0/0.0
set protocols ldp interface ge-0/0/1.0
</pre></div>


<h2 class="wp-block-heading">vMX13</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.23/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.24/31
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 13.13.13.13/32 primary
set routing-options router-id 13.13.13.13
set protocols mpls interface ge-0/0/0.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ldp interface ge-0/0/0.0
set routing-instances customer instance-type vrf
set routing-instances customer interface ge-0/0/1.0
set routing-instances customer route-distinguisher 100:100
set routing-instances customer vrf-target target:100:100
set routing-instances customer vrf-table-label
set routing-instances customer routing-options autonomous-system 65004
set routing-instances customer protocols bgp group customer1 type external
set routing-instances customer protocols bgp group customer1 neighbor 169.254.10.25 peer-as 65003
</pre></div>


<h2 class="wp-block-heading">vMX14</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.25/31
set interfaces ge-0/0/1 unit 0 family inet address 192.168.10.1/24
set routing-options autonomous-system 65003
set protocols bgp group to_provider type external
set protocols bgp group to_provider export customer_routes
set protocols bgp group to_provider neighbor 169.254.10.24 peer-as 65004
set policy-options prefix-list customer_routes 192.168.10.0/24
set policy-options policy-statement customer_routes term direct from protocol direct
set policy-options policy-statement customer_routes term direct from prefix-list customer_routes
set policy-options policy-statement customer_routes term direct then accept
set policy-options policy-statement customer_routes then reject
</pre></div>


<p>Right &#8211; so if you recall from the last post.  This sort of got us what I call our &#8220;base label domain&#8221; configuration.  That is &#8211; each &#8220;block&#8221; should have a means to for an MPLS LSP through it.  This is an exact copy and past of the base configuration from the previous post.  There should be nothing terribly new or exciting to you about this configuration.  </p>



<p>As with the last post, the next step is to configure the CSC-PE routers routing-instance for the orange carrier.  This configuration differs slightly from our last configuration&#8230;</p>



<h2 class="wp-block-heading">vMX6</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-instances orange_carrier instance-type vrf
set routing-instances orange_carrier interface ge-0/0/0.0
set routing-instances orange_carrier route-distinguisher 1:1
set routing-instances orange_carrier vrf-target target:1:1
set routing-instances orange_carrier vrf-table-label
set routing-instances orange_carrier routing-options router-id 6.6.6.6
set routing-instances orange_carrier routing-options autonomous-system 65200
set routing-instances orange_carrier protocols bgp group to_orange_carrier type external
set routing-instances orange_carrier protocols bgp group to_orange_carrier family inet labeled-unicast
set routing-instances orange_carrier protocols bgp group to_orange_carrier neighbor 169.254.10.8 peer-as 65100
set routing-instances orange_carrier protocols mpls interface ge-0/0/0.0
</pre></div>


<p>Notice the lack of OSPF and LDP configuration.  This is now replaced by a BGP peering configuration that will be used to peer to the CSC-PE routers.  vMX9 looks largely the same&#8230;</p>



<h2 class="wp-block-heading">vMX9</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-instances orange_carrier instance-type vrf
set routing-instances orange_carrier interface ge-0/0/1.0
set routing-instances orange_carrier route-distinguisher 1:1
set routing-instances orange_carrier vrf-target target:1:1
set routing-instances orange_carrier vrf-table-label
set routing-instances orange_carrier routing-options router-id 9.9.9.9
set routing-instances orange_carrier routing-options autonomous-system 65200
set routing-instances orange_carrier protocols bgp group to_orange_carrier type external
set routing-instances orange_carrier protocols bgp group to_orange_carrier family inet labeled-unicast
set routing-instances orange_carrier protocols bgp group to_orange_carrier neighbor 169.254.10.17 peer-as 65100
set routing-instances orange_carrier protocols mpls interface ge-0/0/1.0
</pre></div>


<p>Notice that we&#8217;re using the same AS number for the peerings to the CSC-CE routers.  As you might already be guessing, we&#8217;ll have to make some adjustments later on for that to work correctly but for now let&#8217;s table that.  </p>



<p>The next thing to do is to configure BGP within the orange carrier blocks.  To do this, we&#8217;ll peer the PE router to the CSC-CE node iBGP.  Then we&#8217;ll also configure a peering between the CSC-CE and the CSC-PE nodes with eBGP.  Let&#8217;s start on the left side with vMX2&#8230;</p>



<h2 class="wp-block-heading">vMX2</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-options autonomous-system 65100
set protocols bgp group internal type internal
set protocols bgp group internal local-address 2.2.2.2
set protocols bgp group internal family inet labeled-unicast resolve-vpn
set protocols bgp group internal neighbor 5.5.5.5 peer-as 65100
</pre></div>


<p>Nothing too fancy here but do note that we added the flag <code>resolve-vpn</code> to the BGP configuration.  If you recall from our previous posts, this allows the router to add prefixes it receives through BGP-LU to the <code>inet.3</code> table which is required for VPN route resolution.  With that in place, we can configure vMX5 for that same peering as well as another BGP group so it can peer to the CSC-PE node (vMX6 in this case)&#8230;</p>



<h2 class="wp-block-heading">vMX5</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-options autonomous-system 65100
set protocols bgp group internal type internal
set protocols bgp group internal local-address 5.5.5.5
set protocols bgp group internal family inet labeled-unicast
set protocols bgp group internal neighbor 2.2.2.2 peer-as 65100
set protocols bgp group to_green_carrier type external
set protocols bgp group to_green_carrier family inet labeled-unicast
set protocols bgp group to_green_carrier export to_green_carrier
set protocols bgp group to_green_carrier neighbor 169.254.10.9 peer-as 65200
set policy-options policy-statement to_green_carrier term local_loops from prefix-list local_loops
set policy-options policy-statement to_green_carrier term local_loops then accept
set policy-options policy-statement to_green_carrier then reject
set policy-options prefix-list local_loops 2.2.2.2/32
set policy-options prefix-list local_loops 3.3.3.3/32
set policy-options prefix-list local_loops 4.4.4.4/32
set policy-options prefix-list local_loops 5.5.5.5/32
</pre></div>


<p>Notice that we&#8217;re doing the loopback export on the CSC-CE node.  The reason for doing that here and not on vMX2 is that we also know about that route from OSPF.  That is &#8211; vMX5 prefers the route from OSPF so it won&#8217;t advertise a BGP route it learned about through BGP to another BGP peer.  Note that we&#8217;re advertising all of the loopbacks from the left orange block through the BGP-LU session to vMX6.  Now let&#8217;s do the same configuration on the right side orange block&#8230;</p>



<h2 class="wp-block-heading">vMX13</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-options autonomous-system 65100
set protocols bgp group internal type internal
set protocols bgp group internal local-address 13.13.13.13
set protocols bgp group internal family inet labeled-unicast resolve-vpn
set protocols bgp group internal neighbor 10.10.10.10 peer-as 65100
</pre></div>


<h2 class="wp-block-heading">vMX10</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-options autonomous-system 65100
set protocols bgp group internal type internal
set protocols bgp group internal local-address 10.10.10.10
set protocols bgp group internal family inet labeled-unicast
set protocols bgp group internal neighbor 13.13.13.13 peer-as 65100
set protocols bgp group to_green_carrier type external
set protocols bgp group to_green_carrier family inet labeled-unicast
set protocols bgp group to_green_carrier export to_green_carrier
set protocols bgp group to_green_carrier neighbor 169.254.10.16 peer-as 65200
set policy-options policy-statement to_green_carrier term local_loops from prefix-list local_loops
set policy-options policy-statement to_green_carrier term local_loops then accept
set policy-options policy-statement to_green_carrier then reject
set policy-options prefix-list local_loops 10.10.10.10/32
set policy-options prefix-list local_loops 11.11.11.11/32
set policy-options prefix-list local_loops 12.12.12.12/32
set policy-options prefix-list local_loops 13.13.13.13/32

</pre></div>


<p>And last but not least, we need to configure the VPN peering within the green carrier&#8230;</p>



<h2 class="wp-block-heading">vMX6</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-options autonomous-system 65200
set protocols bgp group green_carrier_vpn type internal
set protocols bgp group green_carrier_vpn family inet-vpn unicast
set protocols bgp group green_carrier_vpn neighbor 9.9.1.1 local-address 6.6.1.1
set protocols bgp group green_carrier_vpn neighbor 9.9.1.1 peer-as 65200
</pre></div>


<h2 class="wp-block-heading">vMX9</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-options autonomous-system 65200
set protocols bgp group green_carrier_vpn type internal
set protocols bgp group green_carrier_vpn family inet-vpn unicast
set protocols bgp group green_carrier_vpn neighbor 6.6.1.1 local-address 9.9.1.1
set protocols bgp group green_carrier_vpn neighbor 6.6.1.1 peer-as 65200
</pre></div>


<p>Now what we&#8217;ve done here is slightly changed out BGP configuration to make things easier to read.  The green carrier in this case is wholly inside of AS <code>65200</code> while the orange provider (both blocks) are in AS <code>65100</code>.  Now, we still have the sort of one off eBGP peering between the CE and PE routers in each orange block where we use AS <code>65001</code> to <code>65002</code> and <code>65003</code> to <code>65004</code>.  But to be honest, that really doesn&#8217;t matter too much for us at this point.  As in the last post, that&#8217;s really just a means to get the customer prefixes to the orange PE routers.  It could very easily have been anything else including static routes or an IGP etc.  So our BGP config at this point looks like this&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><a href="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-7.png"><img loading="lazy" decoding="async" width="1015" height="527" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-7.png" alt="" class="wp-image-23324" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-7.png 1015w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-7-300x156.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-7-200x104.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-7-768x399.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-7-624x324.png 624w" sizes="(max-width: 1015px) 100vw, 1015px" /></a></figure></div>



<p>The one BGP peering we&#8217;re clearly missing here is the iBGP VPNv4 peering between vMX2 and vMX13 but before we configure that let&#8217;s make sure we have good reachability&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx3.lab&gt; show route table inet.3 

inet.3: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

2.2.2.2/32         *&#x5B;LDP/9] 00:14:14, metric 1
                    &gt; to 169.254.10.2 via ge-0/0/0.0
4.4.4.4/32         *&#x5B;LDP/9] 00:14:08, metric 1
                    &gt; to 169.254.10.5 via ge-0/0/1.0
5.5.5.5/32         *&#x5B;LDP/9] 00:13:59, metric 1
                    &gt; to 169.254.10.5 via ge-0/0/1.0, Push 299920

admin@vmx3.lab&gt; 
</pre></div>


<p>Well that&#8217;s not what I had expected.  I had hoped that I&#8217;d be getting some BGP-LU routes for the routers in the right orange block that be populated into the <code>inet.3</code> table.  So what&#8217;s going on?  Well &#8211; I sort of mentioned this before, but since each CSC-CE is using the same AS number, we&#8217;ve got an AS loop.  And Juniper is smart enough to know not even to try and advertise routes back to an AS that&#8217;s already in a routes AS path list.  We can validate that here with just one of the prefixes from the right orange block&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7]; title: ; notranslate">
admin@vmx6.lab&gt; show route table orange_carrier.inet.0 11.11.11.11/32              

orange_carrier.inet.0: 11 destinations, 11 routes (11 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

11.11.11.11/32     *&#x5B;BGP/170] 00:14:22, MED 1, localpref 100, from 9.9.1.1
                      AS path: 65100 I, validation-state: unverified
                    &gt; to 169.254.10.11 via ge-0/0/1.0, label-switched-path to-vmx9

admin@vmx6.lab&gt; show route advertising-protocol bgp 169.254.10.8 11.11.11.11/32    

admin@vmx6.lab&gt; 
</pre></div>


<p>Notice that the AS path includes <code>65100</code> which was added by vMX10 as it advertised the loopbacks to vMX9 over the eBGP peering.  Also notice that despite the routes we want being in the table we need them to be in, they aren&#8217;t being sent to vMX5.  To fix this, we can tell the green provider to do an AS override&#8230;</p>



<h2 class="wp-block-heading">vMX6</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-instances orange_carrier protocols bgp group to_orange_carrier neighbor 169.254.10.8 as-override
</pre></div>


<h2 class="wp-block-heading">vMX9</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-instances orange_carrier protocols bgp group to_orange_carrier neighbor 169.254.10.17 as-override 
</pre></div>


<p>AS-override is a commonly misunderstood tool.  It lets a router replace all instances of a peer&#8217;s AS number in the AS-path list with it&#8217;s own.  It&#8217;s important to know that it works on egress.  That is, the AS path will be modified as the prefix leaves the green carrier and is advertised toward the orange carrier.  We can see that now that this configuration is in place&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [5,10,16]; title: ; notranslate">
admin@vmx6.lab&gt; show route receive-protocol bgp 9.9.1.1 11.11.11.11/32   

orange_carrier.inet.0: 11 destinations, 11 routes (11 active, 0 holddown, 0 hidden)
  Prefix		  Nexthop	       MED     Lclpref    AS path
* 11.11.11.11/32          9.9.1.1              1       100        65100 I

bgp.l3vpn.0: 5 destinations, 5 routes (5 active, 0 holddown, 0 hidden)
  Prefix		  Nexthop	       MED     Lclpref    AS path
  1:1:11.11.11.11/32                    
*                         9.9.1.1              1       100        65100 I

admin@vmx6.lab&gt; show route advertising-protocol bgp 169.254.10.8 11.11.11.11/32    

orange_carrier.inet.0: 11 destinations, 11 routes (11 active, 0 holddown, 0 hidden)
  Prefix		  Nexthop	       MED     Lclpref    AS path
* 11.11.11.11/32          Self                                    65200 I

admin@vmx6.lab&gt; 
</pre></div>


<p> We can see that when we get the route from vMX9 the AS path is unchanged.  But when we advertise it to vMX5 we&#8217;ve replaced the <code>65100</code> AS with <code>65200</code>.  Now you might be looking at this and be wondering why that looks wrong.  Indeed, it looks like we&#8217;re missing an AS path entry here.  It&#8217;s each BGP routers job to add it&#8217;s local AS number to the route as it is advertised to an eBGP peer.  In this case, it appears that all we&#8217;ve done is done the replacement.  Where is the AS entry for the route coming from <code>65200</code> in the first place?</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [5]; title: ; notranslate">
admin@vmx5.lab&gt; show route receive-protocol bgp 169.254.10.9 11.11.11.11/32 

inet.0: 20 destinations, 20 routes (20 active, 0 holddown, 0 hidden)
  Prefix		  Nexthop	       MED     Lclpref    AS path
* 11.11.11.11/32          169.254.10.9                            65200 65200 I

admin@vmx5.lab&gt; 
</pre></div>


<p>As it turns out it&#8217;s there.  We just don&#8217;t get to see it with the <code>advertising-protocol</code> command for some reason.  A minor annoyance, but one that can be particularly irritating as you attempt to do troubleshooting.  </p>



<p>Now that we have our AS loop sorted, let&#8217;s look back on vMX2 to see what we have in the <code>inet.3</code> table&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx2.lab&gt; show route table inet.3                        

inet.3: 8 destinations, 8 routes (8 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

3.3.3.3/32         *&#x5B;LDP/9] 00:25:07, metric 1
                    &gt; to 169.254.10.3 via ge-0/0/1.0
4.4.4.4/32         *&#x5B;LDP/9] 00:24:59, metric 1
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Push 299904
5.5.5.5/32         *&#x5B;LDP/9] 00:24:49, metric 1
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Push 299920
10.10.10.10/32     *&#x5B;BGP/170] 00:05:40, localpref 100, from 5.5.5.5
                      AS path: 65200 65200 I, validation-state: unverified
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Push 300608, Push 299920(top)
11.11.11.11/32     *&#x5B;BGP/170] 00:05:40, localpref 100, from 5.5.5.5
                      AS path: 65200 65200 I, validation-state: unverified
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Push 300592, Push 299920(top)
12.12.12.12/32     *&#x5B;BGP/170] 00:05:40, localpref 100, from 5.5.5.5
                      AS path: 65200 65200 I, validation-state: unverified
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Push 300592, Push 299920(top)
13.13.13.13/32     *&#x5B;BGP/170] 00:05:40, localpref 100, from 5.5.5.5
                      AS path: 65200 65200 I, validation-state: unverified
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Push 300592, Push 299920(top)
169.254.10.16/31   *&#x5B;BGP/170] 00:05:40, localpref 100, from 5.5.5.5
                      AS path: 65200 I, validation-state: unverified
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Push 300576, Push 299920(top)

admin@vmx2.lab&gt; 
</pre></div>


<p>Success!  So at this point, we should be able to configure out iBGP VPNv4 peering for the orange carrier&#8230;</p>



<h2 class="wp-block-heading">vMX2</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-options autonomous-system 65100
set protocols bgp group vpn type internal
set protocols bgp group vpn family inet-vpn unicast
set protocols bgp group vpn neighbor 13.13.13.13 local-address 2.2.2.2
set protocols bgp group vpn neighbor 13.13.13.13 peer-as 65100
</pre></div>


<h2 class="wp-block-heading">vMX13</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-options autonomous-system 65100
set protocols bgp group vpn type internal
set protocols bgp group vpn family inet-vpn unicast
set protocols bgp group vpn neighbor 2.2.2.2 local-address 13.13.13.13
set protocols bgp group vpn neighbor 2.2.2.2 peer-as 65100
</pre></div>


<p>And with that complete, our BGP peerings are now complete and should look like this&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter size-large"><a href="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-8.png"><img loading="lazy" decoding="async" width="1015" height="630" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-8.png" alt="" class="wp-image-23327" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-8.png 1015w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-8-300x186.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-8-200x124.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-8-768x477.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-8-624x387.png 624w" sizes="(max-width: 1015px) 100vw, 1015px" /></a></figure></div>



<p>But alas &#8211; our pings still aren&#8217;t working&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
left_client:~# ping 192.168.10.100 -c 5
PING 192.168.10.100 (192.168.10.100) 56(84) bytes of data.
  
--- 192.168.10.100 ping statistics ---
5 packets transmitted, 0 received, 100% packet loss, time 3999ms
  
left_client:~# 
</pre></div>


<p>So what could be going on now?  Well if I were a betting man, I&#8217;d guess that as long as the VPNv4 routes are being sent and accepted between vMX2 and vMX13 that we have a problem with LSP stitching&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx2.lab&gt; show route table customer.inet.0 192.168.10.0/24   

customer.inet.0: 5 destinations, 5 routes (5 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

192.168.10.0/24    *&#x5B;BGP/170] 00:03:55, localpref 100, from 13.13.13.13
                      AS path: 65004 65003 I, validation-state: unverified
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Push 16, Push 300592, Push 299920(top)

admin@vmx2.lab&gt; 
</pre></div>


<p>And that looks good.  So let&#8217;s look at our stitching points&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx10.lab&gt; show route table inet.0 13.13.13.13/32    

inet.0: 20 destinations, 20 routes (20 active, 0 holddown, 0 hidden)
@ = Routing Use Only, # = Forwarding Use Only
+ = Active Route, - = Last Active, * = Both

13.13.13.13/32     *&#x5B;OSPF/10] 00:33:21, metric 3
                    &gt; to 169.254.10.19 via ge-0/0/1.0

admin@vmx10.lab&gt; 
</pre></div>


<p>You see &#8211; the CSC-CE routers think that their best route to get to <code>13.13.13.13/32</code> is through OSPF.  Which is totally fair, but also means that we won&#8217;t be hopping into an LSP on vMX10 to reach vMX13.  And since vMX13 is the endpoint that vMX2 wants to reach&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [6]; title: ; notranslate">
admin@vmx2.lab&gt; show route table customer.inet.0 192.168.10.0/24    

customer.inet.0: 5 destinations, 5 routes (5 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

192.168.10.0/24    *&#x5B;BGP/170] 00:07:36, localpref 100, from 13.13.13.13
                      AS path: 65004 65003 I, validation-state: unverified
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Push 16, Push 300592, Push 299920(top)

admin@vmx2.lab&gt; 
</pre></div>


<p>We now have a problem.  We&#8217;ve covered the fix(es) for this before and in this case, we should be able to get away with the safest option which is to use <code>mpls-forwarding</code>&#8230;</p>



<h2 class="wp-block-heading">vMX5 and vMX10</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set protocols mpls traffic-engineering mpls-forwarding
</pre></div>


<p>With that in place, our preferred route on vMX10 should now become the LSP route from LDP&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [9,10]; title: ; notranslate">
admin@vmx10.lab&gt; show route table inet.0 13.13.13.13/32    

inet.0: 20 destinations, 23 routes (20 active, 0 holddown, 0 hidden)
@ = Routing Use Only, # = Forwarding Use Only
+ = Active Route, - = Last Active, * = Both

13.13.13.13/32     @&#x5B;OSPF/10] 00:00:48, metric 3
                    &gt; to 169.254.10.19 via ge-0/0/1.0
                   #&#x5B;LDP/9] 00:00:48, metric 1
                    &gt; to 169.254.10.19 via ge-0/0/1.0, Push 299904

admin@vmx10.lab&gt; 
</pre></div>


<p>And our pings should now be working as expected&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@left_client:~# ping 192.168.10.100 -c 5
64 bytes from 192.168.10.100: icmp_seq=1 ttl=57 time=3.86 ms
64 bytes from 192.168.10.100: icmp_seq=2 ttl=57 time=4.63 ms
64 bytes from 192.168.10.100: icmp_seq=3 ttl=57 time=4.12 ms
64 bytes from 192.168.10.100: icmp_seq=4 ttl=57 time=6.33 ms
64 bytes from 192.168.10.100: icmp_seq=5 ttl=57 time=4.68 ms
root@left_client:~# 
</pre></div>


<p>Awesome.  So let&#8217;s walk through one direction of this flow with labels just so you can see what&#8217;s going on.  We&#8217;ll start with vMX2&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [8]; title: ; notranslate">
admin@vmx2.lab&gt; show route table customer.inet.0 192.168.10.0/24 

customer.inet.0: 5 destinations, 5 routes (5 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

192.168.10.0/24    *&#x5B;BGP/170] 00:02:19, localpref 100, from 13.13.13.13
                      AS path: 65004 65003 I, validation-state: unverified
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Push 16, Push 300624, Push 299920(top)

admin@vmx2.lab&gt; 
</pre></div>


<p>vMX2 is pushing a whopping 3 labels onto the stack.  Let&#8217;s see what vMX3 does with that top label&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7]; title: ; notranslate">
admin@vmx3.lab&gt; show route table mpls.0 label 299920 

mpls.0: 11 destinations, 11 routes (11 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

299920             *&#x5B;LDP/9] 00:39:30, metric 1
                    &gt; to 169.254.10.5 via ge-0/0/1.0, Swap 299920

admin@vmx3.lab&gt; 
</pre></div>


<p>It will swap it for the same label.  Now onto vMX4&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7]; title: ; notranslate">
admin@vmx4.lab&gt; show route table mpls.0 label 299920 

mpls.0: 11 destinations, 11 routes (11 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

299920             *&#x5B;LDP/9] 00:40:00, metric 1
                    &gt; to 169.254.10.7 via ge-0/0/1.0, Pop      
299920(S=0)        *&#x5B;LDP/9] 00:40:00, metric 1
                    &gt; to 169.254.10.7 via ge-0/0/1.0, Pop      

admin@vmx4.lab&gt; 
</pre></div>


<p>vMX4 is the PHP router for the LDP LSP ending at vMX5 so it will pop the label.  This means that vMX5 will get a top label of <code>300624</code> which was imposed at vMX2&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7]; title: ; notranslate">
admin@vmx5.lab&gt; show route table mpls.0 label 300624 

mpls.0: 19 destinations, 19 routes (19 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

300624             *&#x5B;VPN/170] 00:04:22
                    &gt; to 169.254.10.9 via ge-0/0/1.0, Swap 300832

admin@vmx5.lab&gt; 

</pre></div>


<p>vMX5 will swap that label for <code>300832</code>&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7]; title: ; notranslate">
admin@vmx6.lab&gt; show route table orange_carrier.mpls.0 label 300832 

orange_carrier.mpls.0: 11 destinations, 11 routes (11 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

300832             *&#x5B;VPN/170] 00:05:07, metric2 3, from 9.9.1.1
                    &gt; to 169.254.10.11 via ge-0/0/1.0, label-switched-path to-vmx9

admin@vmx6.lab&gt;    
</pre></div>


<p>vMX6 will use that label to get the traffic into the RSVP LSP to vMX9.  Let&#8217;s look at the actual label operation&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [6]; title: ; notranslate">
admin@vmx6.lab&gt; show route forwarding-table vpn orange_carrier label 300832 
Routing table: orange_carrier.mpls
MPLS:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
300832             user     0                    indr  1048577     2
                              169.254.10.11     Swap 300848, Push 299904(top)      610     2 ge-0/0/1.0

admin@vmx6.lab&gt; 

</pre></div>


<p>Wow &#8211; so vMX6 is going to swap the label of <code>300832</code> for <code>300848</code> and then push <code>299904</code> onto the label.  So our label stack at this point looks like <code>299904</code> <code>300848</code> <code>16</code> (assuming the topmost label is on the left).  It&#8217;s worthwhile here to know where these labels come from.  We can assume that <code>299904</code> is an RSVP label since it&#8217;s the topmost label and we are no in an RSVP domain&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [4]; title: ; notranslate">
admin@vmx6.lab&gt; show rsvp session    
Ingress RSVP: 1 sessions
To              From            State   Rt Style Labelin Labelout LSPname 
9.9.1.1         6.6.1.1         Up       0  1 FF       -   299904 to-vmx9
Total 1 displayed, Up 1, Down 0

Egress RSVP: 1 sessions
To              From            State   Rt Style Labelin Labelout LSPname 
6.6.1.1         9.9.1.1         Up       0  1 FF       3        - to-vmx6
Total 1 displayed, Up 1, Down 0

Transit RSVP: 0 sessions
Total 0 displayed, Up 0, Down 0

admin@vmx6.lab&gt; 
</pre></div>


<p>Jackpot &#8211; now what about <code>300848</code>?</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7,19]; title: ; notranslate">
admin@vmx6.lab&gt; show route receive-protocol bgp 9.9.1.1 13.13.13.13/32 extensive 

orange_carrier.inet.0: 11 destinations, 11 routes (11 active, 0 holddown, 0 hidden)
* 13.13.13.13/32 (1 entry, 1 announced)
     Import Accepted
     Route Distinguisher: 1:1
     VPN Label: 300848
     Nexthop: 9.9.1.1
     MED: 3
     Localpref: 100
     AS path: 65100 I 
     Communities: target:1:1

bgp.l3vpn.0: 5 destinations, 5 routes (5 active, 0 holddown, 0 hidden)

* 1:1:13.13.13.13/32 (1 entry, 0 announced)
     Import Accepted
     Route Distinguisher: 1:1
     VPN Label: 300848
     Nexthop: 9.9.1.1
     MED: 3
     Localpref: 100
     AS path: 65100 I 
     Communities: target:1:1

admin@vmx6.lab&gt; 
</pre></div>


<p>It&#8217;s our BGP LU label used to reach the final LSP endpoint!  Ok, let&#8217;s keep moving along&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [5]; title: ; notranslate">
admin@vmx7.lab&gt; show route forwarding-table label 299904 
Routing table: default.mpls
MPLS:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
299904             user     0 169.254.10.13     Swap 299904      584     2 ge-0/0/1.0

Routing table: __mpls-oam__.mpls
MPLS:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
default            perm     0                    dscd      540     1

admin@vmx7.lab&gt; 
</pre></div>


<p>vMX7 will swap the top label with <code>299904</code> and send it on it&#8217;s way to vMX8&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [5]; title: ; notranslate">
admin@vmx8.lab&gt; show route forwarding-table label 299904 
Routing table: default.mpls
MPLS:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
299904             user     0 169.254.10.15     Pop        584     2 ge-0/0/1.0
299904(S=0)        user     0 169.254.10.15     Pop        585     2 ge-0/0/1.0

Routing table: __mpls-oam__.mpls
MPLS:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
default            perm     0                    dscd      553     1

admin@vmx8.lab&gt; 
</pre></div>


<p>vMX8 is the PHP router in the RSVP domain so it will pop the label and send the remaining label stack to vMX9 (which is <code>300848</code> <code>16</code> (assuming the topmost label is on the left))&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7]; title: ; notranslate">
admin@vmx9.lab&gt; show route table mpls.0 label 300848 

mpls.0: 12 destinations, 12 routes (12 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

300848             *&#x5B;VPN/170] 00:11:32
                    &gt; to 169.254.10.17 via ge-0/0/1.0, Swap 300800

admin@vmx9.lab&gt; 
</pre></div>


<p>vMX9 will swap the label for <code>300800</code>.  Now this is crucial here.  This is the BGP LU route label that vMX10 allocated and sent to vMX9 to tell it how to reach vMX13&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [6]; title: ; notranslate">
admin@vmx10.lab&gt; show route advertising-protocol bgp 169.254.10.16 13.13.13.13/32 extensive   

inet.0: 20 destinations, 23 routes (20 active, 0 holddown, 0 hidden)
@ 13.13.13.13/32 (2 entries, 2 announced)
 BGP group to_green_carrier type External
     Route Label: 300800
     Nexthop: Self
     Flags: Nexthop Change
     MED: 3
     AS path: &#x5B;65100] I 
     Entropy label capable

admin@vmx10.lab&gt; 
</pre></div>


<p>And if we look &#8211; vMX10 will use that label to stitch the traffic into the LDP LSP toward vMX13&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7,16]; title: ; notranslate">
admin@vmx10.lab&gt; show route table mpls.0 label 300800 

mpls.0: 19 destinations, 19 routes (19 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

300800             *&#x5B;VPN/170] 00:13:19
                    &gt; to 169.254.10.19 via ge-0/0/1.0, Swap 299904

admin@vmx10.lab&gt; show ldp database                
Input label database, 10.10.10.10:0--11.11.11.11:0
Labels received: 4
  Label     Prefix
 299872      10.10.10.10/32
      3      11.11.11.11/32
 299888      12.12.12.12/32
 299904      13.13.13.13/32

Output label database, 10.10.10.10:0--11.11.11.11:0
Labels advertised: 4
  Label     Prefix
      3      10.10.10.10/32
 300720      11.11.11.11/32
 300736      12.12.12.12/32
 300752      13.13.13.13/32

admin@vmx10.lab&gt; 
</pre></div>


<p>vMX11 will then get a label of <code>299904</code>&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7]; title: ; notranslate">
admin@vmx11.lab&gt; show route table mpls.0 label 299904 

mpls.0: 11 destinations, 11 routes (11 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

299904             *&#x5B;LDP/9] 00:14:35, metric 1
                    &gt; to 169.254.10.21 via ge-0/0/1.0, Swap 299904

admin@vmx11.lab&gt; 
</pre></div>


<p>Which it will then swap for <code>299904</code> before sending it to vMX12&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7]; title: ; notranslate">
admin@vmx12.lab&gt; show route table mpls.0 label 299904 

mpls.0: 11 destinations, 11 routes (11 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

299904             *&#x5B;LDP/9] 00:50:57, metric 1
                    &gt; to 169.254.10.23 via ge-0/0/1.0, Pop      
299904(S=0)        *&#x5B;LDP/9] 00:50:57, metric 1
                    &gt; to 169.254.10.23 via ge-0/0/1.0, Pop      

admin@vmx12.lab&gt; 
</pre></div>


<p>vMX12 as the PHP router in this LDP domain will pop the label leaving only the initial VPN label imposed by vMX2 on the frame which it will send to vMX13&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7]; title: ; notranslate">
admin@vmx13.lab&gt; show route table mpls.0 label 16 

mpls.0: 11 destinations, 11 routes (11 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

16                 *&#x5B;VPN/0] 00:51:44
                    &gt; via lsi.2 (customer), Pop      

admin@vmx13.lab&gt; 
</pre></div>


<p>Which vMX13 will pop off and dump into the customer routing-instance.  If we put that all together in a diagram it would look like this&#8230;</p>



<figure class="wp-block-image size-large"><a href="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-9.png"><img loading="lazy" decoding="async" width="886" height="508" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-9.png" alt="" class="wp-image-23330" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-9.png 886w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-9-300x172.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-9-200x115.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-9-768x440.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-9-624x358.png 624w" sizes="(max-width: 886px) 100vw, 886px" /></a></figure>



<p>Alright.  So there you have it.  As you can see, the configuration was just about as involved as using LDP and OSPF but strongly mimicked what we had seen earlier when using BGP-LU for LSP stitching.  Clearly design requirements would win in terms of which way you do CsC but I can see the argument for having more routing policy flexibility when using BGP-LU.  </p>
]]></content:encoded>
					
					<wfw:commentRss>http://www.dasblinkenlichten.com/carrier-supporting-carrier-with-bgp-lu/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">23320</post-id>	</item>
		<item>
		<title>Carrier Supporting Carrier with OSPF and LDP</title>
		<link>http://www.dasblinkenlichten.com/carrier-supporting-carrier-with-ospf-and-ldp/</link>
					<comments>http://www.dasblinkenlichten.com/carrier-supporting-carrier-with-ospf-and-ldp/#respond</comments>
		
		<dc:creator><![CDATA[Jon Langemak]]></dc:creator>
		<pubDate>Tue, 05 Nov 2019 16:36:59 +0000</pubDate>
				<category><![CDATA[Juniper]]></category>
		<category><![CDATA[MPLS]]></category>
		<guid isPermaLink="false">http://www.dasblinkenlichten.com/?p=23276</guid>

					<description><![CDATA[I know we haven&#8217;t gotten to the point of actually discussing inter-as option B or option C but we did cover all of the mechanics required for them to work... <a href="http://www.dasblinkenlichten.com/carrier-supporting-carrier-with-ospf-and-ldp/">Read more &#187;</a>]]></description>
										<content:encoded><![CDATA[
<p>I know we haven&#8217;t gotten to the point of actually discussing inter-as option B or option C but we did cover all of the mechanics required for them to work (at least briefly).  Before we go there though &#8211; I did want to cover a different technology that can also help solve our end to end LSP problems.  Carrier Supporting Carrier &#8211; or more commonly just referred to as CsC (or in Cisco parlance Carrier of Carriers or CoC (or maybe I have that backwards?)) is a means to nest MPLS VPN sessions.  Think of it this way.  If you&#8217;re  a huge backbone provider (we&#8217;ll call you the &#8220;uber provider&#8221;) selling transport to customers (we&#8217;ll call them customer carriers) &#8211; there&#8217;s a good chance that those customer carriers will need a way to isolate their customer on their backbone.  How do we do that?  Well MPLS VPNs of course!  However you, the uber provider, also need a means to keep the customer carriers isolated as they traverse your backbone.  So what do we do?  Sounds a lot like a carrier supporting a carrier huh?  CsC to the rescue! </p>



<p>At this point you might be wondering how we got here.  To be clear, I didn&#8217;t just pull CsC out of my &#8220;write a random blog on this topic&#8221; hat.  We&#8217;ve spent the last few posts really trying to describe what it means to have end to end LSPs and why they are so important.  We&#8217;ve talked briefly about how we can do this with label stitching and even touched briefly (admittedly by accident) on the inter-AS architectures B and C that allow us to stitch together discrete label domains.  But those solutions are not the only way to crack this problem.  CsC can solve this problem too!  Although, for some reason, it always seems to be discounted in this space.  I&#8217;m not sure why that is &#8211; perhaps it&#8217;s assumed to be complex or geared toward a specific uses case.  However &#8211; for those of you that read the great book &#8220;MPLS in the SDN era&#8221; you may recall that CsC is covered at the end of chapter 9 which itself is titled &#8220;Inter-Domain MPLS Services&#8221;.  And while the authors don&#8217;t discuss it in detail or specifically call out that it can be used to solve this problem &#8211; I believe it&#8217;s in that chapter for a reason.  Alright &#8211; enough chit chat &#8211; let&#8217;s get to the lab.  </p>



<p>For the sake of making this look more real life(ish) I&#8217;ve expanded the lab greatly as you can see below.  The goal of doing this was to make the LSP segments longer so that we saw some real label operations happening (not just routers popping off labels since they were PHP routers).  </p>



<div class="wp-block-image"><figure class="aligncenter"><a href="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-20.png" target="_blank" rel="noreferrer noopener"><img loading="lazy" decoding="async" width="764" height="483" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-20.png" alt="" class="wp-image-23286" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-20.png 764w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-20-200x126.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-20-300x190.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-20-624x394.png 624w" sizes="(max-width: 764px) 100vw, 764px" /></a></figure></div>



<p>As per usual, the blue numbering indicates interface numbering (eg. 0 = ge-0/0/0) and the black numbering indicates the IP addressing.  In our case, all of the interface addressing is link local and comes out of <code>169.254.10.0/24</code>.  So for example the link between vMX1 and vMX2 is <code>169.254.10.0/31</code>.  I&#8217;ve also removed the loopbacks from the diagram this time around since those will be a little more dynamic this time.  </p>



<p>So let&#8217;s talk about what we want this to look like&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter"><a href="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-25.png" target="_blank" rel="noreferrer noopener"><img loading="lazy" decoding="async" width="886" height="508" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-25.png" alt="" class="wp-image-23292" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-25.png 886w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-25-200x115.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-25-300x172.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-25-768x440.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-25-624x358.png 624w" sizes="(max-width: 886px) 100vw, 886px" /></a></figure></div>



<p>The easiest way to think of this is that we have a carrier that is split in two which is represented by the routers in the orange blocks.  We&#8217;ll call this the &#8220;orange carrier&#8221;.  In the left orange block we have a carrier network which connects to a customer CE router.  That customer wishes to talk to it&#8217;s other CE router on the right side but our orange  carrier doesn&#8217;t have any direct connectivity to it&#8217;s network on the right.  To make the  carrier  &#8220;whole&#8221;, we need to connect the left side of the orange carrier network to the right side.  To do this, we leverage a connection from another transport provider which is represented by the routers in the green box.  We&#8217;ll call this the &#8220;green carrier&#8221;.  Going forward, we&#8217;ll assume that the orange carrier is a customer to the green carrier.   You&#8217;ll notice that we have updated the name of the router to include its role.  You&#8217;ll notice some familiar terminology here.  There are PE, CE, and P routers &#8211; but we also now have a new class of those routers prefaced with &#8220;CSC&#8221;.  The CSC model works much like the normal PE/CE model.  In this case, the orange carrier is a customer of the green carrier.  So the orange carrier node that connects to the green carrier (vMX5) is considered the CSC-CE router while the green carrier router that connects to it&#8217;s customer is considered the CSC-PE router (vMX6).  </p>



<p>So at this point &#8211; why don&#8217;t I stop talking your ear off and we can just get into the config.  Since this configuration is more in depth, Im going to start each router with a different baseline configuration.  Don&#8217;t worry, I&#8217;ll talk through anything that looks crazy&#8230;</p>



<h2 class="wp-block-heading">vMX1</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 10.10.10.1/24
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.0/31
set routing-options autonomous-system 65001
set protocols bgp group to_provider type external
set protocols bgp group to_provider export customer_routes
set protocols bgp group to_provider neighbor 169.254.10.1 peer-as 65002
set policy-options prefix-list customer_routes 10.10.10.0/24
set policy-options policy-statement customer_routes term direct from protocol direct
set policy-options policy-statement customer_routes term direct from prefix-list customer_routes
set policy-options policy-statement customer_routes term direct then accept
set policy-options policy-statement customer_routes then reject

</pre></div>


<p>The base configuration of vMX1 is fairly straight forward.  In this lab, it&#8217;s acting as a pure CE router and our PE/CE protocol of choice is going to be BGP.  vMX1 will come from AS 65001 and connect to the PE in AS 65002.  For the sake of clarity, I haven&#8217;t configured a loopback address since we wont be using it in this lab and I don&#8217;t want to confuse things.  </p>



<p><em>Note: I&#8217;ll just mention here that the choice of CE/PE protocol being BGP here has very little bearing on the larger architecture.  AKA &#8211; Don&#8217;t read too much into the BGP config between the CE and PE routes in the orange carrier. It&#8217;s just there to get the customer prefixes into the VRF table on the PE.  Don&#8217;t let it distract you from the other BGP peerings that are going on that support CsC.  </em></p>



<h2 class="wp-block-heading">vMX2</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.1/31
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.2/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 2.2.2.2/32 primary
set routing-options router-id 2.2.2.2
set protocols mpls interface ge-0/0/1.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
set protocols ldp interface ge-0/0/1.0
set routing-instances customer instance-type vrf
set routing-instances customer interface ge-0/0/0.0
set routing-instances customer route-distinguisher 100:100
set routing-instances customer vrf-target target:100:100
set routing-instances customer vrf-table-label
set routing-instances customer routing-options autonomous-system 65002
set routing-instances customer protocols bgp group customer1 type external
set routing-instances customer protocols bgp group customer1 neighbor 169.254.10.0 peer-as 65001
</pre></div>


<p>The base configuration of vMX2 get&#8217;s us a few things.  First &#8211; an OSPF configuration so that we can talk to the rest of the orange provider router loopbacks.  We also enable MPLS and LDP on the upstream interface.  Secondly &#8211; we define a customer routing instance for our customer.  Inside the routing-instance we define our BGP parameters so that we can peer with the customer. </p>



<h2 class="wp-block-heading">vMX3</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.3/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.4/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 3.3.3.3/32 primary
set routing-options router-id 3.3.3.3
set protocols mpls interface ge-0/0/0.0
set protocols mpls interface ge-0/0/1.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
set protocols ldp interface ge-0/0/0.0
set protocols ldp interface ge-0/0/1.0
</pre></div>


<p>vMX3 is our first P router so the configuration here is drop dead easy.  OSPF, MPLS, and LDP on all of the interfaces.  </p>



<h2 class="wp-block-heading">vMX4</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.5/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.6/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 4.4.4.4/32 primary
set routing-options router-id 4.4.4.4
set protocols mpls interface ge-0/0/0.0
set protocols mpls interface ge-0/0/1.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
set protocols ldp interface ge-0/0/0.0
set protocols ldp interface ge-0/0/1.0
</pre></div>


<p>Same deal here on vMX4.  Another P router configuration.</p>



<h2 class="wp-block-heading">vMX5</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.7/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.8/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 5.5.5.5/32 primary
set routing-options router-id 5.5.5.5
set protocols mpls interface ge-0/0/0.0
set protocols mpls interface ge-0/0/1.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
set protocols ldp interface ge-0/0/0.0
set protocols ldp interface ge-0/0/1.0
</pre></div>


<p>vMX5 is our CSC-CE router between the orange and green provider.  Now this is where things &#8220;could&#8221; get interesting but surprisingly don&#8217;t.  What other configuration wasn&#8217;t that interesting again?  Oh yeah &#8211; the CE config on vMX1 (and vMX14).  That&#8217;s because a CSC-CE should look a lot like a CE router!    Since this is a CE/PE configuration, you might expect BGP here &#8211; but in this case, we&#8217;re not going to do it.  I&#8217;ll talk about that more below.</p>



<h2 class="wp-block-heading">vMX6</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.9/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.10/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 6.6.1.1/32
set routing-options router-id 6.6.1.1
set protocols rsvp interface ge-0/0/1.0
set protocols mpls label-switched-path to-vmx9 to 9.9.1.1
set protocols mpls interface ge-0/0/1.0
set protocols ospf traffic-engineering
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
</pre></div>


<p>The base configuration of the green carrier routers aims to get things rolling for communication within the green carrier.  Notice that the loopback address is different in this case (<code>6.6.1.1</code>) and is a theme we will see with all the routers inside of the green carrier.  Since we&#8217;re going to run RSVP within the green carrier &#8211; we also define a LSP to the far end of the green carrier (vMX9).  Beyond that it&#8217;s just a standard OSPF configuration.  </p>



<h2 class="wp-block-heading">vMX7</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.11/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.12/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 7.7.1.1/32 primary
set routing-options router-id 7.7.1.1
set protocols rsvp interface ge-0/0/1.0
set protocols rsvp interface ge-0/0/0.0
set protocols mpls interface ge-0/0/1.0
set protocols mpls interface ge-0/0/0.0
set protocols ospf traffic-engineering
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
</pre></div>


<p>Again &#8211; a boring P router configuration.  Nothing to see here &#8211; move along.</p>



<h2 class="wp-block-heading">vMX8</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.13/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.14/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 8.8.1.1/32 primary
set routing-options router-id 8.8.1.1
set protocols rsvp interface ge-0/0/1.0
set protocols rsvp interface ge-0/0/0.0
set protocols mpls interface ge-0/0/1.0
set protocols mpls interface ge-0/0/0.0
set protocols ospf traffic-engineering
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
</pre></div>


<p>Same deal &#8211; a boring P router configuration.  Boring is nice right?  🙂</p>



<h2 class="wp-block-heading">vMX9</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.15/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.16/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 9.9.1.1/32
set routing-options router-id 9.9.1.1
set routing-options autonomous-system 65000
set protocols rsvp interface ge-0/0/0.0
set protocols mpls label-switched-path to-vmx6 to 6.6.1.1
set protocols mpls interface ge-0/0/0.0
set protocols ospf traffic-engineering
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
</pre></div>


<p>Just like we saw on vMX6.  This should get us connectivity within the green carrier as well as define the RSVP LSP back to vMX6.  </p>



<h2 class="wp-block-heading">vMX10</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.17/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.18/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 10.10.10.10/32 primary
set routing-options router-id 10.10.10.10
set protocols mpls interface ge-0/0/0.0
set protocols mpls interface ge-0/0/1.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
set protocols ldp interface ge-0/0/0.0
set protocols ldp interface ge-0/0/1.0
</pre></div>


<p>This configuration is pretty much the same that we saw on vMX5.  Don&#8217;t worry about how simple this looks for now.  We&#8217;ll talk more about why this is later on.  </p>



<h2 class="wp-block-heading">vMX11</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.19/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.20/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 11.11.11.11/32 primary
set routing-options router-id 11.11.11.11
set protocols mpls interface ge-0/0/0.0
set protocols mpls interface ge-0/0/1.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
set protocols ldp interface ge-0/0/0.0
set protocols ldp interface ge-0/0/1.0
</pre></div>


<p>Another boring P router config.  OSPF, LDP, and MPLS.  </p>



<h2 class="wp-block-heading">vMX12</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.21/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.22/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces fxp0 unit 0 family inet address 192.168.127.12/24
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 12.12.12.12/32 primary
set routing-options static route 10.171.200.0/22 next-hop 192.168.127.100
set routing-options router-id 12.12.12.12
set protocols mpls interface ge-0/0/0.0
set protocols mpls interface ge-0/0/1.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
set protocols ldp interface ge-0/0/0.0
set protocols ldp interface ge-0/0/1.0
</pre></div>


<p>More boring P router config.</p>



<h2 class="wp-block-heading">vMX13</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.23/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.24/31
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 13.13.13.13/32 primary
set routing-options router-id 13.13.13.13
set protocols mpls interface ge-0/0/0.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ldp interface ge-0/0/0.0
set routing-instances customer instance-type vrf
set routing-instances customer interface ge-0/0/1.0
set routing-instances customer route-distinguisher 100:100
set routing-instances customer vrf-target target:100:100
set routing-instances customer vrf-table-label
set routing-instances customer routing-options autonomous-system 65004
set routing-instances customer protocols bgp group customer1 type external
set routing-instances customer protocols bgp group customer1 neighbor 169.254.10.25 peer-as 65003
</pre></div>


<p>We&#8217;re doing the same thing here that we did on vMX2.  We configure our customer routing instance and map the interface facing the CE into it.  We also configure BGP inside the routing-instance to peer with the CE in AS 65003 and we configure out local AS to 65004.  </p>



<h2 class="wp-block-heading">vMX14</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.25/31
set interfaces ge-0/0/1 unit 0 family inet address 192.168.10.1/24
set routing-options autonomous-system 65003
set protocols bgp group to_provider type external
set protocols bgp group to_provider export customer_routes
set protocols bgp group to_provider neighbor 169.254.10.24 peer-as 65004
set policy-options prefix-list customer_routes 192.168.10.0/24
set policy-options policy-statement customer_routes term direct from protocol direct
set policy-options policy-statement customer_routes term direct from prefix-list customer_routes
set policy-options policy-statement customer_routes term direct then accept
set policy-options policy-statement customer_routes then reject
</pre></div>


<p>Alright &#8211; so that was a lot of configuration &#8211; but hopefully none of it was too new for you.  The goal of the baseline configuration was to get the base protocols running in each of our orange and green &#8220;boxes&#8221;.  At this point, our CE&#8217;s should be configured and peered into their VRFs in the orange carrier.  Let&#8217;s make sure that&#8217;s the case&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [9,10,16]; title: ; notranslate">
admin@vmx2.lab&gt; show bgp summary instance customer                                    
Groups: 1 Peers: 1 Down peers: 0
Table          Tot Paths  Act Paths Suppressed    History Damp State    Pending
customer.inet.0      
                       1          1          0          0          0          0
customer.mdt.0       
                       0          0          0          0          0          0
Peer                     AS      InPkt     OutPkt    OutQ   Flaps Last Up/Dwn State|#Active/Received/Accepted/Damped...
169.254.10.0          65001       2257       2256       0       0    16:53:02 Establ
  customer.inet.0: 1/1/1/0

admin@vmx2.lab&gt; show route receive-protocol bgp 169.254.10.0 table customer.inet.0    

customer.inet.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden)
  Prefix		  Nexthop	       MED     Lclpref    AS path
* 10.10.10.0/24           169.254.10.0                            65001 I

admin@vmx2.lab&gt; 

</pre></div>


<p>Notice the syntax we use to look at things related to the <code>customer</code> routing instance.  In the case of seeing the BGP summary we can tack on the <code>instance customer</code> command.  However, when looking at the routes received we can just call out the table we want to see.  So it looks like the peering is up on the left side and we&#8217;re receiving the advertisement, let&#8217;s check the right side as well&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [9,10,16]; title: ; notranslate">
admin@vmx13.lab&gt; show bgp summary instance customer                                    
Groups: 1 Peers: 1 Down peers: 0
Table          Tot Paths  Act Paths Suppressed    History Damp State    Pending
customer.inet.0      
                       1          1          0          0          0          0
customer.mdt.0       
                       0          0          0          0          0          0
Peer                     AS      InPkt     OutPkt    OutQ   Flaps Last Up/Dwn State|#Active/Received/Accepted/Damped...
169.254.10.25         65003       2256       2254       0       0    16:52:20 Establ
  customer.inet.0: 1/1/1/0

admin@vmx13.lab&gt; show route receive-protocol bgp 169.254.10.25 table customer.inet.0   

customer.inet.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden)
  Prefix		  Nexthop	       MED     Lclpref    AS path
* 192.168.10.0/24         169.254.10.25                           65003 I

admin@vmx13.lab&gt; 
</pre></div>


<p>Again &#8211; all looks good there.  Now let&#8217;s take a look at what&#8217;s going on within the orange carrier left and right boxes.  Based on the baseline configuration I&#8217;d expect that each router within each box can see each other router in the same box both with OSPF as well as LDP.  Let&#8217;s check&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx2.lab&gt; show route table inet.3 

inet.3: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

3.3.3.3/32         *&#x5B;LDP/9] 16:58:21, metric 1
                    &gt; to 169.254.10.3 via ge-0/0/1.0
4.4.4.4/32         *&#x5B;LDP/9] 16:58:19, metric 1
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Push 299792
5.5.5.5/32         *&#x5B;LDP/9] 16:58:08, metric 1
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Push 299808

admin@vmx2.lab&gt; 
</pre></div>


<p>Ok &#8211; so that looks just about like I&#8217;d expect it to look.  vMX2 has <code>inet.3</code> entries for all the other routers within it&#8217;s currently reachable network (orange box).  Confirming this is enough to prove to me that OSPF is also working since LDP relies on it.  Let&#8217;s validate the right side orange box as well&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx13.lab&gt; show route table inet.3 

inet.3: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

10.10.10.10/32     *&#x5B;LDP/9] 16:59:04, metric 1
                    &gt; to 169.254.10.22 via ge-0/0/0.0, Push 299792
11.11.11.11/32     *&#x5B;LDP/9] 16:59:04, metric 1
                    &gt; to 169.254.10.22 via ge-0/0/0.0, Push 299776
12.12.12.12/32     *&#x5B;LDP/9] 16:59:04, metric 1
                    &gt; to 169.254.10.22 via ge-0/0/0.0

admin@vmx13.lab&gt; 
</pre></div>


<p>Perfect.  And last but not least, let&#8217;s confirm that the green carrier also has a baseline configuration.  I&#8217;m hoping that we see an RSVP LSP between vMX6 and vMX9 fully formed&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx6.lab&gt; show rsvp session 
Ingress RSVP: 1 sessions
To              From            State   Rt Style Labelin Labelout LSPname 
9.9.1.1         6.6.1.1         Up       0  1 FF       -   299792 to-vmx9
Total 1 displayed, Up 1, Down 0

Egress RSVP: 1 sessions
To              From            State   Rt Style Labelin Labelout LSPname 
6.6.1.1         9.9.1.1         Up       0  1 FF       3        - to-vmx6
Total 1 displayed, Up 1, Down 0

Transit RSVP: 0 sessions
Total 0 displayed, Up 0, Down 0

admin@vmx6.lab&gt; 
</pre></div>


<p>Perfect!  So where do we go from here?  At this point, we once again appear to have 3 distinct label domains.  Each orange carrier is running LDP within it&#8217;s respective box (left and right side) and the backbone or green carrier is running RSVP.  So let&#8217;s remember what our goal is here&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter"><a href="blob:http://www.dasblinkenlichten.com/9a942dd4-528e-4465-b901-5c2e9d2465ff" target="_blank" rel="noreferrer noopener"><img loading="lazy" decoding="async" width="789" height="405" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image.png" alt="" class="wp-image-23298" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image.png 789w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-200x103.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-300x154.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-768x394.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-624x320.png 624w" sizes="(max-width: 789px) 100vw, 789px" /></a></figure></div>



<p>If it was all the same to the orange carrier, they&#8217;d prefer that this looked like one big happy provider network.  In fact, they require that.  They don&#8217;t care about what the green provider is doing to make that happen &#8211; their only requirement is connectivity between their two discrete networks that supports them running MPLS VPNs end to end (which dictates the use of end to end LSPs).  </p>



<p>Now &#8211; when we talk about CsC &#8211; there are two primary ways to tackle this solution.  One way is to run LDP and an IGP (like OSPF) inside the CSC-PE (vMX6 and vMX9) orange carrier VRF.  This solution is often frowned upon because of the complexities that can come along with using OSPF as a PE/CE protocol as well as the fact that BGP really is the defacto standard for PE/CE peering.  You&#8217;ll hear no disagreement from me on BGP being the clear choice for CE/PE protocols.  It&#8217;s is the defacto standard for good reason.  BGP allows a much greater level of policy control than OSPF does and quite simply is better designed for this kind of situation.  HOWEVER &#8211; one does not always need this level of policy control.  Remember that these types of deployments are not always company A and company B situations where A is trying to protect itself from B and vice-versa.  That is &#8211; there&#8217;s no reason a single entity can&#8217;t use CsC to scale its MPLS network and help stitch together end to end LSPs.  </p>



<p>In my research to write this post, I found out that configuring CsC using LDP and OSPF as I&#8217;ve described above wasn&#8217;t talked about at all.  Cisco had some documentation on it and it&#8217;s mentioned periodically but I couldn&#8217;t find any examples of doing it on JunOS.  That being said, I think it&#8217;s a reasonable solution and so that will be the way we configure CsC in this blog.  We will also try to cover the alternative solution of doing CsC with BGP (BGP-LU specifically) but that will be in another blog.  </p>



<p>So &#8211; now that we have our baseline configuration &#8211; where do we start?  Let&#8217;s start by trying to get the connectivity between the two orange carrier segments working through the green carrier.  As I&#8217;ve hinted at above, to do that we&#8217;ll be configuring LDP and OSPF inside of the VRF on each CSC-PE.  One of the things I&#8217;d like to try to reiterate about CsC is that it is not unlike a standard MPLS VPN setup.  The PE/CE configuration looks pretty darn similar as the CsC layer as it does at the lower customer layer.  So let&#8217;s start by configuring a routing instance for the orange carrier on each CSC-PE&#8230;.</p>



<h2 class="wp-block-heading">vMX6</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces lo0 unit 1 family inet address 6.6.6.6/32
set routing-instances orange_carrier instance-type vrf
set routing-instances orange_carrier interface ge-0/0/0.0
set routing-instances orange_carrier interface lo0.1
set routing-instances orange_carrier route-distinguisher 1:1
set routing-instances orange_carrier vrf-target target:1:1
set routing-instances orange_carrier vrf-table-label
set routing-instances orange_carrier routing-options router-id 6.6.6.6
set routing-instances orange_carrier protocols mpls interface ge-0/0/0.0
set routing-instances orange_carrier protocols mpls interface lo0.1
set routing-instances orange_carrier protocols ospf area 0.0.0.0 interface lo0.1
set routing-instances orange_carrier protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set routing-instances orange_carrier protocols ldp interface ge-0/0/0.0
</pre></div>


<p>The above configuration accomplishes a number of things.  First &#8211; note that we added another loopback interface (<code>lo0.1</code>) which we&#8217;ll use inside the VRF.  Second &#8211; we defined a typical routing-instance configuration that included configuring MPLS, OSPF, and LDP inside of the VRF.  Beyond them being there in the first place, their configuration should look pretty straight forward.  Alright &#8211; now let&#8217;s do the same on vMX9 &#8211; the other CSC-PE&#8230;</p>



<h2 class="wp-block-heading">vMX9</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set interfaces lo0 unit 1 family inet address 9.9.9.9/32
set routing-instances orange_carrier instance-type vrf
set routing-instances orange_carrier interface ge-0/0/1.0
set routing-instances orange_carrier interface lo0.1
set routing-instances orange_carrier route-distinguisher 1:1
set routing-instances orange_carrier vrf-target target:1:1
set routing-instances orange_carrier vrf-table-label
set routing-instances orange_carrier routing-options router-id 9.9.9.9
set routing-instances orange_carrier protocols mpls interface ge-0/0/1.0
set routing-instances orange_carrier protocols mpls interface lo0.1
set routing-instances orange_carrier protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
set routing-instances orange_carrier protocols ospf area 0.0.0.0 interface lo0.1
set routing-instances orange_carrier protocols ldp interface ge-0/0/1.0
</pre></div>


<p> Once that&#8217;s in place.  We should now see that the orange carrier routers view the CSC-PE routers as part of their LDP domain&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx13.lab&gt; show route table inet.3    

inet.3: 4 destinations, 4 routes (4 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

9.9.9.9/32         *&#x5B;LDP/9] 00:17:55, metric 1
                    &gt; to 169.254.10.22 via ge-0/0/0.0, Push 299824
10.10.10.10/32     *&#x5B;LDP/9] 18:54:21, metric 1
                    &gt; to 169.254.10.22 via ge-0/0/0.0, Push 299792
11.11.11.11/32     *&#x5B;LDP/9] 18:54:21, metric 1
                    &gt; to 169.254.10.22 via ge-0/0/0.0, Push 299776
12.12.12.12/32     *&#x5B;LDP/9] 18:54:21, metric 1
                    &gt; to 169.254.10.22 via ge-0/0/0.0

admin@vmx13.lab&gt; 
</pre></div>


<p>Notice that we now see a path to vMX9 in <code>inet.3</code>.  We&#8217;ll see that on the left side we now also see the CSC-PE loopback as well&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx2.lab&gt; show route table inet.3    

inet.3: 4 destinations, 4 routes (4 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

3.3.3.3/32         *&#x5B;LDP/9] 18:57:15, metric 1
                    &gt; to 169.254.10.3 via ge-0/0/1.0
4.4.4.4/32         *&#x5B;LDP/9] 18:57:13, metric 1
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Push 299792
5.5.5.5/32         *&#x5B;LDP/9] 18:57:02, metric 1
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Push 299808
6.6.6.6/32         *&#x5B;LDP/9] 00:00:04, metric 1
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Push 299824

admin@vmx2.lab&gt; 
</pre></div>


<p>Perfect.  So now we&#8217;ve begun the integration of the orange carrier into the green carrier network,  However, beyond providing connectivity for LDP and OSPF &#8211; we haven&#8217;t really done anything.  If the green carrier is going to provide VPN connectivity for the orange carrier &#8211; then we need to provide a way for both of the green carrier CSC-PEs to exchange route information.  This will come in the form a MP-BGP peering in the <code>inet-vpn</code> address family.  So let&#8217;s get that configured next&#8230;</p>



<h2 class="wp-block-heading">vMX6</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-options autonomous-system 65000
set protocols bgp group green_carrier_vpn type internal
set protocols bgp group green_carrier_vpn family inet-vpn unicast
set protocols bgp group green_carrier_vpn neighbor 9.9.1.1 local-address 6.6.1.1
set protocols bgp group green_carrier_vpn neighbor 9.9.1.1 peer-as 65000
</pre></div>


<h2 class="wp-block-heading">vMX9</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-options autonomous-system 65000
set protocols bgp group green_carrier_vpn type internal
set protocols bgp group green_carrier_vpn family inet-vpn unicast
set protocols bgp group green_carrier_vpn neighbor 6.6.1.1 local-address 9.9.1.1
set protocols bgp group green_carrier_vpn neighbor 6.6.1.1 peer-as 65000
</pre></div>


<p>Once that iBGP peering is up &#8211; we should see the session come up and become established.  As we&#8217;ve seen before &#8211; a BGP peering in the <code>inet-vpn</code> family will automatically export all of the VRF or routing-instance routes as VPNv4 routes to the peer&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7,15,16,17,18,19]; title: ; notranslate">
admin@vmx6.lab&gt; show bgp summary                               
Groups: 1 Peers: 1 Down peers: 0
Table          Tot Paths  Act Paths Suppressed    History Damp State    Pending
bgp.l3vpn.0          
                       9          9          0          0          0          0
Peer                     AS      InPkt     OutPkt    OutQ   Flaps Last Up/Dwn State|#Active/Received/Accepted/Damped...
9.9.1.1               65000         11         10       0       0        1:45 Establ
  bgp.l3vpn.0: 9/9/9/0
  orange_carrier.inet.0: 9/9/9/0

admin@vmx6.lab&gt; show route advertising-protocol bgp 9.9.1.1    

orange_carrier.inet.0: 21 destinations, 21 routes (21 active, 0 holddown, 0 hidden)
  Prefix		  Nexthop	       MED     Lclpref    AS path
* 2.2.2.2/32              Self                 4       100        I
* 3.3.3.3/32              Self                 3       100        I
* 4.4.4.4/32              Self                 2       100        I
* 5.5.5.5/32              Self                 1       100        I
* 6.6.6.6/32              Self                         100        I
* 169.254.10.2/31         Self                 4       100        I
* 169.254.10.4/31         Self                 3       100        I
* 169.254.10.6/31         Self                 2       100        I
* 169.254.10.8/31         Self                         100        I

admin@vmx6.lab&gt; 
</pre></div>


<p>As you can see the prefixes we&#8217;re interested in are the left side orange carrier loopbacks.  If we look on vMX9 we should see them in the <code>bgp.l3vpn.0</code> table&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]; title: ; notranslate">
admin@vmx9.lab&gt; show route table bgp.l3vpn.0    

bgp.l3vpn.0: 9 destinations, 9 routes (9 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

1:1:2.2.2.2/32                
                   *&#x5B;BGP/170] 00:02:45, MED 4, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
1:1:3.3.3.3/32                
                   *&#x5B;BGP/170] 00:02:45, MED 3, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
1:1:4.4.4.4/32                
                   *&#x5B;BGP/170] 00:02:45, MED 2, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
1:1:5.5.5.5/32                
                   *&#x5B;BGP/170] 00:02:45, MED 1, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
1:1:6.6.6.6/32                
                   *&#x5B;BGP/170] 00:02:45, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
1:1:169.254.10.2/31                
                   *&#x5B;BGP/170] 00:02:45, MED 4, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
1:1:169.254.10.4/31                
                   *&#x5B;BGP/170] 00:02:45, MED 3, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
1:1:169.254.10.6/31                
                   *&#x5B;BGP/170] 00:02:45, MED 2, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
1:1:169.254.10.8/31                
                   *&#x5B;BGP/170] 00:02:45, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6

admin@vmx9.lab&gt; 
</pre></div>


<p>Perfect.  But now what do we do with these?  Our job as the green carrier is to help exchange these routes between the left and the right side orange carrier networks.  And if we look at the routing instance table, we&#8217;ll see that this has happened&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]; title: ; notranslate">
admin@vmx9.lab&gt; show route table customer_carrier.inet.0 protocol bgp   

customer_carrier.inet.0: 21 destinations, 21 routes (21 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

2.2.2.2/32         *&#x5B;BGP/170] 00:09:09, MED 4, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
3.3.3.3/32         *&#x5B;BGP/170] 00:09:09, MED 3, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
4.4.4.4/32         *&#x5B;BGP/170] 00:09:09, MED 2, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
5.5.5.5/32         *&#x5B;BGP/170] 00:09:09, MED 1, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
6.6.6.6/32         *&#x5B;BGP/170] 00:09:09, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
169.254.10.2/31    *&#x5B;BGP/170] 00:09:09, MED 4, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
169.254.10.4/31    *&#x5B;BGP/170] 00:09:09, MED 3, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
169.254.10.6/31    *&#x5B;BGP/170] 00:09:09, MED 2, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
169.254.10.8/31    *&#x5B;BGP/170] 00:09:09, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6

admin@vmx9.lab&gt; 
</pre></div>


<p>So while that&#8217;s awesome that this worked as expected &#8211; the orange carrier routers in the left box won&#8217;t get these routes?  Why not?  Because we&#8217;re only running OSPF to those routers.  In order for them to get the routes, we have to redistribute them into OSPF.  Since I don&#8217;t really care about the link local addresses, let&#8217;s write a policy to only import the remote loopbacks on both vMX6 and vMX9&#8230;</p>



<h2 class="wp-block-heading">vMX6</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set policy-options prefix-list BGP-INTO-OSPF 9.9.9.9/32 
set policy-options prefix-list BGP-INTO-OSPF 10.10.10.10/32 
set policy-options prefix-list BGP-INTO-OSPF 11.11.11.11/32    
set policy-options prefix-list BGP-INTO-OSPF 12.12.12.12/32    
set policy-options prefix-list BGP-INTO-OSPF 13.13.13.13/32  
set policy-options policy-statement BGP-INTO-OSPF term BGP from protocol bgp
set policy-options policy-statement BGP-INTO-OSPF term BGP from prefix-list BGP-INTO-OSPF
set policy-options policy-statement BGP-INTO-OSPF term BGP then accept
set policy-options policy-statement BGP-INTO-OSPF then reject
set routing-instances orange_carrier protocols ospf export BGP-INTO-OSPF
</pre></div>


<h2 class="wp-block-heading">vMX9</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set policy-options prefix-list BGP-INTO-OSPF 2.2.2.2/32
set policy-options prefix-list BGP-INTO-OSPF 3.3.3.3/32
set policy-options prefix-list BGP-INTO-OSPF 4.4.4.4/32
set policy-options prefix-list BGP-INTO-OSPF 5.5.5.5/32
set policy-options prefix-list BGP-INTO-OSPF 6.6.6.6/32
set policy-options policy-statement BGP-INTO-OSPF term BGP from protocol bgp
set policy-options policy-statement BGP-INTO-OSPF term BGP from prefix-list BGP-INTO-OSPF
set policy-options policy-statement BGP-INTO-OSPF term BGP then accept
set policy-options policy-statement BGP-INTO-OSPF then reject
set routing-instances orange_carrier protocols ospf export BGP-INTO-OSPF
</pre></div>


<p>So with this config our remote PEs should see all of the loopbacks of the orange carrier routers from the remote side&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [16,17,22,23,24,25,26,27]; title: ; notranslate">
admin@vmx2.lab&gt; show route table inet.0    

inet.0: 20 destinations, 20 routes (20 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

2.2.2.2/32         *&#x5B;Direct/0] 19:55:39
                    &gt; via lo0.0
3.3.3.3/32         *&#x5B;OSPF/10] 19:20:18, metric 1
                    &gt; to 169.254.10.3 via ge-0/0/1.0
4.4.4.4/32         *&#x5B;OSPF/10] 19:20:18, metric 2
                    &gt; to 169.254.10.3 via ge-0/0/1.0
5.5.5.5/32         *&#x5B;OSPF/10] 19:20:04, metric 3
                    &gt; to 169.254.10.3 via ge-0/0/1.0
6.6.6.6/32         *&#x5B;OSPF/10] 00:23:07, metric 4
                    &gt; to 169.254.10.3 via ge-0/0/1.0
9.9.9.9/32         *&#x5B;OSPF/150] 00:01:07, metric 0, tag 3489725928
                    &gt; to 169.254.10.3 via ge-0/0/1.0
10.10.10.10/32     *&#x5B;OSPF/10] 00:00:16, metric 5
                    &gt; to 169.254.10.3 via ge-0/0/1.0
10.171.200.0/22    *&#x5B;Static/5] 19:55:39
                    &gt; to 192.168.127.100 via fxp0.0
11.11.11.11/32     *&#x5B;OSPF/10] 00:00:16, metric 6
                    &gt; to 169.254.10.3 via ge-0/0/1.0
12.12.12.12/32     *&#x5B;OSPF/10] 00:00:16, metric 7
                    &gt; to 169.254.10.3 via ge-0/0/1.0
13.13.13.13/32     *&#x5B;OSPF/10] 00:00:16, metric 8
                    &gt; to 169.254.10.3 via ge-0/0/1.0
169.254.10.2/31    *&#x5B;Direct/0] 19:53:21
                    &gt; via ge-0/0/1.0
169.254.10.2/32    *&#x5B;Local/0] 19:53:21
                      Local via ge-0/0/1.0
169.254.10.4/31    *&#x5B;OSPF/10] 19:20:18, metric 2
                    &gt; to 169.254.10.3 via ge-0/0/1.0
169.254.10.6/31    *&#x5B;OSPF/10] 19:20:18, metric 3
                    &gt; to 169.254.10.3 via ge-0/0/1.0
169.254.10.8/31    *&#x5B;OSPF/10] 19:20:04, metric 4
                    &gt; to 169.254.10.3 via ge-0/0/1.0
192.168.127.0/24   *&#x5B;Direct/0] 19:55:39
                    &gt; via fxp0.0
192.168.127.2/32   *&#x5B;Local/0] 19:55:39
                      Local via fxp0.0
224.0.0.2/32       *&#x5B;LDP/9] 19:22:11, metric 1
                      MultiRecv
224.0.0.5/32       *&#x5B;OSPF/10] 19:20:38, metric 1
                      MultiRecv

admin@vmx2.lab&gt; 
</pre></div>


<p>Perfect!  But now we have another problem.  Just because we have a route to them &#8211; doesn&#8217;t mean we can build an LSP to them&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx2.lab&gt; show route table inet.3 

inet.3: 4 destinations, 4 routes (4 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

3.3.3.3/32         *&#x5B;LDP/9] 19:26:30, metric 1
                    &gt; to 169.254.10.3 via ge-0/0/1.0
4.4.4.4/32         *&#x5B;LDP/9] 19:26:28, metric 1
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Push 299792
5.5.5.5/32         *&#x5B;LDP/9] 19:26:17, metric 1
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Push 299808
6.6.6.6/32         *&#x5B;LDP/9] 00:29:19, metric 1
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Push 299824

admin@vmx2.lab&gt; 
</pre></div>


<p>They&#8217;re still missing from our <code>inet.3</code> table.  If our goal is for this to look rather seamless to the orange carrier, then they are going to need <code>inet.3</code> entries in order to build their LSPs.  The fix for this is to modify the default LDP policy.  In Juniper &#8211; by default &#8211; only loopback addresses are exported into LDP (which I think makes things awfully clean).  And when I say exported &#8211; I mean put into the <code>inet.3</code> table.  We need to change this now to import other prefixes, in this case the ones we learned from BGP, into LDP as well.   To do this, we modify the LDP egress policy inside of the VRF as follows&#8230;</p>



<h2 class="wp-block-heading">vMX6 and vMX9</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set policy-options policy-statement ldp-export term from_bgp from protocol bgp
set policy-options policy-statement ldp-export term from_bgp from prefix-list BGP-INTO-OSPF
set policy-options policy-statement ldp-export term from_bgp then accept
set policy-options policy-statement ldp-export term from_l0 from interface lo0.1
set policy-options policy-statement ldp-export term from_l0 then accept
set policy-options policy-statement ldp-export then reject
set routing-instances orange_carrier protocols ldp egress-policy ldp-export
</pre></div>


<p>In addition to validating that they came from BGP we can leverage the existing prefix-list we created <code>BGP-INTO-OSPF</code> to only capture the remote loopbacks we&#8217;re interested in.  We also want to make sure that we include the local loopback interface as part of the export.  Once this is applied on both CSC-PE nodes (vMX6 and vMX9) we should see all the entries we&#8217;d expect in the <code>inet.3</code> table of all of the orange carrier routers.  Let&#8217;s pick one at random to validate that assumption&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx11.lab&gt; show route table inet.3    

inet.3: 9 destinations, 9 routes (9 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

2.2.2.2/32         *&#x5B;LDP/9] 00:04:23, metric 1
                    &gt; to 169.254.10.18 via ge-0/0/0.0, Push 299856
3.3.3.3/32         *&#x5B;LDP/9] 00:04:23, metric 1
                    &gt; to 169.254.10.18 via ge-0/0/0.0, Push 299872
4.4.4.4/32         *&#x5B;LDP/9] 00:04:23, metric 1
                    &gt; to 169.254.10.18 via ge-0/0/0.0, Push 299888
5.5.5.5/32         *&#x5B;LDP/9] 00:04:23, metric 1
                    &gt; to 169.254.10.18 via ge-0/0/0.0, Push 299904
6.6.6.6/32         *&#x5B;LDP/9] 00:04:23, metric 1
                    &gt; to 169.254.10.18 via ge-0/0/0.0, Push 299920
9.9.9.9/32         *&#x5B;LDP/9] 00:00:14, metric 1
                    &gt; to 169.254.10.18 via ge-0/0/0.0, Push 299936
10.10.10.10/32     *&#x5B;LDP/9] 21:54:56, metric 1
                    &gt; to 169.254.10.18 via ge-0/0/0.0
12.12.12.12/32     *&#x5B;LDP/9] 21:54:51, metric 1
                    &gt; to 169.254.10.21 via ge-0/0/1.0
13.13.13.13/32     *&#x5B;LDP/9] 21:54:46, metric 1
                    &gt; to 169.254.10.21 via ge-0/0/1.0, Push 299808

admin@vmx11.lab&gt; 
</pre></div>


<p>Great!  So at this point, we have <code>inet.3</code> resolution for LDP for all of the orange carrier routers as well as their LDP endpoints int he CSC-PE routing-instances! </p>



<p>So at this point &#8211; there&#8217;s really only one thing left to do and that&#8217;s configure a <code>inet-vpn</code> peering for our orange carrier.  To do this, we&#8217;ll peer vMX2 directly to vMX13 since we now have reachability between the two loopbacks through the green carrier.  </p>



<h2 class="wp-block-heading">vMX2</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-options autonomous-system 65100
set protocols bgp group vpn type internal
set protocols bgp group vpn family inet-vpn unicast
set protocols bgp group vpn neighbor 13.13.13.13 local-address 2.2.2.2
set protocols bgp group vpn neighbor 13.13.13.13 peer-as 65100
</pre></div>


<h2 class="wp-block-heading">vMX13</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-options autonomous-system 65100
set protocols bgp group vpn type internal
set protocols bgp group vpn family inet-vpn unicast
set protocols bgp group vpn neighbor 2.2.2.2 local-address 13.13.13.13
set protocols bgp group vpn neighbor 2.2.2.2 peer-as 65100
</pre></div>


<p>Once that&#8217;s configured &#8211; we should see the peering come up and the peers start automatically advertising any VPNv4 prefixes to each other&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [17]; title: ; notranslate">
admin@vmx2.lab&gt; show bgp summary 
Groups: 2 Peers: 2 Down peers: 0
Table          Tot Paths  Act Paths Suppressed    History Damp State    Pending
bgp.l3vpn.0          
                       2          2          0          0          0          0
Peer                     AS      InPkt     OutPkt    OutQ   Flaps Last Up/Dwn State|#Active/Received/Accepted/Damped...
13.13.13.13           65100          6          7       0       0          31 Establ
  customer.inet.0: 2/2/2/0
  bgp.l3vpn.0: 2/2/2/0
169.254.10.0          65001       2946       2947       0       0    22:02:20 Establ
  customer.inet.0: 1/1/1/0

admin@vmx2.lab&gt; show route advertising-protocol bgp 13.13.13.13 

customer.inet.0: 5 destinations, 5 routes (5 active, 0 holddown, 0 hidden)
  Prefix		  Nexthop	       MED     Lclpref    AS path
* 10.10.10.0/24           Self                         100        65002 65001 I
* 169.254.10.0/31         Self                         100        I

admin@vmx2.lab&gt; 
</pre></div>


<p>Perfect!  To help level set where we are with BGP configuration the image below describes the various peerings we have configured at this point.  Note with the exception of the PE/CE eBGP peerings (which are largely of no consequence to the larger design) the two main BGP peerings we have are for a VPNv4 BGP peering between both the CSC-PE nodes of the green carrier and the PE nodes of the orange carrier&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter"><a href="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-6.png" target="_blank" rel="noreferrer noopener"><img loading="lazy" decoding="async" width="860" height="594" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-6.png" alt="" class="wp-image-23314" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-6.png 860w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-6-200x138.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-6-300x207.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-6-768x530.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-6-624x431.png 624w" sizes="(max-width: 860px) 100vw, 860px" /></a></figure></div>



<p>So will our ping work at this point?  Let&#8217;s go look and see&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
left_client:~# ping 192.168.10.100 -c 5
PING 192.168.10.100 (192.168.10.100) 56(84) bytes of data.
 
--- 192.168.10.100 ping statistics ---
5 packets transmitted, 0 received, 100% packet loss, time 3999ms
 
left_client:~# 
</pre></div>


<p>Darn.  Ok &#8211; so what&#8217;s going on now?  Let&#8217;s trace the label path and see if we can sort this out&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx2.lab&gt; show route table customer.inet.0 192.168.10.100 

customer.inet.0: 5 destinations, 5 routes (5 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

192.168.10.0/24    *&#x5B;BGP/170] 00:03:42, localpref 100, from 13.13.13.13
                      AS path: 65004 65003 I, validation-state: unverified
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Push 16, Push 299904(top)

admin@vmx2.lab&gt; 
</pre></div>


<p>Ok &#8211; so vMX2 thinks it should be pushing a VPN label and then what Im guessing is an LDP label&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx2.lab&gt; show ldp database | grep 299904 
 299904      13.13.13.13/32
 299904      13.13.13.13/32

admin@vmx2.lab&gt; 
</pre></div>


<p>Yes, a LDP label onto the frame.  So vMX3 will get a frame with a top label of <code>299904</code>&#8230;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx3.lab&gt; show route table mpls.0 label 299904 

mpls.0: 17 destinations, 17 routes (17 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

299904             *&#x5B;LDP/9] 00:17:28, metric 1
                    &gt; to 169.254.10.5 via ge-0/0/1.0, Swap 299904

admin@vmx3.lab&gt; 
</pre></div>


<p>Which it then swaps for the same label.  So vMX4 will get <code>299904</code>&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx4.lab&gt; show route table mpls.0 label 299904 

mpls.0: 17 destinations, 17 routes (17 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

299904             *&#x5B;LDP/9] 00:18:05, metric 1
                    &gt; to 169.254.10.7 via ge-0/0/1.0, Swap 299904

admin@vmx4.lab&gt; 
</pre></div>


<p>Im sensing a theme here&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx5.lab&gt; show route table mpls.0 label 299904 

mpls.0: 17 destinations, 17 routes (17 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

299904             *&#x5B;LDP/9] 00:18:35, metric 1
                    &gt; to 169.254.10.9 via ge-0/0/1.0, Swap 299904

admin@vmx5.lab&gt; 
</pre></div>


<p>Now what will vMX6 do with it?</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx6.lab&gt; show route table orange_carrier.mpls.0 label 299904 

orange_carrier.mpls.0: 16 destinations, 16 routes (16 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

299904             *&#x5B;LDP/9] 00:19:03, metric 1, metric2 3
                    &gt; to 169.254.10.11 via ge-0/0/1.0, label-switched-path to-vmx9

admin@vmx6.lab&gt; 


</pre></div>


<p>Aha!  It&#8217;s putting it into the RSVP tunnel to vMX9.  Let&#8217;s see what label operation it&#8217;s doing though&#8230;</p>



<p><em>Note: Im doing fancy greps here to see the label operations just since it&#8217;s the easiest way to see the RSVP label operations.</em></p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx6.lab&gt; show route table orange_carrier.mpls.0 label 299904 extensive | grep &quot;Label operation&quot; 
                Label operation: Swap 16, Push 299792(top)
                Label operation: Swap 16
                        Label operation: Swap 16

admin@vmx6.lab&gt; 
</pre></div>


<p>Ok &#8211; so vMX7 will get a label of <code>299792</code>&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx7.lab&gt; show route table mpls.0 label 299792 extensive | grep &quot;Label operation&quot;   
                Label operation: Swap 299792

admin@vmx7.lab&gt; 
</pre></div>


<p>Which is then swaps for <code>299792</code> before it sends it to vMX8&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx8.lab&gt; show route table mpls.0 label 299792 extensive | grep &quot;Label operation&quot; 
                Label operation: Pop      
                Label operation: Pop      

admin@vmx8.lab&gt; 
</pre></div>


<p>vMX8 would be the PHP router for the RSVP label domain so popping the label might make some sense here.  So that would mean that vMX9 would get a label of <code>16</code>&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx9.lab&gt; show route table mpls.0 label 16 

mpls.0: 7 destinations, 7 routes (7 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

16                 *&#x5B;VPN/0] 02:49:55
                    &gt; via lsi.1 (orange_carrier), Pop      

admin@vmx9.lab&gt; 
</pre></div>


<p>Huh.  So it&#8217;s popping the label off and dumping it into the VRF.  So what we have looks a lot like this&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter"><a href="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-4.png" target="_blank" rel="noreferrer noopener"><img loading="lazy" decoding="async" width="886" height="508" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-4.png" alt="" class="wp-image-23308" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-4.png 886w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-4-200x115.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-4-300x172.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-4-768x440.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-4-624x358.png 624w" sizes="(max-width: 886px) 100vw, 886px" /></a></figure></div>



<p>The labels in orange are imposed by the orange carrier while the ones in green are by the green carrier.  If this were a normal MPLS VPN scenario I&#8217;d expect the PHP router for the green carrier, which in our case is vMX8, to pop off the transport label and send a frame with a VPN label to vMX9.  Now remember &#8211; this VPN we&#8217;re talking about here is in the green carrier &#8211; so it&#8217;s the VPN label that was imposed by vMX6 (label <code>16</code> in the above example).  vMX9 would use that VPN label to sort out what VRF the traffic should land in.  But the problem is that when it does that, there&#8217;s still yet another VPN label on the frame which was imposed by our orange carrier.  We can see this in a packet capture between vMX8 and vMX9&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter"><a href="blob:http://www.dasblinkenlichten.com/7f142537-09ad-4c4e-a388-1eec86ab4ddb" target="_blank" rel="noreferrer noopener"><img loading="lazy" decoding="async" width="805" height="349" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-1.png" alt="" class="wp-image-23303" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-1.png 805w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-1-200x87.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-1-300x130.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-1-768x333.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-1-624x271.png 624w" sizes="(max-width: 805px) 100vw, 805px" /></a></figure></div>



<p>We&#8217;d expect (actually need) vMX9 to leave the bottom layer VPN label alone since that&#8217;s only relevant to the orange carrier.  In this case, it&#8217;s popping off the green carriers VPN label and dumping the labelled frame into the <code>orange_carrier</code> VRF which is doing us no good at all.  So why isn&#8217;t it stitching our LSPs together?</p>



<p>The problem as it turns out is related to the <code>vrf-table-label</code> command.  The router in this case is largely doing what we asked it to do.  We&#8217;ve talked about the <code>vrf-table-label</code> command in previous posts &#8211; and you might recall that it has two rather interesting impacts on how the router handles the VRF.  First, when enabled, it assigns the same label for every prefix from that given VRF.  We can see that if we look at the routes being advertised over to vMX6&#8230;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7,17,28,39,50,61,71,82,93]; title: ; notranslate">
admin@vmx9.lab&gt; show route advertising-protocol bgp 6.6.1.1 extensive 

orange_carrier.inet.0: 21 destinations, 21 routes (21 active, 0 holddown, 0 hidden)
* 9.9.9.9/32 (1 entry, 1 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 16
     Nexthop: Self
     Flags: Nexthop Change
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1

* 10.10.10.10/32 (1 entry, 1 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 16
     Nexthop: Self
     Flags: Nexthop Change
     MED: 1
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1 rte-type:0.0.0.0:1:0

* 11.11.11.11/32 (1 entry, 1 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 16
     Nexthop: Self
     Flags: Nexthop Change
     MED: 2
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1 rte-type:0.0.0.0:1:0

* 12.12.12.12/32 (1 entry, 1 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 16
     Nexthop: Self
     Flags: Nexthop Change
     MED: 3
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1 rte-type:0.0.0.0:1:0

* 13.13.13.13/32 (1 entry, 1 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 16
     Nexthop: Self
     Flags: Nexthop Change
     MED: 4
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1 rte-type:0.0.0.0:1:0

* 169.254.10.16/31 (1 entry, 1 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1           
     VPN Label: 16
     Nexthop: Self
     Flags: Nexthop Change
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1

* 169.254.10.18/31 (1 entry, 1 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 16
     Nexthop: Self
     Flags: Nexthop Change
     MED: 2
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1 rte-type:0.0.0.0:1:0

* 169.254.10.20/31 (1 entry, 1 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 16
     Nexthop: Self
     Flags: Nexthop Change
     MED: 3
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1 rte-type:0.0.0.0:1:0

* 169.254.10.22/31 (1 entry, 1 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 16
     Nexthop: Self
     Flags: Nexthop Change
     MED: 4
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1 rte-type:0.0.0.0:1:0

admin@vmx9.lab&gt; 
</pre></div>


<p>Second &#8211; it forces a local VRF lookup through an automatically created LSI interface.  This allows us to overcome some of the problems that come along with having to do things like ARP lookups inside the VRF.  However, in this case, it&#8217;s actually causing us some problems.  If we force a lookup in the VRF &#8211; we can&#8217;t really precompute an end to end LSP path.  So vMX9 in this case is going dump the traffic in the VRF through the LSI interface and try to resolve things there.  That&#8217;s what we saw above.  Unfortunately for us, in this case, that just won&#8217;t work since we have another VPN label on the frame for the orange carrier.  </p>



<p>To fix this, lets start by removing <code>vrf-table-label</code> on both CSC-PE routers&#8230;</p>



<h2 class="wp-block-heading">vMX6 and vMX9</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
delete routing-instances orange_carrier vrf-table-label 
</pre></div>


<p>After making that change, let&#8217;s look at the routes being advertised to vMX6 again&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7,17,28,39,50,61,71,82,93]; title: ; notranslate">
admin@vmx9.lab&gt; show route advertising-protocol bgp 6.6.1.1 extensive    

orange_carrier.inet.0: 21 destinations, 21 routes (21 active, 0 holddown, 0 hidden)
* 9.9.9.9/32 (1 entry, 1 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 301216
     Nexthop: Self
     Flags: Nexthop Change
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1

* 10.10.10.10/32 (1 entry, 1 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 301232
     Nexthop: Self
     Flags: Nexthop Change
     MED: 1
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1 rte-type:0.0.0.0:1:0

* 11.11.11.11/32 (1 entry, 1 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 301232
     Nexthop: Self
     Flags: Nexthop Change
     MED: 2
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1 rte-type:0.0.0.0:1:0

* 12.12.12.12/32 (1 entry, 1 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 301232
     Nexthop: Self
     Flags: Nexthop Change
     MED: 3
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1 rte-type:0.0.0.0:1:0

* 13.13.13.13/32 (1 entry, 1 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 301232
     Nexthop: Self
     Flags: Nexthop Change
     MED: 4
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1 rte-type:0.0.0.0:1:0

* 169.254.10.16/31 (1 entry, 1 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1           
     VPN Label: 301232
     Nexthop: Self
     Flags: Nexthop Change
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1

* 169.254.10.18/31 (1 entry, 1 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 301232
     Nexthop: Self
     Flags: Nexthop Change
     MED: 2
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1 rte-type:0.0.0.0:1:0

* 169.254.10.20/31 (1 entry, 1 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 301232
     Nexthop: Self
     Flags: Nexthop Change
     MED: 3
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1 rte-type:0.0.0.0:1:0

* 169.254.10.22/31 (1 entry, 1 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 301232
     Nexthop: Self
     Flags: Nexthop Change
     MED: 4
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1 rte-type:0.0.0.0:1:0

admin@vmx9.lab&gt; 
</pre></div>


<p>Alright &#8211; do we have a unique label for each possible prefix?  No &#8211; we dont.  The thing is &#8211; locally allocated labels are a means for a router to say &#8220;if I get a label of X I know I need to do Y&#8221;.  So in some cases, the router regardless of destination prefix will do the same thing.  If we look at the destinations for the majority of the prefixes we&#8217;ll see that they are all using the label of <code>301232</code>.  So what does that label do?..</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx9.lab&gt; show route table mpls.0 label 301232                     

mpls.0: 9 destinations, 9 routes (9 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

301232             *&#x5B;VPN/170] 00:04:15
                    &gt; to 169.254.10.17 via ge-0/0/1.0, Pop      
301232(S=0)        *&#x5B;VPN/170] 00:04:15
                    &gt; to 169.254.10.17 via ge-0/0/1.0, Pop      

admin@vmx9.lab&gt; 
</pre></div>


<p>Pop again&#8230;  Darn.  So as you guessed &#8211; our ping still isn&#8217;t working.  So let&#8217;s look at the orange carriers VRF table again&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [24,25,26,27,28,29,30,31,44,45,46,47,48,49,50,51,52,53]; title: ; notranslate">
admin@vmx9.lab&gt; show route table orange_carrier.inet.0 

orange_carrier.inet.0: 21 destinations, 21 routes (21 active, 0 holddown, 0 hidden)
@ = Routing Use Only, # = Forwarding Use Only
+ = Active Route, - = Last Active, * = Both

2.2.2.2/32         *&#x5B;BGP/170] 00:14:25, MED 4, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
3.3.3.3/32         *&#x5B;BGP/170] 00:14:25, MED 3, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
4.4.4.4/32         *&#x5B;BGP/170] 00:14:25, MED 2, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
5.5.5.5/32         *&#x5B;BGP/170] 00:14:25, MED 1, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
6.6.6.6/32         *&#x5B;BGP/170] 00:14:26, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
9.9.9.9/32         *&#x5B;Direct/0] 3d 02:39:56
                    &gt; via lo0.1
10.10.10.10/32     *&#x5B;OSPF/10] 00:22:44, metric 1
                    &gt; to 169.254.10.17 via ge-0/0/1.0
11.11.11.11/32     *&#x5B;OSPF/10] 00:22:44, metric 2
                    &gt; to 169.254.10.17 via ge-0/0/1.0
12.12.12.12/32     *&#x5B;OSPF/10] 00:22:44, metric 3
                    &gt; to 169.254.10.17 via ge-0/0/1.0
13.13.13.13/32     *&#x5B;OSPF/10] 00:22:44, metric 4
                    &gt; to 169.254.10.17 via ge-0/0/1.0
169.254.10.2/31    *&#x5B;BGP/170] 00:14:25, MED 4, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
169.254.10.4/31    *&#x5B;BGP/170] 00:14:25, MED 3, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
169.254.10.6/31    *&#x5B;BGP/170] 00:14:25, MED 2, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
169.254.10.8/31    *&#x5B;BGP/170] 00:14:26, localpref 100, from 6.6.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.14 via ge-0/0/0.0, label-switched-path to-vmx6
169.254.10.16/31   *&#x5B;Direct/0] 3d 02:39:56
                    &gt; via ge-0/0/1.0
169.254.10.16/32   *&#x5B;Local/0] 3d 02:39:56
                      Local via ge-0/0/1.0
169.254.10.18/31   *&#x5B;OSPF/10] 00:22:44, metric 2
                    &gt; to 169.254.10.17 via ge-0/0/1.0
169.254.10.20/31   *&#x5B;OSPF/10] 00:22:44, metric 3
                    &gt; to 169.254.10.17 via ge-0/0/1.0
169.254.10.22/31   *&#x5B;OSPF/10] 00:22:44, metric 4
                    &gt; to 169.254.10.17 via ge-0/0/1.0
224.0.0.2/32       *&#x5B;LDP/9] 3d 02:39:56, metric 1
                      MultiRecv
224.0.0.5/32       *&#x5B;OSPF/10] 3d 02:39:56, metric 1
                      MultiRecv

admin@vmx9.lab&gt; 
</pre></div>


<p>Can anyone spot the issue?  The routes that vMX9 knows about to reach the destination we need to get to (in this case we really want to get to the loopback of vMX13 (<code>13.13.13.13</code>)) are all know locally through OSPF.  That wont work if we want to ride an LSP all the way there!  We&#8217;ve run into this in a previous post &#8211; but the fix for this is to copy the routes from <code>inet.3</code> into <code>inet.0</code> since the router itself can&#8217;t resolve the destinations in <code>inet.3</code> (only BGP can).  To do this, we&#8217;ll use our <code>mpls-forwarding</code> command for MPLS traffic engineering&#8230;</p>



<h2 class="wp-block-heading">vMX6 and vMX9</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-instances orange_carrier protocols mpls traffic-engineering mpls-forwarding  
</pre></div>


<p>Once that&#8217;s in &#8211; our ping should start working&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@left_client:~# ping 192.168.10.100 -c 5
64 bytes from 192.168.10.100: icmp_seq=1 ttl=57 time=3.86 ms
64 bytes from 192.168.10.100: icmp_seq=2 ttl=57 time=4.63 ms
64 bytes from 192.168.10.100: icmp_seq=3 ttl=57 time=4.12 ms
64 bytes from 192.168.10.100: icmp_seq=4 ttl=57 time=6.33 ms
64 bytes from 192.168.10.100: icmp_seq=5 ttl=57 time=4.68 ms
root@left_client:~# 
</pre></div>


<p>And if we look at our routing table again &#8211; we&#8217;ll see that the destinations we&#8217;re interested in now have LDP routes for forwarding&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx9.lab&gt; show route table orange_carrier.inet.0 protocol ldp 

orange_carrier.inet.0: 21 destinations, 25 routes (21 active, 0 holddown, 0 hidden)
@ = Routing Use Only, # = Forwarding Use Only
+ = Active Route, - = Last Active, * = Both

10.10.10.10/32     #&#x5B;LDP/9] 00:01:31, metric 1
                    &gt; to 169.254.10.17 via ge-0/0/1.0
11.11.11.11/32     #&#x5B;LDP/9] 00:01:31, metric 1
                    &gt; to 169.254.10.17 via ge-0/0/1.0, Push 299776
12.12.12.12/32     #&#x5B;LDP/9] 00:01:31, metric 1
                    &gt; to 169.254.10.17 via ge-0/0/1.0, Push 299792
13.13.13.13/32     #&#x5B;LDP/9] 00:01:31, metric 1
                    &gt; to 169.254.10.17 via ge-0/0/1.0, Push 299808
224.0.0.2/32       *&#x5B;LDP/9] 3d 02:44:45, metric 1
                      MultiRecv

admin@vmx9.lab&gt; 
</pre></div>


<p>This is one case where not having <code>vrf-table-label</code> on could be a good idea.  Not having it on allows us to remove the constraint of one label per VRF which means that the router can allocate as many labels as it needs based on how many different actions it needs to perform.  However, this also means that we&#8217;re chewing through labels.  Look at this excerpt of the routes being advertised to vMX6 now&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [4,14,25,36,47]; title: ; notranslate">
* 9.9.9.9/32 (1 entry, 1 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 301216
     Nexthop: Self
     Flags: Nexthop Change
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1

@ 10.10.10.10/32 (2 entries, 2 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 301392
     Nexthop: Self
     Flags: Nexthop Change
     MED: 1
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1 rte-type:0.0.0.0:1:0

@ 11.11.11.11/32 (2 entries, 2 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 301408
     Nexthop: Self
     Flags: Nexthop Change
     MED: 2
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1 rte-type:0.0.0.0:1:0

@ 12.12.12.12/32 (2 entries, 2 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 301424
     Nexthop: Self
     Flags: Nexthop Change
     MED: 3
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1 rte-type:0.0.0.0:1:0

@ 13.13.13.13/32 (2 entries, 2 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 301440
     Nexthop: Self
     Flags: Nexthop Change
     MED: 4
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1 rte-type:0.0.0.0:1:0
</pre></div>


<p>We&#8217;re now allocating a lot more unique labels than we were before.  So is there a happy middle ground here?  As it turns out &#8211; we can also get this to work with <code>vrf-table-label</code> enabled as well.  So let&#8217;s turn that back on&#8230;</p>



<h2 class="wp-block-heading">vMX6 and vMX9</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-instances orange_carrier vrf-table-label 
</pre></div>


<p>Our ping should break once more &#8211; but let&#8217;s talk about why this isn&#8217;t working in greater detail.  Let&#8217;s make sure we&#8217;re seeing the same behavior again&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7,21]; title: ; notranslate">
admin@vmx9.lab&gt; show route advertising-protocol bgp 6.6.1.1 13.13.13.13/32 extensive 

orange_carrier.inet.0: 21 destinations, 25 routes (21 active, 0 holddown, 0 hidden)
@ 13.13.13.13/32 (2 entries, 2 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 16
     Nexthop: Self
     Flags: Nexthop Change
     MED: 4
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1 rte-type:0.0.0.0:1:0

admin@vmx9.lab&gt; show route table mpls.0 label 16  

mpls.0: 7 destinations, 7 routes (7 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

16                 *&#x5B;VPN/0] 00:00:49
                    &gt; via lsi.768 (orange_carrier), Pop      

admin@vmx9.lab&gt; 
</pre></div>


<p>Yep &#8211; ok.  Same problem.  But let&#8217;s think about this for a moment.  We talked earlier that routes from the orange carrier would be turned into VPNv4 routes on the CSC-PEs and then exported back into OSPF.  Since we turned on our traffic engineering flag for <code>mpls-forwarding</code> we copied the routes we care about (the router loopbacks) from <code>inet.3</code> into <code>inet.0</code> right?..</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx9.lab&gt; show route table orange_carrier.inet.0 13.13.13.13/32  

orange_carrier.inet.0: 21 destinations, 25 routes (21 active, 0 holddown, 0 hidden)
@ = Routing Use Only, # = Forwarding Use Only
+ = Active Route, - = Last Active, * = Both

13.13.13.13/32     @&#x5B;OSPF/10] 00:09:29, metric 4
                    &gt; to 169.254.10.17 via ge-0/0/1.0
                   #&#x5B;LDP/9] 00:09:29, metric 1
                    &gt; to 169.254.10.17 via ge-0/0/1.0, Push 299808

admin@vmx9.lab&gt; 
</pre></div>


<p>Remember that the <code>mpls-forwarding</code> option is the safest option to use since it only copies the routes for forwarding use.  But in this case, that&#8217;s actually biting us.  See &#8211; when the router converts those routes to VPNv4 routes it&#8217;s doing so with the OSPF route since that&#8217;s the one thats winning.  We can tell this is the case by looking at the communities attached to the route.  Notice above how the community list included <code>rte-type:0.0.0.0:1:0</code>.  That&#8217;s actually a community that BGP uses so it knows what kind of OSPF route it&#8217;s dealing with.  This is actually the &#8220;route type&#8221; field as described in <a href="https://tools.ietf.org/html/rfc4577">RFC 4577</a>.  The  layout looks like this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
AREA Number (4 bytes) : Route Type (1 byte) : Options (1 byte)
</pre></div>


<p>So we&#8217;re saying this came from area 0 (<code>0.0.0.0</code>) and is a route type of intra-area.  In either case, this is an OSPF route that&#8217;s being translated and sent to vMX6 as a VPNv4 route.  Since vMX9 believes that the OSPF route is the winning route &#8211; it still think that it should pop off what it believes to be the last label (<code>16</code> (the green carrier VPN label)) and send it into the VRF toward the destination which it knows about through OSPF.  To fix this, we need to tell the router to prefer the LDP route.  We can do this by changing our traffic engineering flag to <code>bgp-igp-both-ribs</code> which will whole sale copy the routes from <code>inet.3</code> into <code>inet.0</code>.  Let&#8217;s try that out&#8230;</p>



<h2 class="wp-block-heading">vMX6 and vMX9</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-instances orange_carrier protocols mpls traffic-engineering bgp-igp-both-ribs 
</pre></div>


<p>Now if we look at the routing table, we should see the LDP routes in there and notice how they are no longer just for forwarding, they&#8217;re winning for both forwarding and routing use&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx9.lab&gt; show route table orange_carrier.inet.0 13.13.13.13/32 

orange_carrier.inet.0: 21 destinations, 25 routes (21 active, 0 holddown, 0 hidden)
@ = Routing Use Only, # = Forwarding Use Only
+ = Active Route, - = Last Active, * = Both

13.13.13.13/32     *&#x5B;LDP/9] 00:00:28, metric 1
                    &gt; to 169.254.10.17 via ge-0/0/1.0, Push 299808
                    &#x5B;OSPF/10] 00:47:39, metric 4
                    &gt; to 169.254.10.17 via ge-0/0/1.0

admin@vmx9.lab&gt; 
</pre></div>


<p>This means that the route being sent to vMX6 is no longer the OSPF route&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx9.lab&gt; show route advertising-protocol bgp 6.6.1.1 13.13.13.13/32 extensive    

orange_carrier.inet.0: 21 destinations, 25 routes (21 active, 0 holddown, 0 hidden)
* 13.13.13.13/32 (2 entries, 2 announced)
 BGP group green_carrier_vpn type Internal
     Route Distinguisher: 1:1
     VPN Label: 301568
     Nexthop: Self
     Flags: Nexthop Change
     MED: 1
     Localpref: 100
     AS path: &#x5B;65000] I 
     Communities: target:1:1

admin@vmx9.lab&gt; 
</pre></div>


<p>Notice the lack of the <code>rte-type</code> community above.  And if we look at what the router will do with that label&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7,22]; title: ; notranslate">
admin@vmx9.lab&gt; show route table mpls.0 label 301568 

mpls.0: 12 destinations, 12 routes (12 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

301568             *&#x5B;VPN/170] 00:02:08
                    &gt; to 169.254.10.17 via ge-0/0/1.0, Swap 299808

admin@vmx9.lab&gt; show ldp database instance orange_carrier 
Input label database, 9.9.9.9:0--10.10.10.10:0
Labels received: 10
  Label     Prefix
 300240      2.2.2.2/32
 300256      3.3.3.3/32
 300272      4.4.4.4/32
 300288      5.5.5.5/32
 299920      6.6.6.6/32
 299936      9.9.9.9/32
      3      10.10.10.10/32
 299776      11.11.11.11/32
 299792      12.12.12.12/32
 299808      13.13.13.13/32

Output label database, 9.9.9.9:0--10.10.10.10:0
Labels advertised: 10
  Label     Prefix
 301504      2.2.2.2/32
 301472      3.3.3.3/32
 301456      4.4.4.4/32
 301488      5.5.5.5/32
 299968      6.6.6.6/32
      3      9.9.9.9/32
 301328      10.10.10.10/32
 301344      11.11.11.11/32
 301360      12.12.12.12/32
 301376      13.13.13.13/32

admin@vmx9.lab&gt; 
</pre></div>


<p>It will swap that label with one the one it learned through LDP to get the traffic to vMX13.  And our test ping should once again be working&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@left_client:~# ping 192.168.10.100 -c 5
64 bytes from 192.168.10.100: icmp_seq=1 ttl=57 time=3.86 ms
64 bytes from 192.168.10.100: icmp_seq=2 ttl=57 time=4.63 ms
64 bytes from 192.168.10.100: icmp_seq=3 ttl=57 time=4.12 ms
64 bytes from 192.168.10.100: icmp_seq=4 ttl=57 time=6.33 ms
64 bytes from 192.168.10.100: icmp_seq=5 ttl=57 time=4.68 ms
root@left_client:~# 
</pre></div>


<p>If we were to visualize what an end to end flow would look like it might look like this&#8230;</p>



<p><em>Note: Im only posting the graphic &#8211; we already posted an example of the commands used to get this information earlier.  </em></p>



<div class="wp-block-image"><figure class="aligncenter"><a href="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-5.png" target="_blank" rel="noreferrer noopener"><img loading="lazy" decoding="async" width="886" height="508" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-5.png" alt="" class="wp-image-23309" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-5.png 886w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-5-200x115.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-5-300x172.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-5-768x440.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/11/image-5-624x358.png 624w" sizes="(max-width: 886px) 100vw, 886px" /></a></figure></div>



<p>You can see that the green labels imposed by the green carrier are simply for isolating the orange carrier traffic while it traverses the green carrier.  </p>



<p>So there you have it.  We&#8217;ve used CsC to connect discrete label domains.  In the next post we&#8217;ll talk about how we can do CsC using BGP instead of OSPF and LDP in the CSC-PE VRF.  Stay tuned!</p>
]]></content:encoded>
					
					<wfw:commentRss>http://www.dasblinkenlichten.com/carrier-supporting-carrier-with-ospf-and-ldp/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">23276</post-id>	</item>
		<item>
		<title>Fundamentals of MPLS LSPs</title>
		<link>http://www.dasblinkenlichten.com/fundamentals-of-mpls-lsps/</link>
					<comments>http://www.dasblinkenlichten.com/fundamentals-of-mpls-lsps/#comments</comments>
		
		<dc:creator><![CDATA[Jon Langemak]]></dc:creator>
		<pubDate>Fri, 18 Oct 2019 19:01:46 +0000</pubDate>
				<category><![CDATA[Juniper]]></category>
		<category><![CDATA[MPLS]]></category>
		<guid isPermaLink="false">http://www.dasblinkenlichten.com/?p=23200</guid>

					<description><![CDATA[One of the items that often trips folks up with MPLS is the concept of label switched paths or LSPs. We&#8217;ve talked about them extensively before in many of the... <a href="http://www.dasblinkenlichten.com/fundamentals-of-mpls-lsps/">Read more &#187;</a>]]></description>
										<content:encoded><![CDATA[
<p>One of the items that often trips folks up with MPLS is the concept of label switched paths or LSPs.  We&#8217;ve talked about them extensively before in many of the blog posts here and I&#8217;ve described them a couple of different ways.  Many people look at an LSP as a sort of unidirectional tunnel.  In fact, most network diagrams aiming to describe an LSP often show it just as that &#8211; a tunnel.  It&#8217;s an easy thing to visualize especially when you start talking about nested tunnels or LSPs inside of LSPs, but I also think it can be rather confusing.  This becomes even more confusing when people start talking about end to end LSPs or how a service label is the same end to end as traffic traverses an LSP.  What does that mean?  Where does an LSP start or stop?  Is it really a tunnel?  How far can an LSP reach?  What if we run different label distribution protocols?  In this post, and perhaps the next, I hope to address these questions as well as talk about how we can solve some of the common problems that are often encountered with LSPs.</p>



<p>So let&#8217;s dive right in and talk about the LSP to tunnel analogy.  As we&#8217;ve already seen in previous posts, LSPs are not a tunnel in the sense that most networking engineering folks are accustom to.  When I think of tunneling, the first thing that comes to my mind is GRE tunnels.  In that model, we&#8217;re encapsulating IP inside of IP.  Router  A encapsulates a packet inside another packet and sends it toward the tunnel destination router B.  Router B then strips off the outer IP header and now has the original encapsulated packet to work with.  In MPLS &#8211; it&#8217;s the same model except we&#8217;re dealing with a label instead of an outer IP packet header.  The difference between something like GRE and MPLS is how the transit, or &#8220;routers in the middle&#8221;, deal with the traffic.  In the MPLS world &#8211; transit routers only care about labels and label operations.  They never inspect the packet to make a decision as to how to forward the tunneled traffic.  On the other hand, a GRE transit router still needs to do a route lookup on the outer IP packet to sort out where to send the tunneled traffic.  Another interesting difference to think about is that MPLS routers change the label (in most cases) as a means to facilitate moving traffic through the LSP.  A GRE transit router doesn&#8217;t change the outer, or tunnel, IP packet.  It simply performs a lookup and passes the traffic to the next transit router who will also do a lookup on that same IP header.  When thinking about it at that level &#8211; one could compare an MPLS label to the ethernet headers destination MAC address.  A GRE transit router needs to know the MAC address for the next hop it resolves during it&#8217;s IP lookup and that MAC address needs to be written into the L2 header so that the next hop router knows to accept the frame.  In that case you might compare ARP with a label distribution protocol.  In either case, the routers need to exchange information that tells them how to talk to one another and that information is used to facilitate forwarding at the tunnel level.  The significant difference between the two methodologies when viewed from this perspective is that an MPLS LSP (tunnel) is precomputed on all routers that take part in the LSP.  A GRE router is still performing lookups at each hop along the path to the tunnel destination.  This brings us back to one of the initial selling points of label or tag based forwarding which was that the router did not need to do IP lookups at each hop. </p>



<p>So at a high level &#8211; I think the comparison between an LSP being a networking &#8220;tunnel&#8221; is rather apt since it behaves much like any other tunnel we typically see in networking.  Put more simply &#8211; the mechanism used to move the tunneled traffic from point A to point B never needs to examine the traffic that&#8217;s being tunneled. This is one of the huge benefits of MPLS since it allows us to have architectures like BGP free core designs where &#8220;routers in the middle&#8221; don&#8217;t need to know how to forward tunneled traffic. More importantly, MPLS allows us to tack on additional &#8220;services&#8221; like MPLS VPNs that really make the architecture significantly more useful and scalable than a tunneling technology such as GRE.  </p>



<p>My point with this rambling is that tunneling or encapsulation in networking is an analogy that can be looked at lots of different ways.  I mean one look at the OSI model and you should see that.  Despite this &#8211; when I initially started working with MPLS years ago &#8211;  I had a a hard time picturing an LSP as a tunnel.  It just didn&#8217;t align with my thinking of what a tunnel was.  But once I started hands on working with it, the LSP to tunnel analogy made a lot more sense to me.  You&#8217;re encapsulating some traffic inside of MPLS labels which allow routers along the way to not have to inspect the encapsulated datagrams.  And as we&#8217;ll see in this post (or maybe the next) the LSP tunnel analogy is helpful as we try to visualize things like hierarchical LSPs (don&#8217;t panic &#8211; we haven&#8217;t talked about what those are yet).  </p>



<p>To help cement the concept of LSPs as tunnels, let&#8217;s look at an example lab topology where multiple LSPs come into play&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter"><img loading="lazy" decoding="async" width="814" height="205" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-3.png" alt="" class="wp-image-23209" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-3.png 814w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-3-200x50.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-3-300x76.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-3-768x193.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-3-624x157.png 624w" sizes="(max-width: 814px) 100vw, 814px" /></figure></div>



<p><em>Note: The lab is the same one we used in the previous BGP-LU posts so if you&#8217;ve read those then this should look familiar to you.  </em></p>



<p>Above you can see we have a simple topology.  I&#8217;m including the above diagram to show you the link local IP addressing and the interface names so you can follow along.  In future diagrams, I need to take that stuff out so it isn&#8217;t so cluttered.  The blue numbers represent the interface numbering.  AKA &#8211; <code>0</code> is the same as <code>ge-0/0/0</code>.  Here is what our label distribution protocol use will look like&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter"><img loading="lazy" decoding="async" width="823" height="239" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-4.png" alt="" class="wp-image-23210" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-4.png 823w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-4-200x58.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-4-300x87.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-4-768x223.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-4-624x181.png 624w" sizes="(max-width: 823px) 100vw, 823px" /></figure></div>



<p>The above lab demonstrates an important aspect of MPLS LSPs.  Here we have two sites (left and right (I know I&#8217;m super creative)) separated by a core.  The core in this case is running RSVP as a label distribution protocol whereas the left and right sites are running LDP.  In this initial iteration, we&#8217;re not running any MPLS services across the core (think MPLS VPNs etc), rather we&#8217;re just pursuing the basic MPLS use case of a BGP free core.  The intent here is that the routers between the two tail routers have no knowledge of the prefixes being advertised through BGP between the the left and the right sites.  Routers 1 and 7 provide connectivity for two clients aptly named <code><code>left_client</code></code> and <code>right_client</code>.  Before we layer in BGP &#8211; let&#8217;s look at the base configuration of each of the routers so you can follow along if you like&#8230;</p>



<h2 class="wp-block-heading">vMX1</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set system host-name vmx1.lab
set interfaces ge-0/0/0 unit 0 family inet address 10.10.10.1/24
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.0/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 1.1.1.1/32 primary
set routing-options router-id 1.1.1.1
set protocols ldp interface ge-0/0/1.0
set protocols mpls interface ge-0/0/1.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p

</pre></div>


<h2 class="wp-block-heading">vMX2</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set system host-name vmx2.lab
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.1/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.2/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 2.2.2.2/32 primary
set routing-options router-id 2.2.2.2
set protocols ldp interface ge-0/0/0.0
set protocols ldp interface ge-0/0/1.0
set protocols mpls interface ge-0/0/1.0
set protocols mpls interface ge-0/0/0.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
</pre></div>


<h2 class="wp-block-heading">vMX3</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set system host-name vmx3.lab
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.3/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.4/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 3.3.3.3/32 primary
set protocols rsvp interface ge-0/0/1.0
set protocols ldp interface ge-0/0/0.0 
set protocols mpls label-switched-path to-vmx5 to 5.5.5.5
set protocols mpls interface ge-0/0/1.0
set protocols mpls interface ge-0/0/0.0
set protocols ospf traffic-engineering
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
</pre></div>


<h2 class="wp-block-heading">vMX4</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set system host-name vmx4.lab
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.5/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.6/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 4.4.4.4/32 primary
set routing-options router-id 4.4.4.4
set protocols rsvp interface ge-0/0/1.0
set protocols rsvp interface ge-0/0/0.0
set protocols mpls interface ge-0/0/1.0
set protocols mpls interface ge-0/0/0.0
set protocols ospf traffic-engineering
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
</pre></div>


<h2 class="wp-block-heading">vMX5</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set system host-name vmx5.lab
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.7/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.8/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 5.5.5.5/32 primary
set routing-options router-id 5.5.5.5
set protocols rsvp interface ge-0/0/0.0
set protocols mpls label-switched-path to-vmx3 to 3.3.3.3
set protocols mpls interface ge-0/0/1.0
set protocols mpls interface ge-0/0/0.0
set protocols ospf traffic-engineering
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ldp interface ge-0/0/1.0
</pre></div>


<h2 class="wp-block-heading">vMX6</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set system host-name vmx6.lab
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.9/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 169.254.10.10/31
set interfaces ge-0/0/1 unit 0 family mpls
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 6.6.6.6/32 primary
set routing-options router-id 6.6.6.6
set protocols mpls interface ge-0/0/1.0
set protocols mpls interface ge-0/0/0.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
set protocols ldp interface ge-0/0/0.0
set protocols ldp interface ge-0/0/1.0

</pre></div>


<h2 class="wp-block-heading">vMX7</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set system host-name vmx7.lab
set interfaces ge-0/0/0 unit 0 family inet address 169.254.10.11/31
set interfaces ge-0/0/0 unit 0 family mpls
set interfaces ge-0/0/1 unit 0 family inet address 192.168.10.1/24
set interfaces lo0 unit 0 family inet no-redirects
set interfaces lo0 unit 0 family inet address 7.7.7.7/32 primary
set routing-options static route 10.171.200.0/22 next-hop 192.168.127.100
set routing-options router-id 7.7.7.7
set protocols mpls interface ge-0/0/0.0
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ldp interface ge-0/0/0.0
</pre></div>


<p>As you can see &#8211; this is all pretty standard configuration.  I&#8217;ve enabled MPLS on all of the interfaces where we expect to see labels and RSVP or LDP on the interfaces that require it.  On vMX3 and vMX4 I created RSVP LSPs that simply reference the other IP addresses loopback.  You&#8217;ll also notice that all the routers are part of one big OSPF area.  That&#8217;s me cheating, typically you wouldn&#8217;t have the same IGP area in both the core and the remote sites but for the sake of this discussion, it doesn&#8217;t matter.</p>



<p>Now that we have our base configuration done, let&#8217;s look at configuring BGP.  If our goal was simply to advertise the client prefixes from vMX1 to vMX7 &#8211; you&#8217;d think that a direct peering between the two would be sufficient.  So let&#8217;s imagine that vMX1 is in AS 65001 and vMX2 is in AS 65002 and try that out&#8230;</p>



<h2 class="wp-block-heading">vMX1</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-options autonomous-system 65001 
set protocols bgp group external peer-as 65002 
set protocols bgp group external neighbor 7.7.7.7 family inet unicast               
set protocols bgp group external multihop ttl 255    
set protocols bgp group external type external      
set protocols bgp group external local-address 1.1.1.1 
set policy-options policy-statement bgp-export term direct from protocol direct 
set policy-options policy-statement bgp-export term direct from prefix-list bgp-export           
set policy-options policy-statement bgp-export term direct then accept                    
set protocols bgp group external export bgp-export                                        
set policy-options prefix-list bgp-export 10.10.10.0/24 
</pre></div>


<h2 class="wp-block-heading">vMX7</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-options autonomous-system 65002
set protocols bgp group external peer-as 65001 
set protocols bgp group external neighbor 1.1.1.1 family inet unicast               
set protocols bgp group external multihop ttl 255    
set protocols bgp group external type external  
set protocols bgp group external local-address 7.7.7.7      
set policy-options policy-statement bgp-export term direct from protocol direct 
set policy-options policy-statement bgp-export term direct from prefix-list bgp-export           
set policy-options policy-statement bgp-export term direct then accept                    
set protocols bgp group external export bgp-export                                        
set policy-options prefix-list bgp-export 192.168.10.0/24
</pre></div>


<p>So nothing too fancy here.  The only weird part is that I&#8217;m doing an eBGP peering to a loopback so I need to increase the TTL for multihop but the rest seems pretty straight forward.  Once that config is committed we should see our peerings come up and we should get the routes from the other side&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx1.lab&gt; show route table inet.0 192.168.10.0/24 

inet.0: 22 destinations, 22 routes (22 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

192.168.10.0/24    *&#x5B;BGP/170] 00:03:39, localpref 100, from 7.7.7.7
                      AS path: 65002 I, validation-state: unverified
                    &gt; to 169.254.10.1 via ge-0/0/1.0

admin@vmx1.lab&gt; 
</pre></div>

<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx7.lab&gt; show route table inet.0 10.10.10.0/24 

inet.0: 22 destinations, 22 routes (22 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

10.10.10.0/24      *&#x5B;BGP/170] 00:04:00, localpref 100, from 1.1.1.1
                      AS path: 65001 I, validation-state: unverified
                    &gt; to 169.254.10.10 via ge-0/0/0.0

admin@vmx7.lab&gt; 
</pre></div>


<p>All looks good.  So can the clients ping each other?</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
left_client:~# ping 192.168.10.100 -c 5
PING 192.168.10.100 (192.168.10.100) 56(84) bytes of data.

--- 192.168.10.100 ping statistics ---
5 packets transmitted, 0 received, 100% packet loss, time 3999ms

left_client:~# 
</pre></div>


<p>Negative.  Any guesses as to why?  Let&#8217;s try and track this down&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [11,15]; title: ; notranslate">
admin@vmx1.lab&gt; show route table inet.0 192.168.10.100 extensive 

inet.0: 22 destinations, 22 routes (22 active, 0 holddown, 0 hidden)
192.168.10.0/24 (1 entry, 1 announced)
TSI:
KRT in-kernel 192.168.10.0/24 -&gt; {indirect(1048574)}
        *BGP    Preference: 170/-101
                Next hop type: Indirect, Next hop index: 0
                Address: 0xd0078d0
                Next-hop reference count: 2
                Source: 7.7.7.7
                Next hop type: Router, Next hop index: 583
                Next hop: 169.254.10.1 via ge-0/0/1.0, selected
                Session Id: 0x140
                Protocol next hop: 7.7.7.7
                Indirect next hop: 0xb68cec0 1048574 INH Session ID: 0x142
                State: &lt;Active Ext&gt;
                Local AS: 65001 Peer AS: 65002
                Age: 9 	Metric2: 6 
                Validation State: unverified 
                Task: BGP_65002.7.7.7.7+179
                Announcement bits (2): 0-KRT 6-Resolve tree 2 
                AS path: 65002 I 
                Accepted
                Localpref: 100
                Router ID: 7.7.7.7
                Indirect next hops: 1
                        Protocol next hop: 7.7.7.7 Metric: 6
                        Indirect next hop: 0xb68cec0 1048574 INH Session ID: 0x142
                        Indirect path forwarding next hops: 1
                                Next hop type: Router
                                Next hop: 169.254.10.1 via ge-0/0/1.0
                                Session Id: 0x140
			7.7.7.7/32 Originating RIB: inet.0
			  Metric: 6			  Node path count: 1
			  Forwarding nexthops: 1
				Nexthop: 169.254.10.1 via ge-0/0/1.0
				Session Id: 140

admin@vmx1.lab&gt; 
</pre></div>


<p>If we look at the route we&#8217;re getting for vMX7 &#8211; we&#8217;ll see that it&#8217;s being advertised as having a protocol next hop of <code>7.7.7.7</code>.  This is to be expected since that&#8217;s the router-ID of vMX7.  So let&#8217;s look and see what the router wants to do with this route when we try to talk to <code>right_client</code>&#8230;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [4,5,6]; title: ; notranslate">
admin@vmx1.lab&gt; show route forwarding-table destination 192.168.10.100 
Routing table: default.inet
Internet:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
192.168.10.0/24    user     0                    indr  1048574     2
                              169.254.10.1       ucst      583    19 ge-0/0/1.0

Routing table: __pfe_private__.inet
Internet:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
default            perm     0                    dscd      514     2

Routing table: __juniper_services__.inet
Internet:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
default            perm     0                    dscd      527     2

Routing table: __master.anon__.inet
Internet:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
default            perm     0                    rjct      542     1

admin@vmx1.lab&gt; 
</pre></div>


<p>Notice the above highlighted lines.  vMX1 thinks it should forward the traffic over to <code>169.254.10.1</code> which is the <code>ge-0/0/0</code> interface of vMX2.  This doesn&#8217;t seem like a problem since that&#8217;s the direction we need to go.  So now let&#8217;s go checkout out what vMX2 will do with that traffic&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [4,5]; title: ; notranslate">
admin@vmx2.lab&gt; show route forwarding-table destination 192.168.10.100 
Routing table: default.inet
Internet:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
default            perm     0                    rjct       36     1

Routing table: __juniper_services__.inet
Internet:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
default            perm     0                    dscd      514     2

Routing table: __pfe_private__.inet
Internet:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
default            perm     0                    dscd      527     2

Routing table: __master.anon__.inet
Internet:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
default            perm     0                    rjct      542     1

admin@vmx2.lab&gt; 
</pre></div>


<p>Huh.  So vMX2 wants nothing to do with that traffic is is going to drop it.  So why is that?  Well the reason is that vMX2 doesn&#8217;t know about the route to <code>192.168.10.0/24</code> since that&#8217;s only being shared between the 2 eBGP peers vMX1 and vMX7&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx2.lab&gt; show route table inet.0 192.168.10.0/24 

admin@vmx2.lab&gt; 
</pre></div>


<p>This is one of the reason we wanted to run MPLS &#8211; so that every router doesn&#8217;t need to know about all the BGP prefixes.  So this is actually the behavior we expect to see.  In order for this to work, traffic toward <code>192.168.10.100</code> needs to enter an MPLS LSP on vMX1 and for some reason that isn&#8217;t happening.  </p>



<p>The reason is simple &#8211; MPLS LSPs need to be end to end.  As this design stands, we have 3 distinct label domains.  The left site is only aware of the routers in it&#8217;s LDP domain, the core is only aware of routers speaking RSVP, and the right site is only aware of routers in it&#8217;s LDP domain.  We know that for LDP and RSVP to get into an LSP we need to have an entry in the routers <code>inet.3</code> table.  Let&#8217;s see what vMX1 knows about&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx1.lab&gt; show route table inet.3 

inet.3: 2 destinations, 2 routes (2 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

2.2.2.2/32         *&#x5B;LDP/9] 00:53:22, metric 1
                    &gt; to 169.254.10.1 via ge-0/0/1.0
3.3.3.3/32         *&#x5B;LDP/9] 00:53:20, metric 1
                    &gt; to 169.254.10.1 via ge-0/0/1.0, Push 299792

admin@vmx1.lab&gt; 
</pre></div>


<p>As you can see vMX1 is only aware of vMX2 and vMX3.  If we look at vMX3 we&#8217;ll see that it&#8217;s aware of it&#8217;s LDP peers (vMX1 and vMX2) as well as it&#8217;s RSVP peer (vMX5) in the <code>inet.3</code> table.  In other words &#8211; these are the only routers that vMX3 knows how to get to through an LSP&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx3.lab&gt; show route table inet.3 

inet.3: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

1.1.1.1/32         *&#x5B;LDP/9] 00:11:27, metric 1
                    &gt; to 169.254.10.2 via ge-0/0/0.0, Push 299776
2.2.2.2/32         *&#x5B;LDP/9] 00:11:27, metric 1
                    &gt; to 169.254.10.2 via ge-0/0/0.0
5.5.5.5/32         *&#x5B;RSVP/7/1] 00:53:08, metric 2
                    &gt; to 169.254.10.5 via ge-0/0/1.0, label-switched-path to-vmx5

admin@vmx3.lab&gt; 
</pre></div>


<p>So the problem is that vMX1 doesn&#8217;t know how to get traffic to <code>192.168.10.100</code> into an LSP since it&#8217;s not aware of an LSP towards <code>7.7.7.7</code>.  If we were to quickly enable LDP on the interfaces where we&#8217;re running RSVP currently we&#8217;d see that our ping would start working.  In fact, let&#8217;s do that so you can see&#8230;</p>



<h2 class="wp-block-heading">vMX3</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set protocols ldp interface ge-0/0/1 
</pre></div>


<h2 class="wp-block-heading">vMX4</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set protocols ldp interface ge-0/0/0
set protocols ldp interface ge-0/0/1 
</pre></div>


<h2 class="wp-block-heading">vMX5</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set protocols ldp interface ge-0/0/0
</pre></div>


<p>Now our ping should start working&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@left_client:~# ping 192.168.10.100 -c 5
64 bytes from 192.168.10.100: icmp_seq=1 ttl=57 time=3.86 ms
64 bytes from 192.168.10.100: icmp_seq=2 ttl=57 time=4.63 ms
64 bytes from 192.168.10.100: icmp_seq=3 ttl=57 time=4.12 ms
64 bytes from 192.168.10.100: icmp_seq=4 ttl=57 time=6.33 ms
64 bytes from 192.168.10.100: icmp_seq=5 ttl=57 time=4.68 ms
root@left_client:~# 
</pre></div>


<p>And if we look at the <code>inet.3</code> table on vMX1 again&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx1.lab&gt; show route table inet.3    

inet.3: 6 destinations, 6 routes (6 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

2.2.2.2/32         *&#x5B;LDP/9] 00:57:17, metric 1
                    &gt; to 169.254.10.1 via ge-0/0/1.0
3.3.3.3/32         *&#x5B;LDP/9] 00:57:15, metric 1
                    &gt; to 169.254.10.1 via ge-0/0/1.0, Push 299792
4.4.4.4/32         *&#x5B;LDP/9] 00:01:19, metric 1
                    &gt; to 169.254.10.1 via ge-0/0/1.0, Push 299872
5.5.5.5/32         *&#x5B;LDP/9] 00:00:50, metric 1
                    &gt; to 169.254.10.1 via ge-0/0/1.0, Push 299888
6.6.6.6/32         *&#x5B;LDP/9] 00:00:50, metric 1
                    &gt; to 169.254.10.1 via ge-0/0/1.0, Push 299904
7.7.7.7/32         *&#x5B;LDP/9] 00:00:50, metric 1
                    &gt; to 169.254.10.1 via ge-0/0/1.0, Push 299920

admin@vmx1.lab&gt; 
</pre></div>


<p>Also notice that our forwarding table now shows a push action&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [4,5,6]; title: ; notranslate">
admin@vmx1.lab&gt; show route forwarding-table destination 192.168.10.100    
Routing table: default.inet
Internet:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
192.168.10.0/24    user     0                    indr  1048574     2
                              169.254.10.1      Push 299920      593     2 ge-0/0/1.0

Routing table: __pfe_private__.inet
Internet:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
default            perm     0                    dscd      514     2

Routing table: __juniper_services__.inet
Internet:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
default            perm     0                    dscd      527     2

Routing table: __master.anon__.inet
Internet:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
default            perm     0                    rjct      542     1

admin@vmx1.lab&gt; 
</pre></div>


<p>So let&#8217;s rollback that config on vMX3, vMX4, and vMX5 (<code>rollback 1</code>) and talk about how to solve this.  It&#8217;s obvious to me at this point that we have something that looks like this&#8230;.</p>



<div class="wp-block-image"><figure class="aligncenter"><img loading="lazy" decoding="async" width="823" height="298" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-5.png" alt="" class="wp-image-23211" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-5.png 823w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-5-200x72.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-5-300x109.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-5-768x278.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-5-624x226.png 624w" sizes="(max-width: 823px) 100vw, 823px" /></figure></div>



<p>Within each label domain, we have a path from edge to edge.  The problem is &#8211; we don&#8217;t have an end to end label path from vMX1 to vMX7.  Without that &#8211; we&#8217;d be asking each border router (in this case vMX3 and vMX5) to have the traffic leave one LSP and re-enter another.  That is <code>client_left</code> traffic would need to enter an LSP on vMX1 and head as far as it can to vMX3 which is the LDP LSP endpoint.  At vMX3 some type of lookup would need to take place that would tell the traffic to enter into the RSVP LSP to vMX5 with a similar lookup having to occur at vMX5 to reach vMX7.  I like to think of MPLS forwarding paths as having to be completely pre-determined.  Having to leave an LSP, do a lookup for the next hop, and then enter another LSP, is not pre-determined.  This is one of the bigger misconceptions I find when talking with folks about MPLS.  There is a general assumption that because there exists a path of LSPs that this is the same as having an end to end LSP.</p>



<p>So how do we fix this?  Well &#8211; there are a couple of solutions here.  The one I want to focus on in this post requires the use of BGP-LU or BGP Labelled Unicast.  If you&#8217;re unfamiliar with the labelled unicast address family have a quick read <a href="http://www.dasblinkenlichten.com/understanding-bgp-labeled-unicast/">here</a>.  If you don&#8217;t want to do that &#8211; I can boil it down to saying that it causes BGP to allocate a label for each prefix it advertises.  So why is this helpful?  Well to create an end to end LSP we need some mechanism on our border routers (vMX3 and vMX5) to keep the LSP alive.  That is &#8211; we need to have a label operation it can use to stitch together our discrete LSPs.  Let&#8217;s configure this and I&#8217;ll talk more about it.  </p>



<p>To configure BGP labelled unicast, we&#8217;re going to redo our BGP peering to look more like this&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter"><img loading="lazy" decoding="async" width="862" height="298" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-6.png" alt="" class="wp-image-23214" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-6.png 862w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-6-200x69.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-6-300x104.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-6-768x266.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/09/image-6-624x216.png 624w" sizes="(max-width: 862px) 100vw, 862px" /></figure></div>



<p>You might be asking if we need all these peering sessions for this to work and the short answer is yes &#8211; we&#8217;ll see why shortly.  So let&#8217;s drop these configurations on each router&#8230;</p>



<h2 class="wp-block-heading">vMX1</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
delete protocols bgp
set protocols bgp group internal peer-as 65001
set protocols bgp group internal neighbor 3.3.3.3 family inet labeled-unicast
set protocols bgp group internal local-address 1.1.1.1
set protocols bgp group internal type internal
set protocols bgp group internal export bgp-export 
set policy-options policy-statement bgp-export term direct from protocol direct 
set policy-options policy-statement bgp-export term direct from prefix-list bgp-export           
set policy-options policy-statement bgp-export term direct then accept                                                      
set policy-options prefix-list bgp-export 10.10.10.0/24 
</pre></div>


<h2 class="wp-block-heading">vMX3</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-options autonomous-system 65001
set protocols bgp group internal peer-as 65001
set protocols bgp group internal neighbor 1.1.1.1 family inet labeled-unicast
set protocols bgp group internal local-address 3.3.3.3
set protocols bgp group internal type internal
set protocols bgp group external type external
set protocols bgp group external multihop ttl 255
set protocols bgp group external local-address 3.3.3.3
set protocols bgp group external peer-as 65002
set protocols bgp group external neighbor 5.5.5.5 family inet labeled-unicast
</pre></div>


<h2 class="wp-block-heading">vMX5</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-options autonomous-system 65002
set protocols bgp group internal peer-as 65002
set protocols bgp group internal neighbor 7.7.7.7 family inet labeled-unicast
set protocols bgp group internal local-address 5.5.5.5
set protocols bgp group internal type internal
set protocols bgp group external type external
set protocols bgp group external multihop ttl 255
set protocols bgp group external local-address 5.5.5.5
set protocols bgp group external peer-as 65001
set protocols bgp group external neighbor 3.3.3.3 family inet labeled-unicast
</pre></div>


<h2 class="wp-block-heading">vMX7</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
delete protocols bgp
set protocols bgp group internal peer-as 65002
set protocols bgp group internal neighbor 5.5.5.5 family inet labeled-unicast
set protocols bgp group internal local-address 7.7.7.7
set protocols bgp group internal type internal
set protocols bgp group internal export bgp-export 
set policy-options prefix-list customer1 192.168.10.0/24 
set policy-options policy-statement bgp-export term direct from protocol direct 
set policy-options policy-statement bgp-export term direct from prefix-list bgp-export           
set policy-options policy-statement bgp-export term direct then accept                                                           
set policy-options prefix-list bgp-export 192.168.10.0/24
</pre></div>


<p>Once this configuration is in place, you should be able to validate that all of the BGP peerings are up on each of the 4 routers.  If they aren&#8217;t &#8211; double check your configuration.  Once up &#8211; you should notice that your ping from <code>client_left</code> to <code>client_right</code> is now once again working!</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
left_client:~# ping 192.168.10.100 -c 5
64 bytes from 192.168.10.100: icmp_seq=1 ttl=57 time=3.86 ms
64 bytes from 192.168.10.100: icmp_seq=2 ttl=57 time=4.63 ms
64 bytes from 192.168.10.100: icmp_seq=3 ttl=57 time=4.12 ms
64 bytes from 192.168.10.100: icmp_seq=4 ttl=57 time=6.33 ms
64 bytes from 192.168.10.100: icmp_seq=5 ttl=57 time=4.68 ms
left_client:~# 
</pre></div>


<p>So what magic is happening to allow this to work?  They key to understanding what&#8217;s happening is what I mentioned in my post on BGP-LU&#8230;</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>Anytime a router changes the next-hop of a prefix it’s advertising – it must allocate a new label.</p></blockquote>



<p>And when does BGP router change the next hop of the route it&#8217;s advertising?  Well &#8211; by default that happens over a eBGP peering.  But in the case of BGP-LU it happens more frequently&#8230;</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>When the&nbsp;labeled-unicast&nbsp;statement is used, the local router automatically performs a next hop to self on all routes advertised into EBGP from IBGP and into IBGP from EBGP</p></blockquote>



<p>So in our case &#8211; each of the BGP enabled routers will allocate a new label.  Let&#8217;s dig in and make sure that&#8217;s what&#8217;s is happening.  Let&#8217;s start at vMX1 and look to see what it thinks it should be doing to reach <code>192.168.10.100</code>&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx1.lab&gt; show route table inet.0 192.168.10.100 

inet.0: 22 destinations, 22 routes (22 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

192.168.10.0/24    *&#x5B;BGP/170] 00:09:48, localpref 100, from 3.3.3.3
                      AS path: 65002 I, validation-state: unverified
                    &gt; to 169.254.10.1 via ge-0/0/1.0, Push 301296, Push 301152(top)

admin@vmx1.lab&gt; 
</pre></div>


<p>Interesting.  So not only do we now have an MPLS operation, we have two of them!  So where are these labels coming from?  Let&#8217;s look at little deeper to see what we can find&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7]; title: ; notranslate">
admin@vmx1.lab&gt; show ldp database 
Input label database, 1.1.1.1:0--2.2.2.2:0
Labels received: 3
  Label     Prefix
 301136      1.1.1.1/32
      3      2.2.2.2/32
 301152      3.3.3.3/32

Output label database, 1.1.1.1:0--2.2.2.2:0
Labels advertised: 3
  Label     Prefix
      3      1.1.1.1/32
 299856      2.2.2.2/32
 299872      3.3.3.3/32

admin@vmx1.lab&gt; 
</pre></div>


<p>Ok &#8211; so our top label of <code>301152</code> is something we learned through LDP.  vMX2 is telling us that if we want to get to <code>3.3.3.3</code> we should send it a label of <code>299792</code>.  Ok &#8211; so now where does label <code>301296</code> come from?..</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [6]; title: ; notranslate">
admin@vmx1.lab&gt; show route receive-protocol bgp 3.3.3.3 table inet.0 extensive 

inet.0: 22 destinations, 22 routes (22 active, 0 holddown, 0 hidden)
* 192.168.10.0/24 (1 entry, 1 announced)
     Accepted
     Route Label: 301296
     Nexthop: 3.3.3.3
     Localpref: 100
     AS path: 65002 I 
     Entropy label capable, next hop field matches route next hop

admin@vmx1.lab&gt; 
</pre></div>


<p>It&#8217;s coming from BGP!  So vMX1 thinks to get to <code>192.168.10.100</code> it should push a label of <code>301296</code> followed by <code>301152</code>.  If you&#8217;re confused as to what&#8217;s going on just stay with us for the moment.  Let&#8217;s track this down and figure out what&#8217;s going on.  Let&#8217;s see what vMX2 does with label <code>299792</code>&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx2.lab&gt; show route table mpls.0 label 301152 

mpls.0: 10 destinations, 10 routes (10 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

301152             *&#x5B;LDP/9] 23:10:09, metric 1
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Pop      
301152(S=0)        *&#x5B;LDP/9] 23:10:09, metric 1
                    &gt; to 169.254.10.3 via ge-0/0/1.0, Pop      

admin@vmx2.lab&gt; 
</pre></div>


<p>Alright &#8211; so it&#8217;s going to pop the label and send it out of <code>ge-0/0/1</code> toward vMX3.  This makes sense since we know that vMX1 was trying to send traffic to the end of the local LSP which is vMX3.  So that means vMX3 will get a frame with a single MPLS label of <code>301296</code>.  What will it do with it?..</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx3.lab&gt; show route table mpls.0 label 301296 

mpls.0: 11 destinations, 11 routes (11 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

301296             *&#x5B;VPN/170] 00:12:01, metric2 2, from 5.5.5.5
                    &gt; to 169.254.10.5 via ge-0/0/1.0, label-switched-path to-vmx5

admin@vmx3.lab&gt; 
</pre></div>


<p>It&#8217;s going to push it right into the RSVP LSP toward vMX5.  Brilliant!  So it appears that the use of BGP LU is successfully stitching our LSPs together.  But let&#8217;s back up a second, how is it actually doing this?  Let&#8217;s look at vMX1 again&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx1.lab&gt; show route table inet.3 

inet.3: 2 destinations, 2 routes (2 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

2.2.2.2/32         *&#x5B;LDP/9] 23:11:06, metric 1
                    &gt; to 169.254.10.1 via ge-0/0/1.0
3.3.3.3/32         *&#x5B;LDP/9] 23:10:59, metric 1
                    &gt; to 169.254.10.1 via ge-0/0/1.0, Push 301152

admin@vmx1.lab&gt; 
</pre></div>


<p>Notice that we still don&#8217;t know about the <code>7.7.7.7</code> endpoint so there&#8217;s no way we can create an LSP directly to it.  Let&#8217;s look at the route we&#8217;re getting through BGP in more detail&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [11,23]; title: ; notranslate">
admin@vmx1.lab&gt; show route table inet.0 192.168.10.0/24 extensive 

inet.0: 22 destinations, 22 routes (22 active, 0 holddown, 0 hidden)
192.168.10.0/24 (1 entry, 1 announced)
TSI:
KRT in-kernel 192.168.10.0/24 -&gt; {indirect(1048574)}
        *BGP    Preference: 170/-101
                Next hop type: Indirect, Next hop index: 0
                Address: 0xd007f30
                Next-hop reference count: 2
                Source: 3.3.3.3
                Next hop type: Router, Next hop index: 589
                Next hop: 169.254.10.1 via ge-0/0/1.0, selected
                Label operation: Push 301296, Push 301152(top)
                Label TTL action: prop-ttl, prop-ttl(top)
                Load balance label: Label 301296: None; Label 301152: None; 
                Label element ptr: 0xd007800
                Label parent element ptr: 0xd006de0
                Label element references: 1
                Label element child references: 0
                Label element lsp id: 0
                Session Id: 0x165
                Protocol next hop: 3.3.3.3
                Label operation: Push 301296
                Label TTL action: prop-ttl
                Load balance label: Label 301296: None; 
                Indirect next hop: 0xb68d630 1048574 INH Session ID: 0x167
                State: &lt;Active Int Ext&gt;
                Local AS: 65001 Peer AS: 65001
                Age: 12:40 	Metric2: 1 
                Validation State: unverified 
                Task: BGP_65001.3.3.3.3+50896
                Announcement bits (2): 0-KRT 6-Resolve tree 2 
                AS path: 65002 I 
                Accepted
                Route Label: 301296
                Localpref: 100
                Router ID: 3.3.3.3
                Indirect next hops: 1
                        Protocol next hop: 3.3.3.3 Metric: 1
                        Label operation: Push 301296
                        Label TTL action: prop-ttl
                        Load balance label: Label 301296: None; 
                        Indirect next hop: 0xb68d630 1048574 INH Session ID: 0x167
                        Indirect path forwarding next hops: 1
                                Next hop type: Router
                                Next hop: 169.254.10.1 via ge-0/0/1.0
                                Session Id: 0x165
			3.3.3.3/32 Originating RIB: inet.3
			  Metric: 1			  Node path count: 1
			  Forwarding nexthops: 1
				Nexthop: 169.254.10.1 via ge-0/0/1.0
				Session Id: 0
                                        
admin@vmx1.lab&gt; 
</pre></div>


<p>Ah ha.  So vMX thinks that route is coming from vMX3.  And fortunately for us, vMX1 does know how to get to vMX3 &#8211; through the LDP LSP.  And since we&#8217;re getting a route label through the BGP LU address family, vMX1 also knows that if it wants to reach that destination, it should first push the route label onto the frame before putting it in the LDP LSP.  Let&#8217;s finish walking through what&#8217;s happening from vMX3 to vMX7 and then put it all together in a neat little diagram.  </p>



<p>So we saw that vMX3 was going to stitch our LDP LSP into our RSVP LSP above.  Let&#8217;s look at that again with a little more output&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7,12,26]; title: ; notranslate">
admin@vmx3.lab&gt; show route table mpls.0 label 301296                

mpls.0: 11 destinations, 11 routes (11 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

301296             *&#x5B;VPN/170] 00:22:59, metric2 2, from 5.5.5.5
                    &gt; to 169.254.10.5 via ge-0/0/1.0, label-switched-path to-vmx5

admin@vmx3.lab&gt; show rsvp session lsp name to-vmx5                  
Ingress RSVP: 1 sessions
To              From            State   Rt Style Labelin Labelout LSPname 
5.5.5.5         3.3.3.3         Up       0  1 FF       -   301120 to-vmx5
Total 1 displayed, Up 1, Down 0

Egress RSVP: 1 sessions
Total 0 displayed, Up 0, Down 0

Transit RSVP: 0 sessions
Total 0 displayed, Up 0, Down 0

admin@vmx3.lab&gt; show route forwarding-table label 301296            
Routing table: default.mpls
MPLS:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
301296             user     0                    indr  1048577     2
                              169.254.10.5      Swap 301248, Push 301120(top)      590     2 ge-0/0/1.0

Routing table: __mpls-oam__.mpls
MPLS:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
default            perm     0                    dscd      553     1

admin@vmx3.lab&gt; 
</pre></div>


<p>If we look at like 7 above &#8211; you see that when vMX3 receives label <code>301296</code> from vMX2, it&#8217;s going to push it into the RSVP LSP that we created to vMX5.  If we look at that RSVP LSP we&#8217;ll see that on line 12 it will push label <code>301120</code> to get into that LSP.  But when we look at what&#8217;s going on in the actual forwarding plane, we&#8217;ll see on line 26 that it want&#8217;s to swap the current top label to <code>301248</code> and then push <code>301120</code> to the top of the label stack making it the new top label.  So we know where <code>301120</code> came from, but where did the <code>301248</code> come from?  As you might have guessed &#8211; it comes from BGP&#8230;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [6]; title: ; notranslate">
admin@vmx3.lab&gt; show route receive-protocol bgp 5.5.5.5 extensive 

inet.0: 22 destinations, 22 routes (22 active, 0 holddown, 0 hidden)
* 192.168.10.0/24 (1 entry, 1 announced)
     Accepted
     Route Label: 301248
     Nexthop: 5.5.5.5
     AS path: 65002 I 
     Entropy label capable, next hop field matches route next hop

inet.3: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden)

mpls.0: 11 destinations, 11 routes (11 active, 0 holddown, 0 hidden)

inet6.0: 1 destinations, 1 routes (1 active, 0 holddown, 0 hidden)

admin@vmx3.lab&gt; 
</pre></div>


<p>It comes from vMX5!  Alright &#8211; so now let&#8217;s look at what vMX4 does with that top label&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx4.lab&gt; show route table mpls.0 label 301120 

mpls.0: 10 destinations, 10 routes (10 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

301120             *&#x5B;RSVP/7/1] 23:32:14, metric 1
                    &gt; to 169.254.10.7 via ge-0/0/1.0, label-switched-path to-vmx5
301120(S=0)        *&#x5B;RSVP/7/1] 23:32:14, metric 1
                    &gt; to 169.254.10.7 via ge-0/0/1.0, label-switched-path to-vmx5

admin@vmx4.lab&gt; show route forwarding-table label 301120 
Routing table: default.mpls
MPLS:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
301120             user     0 169.254.10.7      Pop        583     2 ge-0/0/1.0
301120(S=0)        user     0 169.254.10.7      Pop        584     2 ge-0/0/1.0

Routing table: __mpls-oam__.mpls
MPLS:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
default            perm     0                    dscd      540     1

admin@vmx4.lab&gt; 
</pre></div>


<p>So we&#8217;re still in the RSVP LSP &#8211; but since vMX4 is the PHP router we&#8217;ll be popping off the label which means that vMX5 is going to receive a frame with the label <code>301248</code>.  vMX5 will then&#8230;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx5.lab&gt; show route table mpls.0 label 301248 

mpls.0: 11 destinations, 11 routes (11 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

301248             *&#x5B;VPN/170] 00:35:35, metric2 1, from 7.7.7.7
                    &gt; to 169.254.10.9 via ge-0/0/1.0, Swap 301216

admin@vmx5.lab&gt; 
</pre></div>


<p>Notice how vMX5 is only now performing a swap operation to label <code>301216</code>.  AKA &#8211; it will be sending vMX6 a labelled frame with a single label.  Since vMX5 will be pushing us into our tail LSP there&#8217;s no need for a second label for further stitching.  Let&#8217;s finish this up by seeing what vMX6 will do&#8230;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx6.lab&gt; show route table mpls.0 label 301216 

mpls.0: 10 destinations, 10 routes (10 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

301216             *&#x5B;LDP/9] 23:35:20, metric 1
                    &gt; to 169.254.10.11 via ge-0/0/1.0, Pop      
301216(S=0)        *&#x5B;LDP/9] 23:35:20, metric 1
                    &gt; to 169.254.10.11 via ge-0/0/1.0, Pop      

admin@vmx6.lab&gt; 
</pre></div>


<p>vMX6 is the PHP router so it will pop the label and deliver the the naked frame to vMX7 which has the destination network <code>192.168.10.0/24</code> directly attached.  Looking at this from a visual perspective, it would look like this&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter"><a href="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-6.png" target="_blank" rel="noreferrer noopener"><img loading="lazy" decoding="async" width="782" height="297" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-6.png" alt="" class="wp-image-23253" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-6.png 782w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-6-200x76.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-6-300x114.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-6-768x292.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-6-624x237.png 624w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-6-780x297.png 780w" sizes="(max-width: 782px) 100vw, 782px" /></a></figure></div>



<p>Now &#8211; keep in mind that what Im depicting above is the actual datapath.  The arrows above indicate where the label being sent is acted upon or used.  For instance, the LDP labels are used at each hop within the LDP domain whereas the BGP-LU is used and advertised across a label domain and is only used in the stitching from label domain to label domain.  </p>



<p>Now &#8211; let&#8217;s finish up the post by making this a hair more complicated and throwing VPNv4 into the mix.  So as we did in our <a href="http://www.dasblinkenlichten.com/bgp-lu-and-mpls-vpns/">last post</a>, let&#8217;s convert the interfaces facing our clients to VRF type routing-instances&#8230;</p>



<h2 class="wp-block-heading">vMX1</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-instances customer1 instance-type vrf
set routing-instances customer1 interface ge-0/0/0.0
set routing-instances customer1 route-distinguisher 1:1
set routing-instances customer1 vrf-target target:1:1
set routing-instances customer1 vrf-table-label 
</pre></div>


<h2 class="wp-block-heading">vMX7</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set routing-instances customer1 instance-type vrf
set routing-instances customer1 interface ge-0/0/1.0
set routing-instances customer1 route-distinguisher 1:1
set routing-instances customer1 vrf-target target:1:1
set routing-instances customer1 vrf-table-label 
</pre></div>


<p>Now we need to setup a VPNv4 peering to allow the advertisements for each VRF to be exchanged.  There are a couple of ways we could do this, but for the sake of reusing what we already have let&#8217;s just add the <code>inet-vpn</code> address family to the existing BGP sessions&#8230;</p>



<h2 class="wp-block-heading">vMX1</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set protocols bgp group internal neighbor 3.3.3.3 family inet-vpn unicast 
</pre></div>


<h2 class="wp-block-heading">vMX3</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set protocols bgp group internal neighbor 1.1.1.1 family inet-vpn unicast    
set protocols bgp group external neighbor 5.5.5.5 family inet-vpn unicast  
</pre></div>


<h2 class="wp-block-heading">vMX5</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set protocols bgp group internal neighbor 7.7.7.7 family inet-vpn unicast                                       
set protocols bgp group external neighbor 3.3.3.3 family inet-vpn unicast 
</pre></div>


<h2 class="wp-block-heading">vMX7</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set protocols bgp group internal neighbor 5.5.5.5 family inet-vpn unicast 
</pre></div>


<p>Alright &#8211; so once that&#8217;s in &#8211; let&#8217;s make sure that the peering is up and that we&#8217;re exchanging VPNv4 routes&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx7.lab&gt; show bgp summary 
Groups: 1 Peers: 1 Down peers: 0
Table          Tot Paths  Act Paths Suppressed    History Damp State    Pending
inet.0               
                       0          0          0          0          0          0
bgp.l3vpn.0          
                       1          1          0          0          0          0
Peer                     AS      InPkt     OutPkt    OutQ   Flaps Last Up/Dwn State|#Active/Received/Accepted/Damped...
5.5.5.5               65002          5          5       0       0          55 Establ
  inet.0: 0/0/0/0
  bgp.l3vpn.0: 1/1/1/0
  customer1.inet.0: 1/1/1/0

admin@vmx7.lab&gt; show route table customer1.inet.0       

customer1.inet.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

10.10.10.0/24      *&#x5B;BGP/170] 00:00:45, localpref 100, from 5.5.5.5
                      AS path: 65001 I, validation-state: unverified
                    &gt; to 169.254.10.10 via ge-0/0/0.0, Push 302336, Push 301376(top)
192.168.10.0/24    *&#x5B;Direct/0] 00:03:55
                    &gt; via ge-0/0/1.0
192.168.10.1/32    *&#x5B;Local/0] 00:03:55
                      Local via ge-0/0/1.0

admin@vmx7.lab&gt; 
</pre></div>


<p>Nice!  The route for the <code>left_client</code> has arrived and it appears to have been imported correctly.  If we do a quick ping test, we should see that the clients have connectivity as well&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
root@left_client:~# ping 192.168.10.100 -c 5
PING 192.168.10.100 (192.168.10.100) 56(84) bytes of data.
64 bytes from 192.168.10.100: icmp_seq=1 ttl=57 time=4.55 ms
64 bytes from 192.168.10.100: icmp_seq=2 ttl=57 time=5.11 ms
64 bytes from 192.168.10.100: icmp_seq=3 ttl=57 time=4.88 ms
64 bytes from 192.168.10.100: icmp_seq=4 ttl=57 time=5.55 ms
64 bytes from 192.168.10.100: icmp_seq=5 ttl=57 time=3.95 ms
 
--- 192.168.10.100 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4004ms
rtt min/avg/max/mdev = 3.955/4.812/5.553/0.539 ms
root@left_client:~# 
</pre></div>


<p>But hold on a minute.  That seemed too easy &#8211; and if we look carefully at the route output &#8211; we should notice something a little off.  Recall that before we layered in the VPNv4 configuration, the tail routers had to push 2 labels to get traffic from end to end.  One label was a local LDP label and the other was the BGP-LU label that was used to stitch the traffic into the next label domain.  From the looks of the output above, we&#8217;re still only pushing 2 labels.  So where&#8217;s our VPN label?  </p>



<p>If you picked up on that kudos.  And if you know why we&#8217;re still only sending two labels &#8211; I&#8217;ll give you even more kudos.  But before I talk through that, let&#8217;s try an experiment&#8230;</p>



<h2 class="wp-block-heading">vMX1</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
delete protocols bgp group internal neighbor 3.3.3.3 family inet labeled-unicast 
</pre></div>


<h2 class="wp-block-heading">vMX3</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
delete protocols bgp group internal neighbor 1.1.1.1 family inet labeled-unicast 
delete protocols bgp group external neighbor 5.5.5.5 family inet labeled-unicast 
</pre></div>


<h2 class="wp-block-heading">vMX5</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
delete protocols bgp group internal neighbor 7.7.7.7 family inet labeled-unicast 
delete protocols bgp group external neighbor 3.3.3.3 family inet labeled-unicast 
</pre></div>


<h2 class="wp-block-heading">vMX7</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
delete protocols bgp group internal neighbor 5.5.5.5 family inet labeled-unicast 
</pre></div>


<p>You may be surprised to see that after the BGP sessions re-peer that our traffic is once again working.  If you&#8217;re entirely confused as to what&#8217;s going on &#8211; that&#8217;s totally fair at this point.  What&#8217;s happened here is that we&#8217;ve stumbled upon yet another MPLS VPN architecture that falls into the &#8220;MPLS Inter-AS VPN&#8221; category of architectures.  You can read more about that <a href="https://tools.ietf.org/html/rfc4364#section-10">here</a> but they are typically referred to as either option A, B, or C.  Suffice to say &#8211; we just haven&#8217;t gotten there yet &#8211; we&#8217;re still in the process of building the blocks required for those to make sense.  So at this point, I don&#8217;t want to pursue talking through what&#8217;s going on (which happens to be option B) but I do want to finish making this topology work using our existing LSP stitching.  This will look a lot like a Inter-AS option C setup but for now don&#8217;t worry about what the setup looks like.  We&#8217;re just going to focus on making this work and in the coming posts we&#8217;ll talk through the inter-as options in much greater detail.  </p>



<p>So we know that we want to use LSP stitching (BGP-LU) for our VPNv4 setup so let&#8217;s go ahead and put BGP-LU back in (just do a <code>rollback 1</code> on vMX1, 3, 5, 7).  Once that&#8217;s done let&#8217;s figure out why this isn&#8217;t working the way we thought it would.</p>



<p>Let&#8217;s think back to the MPLS VPN scenarios we&#8217;ve discussed previously.  Do you recall what the requirement was for those to work?  Well &#8211; typically we received a VPNv4 advertisement and the next-hop for that prefix was the remote PE router.  This was important because that meant that the VPN label we received as part of that advertisement was used end to end.  That is &#8211; it was at the bottom of the label stack and delivered as the top label to the PE that advertised it.  Let&#8217;s see what we have now&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx7.lab&gt; show route receive-protocol bgp 5.5.5.5 extensive table bgp.l3vpn.0 

bgp.l3vpn.0: 1 destinations, 1 routes (1 active, 0 holddown, 0 hidden)
* 1:1:10.10.10.0/24 (1 entry, 0 announced)
     Import Accepted
     Route Distinguisher: 1:1
     VPN Label: 302400
     Nexthop: 5.5.5.5
     Localpref: 100
     AS path: 65001 I 
     Communities: target:1:1

admin@vmx7.lab&gt; 
</pre></div>


<p>Well &#8211; I can tell you right now that doesn&#8217;t look right.  The next hop is <code>5.5.5.5</code> instead of <code>1.1.1.1</code>.  Let&#8217;s see if the VPN label lines up with what vMX1 is sending&#8230;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx1.lab&gt; show route advertising-protocol bgp 3.3.3.3 extensive 

customer1.inet.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden)
* 10.10.10.0/24 (1 entry, 1 announced)
 BGP group internal type Internal
     Route Distinguisher: 1:1
     VPN Label: 16
     Nexthop: Self
     Flags: Nexthop Change
     Localpref: 100
     AS path: &#x5B;65001] I 
     Communities: target:1:1

admin@vmx1.lab&gt; 
</pre></div>


<p>Nope.  vMX1 is sending a VPN label of <code>16</code>.  So clearly &#8211; this isn&#8217;t working like we think it should.  So it looks to me like vMX7 believes the route has a next-hop of <code>5.5.5.5</code> which is vMX5.  This is default behavior in this case based on how we have the BGP peering configured.  But we really want that next-hop to show up as vMX1.  To do this &#8211; we can tell the border routers (vMX3 and vMX5) to not change the next hop of the routes they are advertising.  Let&#8217;s try doing that&#8230;</p>



<h2 class="wp-block-heading">vMX3</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set protocols bgp group external neighbor 5.5.5.5 multihop no-nexthop-change 
</pre></div>


<h2 class="wp-block-heading">vMX5</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set protocols bgp group external neighbor 3.3.3.3 multihop no-nexthop-change 
</pre></div>


<p>You&#8217;ll notice that it&#8217;s a peer level change so it&#8217;s safe to assume that this impacts all of the route advertisements from that peer.  If we look now &#8211; we&#8217;ll see that we&#8217;ve lost our remote routes in each customer VRF&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx7.lab&gt; show route table customer1.inet.0  

customer1.inet.0: 2 destinations, 2 routes (2 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

192.168.10.0/24    *&#x5B;Direct/0] 00:27:06
                    &gt; via ge-0/0/1.0
192.168.10.1/32    *&#x5B;Local/0] 00:27:06
                      Local via ge-0/0/1.0

admin@vmx7.lab&gt; 
</pre></div>


<p>So let&#8217;s think about this for a second.  In the first example we did above before we layered on VPNv4 &#8211; what prefix were we advertising from the tail routers?  Since it was non-VPN we were simply advertising the prefix we wished to reach.  But in a VPN scenario, what prefix do we need to reach and furthermore recursively resolve through the <code>inet.3</code> table?  The loopback of the remote PE!  So we need to update our <code>bgp-export</code> policy to remove the client subnet and add the PE loopback address.  Let&#8217;s try that out on vMX1 and see what happens&#8230;</p>



<h2 class="wp-block-heading">vMX1</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
delete policy-options prefix-list bgp-export 10.10.10.0/24 
set policy-options prefix-list bgp-export 1.1.1.1/32 
</pre></div>


<p>So that prefix should be coming in the form of a BGP-LU advertisement into vMX3.  Let&#8217;s check&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx3.lab&gt; show route receive-protocol bgp 1.1.1.1 table inet.0 extensive 

inet.0: 20 destinations, 21 routes (20 active, 0 holddown, 0 hidden)
  1.1.1.1/32 (2 entries, 1 announced)
     Accepted
     Route Label: 3
     Nexthop: 1.1.1.1
     Localpref: 100
     AS path: I 
     Entropy label capable, next hop field matches route next hop

admin@vmx3.lab&gt; 
</pre></div>


<p>Perfect &#8211; now let&#8217;s make sure it&#8217;s being sent to vMX5&#8230;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx3.lab&gt; show route advertising-protocol bgp 5.5.5.5 table inet.0 

admin@vmx3.lab&gt; 
</pre></div>


<p>Hmmmm.  Any guesses?  The problem is that vMX3 already has a better router for <code>1.1.1.1//32</code>&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx3.lab&gt; show route table inet.0 1.1.1.1/32 

inet.0: 20 destinations, 21 routes (20 active, 0 holddown, 0 hidden)
@ = Routing Use Only, # = Forwarding Use Only
+ = Active Route, - = Last Active, * = Both

1.1.1.1/32         *&#x5B;OSPF/10] 00:34:32, metric 2
                    &gt; to 169.254.10.2 via ge-0/0/0.0
                    &#x5B;BGP/170] 00:05:59, localpref 100, from 1.1.1.1
                      AS path: I, validation-state: unverified
                    &gt; to 169.254.10.2 via ge-0/0/0.0, Push 301328

admin@vmx3.lab&gt; 
</pre></div>


<p>It knows about it through OSPF.  So huh.  We can&#8217;t take it out of OSPF otherwise we could peer within our AS.  So what&#8217;s the fix?  Typically, in this type of scenario, the loopback is readvertised into BGP as the AS border router.  That is, we let vMX3 and vMX5 advertise the loopback into BGP since they already have it in OSPF.  So let&#8217;s delete the entry we made on vMX1&#8230;</p>



<h2 class="wp-block-heading">vMX1</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
delete policy-options prefix-list bgp-export 1.1.1.1/32 
</pre></div>


<p>And instead put this policy on vMX3 and vMX5&#8230;</p>



<h2 class="wp-block-heading">vMX3</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set policy-options prefix-list bgp-export 1.1.1.1/32 
set policy-options policy-statement bgp-export term direct from prefix-list bgp-export
set policy-options policy-statement bgp-export term direct then accept
set protocols bgp group external export bgp-export
</pre></div>


<h2 class="wp-block-heading">vMX5</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set policy-options prefix-list bgp-export 7.7.7.7/32 
set policy-options policy-statement bgp-export term direct from prefix-list bgp-export
set policy-options policy-statement bgp-export term direct then accept
set protocols bgp group external export bgp-export
</pre></div>


<p>Once that&#8217;s in &#8211; we should see that vMX3 is sending vMX5 the loopback for vMX1&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx3.lab&gt; show route advertising-protocol bgp 5.5.5.5 table inet.0    

inet.0: 20 destinations, 21 routes (20 active, 0 holddown, 0 hidden)
  Prefix		  Nexthop	       MED     Lclpref    AS path
* 1.1.1.1/32              Self                 2                  I

admin@vmx3.lab&gt; 
</pre></div>


<p>Now let&#8217;s see what vMX5 is doing with it&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx5.lab&gt; show route table inet.0 1.1.1.1/32 

inet.0: 20 destinations, 21 routes (20 active, 0 holddown, 0 hidden)
@ = Routing Use Only, # = Forwarding Use Only
+ = Active Route, - = Last Active, * = Both

1.1.1.1/32         *&#x5B;OSPF/10] 00:39:45, metric 4
                    &gt; to 169.254.10.6 via ge-0/0/0.0
                    &#x5B;BGP/170] 00:02:46, MED 2, localpref 100, from 3.3.3.3
                      AS path: 65001 I, validation-state: unverified
                    &gt; to 169.254.10.6 via ge-0/0/0.0, label-switched-path to-vmx3

admin@vmx5.lab&gt; 
</pre></div>


<p>AHHHH!  We have the same problem.  Our sin of taking a shortcut and using one flat IGP domain is coming back to bite us.  Let&#8217;s fix this by breaking the OSPF domains into 3 areas&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter"><a href="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-14.png" target="_blank" rel="noreferrer noopener"><img loading="lazy" decoding="async" width="861" height="242" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-14.png" alt="" class="wp-image-23269" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-14.png 861w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-14-200x56.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-14-300x84.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-14-768x216.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-14-624x175.png 624w" sizes="(max-width: 861px) 100vw, 861px" /></a></figure></div>



<p>We&#8217;ll make the middle area the core or area 0, the left area area 1, and the right area area 2.  Areas 1 and 2 will be stub areas and we&#8217;ll filter out most of the prefixes and loopbacks from entering area 0 except for the loopbacks of the border routers since that&#8217;s what&#8217;s used for peering etc.  </p>



<h2 class="wp-block-heading">vMX1</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
delete protocols ospf
set protocols ospf area 0.0.0.1 stub
set protocols ospf area 0.0.0.1 interface lo0.0
set protocols ospf area 0.0.0.1 interface ge-0/0/1.0 interface-type p2p
</pre></div>


<h2 class="wp-block-heading">vMX2</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
delete protocols ospf
set protocols ospf area 0.0.0.1 stub
set protocols ospf area 0.0.0.1 interface lo0.0
set protocols ospf area 0.0.0.1 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.1 interface ge-0/0/1.0 interface-type p2p
</pre></div>


<h2 class="wp-block-heading">vMX3</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
delete protocols ospf
set protocols ospf area 0.0.0.1 stub
set protocols ospf traffic-engineering
set protocols ospf area 0.0.0.1 interface lo0.0
set protocols ospf area 0.0.0.1 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
set protocols ospf area 0.0.0.0 network-summary-export ospf-export 
set policy-options prefix-list ospf-export 3.3.3.3/32
set policy-options policy-statement ospf-export term accept from prefix-list ospf-export 
set policy-options policy-statement ospf-export term accept then accept   
set policy-options policy-statement ospf-export then reject  
</pre></div>


<h2 class="wp-block-heading">vMX4</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
delete protocols ospf
set protocols ospf traffic-engineering
set protocols ospf area 0.0.0.0 interface lo0.0
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/1.0 interface-type p2p
</pre></div>


<h2 class="wp-block-heading">vMX5</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
delete protocols ospf
set protocols ospf area 0.0.0.2 stub
set protocols ospf traffic-engineering
set protocols ospf area 0.0.0.2 interface lo0.0
set protocols ospf area 0.0.0.2 interface ge-0/0/1.0 interface-type p2p
set protocols ospf area 0.0.0.0 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.0 network-summary-export ospf-export 
set policy-options prefix-list ospf-export 5.5.5.5/32
set policy-options policy-statement ospf-export term accept from prefix-list ospf-export 
set policy-options policy-statement ospf-export term accept then accept   
set policy-options policy-statement ospf-export then reject 
</pre></div>


<h2 class="wp-block-heading">vMX6</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
delete protocols ospf
set protocols ospf area 0.0.0.2 stub
set protocols ospf area 0.0.0.2 interface lo0.0
set protocols ospf area 0.0.0.2 interface ge-0/0/0.0 interface-type p2p
set protocols ospf area 0.0.0.2 interface ge-0/0/1.0 interface-type p2p
</pre></div>


<h2 class="wp-block-heading">vMX7</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
delete protocols ospf
set protocols ospf area 0.0.0.2 stub
set protocols ospf area 0.0.0.2 interface lo0.0
set protocols ospf area 0.0.0.2 interface ge-0/0/0.0 interface-type p2p
</pre></div>


<p>Right &#8211; now that we have that in place &#8211; vMX5 should no longer be getting a <code>1.1.1.1/32</code> advertisement from OSPF so it should be passing the BGP advertisement onto vMX7&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx5.lab&gt; show route advertising-protocol bgp 7.7.7.7                         

inet.0: 17 destinations, 17 routes (17 active, 0 holddown, 0 hidden)
  Prefix		  Nexthop	       MED     Lclpref    AS path
* 1.1.1.1/32              3.3.3.3              2       100        65001 I

admin@vmx5.lab&gt; 
</pre></div>


<p>Perfect!  Now let&#8217;s see what vMX7 thinks about all of this&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx7.lab&gt; show route 1.1.1.1/32 

inet.0: 16 destinations, 16 routes (16 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

1.1.1.1/32         *&#x5B;BGP/170] 04:04:16, MED 2, localpref 100, from 5.5.5.5
                      AS path: 65001 I, validation-state: unverified
                    &gt; to 169.254.10.10 via ge-0/0/0.0, Push 302656

admin@vmx7.lab&gt; 
</pre></div>


<p>Well &#8211; we have the route now that we need &#8211; but the route is in <code>inet.0</code> and for MPLS VPNs to work we have to recurse through the <code>inet.3</code> table.  So how do we get the prefix there?  We could do a rib-group copy but since this was learned through BGP-LU we can simply use the <code>resolve-vpn</code> flag on the BGP session to move it over as we saw in <a href="http://www.dasblinkenlichten.com/bgp-lu-and-mpls-vpns/">the last post</a>.  Let&#8217;s try that out&#8230;</p>



<h2 class="wp-block-heading">vMX1</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set protocols bgp group internal neighbor 3.3.3.3 family inet labeled-unicast resolve-vpn 
</pre></div>


<h2 class="wp-block-heading">vMX7</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set protocols bgp group internal neighbor 5.5.5.5 family inet labeled-unicast resolve-vpn 
</pre></div>


<p>Now that that&#8217;s there on each tail router, we should see the entry in the <code>inet.3</code> table&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx7.lab&gt; show route table inet.3 

inet.3: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

1.1.1.1/32         *&#x5B;BGP/170] 00:01:00, MED 2, localpref 100, from 5.5.5.5
                      AS path: 65001 I, validation-state: unverified
                    &gt; to 169.254.10.10 via ge-0/0/0.0, Push 302656
5.5.5.5/32         *&#x5B;LDP/9] 07:43:50, metric 1
                    &gt; to 169.254.10.10 via ge-0/0/0.0, Push 301376
6.6.6.6/32         *&#x5B;LDP/9] 07:43:50, metric 1
                    &gt; to 169.254.10.10 via ge-0/0/0.0

admin@vmx7.lab&gt; 
</pre></div>


<p>Awesome.  But if you look, you&#8217;ll see that the tail routers are no longer receiving the remote customer prefix.  And if we look further, we&#8217;ll see that the boundary routers (vMX3 and vMX5) aren&#8217;t even passing it along anymore&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx5.lab&gt; show route advertising-protocol bgp 7.7.7.7    

inet.0: 17 destinations, 17 routes (17 active, 0 holddown, 0 hidden)
  Prefix		  Nexthop	       MED     Lclpref    AS path
* 1.1.1.1/32              3.3.3.3              2       100        65001 I

admin@vmx5.lab&gt; 
</pre></div>


<p>If we look in the <code>bgp.l3vpn.0</code> table we&#8217;ll see that it&#8217;s hidden&#8230;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx5.lab&gt; show route table bgp.l3vpn.0 hidden extensive 

bgp.l3vpn.0: 2 destinations, 2 routes (1 active, 0 holddown, 1 hidden)
1:1:10.10.10.0/24 (1 entry, 0 announced)
         BGP    Preference: 170/-101
                Route Distinguisher: 1:1
                Next hop type: Unusable, Next hop index: 0
                Address: 0xa0ef164
                Next-hop reference count: 1
                State: &lt;Hidden Ext ProtectionPath ProtectionCand&gt;
                Local AS: 65002 Peer AS: 65001
                Age: 4:08:09 
                Validation State: unverified 
                Task: BGP_65001.3.3.3.3+54361
                AS path: 65001 I 
                Communities: target:1:1
                Accepted
                VPN Label: 16
                Localpref: 100
                Router ID: 3.3.3.3
                Indirect next hops: 1
                        Protocol next hop: 1.1.1.1
                        Label operation: Push 16
                        Label TTL action: prop-ttl
                        Load balance label: Label 16: None; 
                        Indirect next hop: 0x0 - INH Session ID: 0x0

admin@vmx5.lab&gt; 
</pre></div>


<p>It&#8217;s saying that the next-hop is unusable.  Now why would that be?  We know about the <code>1.1.1.1/32</code> prefix in the <code>inet.0</code> table&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx5.lab&gt; show route 1.1.1.1     

inet.0: 17 destinations, 17 routes (17 active, 0 holddown, 0 hidden)
@ = Routing Use Only, # = Forwarding Use Only
+ = Active Route, - = Last Active, * = Both

1.1.1.1/32         *&#x5B;BGP/170] 04:08:38, MED 2, localpref 100, from 3.3.3.3
                      AS path: 65001 I, validation-state: unverified
                    &gt; to 169.254.10.6 via ge-0/0/0.0, label-switched-path to-vmx3

admin@vmx5.lab&gt; 
</pre></div>


<p>Our problem is that we need to know about it in the <code>inet.3</code> table for it to be a valid VPNv4 route.  The fix for this is the same that we just did on the tail routers.  We need to tell it to move the prefixes it learns through BGP-LU from <code>inet.0</code> to <code>inet.3</code> when they are listed as next-hops for VPNv4 prefixes.  We&#8217;ll configure that on each boundary router on the external BGP peer&#8230;</p>



<h2 class="wp-block-heading">vMX3</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set protocols bgp group external neighbor 5.5.5.5 family inet labeled-unicast resolve-vpn
</pre></div>


<h2 class="wp-block-heading">vMX5</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set protocols bgp group external neighbor 3.3.3.3 family inet labeled-unicast resolve-vpn 
</pre></div>


<p>After that add on both border routers we should be in business&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [11,12,13,14,15,16]; title: ; notranslate">
admin@vmx5.lab&gt; show route 1.1.1.1 

inet.0: 17 destinations, 17 routes (17 active, 0 holddown, 0 hidden)
@ = Routing Use Only, # = Forwarding Use Only
+ = Active Route, - = Last Active, * = Both

1.1.1.1/32         *&#x5B;BGP/170] 23:55:08, MED 2, localpref 100, from 3.3.3.3
                      AS path: 65001 I, validation-state: unverified
                    &gt; to 169.254.10.6 via ge-0/0/0.0, label-switched-path to-vmx3

inet.3: 4 destinations, 4 routes (4 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

1.1.1.1/32         *&#x5B;BGP/170] 19:44:55, MED 2, localpref 100, from 3.3.3.3
                      AS path: 65001 I, validation-state: unverified
                    &gt; to 169.254.10.6 via ge-0/0/0.0, label-switched-path to-vmx3

admin@vmx5.lab&gt; 
</pre></div>


<p>Alright so let&#8217;s look at our tail routers again and see what&#8217;s up with our customer VRF routing table&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [6,7,8]; title: ; notranslate">
admin@vmx7.lab&gt; show route table customer1.inet.0                            

customer1.inet.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

10.10.10.0/24      *&#x5B;BGP/170] 19:46:07, localpref 100, from 5.5.5.5
                      AS path: 65001 I, validation-state: unverified
                    &gt; to 169.254.10.10 via ge-0/0/0.0, Push 16, Push 302656(top)
192.168.10.0/24    *&#x5B;Direct/0] 1d 03:30:41
                    &gt; via ge-0/0/1.0
192.168.10.1/32    *&#x5B;Local/0] 1d 03:30:41
                      Local via ge-0/0/1.0

admin@vmx7.lab&gt; 
</pre></div>


<p>Alright!  We have the remote route now and if we dig a little deeper, we&#8217;ll see that it&#8217;s showing up with a protocol next-hop of <code>1.1.1.1</code> which is what we wanted&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [41]; title: ; notranslate">
customer1.inet.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden)
10.10.10.0/24 (1 entry, 1 announced)
TSI:
KRT in-kernel 10.10.10.0/24 -&gt; {indirect(1048575)}
        *BGP    Preference: 170/-101
                Route Distinguisher: 1:1
                Next hop type: Indirect, Next hop index: 0
                Address: 0xd007cf0
                Next-hop reference count: 3
                Source: 5.5.5.5
                Next hop type: Router, Next hop index: 581
                Next hop: 169.254.10.10 via ge-0/0/0.0, selected
                Label operation: Push 16, Push 302656(top)
                Label TTL action: prop-ttl, prop-ttl(top)
                Load balance label: Label 16: None; Label 302656: None; 
                Label element ptr: 0xd0072c0
                Label parent element ptr: 0xd006d80
                Label element references: 1
                Label element child references: 0
                Label element lsp id: 0
                Session Id: 0x192
                Protocol next hop: 1.1.1.1
                Label operation: Push 16
                Label TTL action: prop-ttl
                Load balance label: Label 16: None; 
                Indirect next hop: 0xb68bfe0 1048575 INH Session ID: 0x198
                State: &lt;Secondary Active Int Ext ProtectionCand&gt;
                Local AS: 65002 Peer AS: 65002
                Age: 19:46:04 	Metric2: 4 
                Validation State: unverified 
                Task: BGP_65002.5.5.5.5+49544
                Announcement bits (1): 0-KRT 
                AS path: 65001 I 
                Communities: target:1:1
                Import Accepted
                VPN Label: 16
                Localpref: 100
                Router ID: 5.5.5.5
                Primary Routing Table bgp.l3vpn.0
                Indirect next hops: 1
                        Protocol next hop: 1.1.1.1 Metric: 4
                        Label operation: Push 16
                        Label TTL action: prop-ttl
                        Load balance label: Label 16: None; 
                        Indirect next hop: 0xb68bfe0 1048575 INH Session ID: 0x198
                        Indirect path forwarding next hops: 1
                                Next hop type: Router
                                Next hop: 169.254.10.10 via ge-0/0/0.0
                                Session Id: 0x192
			1.1.1.1/32 Originating RIB: inet.3
			  Metric: 4			  Node path count: 1
                                        
admin@vmx7.lab&gt; 
</pre></div>


<p>Cool &#8211; but take a look at that label operation.  We&#8217;re still only pushing two labels at this point which doesn&#8217;t align with our thinking of the LSPs as being transport and the VPN label as being end to end.  So what&#8217;s going on now?  If we step back a bit and think about what we&#8217;re trying to do &#8211; let&#8217;s remember what had to happen in our non-VPN example we did above.  To get from label domain to label domain we had to think that our next hop for the resolved route was within our label domain.  That is &#8211; our ingress router had to believe that the resolved next hop lived within it&#8217;s own AS.  Now we know that our VPN route has had it&#8217;s next-hop preserved as <code>1.1.1.1</code> on vMX7 which is what we wanted, but what does the route for <code>1.1.1.1/32</code> actually look like&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [37]; title: ; notranslate">
inet.3: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden)
1.1.1.1/32 (1 entry, 1 announced)
        *BGP    Preference: 170/-101
                Next hop type: Indirect, Next hop index: 0
                Address: 0xd006d90
                Next-hop reference count: 4
                Source: 5.5.5.5
                Next hop type: Router, Next hop index: 610
                Next hop: 169.254.10.10 via ge-0/0/0.0, selected
                Label operation: Push 302656
                Label TTL action: prop-ttl
                Load balance label: Label 302656: None; 
                Label element ptr: 0xd007500
                Label parent element ptr: 0x0
                Label element references: 2
                Label element child references: 1
                Label element lsp id: 0
                Session Id: 0x192
                Protocol next hop: 3.3.3.3
                Label operation: Push 302656
                Label TTL action: prop-ttl
                Load balance label: Label 302656: None; 
                Indirect next hop: 0xb68cb90 1048574 INH Session ID: 0x19b
                State: &lt;Secondary Active Int Ext&gt;
                Local AS: 65002 Peer AS: 65002
                Age: 51         Metric: 2       Metric2: 4 
                Validation State: unverified 
                Task: BGP_65002.5.5.5.5+49544
                Announcement bits (3): 2-Resolve tree 1 3-Resolve tree 2 4-Resolve_IGP_FRR task 
                AS path: 65001 I 
                Accepted
                Route Label: 302656
                Localpref: 100
                Router ID: 5.5.5.5
                Primary Routing Table inet.0
                Indirect next hops: 1
                        Protocol next hop: 3.3.3.3 Metric: 4
                        Label operation: Push 302656
                        Label TTL action: prop-ttl
                        Load balance label: Label 302656: None; 
                        Indirect next hop: 0xb68cb90 1048574 INH Session ID: 0x19b
                        Indirect path forwarding next hops: 1
                                Next hop type: Router
                                Next hop: 169.254.10.10 via ge-0/0/0.0
                                Session Id: 0x192
                        3.3.3.3/32 Originating RIB: inet.0
                          Metric: 4                       Node path count: 1
                          Forwarding nexthops: 1
                                Nexthop: 169.254.10.10 via ge-0/0/0.0
                                Session Id: 192

admin@vmx7.lab&gt; 
</pre></div>


<p>Aha!  The protocol next-hop we&#8217;re getting for that route is the loopback of vMX3 which is not within our label domain/AS.  If we want BGP-LU to do our stitching as part of the transport LSP we need to fix this.  To fix this, we need to tell each border router to do a next-hop self on the BGP-LU route that&#8217;s being advertised to the tail router in it&#8217;s own AS.  Let&#8217;s try that out&#8230;</p>



<h2 class="wp-block-heading">vMX3 and vMX5</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set policy-options policy-statement nhs term nhs from family inet 
set policy-options policy-statement nhs term nhs then next-hop self
set protocols bgp group internal export nhs
</pre></div>


<p>Now let&#8217;s look at that route again&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [39]; title: ; notranslate">
admin@vmx7.lab&gt; show route 1.1.1.1 table inet.3 extensive    

inet.3: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden)
1.1.1.1/32 (1 entry, 1 announced)
        *BGP    Preference: 170/-101
                Next hop type: Indirect, Next hop index: 0
                Address: 0xd0078d0
                Next-hop reference count: 4
                Source: 5.5.5.5
                Next hop type: Router, Next hop index: 613
                Next hop: 169.254.10.10 via ge-0/0/0.0, selected
                Label operation: Push 302496, Push 301376(top)
                Label TTL action: prop-ttl, prop-ttl(top)
                Load balance label: Label 302496: None; Label 301376: None; 
                Label element ptr: 0xd007f20
                Label parent element ptr: 0xd006e40
                Label element references: 2
                Label element child references: 1
                Label element lsp id: 0
                Session Id: 0x192
                Protocol next hop: 5.5.5.5
                Label operation: Push 302496
                Label TTL action: prop-ttl
                Load balance label: Label 302496: None; 
                Indirect next hop: 0xb68c640 1048576 INH Session ID: 0x19c
                State: &lt;Secondary Active Int Ext&gt;
                Local AS: 65002 Peer AS: 65002
                Age: 9  Metric: 2       Metric2: 1 
                Validation State: unverified 
                Task: BGP_65002.5.5.5.5+49544
                Announcement bits (3): 2-Resolve tree 1 3-Resolve tree 2 4-Resolve_IGP_FRR task 
                AS path: 65001 I 
                Accepted
                Route Label: 302496
                Localpref: 100
                Router ID: 5.5.5.5
                Primary Routing Table inet.0
                Indirect next hops: 1
                        Protocol next hop: 5.5.5.5 Metric: 1
                        Label operation: Push 302496
                        Label TTL action: prop-ttl
                        Load balance label: Label 302496: None; 
                        Indirect next hop: 0xb68c640 1048576 INH Session ID: 0x19c
                        Indirect path forwarding next hops: 1
                                Next hop type: Router
                                Next hop: 169.254.10.10 via ge-0/0/0.0
                                Session Id: 0x192
                        5.5.5.5/32 Originating RIB: inet.3
                          Metric: 1                       Node path count: 1
                          Forwarding nexthops: 1
                                Nexthop: 169.254.10.10 via ge-0/0/0.0
                                Session Id: 0

admin@vmx7.lab&gt; 
</pre></div>


<p>Bingo!  Now let&#8217;s look at the route in the customer VRF for the remote prefix&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [8]; title: ; notranslate">
admin@vmx7.lab&gt; show route table customer1.inet.0    

customer1.inet.0: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

10.10.10.0/24      *&#x5B;BGP/170] 00:02:36, localpref 100, from 5.5.5.5
                      AS path: 65001 I, validation-state: unverified
                    &gt; to 169.254.10.10 via ge-0/0/0.0, Push 16, Push 302496, Push 301376(top)
192.168.10.0/24    *&#x5B;Direct/0] 1d 04:38:57
                    &gt; via ge-0/0/1.0
192.168.10.1/32    *&#x5B;Local/0] 1d 04:38:57
                      Local via ge-0/0/1.0

admin@vmx7.lab&gt; 
</pre></div>


<p>And at long last &#8211; the routing tables look as they should.  We now have the 3 labels we were looking for.  Whew.  But&#8230;  Our ping still isn&#8217;t working&#8230;.</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
left_client:~# ping 192.168.10.100 -c 5
PING 192.168.10.100 (192.168.10.100) 56(84) bytes of data.

--- 192.168.10.100 ping statistics ---
5 packets transmitted, 0 received, 100% packet loss, time 3999ms

left_client:~# 
</pre></div>


<p>Grumble.  Ok.  At this point, let&#8217;s do a label trace from tail router to tail router and see what&#8217;s going on now.  From the output above, we know that vMX6 is going to get a top label of <code>301376</code>.  Presumably this is for the LDP LSP but let&#8217;s make sure&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [5]; title: ; notranslate">
admin@vmx7.lab&gt; show ldp database 
Input label database, 7.7.7.7:0--6.6.6.6:0
Labels received: 3
  Label     Prefix
 301376      5.5.5.5/32
      3      6.6.6.6/32
 301408      7.7.7.7/32

Output label database, 7.7.7.7:0--6.6.6.6:0
Labels advertised: 3
  Label     Prefix
 299984      5.5.5.5/32
 299968      6.6.6.6/32
      3      7.7.7.7/32

admin@vmx7.lab&gt; 
</pre></div>


<p>Yep &#8211; ok, that checks out.  Let&#8217;s see what vMX6 does with this label now&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx6.lab&gt; show route table mpls.0 label 301376 

mpls.0: 10 destinations, 10 routes (10 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

301376             *&#x5B;LDP/9] 1d 04:43:40, metric 1
                    &gt; to 169.254.10.8 via ge-0/0/0.0, Pop      
301376(S=0)        *&#x5B;LDP/9] 1d 04:43:40, metric 1
                    &gt; to 169.254.10.8 via ge-0/0/0.0, Pop      

admin@vmx6.lab&gt; 
</pre></div>


<p>Alright &#8211; it is the PHP router for the LDP LSP so it will pop that label and pass along what&#8217;s left to vMX5.  So vMX5 will get a top label of <code>302496</code> which if we go back and look at vMX7 again, we&#8217;ll see is the BGP-LU stitching label that it received&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [6]; title: ; notranslate">
admin@vmx7.lab&gt; show route receive-protocol bgp 5.5.5.5 1.1.1.1/32 table inet.3 extensive 

inet.3: 3 destinations, 3 routes (3 active, 0 holddown, 0 hidden)
* 1.1.1.1/32 (1 entry, 1 announced)
     Accepted
     Route Label: 302496
     Nexthop: 5.5.5.5
     MED: 2
     Localpref: 100
     AS path: 65001 I 
     Entropy label capable, next hop field matches route next hop

admin@vmx7.lab&gt; 
</pre></div>


<p>Checks out.  Let&#8217;s see what vMX5 will do with that label now&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [15]; title: ; notranslate">
admin@vmx5.lab&gt; show route table mpls.0 label 302496 extensive 

mpls.0: 12 destinations, 12 routes (12 active, 0 holddown, 0 hidden)
302496 (1 entry, 1 announced)
TSI:
KRT in-kernel 302496 /52 -&gt; {indirect(1048575)}
        *VPN    Preference: 170
                Next hop type: Indirect, Next hop index: 0
                Address: 0xd007510
                Next-hop reference count: 2
                Source: 3.3.3.3
                Next hop type: Router, Next hop index: 589
                Next hop: 169.254.10.6 via ge-0/0/0.0, selected
                Label-switched-path to-vmx3
                Label operation: Swap 302656, Push 301552(top)
                Label TTL action: prop-ttl, prop-ttl(top)
                Load balance label: Label 302656: None; Label 301552: None; 
                Label element ptr: 0xd0076e0
                Label parent element ptr: 0xd007c20
                Label element references: 1
                Label element child references: 0
                Label element lsp id: 0
                Session Id: 0x19a
                Protocol next hop: 3.3.3.3
                Label operation: Swap 302656
                Load balance label: Label 302656: None; 
                Indirect next hop: 0xb68d300 1048575 INH Session ID: 0x1a5
                State: &lt;Active Int Ext&gt;
                Local AS: 65002 
                Age: 7:57 	Metric2: 2 
                Validation State: unverified 
                Task: BGP_RT_Background
                Announcement bits (1): 1-KRT 
                AS path: 65001 I 
		Ref Cnt: 1
                Indirect next hops: 1
                        Protocol next hop: 3.3.3.3 Metric: 2
                        Label operation: Swap 302656
                        Load balance label: Label 302656: None; 
                        Indirect next hop: 0xb68d300 1048575 INH Session ID: 0x1a5
                        Indirect path forwarding next hops: 1
                                Next hop type: Router
                                Next hop: 169.254.10.6 via ge-0/0/0.0
                                Session Id: 0x19a
			3.3.3.3/32 Originating RIB: inet.3
			  Metric: 2			  Node path count: 1
			  Forwarding nexthops: 1
				Nexthop: 169.254.10.6 via ge-0/0/0.0
				Session Id: 0

admin@vmx5.lab&gt; 
</pre></div>


<p>Since this is going into an RSVP LSP we have to do the extensive output to see the label operation but as you can see it&#8217;s going to swap <code>302496</code> for <code>302656</code> and then push another label of <code>301552</code>.  So vMX4 will be getting a top label of <code>301552</code> followed by <code>302656</code> and then our VPN label of <code>16</code>.  Ok so what does vMX4 do with that top label?</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [13]; title: ; notranslate">
admin@vmx4.lab&gt; show route table mpls.0 label 301552 extensive 

mpls.0: 10 destinations, 10 routes (10 active, 0 holddown, 0 hidden)
301552 (1 entry, 1 announced)
TSI:
KRT in-kernel 301552 /52 -&gt; {Pop       Flags acct EL-capable}
        *RSVP   Preference: 7/1
                Next hop type: Router, Next hop index: 585
                Address: 0xd007450
                Next-hop reference count: 2
                Next hop: 169.254.10.4 via ge-0/0/0.0, selected
                Label-switched-path to-vmx3
                Label operation: Pop      
                Load balance label: None; 
                Label element ptr: 0xd007560
                Label parent element ptr: 0x0
                Label element references: 2
                Label element child references: 0
                Label element lsp id: 0
                Session Id: 0x181
                State: &lt;Active Int AckRequest Accounting EL-capable&gt;
                Age: 1d 4:48:51         Metric: 1 
                Validation State: unverified 
                Task: RSVP
                Announcement bits (1): 1-KRT 
                AS path: I 

301552(S=0) (1 entry, 1 announced)
TSI:
KRT in-kernel 301552 /56 -&gt; {Pop       Flags acct EL-capable}
        *RSVP   Preference: 7/1
                Next hop type: Router, Next hop index: 586
                Address: 0xd006f70
                Next-hop reference count: 2
                Next hop: 169.254.10.4 via ge-0/0/0.0, selected
                Label-switched-path to-vmx3
                Label operation: Pop      
                Load balance label: None; 
                Label element ptr: 0xd006fc0
                Label parent element ptr: 0x0
                Label element references: 1
                Label element child references: 0
                Label element lsp id: 0
                Session Id: 0x181
                State: &lt;Active Int AckRequest Accounting EL-capable&gt;
                Age: 1d 4:48:51         Metric: 1 
                Validation State: unverified 
                Task: RSVP
                Announcement bits (1): 1-KRT 
                AS path: I 

admin@vmx4.lab&gt; 
</pre></div>


<p>Alright &#8211; to it&#8217;s going to pop it.  Which again &#8211; makes sense since we are now in the RSVP domain and vMX4 would be the PHP router for this LSP to vMX3.  So vMX3 is going to get a top label of <code>302656</code>.  Let&#8217;s see what vMX3 does with that&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [7]; title: ; notranslate">
admin@vmx3.lab&gt; show route table mpls.0 label 302656 

mpls.0: 12 destinations, 12 routes (12 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

302656             *&#x5B;VPN/170] 1d 01:13:08
                    &gt; to 169.254.10.2 via ge-0/0/0.0, Pop      
302656(S=0)        *&#x5B;VPN/170] 1d 01:13:08
                    &gt; to 169.254.10.2 via ge-0/0/0.0, Pop      

admin@vmx3.lab&gt; 
</pre></div>


<p>Pop&#8230;  Huh.  That doesn&#8217;t seem right.  That would mean that vMX2 would be getting just the VPN label.  Let&#8217;s do a packet capture between vMX3 and vMX4 to see if that&#8217;s whats going on&#8230;</p>



<p><em>Note: Im assuming in this case that you&#8217;re trying to do a ping from </em><code><em>right_client</em></code><em> to </em><code><em>left_client</em></code><em>.  I realized as I was writing this that I&#8217;ve been doing all my other tests from left to right so apologies for the change up as part of this trace.</em></p>



<div class="wp-block-image"><figure class="aligncenter"><a href="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-7.png"><img loading="lazy" decoding="async" width="809" height="382" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-7.png" alt="" class="wp-image-23258" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-7.png 809w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-7-200x94.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-7-300x142.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-7-768x363.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-7-624x295.png 624w" sizes="(max-width: 809px) 100vw, 809px" /></a></figure></div>



<p>Alright &#8211; so sure enough &#8211; that&#8217;s what it&#8217;s doing.  As we know &#8211; the VPN label is only relevant to the PE that&#8217;s advertising this.  In this case, vMX2 is going to be getting a frame with a single VPN label as the top frame.  And if our assumptions are correct, vMX2 will not know what to do with it&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx2.lab&gt; show route table mpls.0 label 16 

admin@vmx2.lab&gt; show route forwarding-table label 16 
Routing table: default.mpls
MPLS:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
default            perm     0                    dscd       50     1

Routing table: __mpls-oam__.mpls
MPLS:
Destination        Type RtRef Next hop           Type Index    NhRef Netif
default            perm     0                    dscd      553     1

admin@vmx2.lab&gt; 
</pre></div>


<p>Yep &#8211; so it&#8217;s going to discard.  So what&#8217;s going on now?  The problem seems to be on vMX3.  So let&#8217;s take a look and see what&#8217;s going on there.  We know that vMX3 is responsible for stitching the traffic coming from the RSVP LSP into the LDP LSP to vMX1.  So let&#8217;s take a look at vMX3 and see if we have a LSP to vMX1&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; highlight: [6,7]; title: ; notranslate">
admin@vmx3.lab&gt; show route table inet.3 

inet.3: 4 destinations, 4 routes (4 active, 0 holddown, 0 hidden)
+ = Active Route, - = Last Active, * = Both

1.1.1.1/32         *&#x5B;LDP/9] 1d 01:21:19, metric 1
                    &gt; to 169.254.10.2 via ge-0/0/0.0, Push 301328
2.2.2.2/32         *&#x5B;LDP/9] 1d 01:21:19, metric 1
                    &gt; to 169.254.10.2 via ge-0/0/0.0
5.5.5.5/32         *&#x5B;RSVP/7/1] 1d 04:58:22, metric 2
                    &gt; to 169.254.10.5 via ge-0/0/1.0, label-switched-path to-vmx5
7.7.7.7/32         *&#x5B;BGP/170] 21:10:16, MED 2, localpref 100, from 5.5.5.5
                      AS path: 65002 I, validation-state: unverified
                    &gt; to 169.254.10.5 via ge-0/0/1.0, label-switched-path to-vmx5

admin@vmx3.lab&gt;
</pre></div>


<p>Alright so that looks good.  So what&#8217;s going on here?  Well &#8211; this sentence from the<a href="https://www.juniper.net/documentation/en_US/junos/topics/usage-guidelines/mpls-adding-lsp-related-routes-to-the-inet-3-routing-table.html"> Juniper documentation</a> describes the issue pretty well&#8230;</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow"><p>The only use for inet.3 or inet6.3 is to permit BGP to perform next-hop resolution.</p></blockquote>



<p>Put more simply &#8211; BGP is the only protocol that will recursively resolve it&#8217;s next hop in the <code>inet3</code>. table.  So if vMX3 can&#8217;t resolve the LSP in <code>inet.3</code> then what does it do?  It looks in <code>inet.0</code>&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx3.lab&gt; show route table inet.0 1.1.1.1/32   

inet.0: 17 destinations, 17 routes (17 active, 0 holddown, 0 hidden)
@ = Routing Use Only, # = Forwarding Use Only
+ = Active Route, - = Last Active, * = Both

1.1.1.1/32         *&#x5B;OSPF/10] 1d 01:33:06, metric 2
                    &gt; to 169.254.10.2 via ge-0/0/0.0

admin@vmx3.lab&gt; 
</pre></div>


<p>And in <code>inet.0</code> we have a route to <code>1.1.1.1/32</code> through OSPF.  Since vMX3 thinks that it&#8217;s best path to <code>1.1.1.1/32</code> is through OSPF it will send a BGP-LU label to vMX5 indicating that it should pop off the label so that it can forward the packet normally.  The problem is that once the frame gets to vMX3 &#8211; there are still two labels on the frame meaning that vMX4 will receive a label with a single VPN label which it has no idea what to do with.  </p>



<div class="wp-block-image"><figure class="aligncenter"><a href="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-15.png" target="_blank" rel="noreferrer noopener"><img loading="lazy" decoding="async" width="783" height="349" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-15.png" alt="" class="wp-image-23270" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-15.png 783w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-15-200x89.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-15-300x134.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-15-768x342.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-15-624x278.png 624w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-15-780x349.png 780w" sizes="(max-width: 783px) 100vw, 783px" /></a></figure></div>



<p>The fix for this is to get the forwarding information required from <code>inet.3</code> into the <code>inet.0</code> table.  There are many many many ways to do this, but in my opinion, the easiest and the safest way to do this is by telling the router to use routes from <code>inet.3</code> for forwarding only&#8230;</p>



<h2 class="wp-block-heading">vMX3 and vMX5</h2>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
set protocols mpls traffic-engineering mpls-forwarding 
</pre></div>


<p>After we add that our ping should start working!  Let&#8217;s now look at the <code>inet.0</code> table on vMX3 again for the <code>1.1.1.1/32</code> prefix&#8230;</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: plain; title: ; notranslate">
admin@vmx3.lab&gt; show route table inet.0 1.1.1.1/32 

inet.0: 17 destinations, 20 routes (17 active, 0 holddown, 0 hidden)
@ = Routing Use Only, # = Forwarding Use Only
+ = Active Route, - = Last Active, * = Both

1.1.1.1/32         @&#x5B;OSPF/10] 00:00:38, metric 2
                    &gt; to 169.254.10.2 via ge-0/0/0.0
                   #&#x5B;LDP/9] 00:00:38, metric 1
                    &gt; to 169.254.10.2 via ge-0/0/0.0, Push 301328

admin@vmx3.lab&gt; 
</pre></div>


<p>Now we can see that the LDP router (with the lower priority) is in the table and has the <code>#</code> next to it indicating that it&#8217;s being used for just forwarding traffic and not as part of any route selection protocol.  So now our picture should look like this&#8230;</p>



<div class="wp-block-image"><figure class="aligncenter"><a href="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-16.png" target="_blank" rel="noreferrer noopener"><img loading="lazy" decoding="async" width="783" height="349" src="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-16.png" alt="" class="wp-image-23271" srcset="http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-16.png 783w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-16-200x89.png 200w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-16-300x134.png 300w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-16-768x342.png 768w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-16-624x278.png 624w, http://www.dasblinkenlichten.com/wp-content/uploads/2019/10/image-16-780x349.png 780w" sizes="(max-width: 783px) 100vw, 783px" /></a></figure></div>



<p>Alright &#8211; well that was a long road to get here.  But we finally made it and walking through troubleshooting something like this is always helpful in my opinion.  Clearly, we had to jump through some hoops because of the way our BGP peering was setup so in the next post we&#8217;ll talk through some other options for how to do this.  If any of this seemed confusing to you, I&#8217;d encourage you to read the <a href="http://www.dasblinkenlichten.com/understanding-bgp-labeled-unicast/">post on BPG-LU</a> as well as <a href="http://www.dasblinkenlichten.com/bgp-lu-and-mpls-vpns/">the one on MPLS VPNs with BGP LU</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>http://www.dasblinkenlichten.com/fundamentals-of-mpls-lsps/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">23200</post-id>	</item>
	</channel>
</rss>
