Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save mvandermeulen/0e710a9232a2933e932ee616ac1c63ce to your computer and use it in GitHub Desktop.

Select an option

Save mvandermeulen/0e710a9232a2933e932ee616ac1c63ce to your computer and use it in GitHub Desktop.
Script+json data format for generating frr evpn setup with single vxlan device and vlan aware bridge
#!/usr/bin/python3
#todo: turn this into a template to make it easy to consume with ansible
import subprocess
import argparse
import json
import socket
def cmd(command, check=True):
return subprocess.run(command, shell=True, text=True, check=check, stdout=subprocess.PIPE).stdout
def main(local_ip, config_file):
with open(config_file) as f:
parsed = json.loads(f.read())
interfaces = cmd(f"ip -j link")
parsed_ifs = json.loads(interfaces)
parsed_ifs_names = set([intif["ifname"] for intif in parsed_ifs if "ifname" in intif])
nodename = socket.gethostname().split('.')[0]
#delete old
cmd(f"ip link del dev {parsed['bridge']['name']}", check=False)
cmd(f"ip link del dev {parsed['bridge']['vxlan_name']}", check=False)
#svi single vxlan
cmd(f"ip link add dev {parsed['bridge']['name']} type bridge stp_state 0 vlan_filtering 1")
cmd(f"ip link set dev {parsed['bridge']['name']} addrgenmode none mtu 9000")
cmd(f"ip link add dev {parsed['bridge']['vxlan_name']} type vxlan dstport 4789 local {local_ip} nolearning external vnifilter")
cmd(f"ip link set dev {parsed['bridge']['vxlan_name']} master {parsed['bridge']['name']} addrgenmode none mtu 8950")
cmd(f"bridge link set dev {parsed['bridge']['vxlan_name']} vlan_tunnel on")
cmd(f"bridge link set dev {parsed['bridge']['vxlan_name']} neigh_suppress on")
cmd(f"bridge link set dev {parsed['bridge']['vxlan_name']} learning off")
cmd(f"bridge vlan del dev {parsed['bridge']['name']} vid 1 self")
cmd(f"bridge vlan del dev {parsed['bridge']['vxlan_name']} vid 1")
cmd(f"ip link set {parsed['bridge']['name']} up")
cmd(f"ip link set {parsed['bridge']['vxlan_name']} up")
for vrf in parsed['vrfs']:
#l3 svi
cmd(f"ip link del {vrf['svi']['name']}", check=False)
cmd(f"ip link del m{vrf['svi']['name']}", check=False)
cmd(f"bridge vlan del dev {parsed['bridge']['name']} vid {vrf['svi']['vlan']} self", check=False)
cmd(f"bridge vlan add dev {parsed['bridge']['name']} vid {vrf['svi']['vlan']} self")
node_hwaddress = vrf['svi']['nodes'][nodename]
cmd(f"ip link add {vrf['svi']['name']} link {parsed['bridge']['name']} address {node_hwaddress} type vlan id {vrf['svi']['vlan']}")
cmd(f"ip link set {vrf['svi']['name']} addrgenmode none master {vrf['name']} mtu 8950")
#create macvlan for shared anycast mac address?
cmd(f"ip link add m{vrf['svi']['name']} link {vrf['svi']['name']} address {vrf['svi']['hwaddr']} type macvlan mode private")
cmd(f"sysctl -w net.ipv4.conf.{vrf['svi']['name']}.arp_accept=2")
cmd(f"sysctl -w net.ipv6.conf.{vrf['svi']['name']}.accept_untracked_na=2")
cmd(f"bridge vni add dev {parsed['bridge']['vxlan_name']} vni {vrf['svi']['vni']}")
cmd(f"bridge vlan add dev {parsed['bridge']['vxlan_name']} vid {vrf['svi']['vlan']}")
cmd(f"bridge vlan add dev {parsed['bridge']['vxlan_name']} vid {vrf['svi']['vlan']} tunnel_info id {vrf['svi']['vni']}")
cmd(f"ip link set {vrf['svi']['name']} up")
cmd(f"ip link set m{vrf['svi']['name']} up")
#l2 vlan to vxlan mappings and bridge ports
for vlan in [vlan for vlan in vrf['vlans'] if (vlan['name'] in parsed_ifs_names) and (nodename in vlan['nodes'])]:
node_ips = vlan['nodes'][nodename]
cmd(f"bridge vlan del dev {parsed['bridge']['name']} vid {vlan['vlan']} self", check=False)
cmd(f"bridge vlan del dev {parsed['bridge']['vxlan_name']} vid {vlan['vlan']}", check=False)
cmd(f"bridge vlan add dev {parsed['bridge']['name']} vid {vlan['vlan']} self")
cmd(f"bridge vlan add dev {parsed['bridge']['vxlan_name']} vid {vlan['vlan']}")
cmd(f"ip link set {vlan['name']} master {parsed['bridge']['name']}")
cmd(f"ip link del vl{vlan['name']}", check=False)
cmd(f"ip link add vl{vlan['name']} link {parsed['bridge']['name']} address {vlan['hwaddr']} type vlan id {vlan['vlan']}")
cmd(f"ip link set vl{vlan['name']} master {vrf['name']} mtu 8950")
cmd(f"sysctl -w net.ipv4.conf.{vlan['name']}.arp_accept=2")
cmd(f"sysctl -w net.ipv6.conf.{vlan['name']}.accept_untracked_na=2")
cmd(f"ip addr replace {node_ips['ipv4']} dev vl{vlan['name']}")
cmd(f"ip addr replace {node_ips['ipv6']} dev vl{vlan['name']}")
cmd(f"ip addr replace {vlan['ipv4']} dev vl{vlan['name']}")
cmd(f"ip addr replace {vlan['ipv6']} dev vl{vlan['name']}")
cmd(f"bridge vlan del dev {vlan['name']} vid 1")
cmd(f"bridge vlan del dev {parsed['bridge']['vxlan_name']} vid {vlan['vlan']}")
cmd(f"bridge vlan del dev {parsed['bridge']['vxlan_name']} vid {vlan['vlan']} tunnel_info id {vlan['vni']}")
cmd(f"bridge vni add dev {parsed['bridge']['vxlan_name']} vni {vlan['vni']}")
cmd(f"bridge vlan add dev {parsed['bridge']['vxlan_name']} vid {vlan['vlan']}")
cmd(f"bridge vlan add dev {parsed['bridge']['vxlan_name']} vid {vlan['vlan']} tunnel_info id {vlan['vni']}")
cmd(f"bridge vlan del dev {vlan['name']} vid {vlan['vlan']} untagged pvid", check=False)
cmd(f"bridge vlan add dev {vlan['name']} vid {vlan['vlan']} untagged pvid")
cmd(f"ip link set vl{vlan['name']} up")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("local", help="The local ip address")
parser.add_argument("-c", "--config", help="The config file path (json format)", default="/etc/evpn.json")
args = parser.parse_args()
main(args.local, args.config)
{
"bridge": {
"name": "vxbr0",
"vxlan_name": "vx0"
},
"vrfs": [
{
"name": "vrf0",
"svi": {
"name": "vrf0l3",
"vni": 32000,
"vlan": 32,
"hwaddr": "2a:1b:c4:c0:b7:ff",
"nodes": {
"node0": "2a:1b:c4:ff:ad:f8",
"node1": "2a:1b:c4:f1:2b:cb",
"node2": "2a:1b:c4:c4:f3:01",
"node3": "2a:1b:c4:16:0d:66",
"node4": "2a:1b:c4:4f:3d:5c"
}
},
"vlans": [
{
"name": "vlan0",
"vni": 32010,
"vlan": 300,
"ipv4": "10.32.0.1/24",
"ipv6": "fddf:3bdb:9eee:2000::1/64",
"hwaddr": "2a:1b:c4:fb:b0:db",
"nodes": {
"node0": {
"ipv4": "10.32.0.3/24",
"ipv6": "fddf:3bdb:9eee:2000::3/64"
},
"node1": {
"ipv4": "10.32.0.4/24",
"ipv6": "fddf:3bdb:9eee:2000::4/64"
},
"node2": {
"ipv4": "10.32.0.5/24",
"ipv6": "fddf:3bdb:9eee:2000::5/64"
},
"node3": {
"ipv4": "10.32.0.6/24",
"ipv6": "fddf:3bdb:9eee:2000::6/64"
}
}
},
{
"name": "vlan1",
"vni": 32020,
"vlan": 301,
"ipv4": "10.32.10.1/24",
"ipv6": "fddf:3bdb:9eee:20a0::1/64",
"hwaddr": "2a:1b:c4:2d:e8:3f",
"nodes": {
"node0": {
"ipv4": "10.32.10.3/24",
"ipv6": "fddf:3bdb:9eee:20a0::3/64"
},
"node1": {
"ipv4": "10.32.10.4/24",
"ipv6": "fddf:3bdb:9eee:20a0::4/64"
},
"node2": {
"ipv4": "10.32.10.5/24",
"ipv6": "fddf:3bdb:9eee:20a0::5/64"
},
"node3": {
"ipv4": "10.32.10.6/24",
"ipv6": "fddf:3bdb:9eee:20a0::6/64"
}
}
},
{
"name": "vlan3",
"vni": 32030,
"vlan": 302,
"ipv4": "10.32.20.1/24",
"ipv6": "fddf:3bdb:9eee:2014::1/64",
"hwaddr": "2a:1b:c4:2d:e8:3e",
"nodes": {
"node4": {
"ipv4": "10.32.20.2/24",
"ipv6": "fddf:3bdb:9eee:2014::2/64"
}
}
}
]
},
{
"name": "vrf1",
"svi": {
"name": "vrf1l3",
"vni": 16000,
"vlan": 16,
"hwaddr": "2a:1b:c4:c0:b7:ff",
"nodes": {
"node0": "2a:1b:c4:d4:23:b4",
"node1": "2a:1b:c4:c2:92:51",
"node2": "2a:1b:c4:ba:2b:6d",
"node3": "2a:1b:c4:b1:e3:f8",
"node4": "2a:1b:c4:69:e3:7c"
}
},
"vlans": [
{
"name": "vlan10",
"vni": 16010,
"vlan": 400,
"ipv4": "10.16.0.1/24",
"ipv6": "fddf:3bdb:9eee:1000::1/64",
"hwaddr": "2a:1b:c4:2f:f4:79",
"nodes": {
"node0": {
"ipv4": "10.16.0.3/24",
"ipv6": "fddf:3bdb:9eee:1000::3/64"
},
"node1": {
"ipv4": "10.16.0.4/24",
"ipv6": "fddf:3bdb:9eee:1000::4/64"
},
"node2": {
"ipv4": "10.16.0.5/24",
"ipv6": "fddf:3bdb:9eee:1000::5/64"
},
"node3": {
"ipv4": "10.16.0.6/24",
"ipv6": "fddf:3bdb:9eee:1000::6/64"
}
}
},
{
"name": "vlan11",
"vni": 16020,
"vlan": 401,
"ipv4": "10.16.10.1/24",
"ipv6": "fddf:3bdb:9eee:10a0::1/64",
"hwaddr": "2a:1b:c4:4b:01:3e",
"nodes": {
"node4": {
"ipv4": "10.16.10.3/24",
"ipv6": "fddf:3bdb:9eee:10a0::3/64"
}
}
}
]
}
]
}
frr version 10.2.1
frr defaults traditional
hostname node0
log syslog informational
service integrated-vtysh-config
!
ip router-id 10.0.0.70
!
vrf vrf0
vni 32000
exit-vrf
!
vrf vrf1
vni 16000
exit-vrf
!
interface lo
ip address 10.0.0.70/32
ipv6 address fd04:bf63::70/128
ipv6 ospf6 area 0.0.0.0
ipv6 ospf6 passive
exit
!
interface uplink1
ipv6 ospf6 area 0.0.0.0
ipv6 ospf6 network point-to-point
exit
!
interface vrf0
ip address 10.0.0.70/32
ipv6 address fd04:bf63::70/128
exit
!
interface vrf1
ip address 10.0.0.70/32
ipv6 address fd04:bf63::70/128
exit
!
! my switch only supports vxlan ipv4, which explains the gymnastics here
router bgp 65000
bgp router-id 10.0.0.70
no bgp ebgp-requires-policy
no bgp default ipv4-unicast
neighbor EVPN peer-group
neighbor EVPN remote-as internal
neighbor EVPN update-source 10.0.0.70
neighbor EVPN capability extended-nexthop
neighbor PEERS peer-group
neighbor PEERS remote-as internal
neighbor PEERS update-source fd04:bf63::70
neighbor PEERS capability dynamic
neighbor PEERS capability extended-nexthop
neighbor 10.0.0.27 peer-group EVPN
neighbor 10.0.0.30 peer-group EVPN
neighbor 10.0.0.50 peer-group EVPN
neighbor 10.0.0.60 peer-group EVPN
neighbor fd04:bf63::27 peer-group PEERS
neighbor fd04:bf63::30 peer-group PEERS
neighbor fd04:bf63::50 peer-group PEERS
neighbor fd04:bf63::60 peer-group PEERS
!
address-family ipv4 unicast
redistribute connected
neighbor PEERS activate
exit-address-family
!
address-family ipv6 unicast
redistribute connected
neighbor PEERS activate
exit-address-family
!
address-family l2vpn evpn
neighbor EVPN activate
advertise-all-vni
advertise-svi-ip
exit-address-family
exit
!
router bgp 65000 vrf vrf0
bgp router-id 10.0.0.70
no bgp network import-check
!
address-family ipv4 unicast
redistribute connected
exit-address-family
!
address-family ipv6 unicast
redistribute connected
exit-address-family
!
address-family l2vpn evpn
advertise ipv4 unicast
advertise ipv6 unicast
rd 10.0.0.70:32
route-target import 65000:32000
route-target export 65000:32000
exit-address-family
exit
!
router bgp 65000 vrf vrf1
bgp router-id 10.0.0.70
!
address-family ipv4 unicast
redistribute connected
exit-address-family
!
address-family ipv6 unicast
redistribute connected
exit-address-family
!
address-family l2vpn evpn
advertise ipv4 unicast
advertise ipv6 unicast
rd 10.0.0.70:16
route-target import 65000:16000
route-target export 65000:16000
exit-address-family
exit
!
router ospf6
redistribute connected
exit
!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment