{"id":775,"date":"2026-01-14T20:05:04","date_gmt":"2026-01-14T20:05:04","guid":{"rendered":"http:\/\/blog.miguelsarmiento.com\/?p=775"},"modified":"2026-01-15T19:41:29","modified_gmt":"2026-01-15T19:41:29","slug":"srv6-l3vpn-no-mpls-oh-my","status":"publish","type":"post","link":"https:\/\/blog.miguelsarmiento.com\/?p=775","title":{"rendered":"SRV6, L3VPN, No MPLS, Oh My!"},"content":{"rendered":"<p>Hello there.<\/p>\n<p>For something completely different now.<\/p>\n<p>I have read about SRV6 and the benefits it brings. Never used it in production though.<\/p>\n<p>Thus I decided give it a try, created a lab and configured it to use SRV6.<\/p>\n<p>So let&#8217;s go.<!--more--><\/p>\n<h2>Motivation<\/h2>\n<p>SRV6 (Segment Routing over IPV6) allows you to use\u00a0the native IPv6 data plane to carry instructions (segments) for packet forwarding, essentially encoding network paths and functions directly in the packet header.<\/p>\n<p>It is fairly efficient, you use segments as part of network functions, utilizes extensions already built in the IPv6 headers, allows for source routing and because the transit routers just forward IPV6 packets, it simplifies the setup of your core network.<\/p>\n<p>Due to this you do not need for example MPLS labels and it supports L3VPNs, L2VPNs and other technologies, thus it has &#8220;Extensibility&#8221;.<\/p>\n<h2>SETUP<\/h2>\n<p>Figure 1. shows the network setup we will use.<\/p>\n<figure id=\"attachment_774\" aria-describedby=\"caption-attachment-774\" style=\"width: 902px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/blog.miguelsarmiento.com\/wp-content\/uploads\/2026\/01\/srv6-lab.png\" target=\"_blank\" rel=\"noopener noreferrer\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-774 size-full\" src=\"https:\/\/blog.miguelsarmiento.com\/wp-content\/uploads\/2026\/01\/srv6-lab.png\" alt=\"\" width=\"902\" height=\"678\" srcset=\"https:\/\/blog.miguelsarmiento.com\/wp-content\/uploads\/2026\/01\/srv6-lab.png 902w, https:\/\/blog.miguelsarmiento.com\/wp-content\/uploads\/2026\/01\/srv6-lab-300x225.png 300w, https:\/\/blog.miguelsarmiento.com\/wp-content\/uploads\/2026\/01\/srv6-lab-768x577.png 768w\" sizes=\"auto, (max-width: 767px) 89vw, (max-width: 1000px) 54vw, (max-width: 1071px) 543px, 580px\" \/><\/a><figcaption id=\"caption-attachment-774\" class=\"wp-caption-text\">Fig1. SRV6 network diagram<\/figcaption><\/figure>\n<p>A few remarks.<\/p>\n<ul>\n<li>CE1 and CE2 are the customer equipment devices. We just use IPv4 and a default route to connect to the core. Of course we could have also configured eBGP for this. I wanted to keep it simple.<\/li>\n<li>The core consists of Nexus 9000v switches running version 9.3(5)<\/li>\n<li>Enable srv6 and IS-IS.<\/li>\n<li>On the 9000v you need to actually assign an IPv6 address for IPv6 to function.<\/li>\n<li>NXOS is a transit router. We are also using ULA IPv6 addresses for the data plane. Of course you could use global addresses but remember global addresses are routable on the Internet. Thus in a production environment you need to have your firewall prevent access (you use an IPv6 firewall in your network don&#8217;t you!).<\/li>\n<li>We implement IS-IS as the underlay.<\/li>\n<li>We configure a VRF at the PE edges to communicate with the CE devices.<\/li>\n<li>Notice, the clients are not running IPv6 just IPv4. Our data plane is IPv6 only but we are routing IPv4 on top of it.<\/li>\n<\/ul>\n<h2>Configurations<\/h2>\n<h3>CE1 and CE2<\/h3>\n<pre>ce1#sh ip int bri\r\nInterface IP-Address OK? Method Status Protocol\r\nFastEthernet0\/0 unassigned YES unset administratively down down \r\nEthernet1\/0 unassigned YES unset up up \r\nEthernet1\/1 192.168.1.2 YES manual up up \r\nEthernet1\/2 unassigned YES unset up up \r\nEthernet1\/3 unassigned YES manual up up \r\n\r\nip route 0.0.0.0 0.0.0.0 192.168.1.1\r\n\r\nce2#sh ip int bri\r\nInterface IP-Address OK? Method Status Protocol\r\nFastEthernet0\/0 unassigned YES unset administratively down down \r\nEthernet1\/0 unassigned YES unset up up \r\nEthernet1\/1 192.168.2.2 YES manual up up \r\nEthernet1\/2 unassigned YES unset up up \r\nEthernet1\/3 unassigned YES unset up \r\n\r\nip route 0.0.0.0 0.0.0.0 192.168.2.1<\/pre>\n<h3>NXOS<\/h3>\n<p>First the IS-IS router instance.<\/p>\n<pre>router isis 1\r\n net 49.0001.0000.0000.0002.00\r\n is-type level-2\r\n metric-style transition\r\n address-family ipv6 unicast\r\n segment-routing srv6\r\n locator mylocator\r\n\r\ninterface Ethernet1\/2\r\n ipv6 router isis 1\r\n\r\ninterface Ethernet1\/3\r\n ipv6 router isis 1<\/pre>\n<p>Notice that I have defined the locator for SRV6 but I do not have such locator configured on this device. It is not needed.<\/p>\n<p>The interfaces.<\/p>\n<pre>nxos-9k# sh ipv6 int bri\r\nIPv6 Interface Status for VRF \"default\"(1)\r\nInterface IPv6 Address\/Link-local Address Interface Status \r\n                                          prot\/link\/admin\r\nEth1\/2    fc00::22                        up\/up\/up\r\n          fe80::200:ff:fe00:2002 \r\nEth1\/3    fc00::33                        up\/up\/up\r\n          fe80::200:ff:fe00:3003<\/pre>\n<pre>interface Ethernet1\/2\r\n no switchport\r\n mac-address 0000.0000.2002\r\n ipv6 address fc00::22\/128\r\n ipv6 router isis 1\r\n no shutdown\r\n\r\ninterface Ethernet1\/3\r\n no switchport\r\n mac-address 0000.0000.3003\r\n ipv6 address fc00::33\/128\r\n ipv6 router isis 1\r\n no shutdown<\/pre>\n<p>One more thing, notice that I am giving each interface a custom MAC address. In my lab (EVE-NG) the interfaces are created with the same mac addresses, this cause issues thus the new assignment of mac addresses.<\/p>\n<h3>PE1 and PE2<\/h3>\n<p>Below are relevant parts for each device configuration.<\/p>\n<h4>PE1<\/h4>\n<pre>segment-routing\r\n srv6\r\n locators\r\n locator mylocator\r\n prefix fc00:0:0:1::\/64\r\n encapsulation\r\n source-address fc00::1\r\n\r\nroute-map EVERYTHING permit 10\r\nvrf context management\r\nvrf context one\r\n rd 1:1\r\n address-family ipv4 unicast\r\n route-target import 1:1\r\n route-target export 1:1\r\n-----\r\ninterface Ethernet1\/1\r\n no switchport\r\n mac-address 0000.0000.aaaa\r\n vrf member one\r\n ip address 192.168.1.1\/24\r\n no shutdown\r\n\r\ninterface Ethernet1\/2\r\n no switchport\r\n mac-address 0000.0000.1001\r\n ipv6 address fc00::11\/128\r\n ipv6 router isis 1\r\n no shutdown\r\n-----\r\nrouter isis 1\r\n net 49.0001.0000.0000.0001.00\r\n metric-style transition\r\n address-family ipv6 unicast\r\n segment-routing srv6\r\n locator mylocator\r\nrouter bgp 64512\r\n router-id 10.0.0.1\r\n segment-routing srv6\r\n locator mylocator\r\n alloc mode per-vrf\r\n address-family ipv4 unicast\r\n redistribute direct route-map EVERYTHING\r\n address-family vpnv4 unicast\r\n neighbor fc00::3\r\n remote-as 64512\r\n update-source loopback0\r\n address-family ipv4 unicast\r\n address-family vpnv4 unicast\r\n send-community\r\n send-community extended\r\n vrf one\r\n address-family ipv4 unicast\r\n redistribute direct route-map EVERYTHING\r\n segment-routing srv6\r\n alloc mode per-vrf\r\n address-family ipv6 unicast\r\n redistribute direct route-map EVERYTHING\r\n<\/pre>\n<h4>PE2<\/h4>\n<pre>segment-routing\r\n srv6\r\n locators\r\n locator mylocator\r\n prefix fc00:0:0:3::\/64\r\n encapsulation\r\n source-address fc00::3\r\n\r\nroute-map EVERYTHING permit 10\r\nvrf context management\r\nvrf context one\r\n rd 1:1\r\n address-family ipv4 unicast\r\n route-target import 1:1\r\n route-target export 1:1\r\n\r\ninterface Ethernet1\/1\r\n no switchport\r\n mac-address 0000.0000.bbbb\r\n vrf member one\r\n ip address 192.168.2.1\/24\r\n no shutdown\r\n\r\ninterface Ethernet1\/3\r\n no switchport\r\n mac-address 0000.0000.1002\r\n ipv6 address fc00::13\/128\r\n ipv6 router isis 1\r\n no shutdown\r\n\r\ninterface loopback0\r\n ipv6 address fc00::3\/128\r\n ipv6 router isis 1\r\n\r\nrouter isis 1\r\n net 49.0001.0000.0000.0003.00\r\n metric-style transition\r\n address-family ipv6 unicast\r\n segment-routing srv6\r\n locator mylocator\r\nrouter bgp 64512\r\n router-id 10.0.0.3\r\n segment-routing srv6\r\n locator mylocator\r\n address-family ipv4 unicast\r\n redistribute direct route-map EVERYTHING\r\n address-family vpnv4 unicast\r\n neighbor fc00::1\r\n remote-as 64512\r\n update-source loopback0\r\n address-family ipv4 unicast\r\n address-family vpnv4 unicast\r\n send-community\r\n send-community extended\r\n vrf one\r\n address-family ipv4 unicast\r\n redistribute direct route-map EVERYTHING\r\n segment-routing srv6\r\n alloc mode per-vrf\r\n address-family ipv6 unicast\r\n redistribute direct route-map EVERYTHING<\/pre>\n<p>Of course the values I did use for AS, ULA and IS-IS routing will need to change for your particular case.<\/p>\n<p>Notice the definition of the locator (on a 9000v you can only have one locator).<\/p>\n<p>We use encapsulation, the loopback interface. We also define the VRF we will use to the CE devices.<\/p>\n<p>Finally we use a route map to redistribute connected routes. the 9000v does not have a &#8220;redistribute connected&#8221; command anymore why I do not know.<\/p>\n<h2>Verification and Testing<\/h2>\n<p>If everything goes well you should be able to complete some verification steps and tests.<\/p>\n<p>First you should be able to ping between PE1 and the spine.<\/p>\n<pre>pe1# ping6 fc00::22 source-interface ethernet 1\/2 \r\nPING6 fc00::22 (fc00::22): 56 data bytes\r\n64 bytes from fc00::22: icmp_seq=0 time=7.433 ms\r\n64 bytes from fc00::22: icmp_seq=1 time=4.94 ms\r\n64 bytes from fc00::22: icmp_seq=2 time=5.453 ms\r\n64 bytes from fc00::22: icmp_seq=3 time=7.608 ms\r\n64 bytes from fc00::22: icmp_seq=4 time=4.382 ms<\/pre>\n<p>Pings to the spine from the PEs should work. If not you need to fix IPv6.<\/p>\n<p>Then check that IS-IS has formed adjacencies, if not fix the issue, SRV6 will not work.<\/p>\n<pre>\u00a0pe1# sh isis 1 topology \r\nIS-IS process: 1\r\nVRF: default\r\nTopology ID: 0\r\n\r\nIS-IS Level-1 IS routing table\r\n\r\nIS-IS Level-2 IS routing table\r\nnxos-9k.00, Instance 0x00000014\r\n *via nxos-9k, Ethernet1\/2, metric 40\r\nnxos-9k.02, Instance 0x00000014\r\n *via nxos-9k, Ethernet1\/2, metric 80\r\npe2.00, Instance 0x00000014\r\n *via nxos-9k, Ethernet1\/2, metric 80<\/pre>\n<p>You should see something similar on the other 9000v switches.<\/p>\n<pre>pe1# sh ipv6 route isis-1\u00a0\r\nIPv6 Routing Table for VRF \"default\"\r\n'*' denotes best ucast next-hop\r\n'**' denotes best mcast next-hop\r\n'[x\/y]' denotes [preference\/metric]\r\n\r\nfc00::3\/128, ubest\/mbest: 1\/0\r\n *via fe80::200:ff:fe00:2002, Eth1\/2, [115\/81], 19:42:53, isis-1, L2\r\nfc00::13\/128, ubest\/mbest: 1\/0\r\n *via fe80::200:ff:fe00:2002, Eth1\/2, [115\/120], 19:42:53, isis-1, L2\r\nfc00::22\/128, ubest\/mbest: 1\/0\r\n *via fe80::200:ff:fe00:2002, Eth1\/2, [115\/80], 19:42:54, isis-1, L2\r\nfc00::33\/128, ubest\/mbest: 1\/0\r\n *via fe80::200:ff:fe00:2002, Eth1\/2, [115\/80], 19:42:54, isis-1, L2\r\nfc00:0:0:3::\/64, ubest\/mbest: 1\/0\r\n *via fe80::200:ff:fe00:2002, Eth1\/2, [115\/80], 16:05:06, isis-1, L2<\/pre>\n<p>On PE2 you should see something similar. Finally show the locator information.<\/p>\n<pre>pe1# show srv6 locator detail \r\nName ID Prefix Status \r\n-------------------- ------- ------------------------ ------------\r\nmylocator 1 fc00:0:0:1::\/64 Up \r\nNumber of SID: 4\r\nCreate time: 01-14 00:07:48.960784\r\nModify time: 01-14 03:17:45.982995, reason: Locator up<\/pre>\n<p>It should be up. I forgot the encapsulation command and could not understand why things were not working!<\/p>\n<p>Now BGP should be working.<\/p>\n<pre>pe1# sh bgp all summary \r\nBGP summary information for VRF default, address family IPv4 Unicast\r\nBGP router identifier 10.0.0.1, local AS number 64512\r\nBGP table version is 3, IPv4 Unicast config peers 1, capable peers 1\r\n0 network entries and 0 paths using 0 bytes of memory\r\nBGP attribute entries [0\/0], BGP AS path entries [0\/0]\r\nBGP community entries [0\/0], BGP clusterlist entries [0\/0]\r\n\r\nNeighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up\/Down State\/PfxRcd\r\nfc00::3 4 64512 1189 1186 3 0 0 19:31:06 0 \r\n\r\nBGP summary information for VRF default, address family IPv6 Unicast\r\n\r\nBGP summary information for VRF default, address family VPNv4 Unicast\r\nBGP router identifier 10.0.0.1, local AS number 64512\r\nBGP table version is 9, VPNv4 Unicast config peers 1, capable peers 1\r\n2 network entries and 2 paths using 488 bytes of memory\r\nBGP attribute entries [1\/172], BGP AS path entries [0\/0]\r\nBGP community entries [0\/0], BGP clusterlist entries [0\/0]\r\n\r\nNeighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up\/Down State\/PfxRcd\r\nfc00::3 4 64512 1189 1186 9 0 0 19:31:06 1 \r\n\r\nBGP summary information for VRF default, address family VPNv6 Unicast<\/pre>\n<p>You see two entries because you have vpnv4 and ipv4 unicast entries.<\/p>\n<p>Now for the pay-off:<\/p>\n<pre>pe1# sh ip route vrf one\r\nIP Route Table for VRF \"one\"\r\n'*' denotes best ucast next-hop\r\n'**' denotes best mcast next-hop\r\n'[x\/y]' denotes [preference\/metric]\r\n'%&lt;string&gt;' in via output denotes VRF &lt;string&gt;\r\n\r\n192.168.1.0\/24, ubest\/mbest: 1\/0, attached\r\n *via 192.168.1.1, Eth1\/1, [0\/0], 20:05:28, direct\r\n192.168.1.1\/32, ubest\/mbest: 1\/0, attached\r\n *via 192.168.1.1, Eth1\/1, [0\/0], 20:05:28, local\r\n<strong>192.168.2.0\/24, ubest\/mbest: 1\/0<\/strong>\r\n<strong> *via fe80::200:ff:fe00:2002%default, Eth1\/2, [200\/0], 16:14:39, bgp-64512, internal, tag 64512<\/strong><\/pre>\n<p>As you can see we see the network from CE2 received via Eth1\/2. You will see the same on PE2 and the CE1 IPv4 network will be visible.<\/p>\n<p>Finally from either CE we should be able to ping the other side.<\/p>\n<pre>ce2#ping 192.168.1.2\r\nType escape sequence to abort.\r\nSending 5, 100-byte ICMP Echos to 192.168.1.2, timeout is 2 seconds:\r\n!!!!!\r\n\r\nce1#ping 192.168.2.2\r\nType escape sequence to abort.\r\nSending 5, 100-byte ICMP Echos to 192.168.2.2, timeout is 2 seconds:\r\n!!!!!<\/pre>\n<p>Bingo! We are done.<\/p>\n<h2>Final Remarks<\/h2>\n<p>Of course this is a very simple setup. While you could load the complete configurations at once, I would recommend the steady approach.<\/p>\n<p>Configure IPv6, then IS-IS, go onto the locator and VRFs and finally BGP. To me at least it gave me a workflow on how to configure it from scratch.<\/p>\n<ul>\n<li>In a data center or production environment you will need more spines routers (redundancy).<\/li>\n<li>You will also have several PEs and CEs. You will then use eBGP to exchange routes between them.<\/li>\n<li>You can use OSPF or BGP for the underlay. I did use IS-IS because it is very simple to implement.<\/li>\n<li>In a data center environment a Route Reflector or reflectors should also be used. Cisco recommends this since it will allow scalability as your PEs will peer with the reflector(s).<\/li>\n<\/ul>\n<p>You can use GUAs addresses as stated. However, ULAs gives you flexibility, do not need to waste addresses (although with the vast amount of IPv6 addresses that is not a problem).<\/p>\n<p>Using SRV6 eliminates the use of MPLS labels, LDP or RSVP-TE. This simplifies your control plane and because the scalability of the IPv6 address space it gives you native support for advanced capabilities like network slicing, traffic engineering, end-to-end encryption and more.<\/p>\n<p>There you have it.<\/p>\n<p>Hope you enjoy this lab.<\/p>\n<p>Ciao.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hello there. For something completely different now. I have read about SRV6 and the benefits it brings. Never used it in production though. Thus I decided give it a try, created a lab and configured it to use SRV6. So let&#8217;s go.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-775","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/blog.miguelsarmiento.com\/index.php?rest_route=\/wp\/v2\/posts\/775","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.miguelsarmiento.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.miguelsarmiento.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.miguelsarmiento.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.miguelsarmiento.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=775"}],"version-history":[{"count":36,"href":"https:\/\/blog.miguelsarmiento.com\/index.php?rest_route=\/wp\/v2\/posts\/775\/revisions"}],"predecessor-version":[{"id":811,"href":"https:\/\/blog.miguelsarmiento.com\/index.php?rest_route=\/wp\/v2\/posts\/775\/revisions\/811"}],"wp:attachment":[{"href":"https:\/\/blog.miguelsarmiento.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=775"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.miguelsarmiento.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=775"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.miguelsarmiento.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=775"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}