Today I wanted to play around with VXLAN as I've never touched it and I had nothing better to do.

This has been tested with the latest VyOS nightly 1.5-rolling-202403050022. First, I read the related VyOS blog post and the FRR docs. I really prefer learning while doing things instead of just reading the theory. Even though it means I will likely mess something up, that's precisely what the Lab is there for.

EVPN - BGP

First I need to enable address-familty l2vpn-evpn on my route reflectors. I have four of them, I will only be touching one for now (They all have the same copy of same routes).

I will change the L2TPv3 PW I have internally between OSR1CR1 to OSR1CR3 (I use it for something equivalent to a EVPL, internal backhaul of my WAN from one server to another), to EVPN-VXLAN. These two routers are adjacent to each other, connected via 2x2.5GbE. My Lab has IS-IS as an IGP, MPLS-enabled via SR.

Diagram

Example config at OSR1RR1 (Route Reflector) for iBGP RR-client peering to OSR1CR3 (Core):

 1fabrizzio@OSR1RR1# show protocols bgp neighbor 192.168.254.12
 2 address-family {
 3     ipv4-unicast {
 4         addpath-tx-all
 5         route-map {
 6             import RTR_OSR1
 7         }
 8         route-reflector-client
 9     }
10     ipv6-unicast {
11         addpath-tx-all
12         route-map {
13             import RTR_OSR1
14         }
15         route-reflector-client
16     }
17 }
18 bfd {
19     profile IBGP_BFD
20 }
21 description OSR1CR3
22 remote-as internal
23 timers {
24     connect 1
25 }
26 update-source dum0

I just need a couple of commands on the RR:

 1fabrizzio@OSR1RR1# set protocols bgp neighbor 192.168.254.12 address-family l2vpn-evpn route-reflector-client 
 2fabrizzio@OSR1RR1# set protocols bgp neighbor 192.168.254.14 address-family l2vpn-evpn route-reflector-client 
 3
 4
 5L2VPN EVPN Summary (VRF default):
 6BGP router identifier 192.168.254.50, local AS number 4200000001 vrf-id 0
 7BGP table version 0
 8RIB entries 0, using 0 bytes of memory
 9Peers 2, using 40 KiB of memory
10
11Neighbor        V         AS   MsgRcvd   MsgSent   TblVer  InQ OutQ  Up/Down State/PfxRcd   PfxSnt Desc
12192.168.254.12  4 4200000001        39       265        0    0    0 00:00:26        NoNeg    NoNeg OSR1CR3
13192.168.254.14  4 4200000001        50       267        0    0    0 00:00:10        NoNeg    NoNeg OSR1CR5

then on the core routers:

 1neighbor 192.168.254.50 {
 2     address-family {
 3         ipv4-unicast {
 4             addpath-tx-all
 5             nexthop-self {
 6             }
 7             route-map {
 8                 import prevent_ibgp_blackholing
 9             }
10             soft-reconfiguration {
11                 inbound
12             }
13         }
14         ipv6-unicast {
15             addpath-tx-all
16             nexthop-self {
17             }
18             route-map {
19                 export ibgp_ula_nh
20                 import prevent_ibgp_blackholing
21             }
22             soft-reconfiguration {
23                 inbound
24             }
25         }
26     }
27     bfd {
28         profile IBGP_BFD
29     }
30     description "To OSR1RR1"
31     remote-as internal
32     update-source dum0
33
34
35fabrizzio@OSR1CR3# set protocols bgp neighbor 192.168.254.50 address-family l2vpn-evpn nexthop-self
36
37fabrizzio@OSR1CR3# set protocols bgp address-family l2vpn-evpn advertise-all-vni

This should be enough BGP for today :) I did the same on OSR1CR5.

Moving from L2TPv3 tunnel to VXLAN

Switching over is easy. First I deploy VXLAN interface on one of the core routers

1fabrizzio@OSR1CR3# set interfaces vxlan vxlan700 parameters nolearning 
2fabrizzio@OSR1CR3# set interfaces vxlan vxlan700 port 4789
3fabrizzio@OSR1CR3# set interfaces vxlan vxlan700 source-address 192.168.254.12
4fabrizzio@OSR1CR3# set interfaces vxlan vxlan700 vni 700

Then just switch over one bridge member from L2TPv3 to VXLAN

 1fabrizzio@OSR1CR3# show interfaces bridge br5
 2 description "WAN OSR1BR2 - OSR1CR3 - OSR1CR5 VLAN 700 BR2"
 3 enable-vlan
 4 ipv6 {
 5     address {
 6         no-default-link-local
 7     }
 8 }
 9 member {
10     interface eth17 {
11         allowed-vlan 100
12         native-vlan 100
13     }
14     interface l2tpeth5 {
15         allowed-vlan 100
16         native-vlan 100
17     }
18 }
19
20fabrizzio@OSR1CR3# delete interfaces bridge br5 member interface l2tpeth5
21fabrizzio@OSR1CR3# set interfaces bridge br5 member interface vxlan700 allowed-vlan 100
22fabrizzio@OSR1CR3# set interfaces bridge br5 member interface vxlan700 native-vlan 100

MACs are already being sent via BGP-EVPN at this point

 1fabrizzio@OSR1CR3:~$ show bgp l2vpn evpn 
 2BGP table version is 2, local router ID is 192.168.254.12
 3Status codes: s suppressed, d damped, h history, * valid, > best, i - internal
 4Origin codes: i - IGP, e - EGP, ? - incomplete
 5EVPN type-1 prefix: [1]:[EthTag]:[ESI]:[IPlen]:[VTEP-IP]:[Frag-id]
 6EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]
 7EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]
 8EVPN type-4 prefix: [4]:[ESI]:[IPlen]:[OrigIP]
 9EVPN type-5 prefix: [5]:[EthTag]:[IPlen]:[IP]
10
11   Network          Next Hop            Metric LocPrf Weight Path
12Route Distinguisher: 192.168.254.12:2
13 *> [2]:[0]:[48]:[ce:a7:7a:xx:xx:xx]
14                    192.168.254.12                     32768 i
15                    ET:8 RT:59905:700
16 *> [3]:[0]:[32]:[192.168.254.12]
17                    192.168.254.12                     32768 i
18                    ET:8 RT:59905:700

After switching over from L2TPv3 to VXLAN on OSR1CR5 there are MACs learnt and sent over BGP at both ends:

 1fabrizzio@OSR1CR5:~$ show bgp l2vpn evpn 
 2BGP table version is 2, local router ID is 192.168.254.14
 3Status codes: s suppressed, d damped, h history, * valid, > best, i - internal
 4Origin codes: i - IGP, e - EGP, ? - incomplete
 5EVPN type-1 prefix: [1]:[EthTag]:[ESI]:[IPlen]:[VTEP-IP]:[Frag-id]
 6EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]
 7EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]
 8EVPN type-4 prefix: [4]:[ESI]:[IPlen]:[OrigIP]
 9EVPN type-5 prefix: [5]:[EthTag]:[IPlen]:[IP]
10
11   Network          Next Hop            Metric LocPrf Weight Path
12Route Distinguisher: 192.168.254.12:2
13 *>i[2]:[0]:[48]:[ce:a7:7a:xx:xx:xx]
14                    192.168.254.12           0    100      0 i
15                    RT:59905:700 ET:8
16 *>i[3]:[0]:[32]:[192.168.254.12]
17                    192.168.254.12           0    100      0 i
18                    RT:59905:700 ET:8
19Route Distinguisher: 192.168.254.14:2
20 *> [2]:[0]:[48]:[6c:eb:b6:xx:xx:xx]
21                    192.168.254.14                     32768 i
22                    ET:8 RT:59905:700
23 *> [3]:[0]:[32]:[192.168.254.14]
24                    192.168.254.14                     32768 i
25                    ET:8 RT:59905:700

I also made sure that it's not reordering traffic within a flow. OSR1CR3 to OSR1CR5 are adjacent to each other, connected by 2x2.5GbE links, I ran a 16-thread IPerf3 test over the VXLAN-L2 connection and there was no reordering within the same flow. Furthermore the ECMP paths OSR1CR3 <> OSR1CR5 were both utilized!!

Because I am running IS-IS + MPLS (SR) internally, I wanted to make sure that there wouldn't be any problems with VXLAN if the IPv4 next-hop would have an MPLS label. The test OSR1CR3 <> OSR1CR5 doesn't really have any labels (adjacent to each other w/PHP, so implicit null)

1fabrizzio@OSR1CR3:~$ sh ip route 192.168.254.14
2Routing entry for 192.168.254.14/32
3  Known via "isis", distance 115, metric 1010, best
4  Last update 04:03:32 ago
5  * 172.27.16.18, via eth2, label implicit-null, weight 1
6  * 172.27.16.22, via eth3, label implicit-null, weight 1

I also haven't bothered trying this out in an actual multi-point fashion so far.

So the best way to try this out would be testing this again, this time creating a bridge interface at OSR2CR2 (at OSR2) connected to a VLAN there. Then bringing it to OSR1CR6 to a bridge there. This is not totally useless as I can bring one of my LANs from OSR2 to OSR1 in case I need to troubleshoot anything. OSR1CR6 and OSR2CR2 are not adjacent to each other so there will be MPLS labels involved here.

1fabrizzio@OSR1CR6:~$ sh ip route 192.168.254.17
2Routing entry for 192.168.254.17/32
3  Known via "isis", distance 115, metric 46110, best
4  Last update 04:45:11 ago
5  * 172.27.16.45, via eth0, label 16170, weight 1

Performance issues

After doing the configs I ran into exactly the same performance issue with TX drops on the core facing interface at the edge routers and large TCP segments being dropped :( Same thing I saw on my previous post.

 1fabrizzio@OSR1CR6:~$ sh interfaces  ethernet eth0
 2eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1800 qdisc mq state UP group default qlen 1000
 3    link/ether 52:d6:dc:f3:9c:28 brd ff:ff:ff:ff:ff:ff
 4    altname enp0s18
 5    altname ens18
 6    inet 172.27.16.46/30 brd 172.27.16.47 scope global eth0
 7       valid_lft forever preferred_lft forever
 8    inet6 2a0e:8f02:21d1:feed:0:1:12:12/126 scope global 
 9       valid_lft forever preferred_lft forever
10    inet6 fe80::50d6:dcff:fef3:9c28/64 scope link 
11       valid_lft forever preferred_lft forever
12    Description: To OSR1CR5
13
14    RX:      bytes  packets  errors  dropped  overrun       mcast
15         126316674   369467       0        8        0           0
16    TX:      bytes  packets  errors  dropped  carrier  collisions
17         140990639   833966       0     4944        0           0 <<<<
18         
19         fabrizzio@osr1test3:~$ iperf3 -c 192.168.35.3 
20Connecting to host 192.168.35.3, port 5201
21[  5] local 192.168.35.109 port 48946 connected to 192.168.35.3 port 5201
22[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
23[  5]   0.00-1.00   sec   160 KBytes  1.31 Mbits/sec   32   4.24 KBytes       
24[  5]   1.00-2.00   sec  80.6 KBytes   660 Kbits/sec   18   4.24 KBytes       
25[  5]   2.00-3.00   sec   119 KBytes   973 Kbits/sec   18   7.07 KBytes       
26[  5]   3.00-4.00   sec  79.2 KBytes   649 Kbits/sec   24   2.83 KBytes       
27[  5]   4.00-5.00   sec  79.2 KBytes   649 Kbits/sec   18   5.66 KBytes       
28[  5]   5.00-6.00   sec  79.2 KBytes   649 Kbits/sec   18   5.66 KBytes       
29[  5]   6.00-7.00   sec   119 KBytes   973 Kbits/sec   30   2.83 KBytes       
30[  5]   7.00-8.00   sec  79.2 KBytes   649 Kbits/sec   20   1.41 KBytes       
31[  5]   8.00-9.00   sec  39.6 KBytes   324 Kbits/sec   14   4.24 KBytes       
32[  5]   9.00-10.00  sec   119 KBytes   974 Kbits/sec   24   2.83 KBytes       
33- - - - - - - - - - - - - - - - - - - - - - - - -
34[ ID] Interval           Transfer     Bitrate         Retr
35[  5]   0.00-10.00  sec   953 KBytes   781 Kbits/sec  216             sender
36[  5]   0.00-10.02  sec   872 KBytes   714 Kbits/sec                  receiver

Because I really really want EVPN+VXLAN to be working, I will just assign each router another loopback IP in addition to their current one. I will just not assign them any prefix SID under segment routing so I don't get any MPLS goodness on them.

1fabrizzio@OSR1CR6# set interfaces dummy dum4 address 192.168.254.117/32
2fabrizzio@OSR1CR6# set interfaces dummy dum4 description "For VXLAN - no MPLS/SR"
3fabrizzio@OSR1CR6# set protocols isis interface dum4 passive
4fabrizzio@OSR1CR6# set interfaces vxlan vxlan35 source-address 192.168.254.115

That didn't fix the issue

 1fabrizzio@osr1test3:~$ iperf3 -c 192.168.35.3 -P4 -R
 2Connecting to host 192.168.35.3, port 5201
 3Reverse mode, remote host 192.168.35.3 is sending
 4[  5] local 192.168.35.109 port 42122 connected to 192.168.35.3 port 5201
 5[  7] local 192.168.35.109 port 42130 connected to 192.168.35.3 port 5201
 6[ 13] local 192.168.35.109 port 42140 connected to 192.168.35.3 port 5201
 7[ 15] local 192.168.35.109 port 42152 connected to 192.168.35.3 port 5201
 8^C[ ID] Interval           Transfer     Bitrate
 9[  5]   0.00-0.83   sec  62.2 KBytes   616 Kbits/sec                  
10[  7]   0.00-0.83   sec  65.0 KBytes   644 Kbits/sec                  
11[ 13]   0.00-0.83   sec  42.4 KBytes   420 Kbits/sec                  
12[ 15]   0.00-0.83   sec  45.2 KBytes   448 Kbits/sec                  
13[SUM]   0.00-0.83   sec   215 KBytes  2.13 Mbits/sec                  
14- - - - - - - - - - - - - - - - - - - - - - - - -
15[ ID] Interval           Transfer     Bitrate
16[  5]   0.00-0.83   sec  0.00 Bytes  0.00 bits/sec                  sender
17[  5]   0.00-0.83   sec  62.2 KBytes   616 Kbits/sec                  receiver
18[  7]   0.00-0.83   sec  0.00 Bytes  0.00 bits/sec                  sender
19[  7]   0.00-0.83   sec  65.0 KBytes   644 Kbits/sec                  receiver
20[ 13]   0.00-0.83   sec  0.00 Bytes  0.00 bits/sec                  sender
21[ 13]   0.00-0.83   sec  42.4 KBytes   420 Kbits/sec                  receiver
22[ 15]   0.00-0.83   sec  0.00 Bytes  0.00 bits/sec                  sender
23[ 15]   0.00-0.83   sec  45.2 KBytes   448 Kbits/sec                  receiver
24[SUM]   0.00-0.83   sec  0.00 Bytes  0.00 bits/sec                  sender
25[SUM]   0.00-0.83   sec   215 KBytes  2.13 Mbits/sec                  receiver
26iperf3: interrupt - the client has terminated
27
28
29
30fabrizzio@OSR1CR6:~$ sh int ethernet eth0
31eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1800 qdisc mq state UP group default qlen 1000
32    link/ether 52:d6:dc:f3:9c:28 brd ff:ff:ff:ff:ff:ff
33    altname enp0s18
34    altname ens18
35    inet 172.27.16.46/30 brd 172.27.16.47 scope global eth0
36       valid_lft forever preferred_lft forever
37    inet6 2a0e:8f02:21d1:feed:0:1:12:12/126 scope global 
38       valid_lft forever preferred_lft forever
39    inet6 fe80::50d6:dcff:fef3:9c28/64 scope link 
40       valid_lft forever preferred_lft forever
41    Description: To OSR1CR5
42
43    RX:    bytes  packets  errors  dropped  overrun       mcast
44         3152902     5949       0       25        0           0
45    TX:    bytes  packets  errors  dropped  carrier  collisions
46         2976484     6099       0      284        0           0 <<<<

Just as the last time, disabling TSO on the tunnel interface (for this case VXLAN35 on both OSR1CR6 and OSR2CR2) fixes the problem:

 1fabrizzio@OSR1CR6:~$ ethtool -K vxlan35 tso off
 2
 3
 4fabrizzio@osr1test3:~$ iperf3 -c 192.168.35.3
 5Connecting to host 192.168.35.3, port 5201
 6[  5] local 192.168.35.109 port 47178 connected to 192.168.35.3 port 5201
 7[ ID] Interval           Transfer     Bitrate         Retr  Cwnd
 8[  5]   0.00-1.00   sec  33.3 MBytes   280 Mbits/sec  204    573 KBytes       
 9[  5]   1.00-2.00   sec  27.5 MBytes   231 Mbits/sec    0    615 KBytes       
10[  5]   2.00-3.00   sec  28.8 MBytes   241 Mbits/sec    0    642 KBytes       
11[  5]   3.00-4.00   sec  25.0 MBytes   210 Mbits/sec   21    662 KBytes       
12[  5]   4.00-5.00   sec  28.8 MBytes   241 Mbits/sec    8    509 KBytes       
13[  5]   5.00-6.00   sec  27.5 MBytes   231 Mbits/sec    0    576 KBytes       
14[  5]   6.00-7.00   sec  31.2 MBytes   262 Mbits/sec    0    624 KBytes       
15[  5]   7.00-8.00   sec  30.0 MBytes   252 Mbits/sec    0    655 KBytes       
16[  5]   8.00-9.00   sec  32.5 MBytes   273 Mbits/sec    0    672 KBytes       
17[  5]   9.00-10.00  sec  26.2 MBytes   220 Mbits/sec  130    495 KBytes       
18- - - - - - - - - - - - - - - - - - - - - - - - -
19[ ID] Interval           Transfer     Bitrate         Retr
20[  5]   0.00-10.00  sec   291 MBytes   244 Mbits/sec  363             sender
21[  5]   0.00-10.02  sec   288 MBytes   242 Mbits/sec                  receiver

Permanent fix

I asked ChatGPT to give me a bash script to disable TSO on all VXLAN interfaces:

1Hi dear bot.
2I want to run a bash script that will do 
3ethtool -K [interface_name] tso off
4for all interfaces starting with "vxlan".
5could you please let me know how to do it?. I promise I will tip you $200 :)

ChatGPT gave me:

 1#!/bin/bash
 2
 3# Get a list of all network interfaces
 4interfaces=$(ls /sys/class/net)
 5
 6# Loop through each interface
 7for interface in $interfaces; do
 8    # Check if the interface name starts with "vxlan"
 9    if [[ $interface == vxlan* ]]; then
10        # Disable TSO for the interface
11        sudo ethtool -K $interface tso off
12        echo "Disabled TSO for interface $interface"
13    fi
14done

ChatGPT interaction

Works wonders :)

Librespeed test done over stretched L2 via VXLAN

I rolled back the new loopbacks, the EVPN-VXLAN works just fine even though it is running over MPLS - no need to complicate my config with new loopbacks.

120:03:55.585321 MPLS (label 16170, tc 0, [S], ttl 16) IP 192.168.254.15.34338 > 192.168.254.17.4789: VXLAN, flags [I] (0x08), vni 35
2IP 192.168.35.109.38590 > 192.168.35.3.5201: Flags [.], seq 35796045:35797493, ack 1, win 502, options [nop,nop,TS val 203409691 ecr 118461559], length 1448
320:03:55.585322 MPLS (label 16170, tc 0, [S], ttl 16) IP 192.168.254.15.34338 > 192.168.254.17.4789: VXLAN, flags [I] (0x08), vni 35
4IP 192.168.35.109.38590 > 192.168.35.3.5201: Flags [.], seq 35797493:35798941, ack 1, win 502, options [nop,nop,TS val 203409691 ecr 118461559], length 1448
520:03:55.585322 MPLS (label 16170, tc 0, [S], ttl 16) IP 192.168.254.15.34338 > 192.168.254.17.4789: VXLAN, flags [I] (0x08), vni 35
6IP 192.168.35.109.38590 > 192.168.35.3.5201: Flags [.], seq 35798941:35800389, ack 1, win 502, options [nop,nop,TS val 203409691 ecr 118461559], length 1448
720:03:55.585343 MPLS (label 16170, tc 0, [S], ttl 16) IP 192.168.254.15.34338 > 192.168.254.17.4789: VXLAN, flags [I] (0x08), vni 35

I've then deployed this script to run on commit and on boot after config:

 1echo '# Get a list of all network interfaces
 2interfaces=$(ls /sys/class/net)
 3
 4# Loop through each interface
 5for interface in $interfaces; do
 6    # Check if the interface name starts with "vxlan"
 7    if [[ $interface == vxlan* ]]; then
 8        # Disable TSO for the interface
 9        sudo ethtool -K $interface tso off
10        echo "Disabled TSO for interface $interface"
11    fi
12done' >> /config/scripts/vyos-postconfig-bootup.script
13mkdir /config/scripts/commit 
14mkdir /config/scripts/commit/post-hooks.d
15cp /config/scripts/vyos-postconfig-bootup.script /config/scripts/commit/post-hooks.d/98-disable-tso-on-vxlan.script

All is good now :)