<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Posts on Packet Driving</title>
        <link>https://packetdriving.netlify.app/posts/</link>
        <description>Recent content in Posts on Packet Driving</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-us</language>
        <copyright>&lt;a href=&#34;https://creativecommons.org/licenses/by-nc/4.0/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;CC BY-NC 4.0&lt;/a&gt;</copyright>
        <lastBuildDate>Sat, 30 Mar 2024 00:00:00 +0000</lastBuildDate>
        <atom:link href="https://packetdriving.netlify.app/posts/index.xml" rel="self" type="application/rss+xml" />
        
        <item>
            <title>A Year of Golang</title>
            <link>https://packetdriving.netlify.app/posts/2024/03/a-year-of-golang/</link>
            <pubDate>Sat, 30 Mar 2024 00:00:00 +0000</pubDate>
            
            <guid>https://packetdriving.netlify.app/posts/2024/03/a-year-of-golang/</guid>
            <description>To better learn Golang, I&amp;rsquo;ll be taking the next year to focus on the language. I&amp;rsquo;ll attempt to use Golang in all of my side projects, and keep them small and focused without letting them go on forever. With that said I&amp;rsquo;ll be using two methodologies when it comes to writing side projects.
 The Cult of Done Prototyping  I&amp;rsquo;ll be using this repo to track progress as the year goes on.</description>
            <content type="html"><![CDATA[<p>To better learn Golang, I&rsquo;ll be taking the next year to focus on the language. I&rsquo;ll attempt to use Golang in all of my side projects, and keep them small and focused without letting them go on forever. With that said I&rsquo;ll be using two methodologies when it comes to writing side projects.</p>
<ul>
<li>The Cult of Done</li>
<li>Prototyping</li>
</ul>
<p>I&rsquo;ll be using <a href="https://github.com/barryCrunch/year-of-golang/tree/main">this</a> repo to track progress as the year goes on.</p>
<h2 id="the-cult-of-done">The Cult of Done</h2>
<p>Created by Bre Pettis and Kio Stark <a href="https://medium.com/@bre/the-cult-of-done-manifesto-724ca1c2ff13">here</a>, I discovered &ldquo;The Cult of Done&rdquo; through a YouTube video by <a href="https://www.youtube.com/watch?v=bJQj1uKtnus">No Boiler Plate</a>. It is an approach to creative work, that forces you to finish the work at hand. It comes with thirteen tenants listed below -</p>
<ul>
<li>There are three states of being. Not knowing, action, and completion.</li>
<li>Accept that everything is a draft. It helps to get it done.</li>
<li>There is no editing stage.</li>
<li>Pretending you know what you’re doing is almost the same as knowing what you are doing, so just accept that you know what you’re doing even if you don’t and do it.</li>
<li>Banish procrastination. If you wait more than a week to get an idea done, abandon it.</li>
<li>The point of being done is not to finish but to get other things done.</li>
<li>Once you’re done you can throw it away.</li>
<li>Laugh at perfection. It’s boring and keeps you from being done.</li>
<li>People without dirty hands are wrong. Doing something makes you right.</li>
<li>Failure counts as done. So do mistakes.</li>
<li>Destruction is a variant of done.</li>
<li>If you have an idea and publish it on the internet, that counts as a ghost of done.</li>
<li>Done is the engine of more.</li>
</ul>
<p>The plan is to release lots and lots of projects. Some of them may work but some of them may be failures. The purpose though is not the projects themselves but the learning of Golang (and other concepts) along the way.</p>
<h2 id="prototype---throw-away-your-first-draft">Prototype - Throw away your first draft</h2>
<p>A recent <a href="https://ntietz.com/blog/throw-away-your-first-draft/">blog post</a> by Nicole Tietz on the importance of prototyping features and initial projects. To put it simply, when first tackling a coding problem, the first draft should be an effort to understand what you are building. Mistakes will be made, the code will be messy, and it won&rsquo;t follow best practices. The next step is to throw away the code that you wrote. Mistakes will have been made, the code is messy and it doesn&rsquo;t follow best practices. The next draft will be a clean slate with none of the baggage, but now the second draft can be written with the experience of already solving the problem once.</p>
<h2 id="how-the-repo-works">How the Repo Works</h2>
<p>The repo is split into two main branches, Main and Prototype. Most of the work will be done in the prototype branch. Each directory in the branch will be its own mini-project. Most of the projects will never exist outside of the prototype branch. However if one of the projects is particularly interesting, I plan on rewriting the project (from scratch) within the main branch.</p>
]]></content>
        </item>
        
        <item>
            <title>NAPALM Network Compliance</title>
            <link>https://packetdriving.netlify.app/posts/2021/11/napalm-network-compliance/</link>
            <pubDate>Sun, 14 Nov 2021 00:00:00 +0000</pubDate>
            
            <guid>https://packetdriving.netlify.app/posts/2021/11/napalm-network-compliance/</guid>
            <description>The Overview Recently I had to provide the ability to validate network state on a specific environment. While this offered an excellent opportunity to use NAPALMs&#39; validation features, it also became an exercise in recursion. The validation with NAPALM is relatively straightforward, but the python dictionary returned is complex and of an unknown origin. To display the dictionary without coding out every single use case, I wrote a recursive function to do it for me.</description>
            <content type="html"><![CDATA[<h2 id="the-overview">The Overview</h2>
<p>Recently I had to provide the ability to validate network state on a specific environment. While this offered an excellent opportunity to use NAPALMs' validation features, it also became an exercise in recursion. The validation with NAPALM is relatively straightforward, but the python dictionary returned is complex and of an unknown origin. To display the dictionary without coding out every single use case, I wrote a recursive function to do it for me.</p>
<h2 id="the-topology">The Topology</h2>
<p>The topology is pretty simple with three routers where router-A connects to both router-B and router-C. If you are interested in the configuration, you can reference the previous blog post <a href="/posts/2021/10/basic-lab-automation-napalm-eve-ng/">here</a>.</p>
<p><img src="/static/basic-lab-automation-topology.PNG" alt="Topology"></p>
<h2 id="the-objective">The Objective</h2>
<p>The overall goal is to validate the state of the network and effectively communicate the results of that validation to the user.</p>
<h2 id="the-script">The Script</h2>
<p>First, we&rsquo;ll want to import the get_network driver from NAPALM.</p>
<pre><code>from napalm import get_network_driver
</code></pre><p>We will then import some more utility modules.</p>
<pre><code>from colors import bcolors
from os.path import exists
from os import system, name
</code></pre><p>Now let&rsquo;s define some utility functions. The clear() function will clear the CLI screen in case there is any stale output still there. We leverage the system module to determine if we are running the script on a nix or windows based environment.</p>
<pre><code>def clear():
    if name == 'nt':
        _ = system('cls')
    else:
        _ = system('clear')

</code></pre><p>The following utility function will print our test results in a more readable format, with special Unicode characters.</p>
<pre><code>def print_result(test_name, test_result, level):
    if test_result:
        print((&quot; &quot; * level) + u'\u2705' + &quot; - &quot; + test_name)
    else:
        print((&quot; &quot; * level) + u'\u274c' + &quot; - &quot; + test_name)
</code></pre><p>The next function is the real &ldquo;meat and potatoes&rdquo; of the entire compliance script. It is a recursive function that will allow us to move through the JSON blob returned by the NAPALM compliance_report() method.</p>
<pre><code>def display_compliance_info(output, level=0):
    keywords = ['extra', 'missing', 'skipped', 'complies', 'present', 'diff']
    for check, value in output.items():
        if check not in keywords:
            print_result(check, value['complies'], level)
            if 'present' in value.keys():
                display_compliance_info(value['present'], level+2)
            elif 'diff' in value.keys():
                if len(value['diff']['missing']) &gt; 0:
                    for missing_value in value['diff']['missing']:
                        print_result(missing_value, False, level+2)
                display_compliance_info(value['diff']['present'], level+2)

</code></pre><p>To better understand what this function does, let&rsquo;s first look at the output that we receive from NAPALM.</p>
<pre><code>{
  &quot;get_facts&quot;: {
    &quot;complies&quot;: true,
    &quot;present&quot;: { &quot;hostname&quot;: { &quot;complies&quot;: true, &quot;nested&quot;: false } },
    &quot;missing&quot;: [],
    &quot;extra&quot;: []
  },
  &quot;get_interfaces_ip&quot;: {
    &quot;complies&quot;: true,
    &quot;present&quot;: {
      &quot;Ethernet1&quot;: { &quot;complies&quot;: true, &quot;nested&quot;: true },
      &quot;Ethernet2&quot;: { &quot;complies&quot;: true, &quot;nested&quot;: true }
    },
    &quot;missing&quot;: [],
    &quot;extra&quot;: []
  },
  &quot;get_bgp_neighbors&quot;: {
    &quot;complies&quot;: false,
    &quot;present&quot;: {
      &quot;global&quot;: {
        &quot;diff&quot;: {
          &quot;complies&quot;: false,
          &quot;present&quot;: {
            &quot;router_id&quot;: { &quot;complies&quot;: true, &quot;nested&quot;: false },
            &quot;peers&quot;: {
              &quot;diff&quot;: {
                &quot;complies&quot;: false,
                &quot;present&quot;: {
                  &quot;192.168.0.2&quot;: { &quot;complies&quot;: true, &quot;nested&quot;: true }
                },
                &quot;missing&quot;: [&quot;10.0.0.1&quot;],
                &quot;extra&quot;: [&quot;10.0.0.2&quot;]
              },
              &quot;complies&quot;: false,
              &quot;nested&quot;: true
            }
          },
          &quot;missing&quot;: [],
          &quot;extra&quot;: []
        },
        &quot;complies&quot;: false,
        &quot;nested&quot;: true
      }
    },
    &quot;missing&quot;: [],
    &quot;extra&quot;: []
  },
  &quot;skipped&quot;: [],
  &quot;complies&quot;: false
}


</code></pre><p>As we can see, the dictionary can be reasonably complex and of an unknown depth. Because of those two potential items, recursion will be the best way to tackle the iteration through this function. The general premise of the function is to loop through each initial level of the dictionary. In the event of a unique key (get_facts, get_interface_ip, get_bgp_neighbors, global, etc.), we print the value of the complies key, which represents the overall result for the section. If we see the key &ldquo;present,&rdquo; we know there is another section to the output, so we call the function again, passing it the value of present, and do it all over again. We do the same thing if we see a non-empty value in the &ldquo;missing&rdquo; value of the &ldquo;diff&rdquo; section.</p>
<p>The result of this function allows us to do two things.</p>
<ol>
<li>We can keep the code short since we do not have to plan for every possible variation that the NAPALM compliance function returns.</li>
<li>We also don&rsquo;t have to rewrite the script every time a new check is added to NAPALM. All we need to do is update our check file.</li>
</ol>
<p>The remaining portion of the script will loop through the defined devices, and for each device it will check to see if a compliance file exists for the device. If the file exists, it will run the <strong><em>compliance_report</em></strong> function and display the output.</p>
<pre><code>def main():
    clear()

    devices = [
        {'name': 'router-A', 'host': '192.168.86.201'},
        {'name': 'router-B', 'host': '192.168.86.202'},
        {'name': 'router-C', 'host': '192.168.86.203'},
    ]

    driver = get_network_driver(&quot;eos&quot;)

    for device in devices:
        with driver(device['host'], 'admin', 'test') as active_device:
            compliance_file = f&quot;compliance/{device['name']}.yml&quot;
            if exists(compliance_file):
                print(
                    f&quot;Checking {bcolors.HEADER}{device['name']}{bcolors.ENDC} for compliance...&quot;)
                result = active_device.compliance_report(compliance_file)
                display_compliance_info(result)
                print('\n')


if __name__ == &quot;__main__&quot;:
    main()

</code></pre><h2 id="the-compliance-file">The Compliance File</h2>
<p>NAPALMs compliance file is pretty elegant. All checks are defined as YAML and pretty self-explanatory. Below, we are checking for three things: the device&rsquo;s hostname, the interface configuration, and the BGP configuration. If any of these checks fail, the overall parent object also fails.</p>
<pre><code>---
- get_facts:
    hostname: router-A

- get_interfaces_ip:
    Ethernet1:
      ipv4:
        10.0.0.1:
          prefix_length: 30
    Ethernet2:
      ipv4:
        192.168.0.1:
          prefix_length: 30

- get_bgp_neighbors:
    global:
      router_id: 1.1.1.1
      peers:
        _mode: strict
        10.0.0.1:
          is_enabled: true
          is_up: true
        192.168.0.2:
          is_enabled: true
          is_up: true

</code></pre><h2 id="output">Output</h2>
<p>Below is a screenshot of the output for the three devices. I purposely entered some incorrect information into the compliance file for <strong>router-a</strong> to see the result of a failed compliance run.</p>
<p><img src="/static/compliance-output.PNG" alt="Output"></p>
<h2 id="summary">Summary</h2>
<p>This little project ended up being more work than I originally anticipated, but it gave me some good practice on recursion. While sometimes it may initially seem easier to just brute force your way through a problem, spending the time upfront on the algorithmic side can save you a lot of work on the backend.</p>
<h2 id="resources">Resources</h2>
<p>Code:
<a href="https://github.com/barryCrunch/lab-automation-demo">https://github.com/barryCrunch/lab-automation-demo</a></p>
<p>Napalm Documentation:
<a href="https://napalm.readthedocs.io/en/latest/index.html">https://napalm.readthedocs.io/en/latest/index.html</a></p>
]]></content>
        </item>
        
        <item>
            <title>Basic Lab Automation - NAPALM &amp; EVE-NG</title>
            <link>https://packetdriving.netlify.app/posts/2021/10/basic-lab-automation-napalm-eve-ng/</link>
            <pubDate>Sun, 24 Oct 2021 00:00:00 +0000</pubDate>
            
            <guid>https://packetdriving.netlify.app/posts/2021/10/basic-lab-automation-napalm-eve-ng/</guid>
            <description>The Problem Due to some new environments that we have been spinning up at work, I have been labing a lot of new tech out recently. Because of this, I found myself typing out many commands on the CLI or having to re-type out configurations from scratch if my lab misbehaved. To reduce the time spent on the tedious, I decided to write up a quick script to manage my infrastructure through static files.</description>
            <content type="html"><![CDATA[<h2 id="the-problem">The Problem</h2>
<p>Due to some new environments that we have been spinning up at work, I have been labing a lot of new tech out recently. Because of this, I found myself typing out many commands on the CLI or having to re-type out configurations from scratch if my lab misbehaved. To reduce the time spent on the tedious, I decided to write up a quick script to manage my infrastructure through static files.</p>
<h2 id="the-topology">The Topology</h2>
<p>I will leverage a pretty basic setup to outline the idea, but you can get rather complicated with this basic script since NAPALM supports a whole slew of device types. Below we have three routers, with router-A having a leg to both router-B and router-C. Router-A is also running BGP between itself and each directly attached neighbor. The management interfaces on each Arista switch leg out locally to my home network, which allows me to access each vEOS natively via ssh.</p>
<p><img src="/static/basic-lab-automation-topology.PNG" alt="Topology"></p>
<h2 id="the-objective">The Objective</h2>
<p>Instead of having the configuration on the device be the source of truth, it will be a set of flat files on my computer. I should then be able to &ldquo;push&rdquo; these configuration files to the devices removing the need for any manual CLI work. Below is a simple diagram of the workflow that I imagine when setting up and managing a device in the lab.</p>
<p><img src="/static/basic-lab-automation-flow-chart.PNG" alt="Topology"></p>
<h2 id="the-script">The Script</h2>
<p>First we&rsquo;ll want to import the get_network driver from NAPALM</p>
<pre><code>from napalm import get_network_driver
</code></pre><p>Then we will define our list of devices. We could get a little clever here and leverage nornir or netbox but for the sake of a MVP we&rsquo;ll just have a list of dictionaries.</p>
<pre><code>devices = [
      {'name': 'router-A', 'host': '192.168.86.201'},
      {'name': 'router-B', 'host': '192.168.86.202'},
      {'name': 'router-C', 'host': '192.168.86.203'},
]
</code></pre><p>Once we have the dictionary all we need to do is loop through it and apply the local file to each respective device.</p>
<pre><code>for device in devices:
      driver = get_network_driver('eos')
      active_device = driver(device['host'], 'admin', 'password')
      active_device.open()
      active_device.load_replace_candidate(filename=f&quot;configurations/{device['name']}.conf&quot;)
      compare_result = active_device.compare_config()

      if compare_result == '':
            active_device.discard_config()
      else:
            active_device.commit_config()
      active_device.close()
</code></pre><p>As you can see, the logic is pretty quick and dirty. First, we open up a connection to the device. Then we proceed to stage the configuration file by loading it to the device. We then check to see if there are any changes. If the string is not empty, we commit the configuration; otherwise, we discard it. And finally, we close the session to the device.</p>
<p><em>The <strong>load_replace_candidate()</strong> method will return a string with the changes to the configuration; if there are no changes, it will return an empty string.</em></p>
<h2 id="output">Output</h2>
<p>Below is an output from running the script. I have added some print statments to log the output.</p>
<pre><code>(arista) mbarry@desktop:~/projects/automation$ python deployment.py
******************************************************************
Connectiong to router-A...
Loading configuration...
Configuration changes....

@@ -23,6 +23,8 @@
    ip address 192.168.0.1/30
 !
 interface Ethernet3
+   no switchport
+   ip address 10.1.1.1/30
 !
 interface Ethernet4
 !
Commiting configuration....
COMPLETED


******************************************************************
Connectiong to router-B...
Loading configuration...
&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;NO CHANGES&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;


******************************************************************
Connectiong to router-C...
Loading configuration...
&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;NO CHANGES&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt;
</code></pre><p>We can see I added some IP address configuration to router-A on interface Ethernet3 and made no changes to router-B or router-C.</p>
<h2 id="summary">Summary</h2>
<p>Nothing too complicated above, but it allows me to iterate through my labs a bit quicker. Plus if you start storing the configuration in a git repo and wrap it in a CI/CD pipeline with pre and post validations you have yourself a genuine Infrastructure-As-Code deployment.</p>
<h2 id="resources">Resources</h2>
<p>Code:
<a href="https://github.com/barryCrunch/lab-automation-demo">https://github.com/barryCrunch/lab-automation-demo</a></p>
<p>Napalm Documentation:
<a href="https://napalm.readthedocs.io/en/latest/index.html">https://napalm.readthedocs.io/en/latest/index.html</a></p>
]]></content>
        </item>
        
        <item>
            <title>BGP Route Leaking - VRF to Global</title>
            <link>https://packetdriving.netlify.app/posts/2021/08/bgp-route-leaking-vrf-to-global/</link>
            <pubDate>Thu, 12 Aug 2021 00:00:00 +0000</pubDate>
            
            <guid>https://packetdriving.netlify.app/posts/2021/08/bgp-route-leaking-vrf-to-global/</guid>
            <description>The Problem Recently I came across the requirement to leak routes from a VRF to the Global Route Table (GRT) and vice versa. The task took me longer than I care to admit, and my biggest stumbling block was deciding how exactly to go about it. The way described below is just one way to accomplish the task and specifically works for exchanges across dynamic routing protocols. Below is only one way to do it and the way that I found to be the most intuitive.</description>
            <content type="html"><![CDATA[<h2 id="the-problem">The Problem</h2>
<p>Recently I came across the requirement to leak routes from a VRF to the Global Route Table (GRT) and vice versa. The task took me longer than I care to admit, and my biggest stumbling block was deciding how exactly to go about it.  The way described below is just one way to accomplish the task and specifically works for exchanges across dynamic routing protocols. Below is only one way to do it and the way that I found to be the most intuitive.</p>
<h2 id="the-topology">The Topology</h2>
<p>We are going to work off of an elementary topology with three routers. The first and third routers will exist within a single VRF, while the second router will have an interface
in each VRF. The second router will peer with the first and third via BGP and leak routes from VRF-A to its GRT and vice versa.</p>
<p><img src="/static/route-leaking-topology.PNG" alt="Topology"></p>
<h4 id="router01-base-config">router01 base config</h4>
<pre><code>hostname router01
!
interface Loopback1
 ip address 10.1.1.1 255.255.255.255
!
interface GigabitEthernet0/0
 ip address 10.0.0.2 255.255.255.252
 duplex auto
 speed auto
 media-type rj45
!
router bgp 65002
 bgp log-neighbor-changes
 neighbor 10.0.0.1 remote-as 6500
 !
 address-family ipv4
  redistribute connected
  neighbor 10.0.0.1 activate
 exit-address-family
!
</code></pre><p>Nothing special here; we just apply the IP addresses to the interfaces. Establish a BGP relationship with router02 and advertise our connected interfaces.</p>
<h4 id="router02-base-config">router02 base config</h4>
<pre><code>hostname router02
!
ip vrf VRF-A
 rd 1:1
 import ipv4 unicast map GLOBAL_TO_VRF
 export ipv4 unicast map VRF_TO_GLOBAL
 route-target export 1:1
 route-target import 1:1
!
interface GigabitEthernet0/0
 ip vrf forwarding VRF-A
 ip address 192.168.0.1 255.255.255.252
 duplex auto
 speed auto
 media-type rj45
!
interface GigabitEthernet0/1
 ip address 10.0.0.1 255.255.255.252
 duplex auto
 speed auto
 media-type rj45
!
!
router bgp 6500
 bgp log-neighbor-changes
 neighbor 10.0.0.2 remote-as 65002
 !
 address-family ipv4
  neighbor 10.0.0.2 activate
 exit-address-family
 !
 address-family ipv4 vrf VRF-A
  neighbor 192.168.0.2 remote-as 65001
  neighbor 192.168.0.2 activate
 exit-address-family
!
!
</code></pre><p>A couple noteworthy things in the router02 configuration. We create the VRF with the command:</p>
<pre><code>ip vrf VRF-A
</code></pre><p>We then set a route descriptor of <code>1:1</code> with the following command:</p>
<pre><code>rd 1:1
</code></pre><p>After creating our VRF, we place interface <code>GigabitEthernet0/0</code> in that VRF with <code>ip forwarding vrf VRF-A</code> under the interface configuration.</p>
<pre><code>interface GigabitEthernet0/0
  ip forwarding vrf VRF-A
</code></pre><h4 id="router03-base-config">router03 base config</h4>
<pre><code>hostname router03
!
interface Loopback1
 ip address 192.168.1.1 255.255.255.255
!
interface GigabitEthernet0/0
 ip address 192.168.0.2 255.255.255.252
 duplex auto
 speed auto
 media-type rj45
!
router bgp 65001
 bgp log-neighbor-changes
 neighbor 192.168.0.1 remote-as 6500
 !
 address-family ipv4
  redistribute connected
  neighbor 192.168.0.1 activate
 exit-address-family
!
</code></pre><h2 id="objective">Objective</h2>
<p>The goal will be to just share <code>10.1.1.1/32</code> into VRF-A and <code>192.168.1.1/32</code> into the Global Route Table. When all is said and done, we should be able to ping from Loopback1 on router01 to Loopback1 on router03.</p>
<h2 id="configuration">Configuration</h2>
<p>Before we start importing and exporting routes, we will need to create a couple of route maps to identify the routes we want to import/export.</p>
<pre><code>ip prefix-list GLOBAL_TO_VRF seq 10 permit 10.1.1.1/32
!
ip prefix-list VRF_TO_GLOBAL seq 10 permit 192.168.1.1/32
!
route-map VRF_TO_GLOBAL permit 10
 match ip address prefix-list VRF_TO_GLOBAL
!
route-map GLOBAL_TO_VRF permit 10
 match ip address prefix-list GLOBAL_TO_VRF
!
</code></pre><p>We know we want to grab the two /32 routes, so we create two prefix lists and then use those to identify the addresses within the route maps.</p>
<p>Once the route maps are built, they are applied to import/export statements within the vrf configuration.</p>
<pre><code>ip vrf VRF-A
 import ipv4 unicast map GLOBAL_TO_VRF
 export ipv4 unicast map VRF_TO_GLOBAL
</code></pre><p>We are almost there. The last configuration required is to configure the route target on exported routes. This will tag any routes exported from VRF-A with the RD <code>1:1</code></p>
<pre><code>ip vrf VRF-A
 route-target export 1:1
</code></pre><h2 id="validation">Validation</h2>
<p>First, let&rsquo;s check the Global Route Table on <code>router02</code></p>
<pre><code>router02#show ip route

Gateway of last resort is not set

      10.0.0.0/8 is variably subnetted, 4 subnets, 2 masks
C        10.0.0.0/30 is directly connected, GigabitEthernet0/1
L        10.0.0.1/32 is directly connected, GigabitEthernet0/1
B        10.1.1.1/32 [20/0] via 10.0.0.2, 01:20:51
B        10.3.3.3/32 [20/0] via 10.0.0.2, 01:29:03
      192.168.1.0/32 is subnetted, 1 subnets
B        192.168.1.1 [20/0] via 192.168.0.2 (VRF-A), 01:27:56
</code></pre><p>At the bottom, we see the route for <code>192.168.1.1/32</code> exported from <code>VRF-A</code>.</p>
<p>When we look at the route table for VRF-A we see the route for <code>10.1.1.1/32</code>.</p>
<pre><code>router02#show ip route vrf VRF-A

Routing Table: VRF-A

Gateway of last resort is not set

      10.0.0.0/32 is subnetted, 1 subnets
B        10.1.1.1 [20/0] via 10.0.0.2, 01:26:28
      192.168.0.0/24 is variably subnetted, 2 subnets, 2 masks
C        192.168.0.0/30 is directly connected, GigabitEthernet0/0
L        192.168.0.1/32 is directly connected, GigabitEthernet0/0
      192.168.1.0/32 is subnetted, 1 subnets
B        192.168.1.1 [20/0] via 192.168.0.2, 01:30:09
</code></pre><p>The route for <code>10.1.1.1/32</code> isn&rsquo;t tagged with a RD or the name of VRF since it didn&rsquo;t come from a particular VRF but the Global Routing Table. Once we have the routes properly injected into each route table we should now see them in the route tables of router01 and router03.</p>
<pre><code>router03#show ip route

Gateway of last resort is not set

      10.0.0.0/32 is subnetted, 1 subnets
B        10.1.1.1 [20/0] via 192.168.0.1, 01:41:19
      192.168.0.0/24 is variably subnetted, 2 subnets, 2 masks
C        192.168.0.0/30 is directly connected, GigabitEthernet0/0
L        192.168.0.2/32 is directly connected, GigabitEthernet0/0
      192.168.1.0/32 is subnetted, 1 subnets
C        192.168.1.1 is directly connected, Loopback1
</code></pre><pre><code>router01#show ip route

Gateway of last resort is not set

      10.0.0.0/8 is variably subnetted, 4 subnets, 2 masks
C        10.0.0.0/30 is directly connected, GigabitEthernet0/0
L        10.0.0.2/32 is directly connected, GigabitEthernet0/0
C        10.1.1.1/32 is directly connected, Loopback1
C        10.3.3.3/32 is directly connected, Loopback2
      192.168.1.0/32 is subnetted, 1 subnets
B        192.168.1.1 [20/0] via 10.0.0.1, 01:45:33
</code></pre><p>And finally, we should now be able to ping the loopback on <code>router03</code> while sourcing from the loopback on <code>router01</code>.</p>
<pre><code>router01#ping 192.168.1.1 source 10.1.1.1
Type escape sequence to abort.
Sending 5, 100-byte ICMP Echos to 192.168.1.1, timeout is 2 seconds:
Packet sent with a source address of 10.1.1.1 
!!!!!
Success rate is 100 percent (5/5), round-trip min/avg/max = 5/7/10 ms
</code></pre><h2 id="summary">Summary</h2>
<p>Overall this is a pretty quick and straightforward way to import/export routes from a VRF to the Global route table. It should be noted that this will only work if you are utilizing some sort of dynamic routing protocol. If you would like to use static/connected routes, some different approaches need to be taken. I have included some links to resources below.</p>
<h2 id="resources">Resources</h2>
<p>Pretty comprehensive blog on route leaking between VRF and GRT using several different methods.<br>
<a href="https://ipwithease.com/route-leaking-between-vrf-and-global-routing-table">https://ipwithease.com/route-leaking-between-vrf-and-global-routing-table/</a></p>
<p>Cisco configuration guide, the one that got it over the line for me.<br>
<a href="https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/iproute_bgp/configuration/15-mt/irg-15-mt-book/irg-prefix-export.html">https://www.cisco.com/c/en/us/td/docs/ios-xml/ios/iproute_bgp/configuration/15-mt/irg-15-mt-book/irg-prefix-export.html</a></p>
]]></content>
        </item>
        
    </channel>
</rss>
