In this post I’m going to talk about how to create, edit, and delete VRFs in linux with python. To follow along I have created a vagrant environment and example python script in github.

Here’s our environment:

Environment

Prior to us creating the VRFs all servers connected to site1router will be able to talk to each other. In this post we will create two VRFs, vrf1 and vrf2. On site1router we will then associate interfaces eth1 and eth2 with vrf1 and interfaces eth3 and eth4 with vrf2. Once the interface is associated with the vrf the network configured on the interface will be contained to their respective vrf route table. This will prevent servers on seperate VRFs from being able to communicate.

Prerequisites

If you’re following along via the vagrant environment these are handled for you but if you’re wanting to perform this on your own you need to ensure that the following prerequisites are met.

On the linux host that the VRFs will be created on you need to make sure the following is installed:

Pyroute2 is the python package that is going to allow for the management of VRFs. It’s very useful for interacting with the linux network stack with python.

On the same linux host you need to ensure that you have enabled ipv4 forwarding so that it can route packets. From the CLI you can enable via:

sudo sysctl -w net.ipv4.ip_forward=1

VRFs CLI Commands

Before we show how to set this up within python lets review the CLI commands that you would enter if you wanted to manually configure. We’re going to be using our vagrant environment as the example for the commands to use.

Create VRFs
sudo ip link add vrf1 type vrf table 1
sudo ip link set dev vrf1 up
sudo ip link add vrf2 type vrf table 2
sudo ip link set dev vrf2 up
Associate Interfaces with VRF
sudo ip link set dev eth1 master vrf1
sudo ip link set dev eth2 master vrf1
sudo ip link set dev eth3 master vrf2
sudo ip link set dev eth4 master vrf2
Remove Interfaces from VRF
sudo ip link set dev eth1 nomaster
sudo ip link set dev eth2 nomaster
sudo ip link set dev eth3 nomaster
sudo ip link set dev eth4 nomaster
Delete VRFs
sudo ip link delete vrf1
sudo ip link delete vrf2

VRFs in Python

Validate Pre Configuration

Prior to creating our VRFs and associating the interfaces let’s confirm one of our servers can talk to all the others via a simple ping.

λ vagrant ssh site1server1
Last login: Sun May 26 07:34:55 2019 from 10.0.2.2
vagrant@site1server1:~$ ping 10.1.2.10
PING 10.1.2.10 (10.1.2.10) 56(84) bytes of data.
64 bytes from 10.1.2.10: icmp_seq=1 ttl=63 time=1.11 ms
64 bytes from 10.1.2.10: icmp_seq=2 ttl=63 time=0.570 ms
^C
--- 10.1.2.10 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 0.570/0.840/1.111/0.272 ms
vagrant@site1server1:~$ ping 10.1.3.10
PING 10.1.3.10 (10.1.3.10) 56(84) bytes of data.
64 bytes from 10.1.3.10: icmp_seq=1 ttl=63 time=0.960 ms
64 bytes from 10.1.3.10: icmp_seq=2 ttl=63 time=2.02 ms
64 bytes from 10.1.3.10: icmp_seq=3 ttl=63 time=1.80 ms
^C
--- 10.1.3.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 0.960/1.596/2.026/0.460 ms
vagrant@site1server1:~$ ping 10.1.4.10
PING 10.1.4.10 (10.1.4.10) 56(84) bytes of data.
64 bytes from 10.1.4.10: icmp_seq=1 ttl=63 time=72.3 ms
64 bytes from 10.1.4.10: icmp_seq=2 ttl=63 time=1.93 ms
64 bytes from 10.1.4.10: icmp_seq=3 ttl=63 time=1.92 ms
^C
--- 10.1.4.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 1.929/25.414/72.377/33.207 ms
vagrant@site1server1:~$
Create VRFs
from pyroute2 import IPDB

with IPDB() as ipdb:
    with ipdb.create(kind="vrf", ifname="vrf1", vrf_table=1) as vrf:
        vrf.up()
    with ipdb.create(kind="vrf", ifname="vrf2",vrf_table=2) as vrf:
        vrf.up()
Associate Interfaces with VRF
from pyroute2 import IPDB

with IPDB() as ipdb:
    with ipdb.interfaces["vrf1"] as vrf:
        vrf.add_port(ipdb.interfaces["eth1"].index)
        vrf.add_port(ipdb.interfaces["eth2"].index)
    with ipdb.interfaces["vrf2"] as vrf:
        vrf.add_port(ipdb.interfaces["eth3"].index)
        vrf.add_port(ipdb.interfaces["eth4"].index)
Validate Post Configuration

After creating our VRFs and associating out interfaces to their respective VRF let’s confirm the site1router route table is updated to reflect the VRFs and that the servers can only communicate with other hosts associated with the same VRF.

List site1router vrfs.

vagrant@site1router:~$ ip link show type vrf
7: vrf1: <MASTER,UP,LOWER_UP> mtu 65536 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 62:ec:0e:17:e6:69 brd ff:ff:ff:ff:ff:ff
8: vrf2: <MASTER,UP,LOWER_UP> mtu 65536 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 46:44:1f:75:a1:cb brd ff:ff:ff:ff:ff:ff
vagrant@site1router:~$

Check site1router global route table.

vagrant@site1router:~$ route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         _gateway        0.0.0.0         UG    100    0        0 eth0
10.0.2.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0
_gateway        0.0.0.0         255.255.255.255 UH    100    0        0 eth0
vagrant@site1router:~$

Check site1router vrf1 and vrf2 route table.

vagrant@site1router:~$ ip route show vrf vrf1
10.1.1.0/24 dev eth1 proto kernel scope link src 10.1.1.1
10.1.2.0/24 dev eth2 proto kernel scope link src 10.1.2.1
vagrant@site1router:~$ ip route show vrf vrf2
10.1.3.0/24 dev eth3 proto kernel scope link src 10.1.3.1
10.1.4.0/24 dev eth4 proto kernel scope link src 10.1.4.1
vagrant@site1router:~$

Confirm site1server1 can only talk to site1server2.

vagrant@site1server1:~$ ping 10.1.2.10
PING 10.1.2.10 (10.1.2.10) 56(84) bytes of data.
64 bytes from 10.1.2.10: icmp_seq=1 ttl=63 time=1.49 ms
64 bytes from 10.1.2.10: icmp_seq=2 ttl=63 time=1.52 ms
^C
--- 10.1.2.10 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 1.492/1.507/1.523/0.041 ms
vagrant@site1server1:~$ ping 10.1.3.10
PING 10.1.3.10 (10.1.3.10) 56(84) bytes of data.
^C
--- 10.1.3.10 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2028ms

vagrant@site1server1:~$ ping 10.1.4.10
PING 10.1.4.10 (10.1.4.10) 56(84) bytes of data.
^C
--- 10.1.4.10 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 3070ms

vagrant@site1server1:~$

Confirm site1server3 can only talk to site1server3.

vagrant@site1server3:~$ ping 10.1.4.10
PING 10.1.4.10 (10.1.4.10) 56(84) bytes of data.
64 bytes from 10.1.4.10: icmp_seq=1 ttl=63 time=1.14 ms
64 bytes from 10.1.4.10: icmp_seq=2 ttl=63 time=1.95 ms
64 bytes from 10.1.4.10: icmp_seq=3 ttl=63 time=1.65 ms
^C
--- 10.1.4.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2005ms
rtt min/avg/max/mdev = 1.149/1.588/1.959/0.337 ms
vagrant@site1server3:~$ ping 10.1.1.10
PING 10.1.1.10 (10.1.1.10) 56(84) bytes of data.
^C
--- 10.1.1.10 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2035ms

vagrant@site1server3:~$ ping 10.1.2.10
PING 10.1.2.10 (10.1.2.10) 56(84) bytes of data.
^C
--- 10.1.2.10 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2051ms

vagrant@site1server3:~$
Remove Interfaces from VRF

At this time I have not found a way to remove an interface from a vrf via pyroute2. A work around for now is just issuing the shell commands in python.

import subprocess

subprocess.run(f"ip link set dev eth1 nomaster", shell=True)
subprocess.run(f"ip link set dev eth2 nomaster", shell=True)
subprocess.run(f"ip link set dev eth3 nomaster", shell=True)
subprocess.run(f"ip link set dev eth4 nomaster", shell=True)
Delete VRFs
from pyroute2 import IPDB

with IPDB() as ipdb:
    with ipdb.interfaces["vrf1"] as vrf:
        vrf.remove()
    with ipdb.interfaces["vrf2"] as vrf:
        vrf.remove()
Validate Post Deletion

After removing our interfaces from the VRFs and deleting the VRFs lets confirm the global route table is updated on site1router to reflect this change and confirm the servers can now communicate with each other.

List site1router vrfs.

vagrant@site1router:~$ ip link show type vrf
vagrant@site1router:~$

Check site1router global route table.

vagrant@site1router:~$ route
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
default         _gateway        0.0.0.0         UG    100    0        0 eth0
10.0.2.0        0.0.0.0         255.255.255.0   U     0      0        0 eth0
_gateway        0.0.0.0         255.255.255.255 UH    100    0        0 eth0
10.1.1.0        0.0.0.0         255.255.255.0   U     0      0        0 eth1
10.1.2.0        0.0.0.0         255.255.255.0   U     0      0        0 eth2
10.1.3.0        0.0.0.0         255.255.255.0   U     0      0        0 eth3
10.1.4.0        0.0.0.0         255.255.255.0   U     0      0        0 eth4
vagrant@site1router:~$

Confirm site1server1 can talk to all hosts.

vagrant@site1server1:~$ ping 10.1.2.10
PING 10.1.2.10 (10.1.2.10) 56(84) bytes of data.
64 bytes from 10.1.2.10: icmp_seq=1 ttl=63 time=1.09 ms
64 bytes from 10.1.2.10: icmp_seq=2 ttl=63 time=1.80 ms
64 bytes from 10.1.2.10: icmp_seq=3 ttl=63 time=1.62 ms
^C
--- 10.1.2.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 1.090/1.507/1.807/0.305 ms
vagrant@site1server1:~$ ping 10.1.3.10
PING 10.1.3.10 (10.1.3.10) 56(84) bytes of data.
64 bytes from 10.1.3.10: icmp_seq=1 ttl=63 time=0.996 ms
64 bytes from 10.1.3.10: icmp_seq=2 ttl=63 time=0.978 ms
64 bytes from 10.1.3.10: icmp_seq=3 ttl=63 time=0.699 ms
^C
--- 10.1.3.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 0.699/0.891/0.996/0.135 ms
vagrant@site1server1:~$ ping 10.1.4.10
PING 10.1.4.10 (10.1.4.10) 56(84) bytes of data.
64 bytes from 10.1.4.10: icmp_seq=1 ttl=63 time=0.948 ms
64 bytes from 10.1.4.10: icmp_seq=2 ttl=63 time=1.83 ms
64 bytes from 10.1.4.10: icmp_seq=3 ttl=63 time=1.72 ms
^C
--- 10.1.4.10 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 0.948/1.502/1.832/0.395 ms
vagrant@site1server1:~$

Helpful References

I couldn’t have created this post without the following helpful sites.