Ever since I've heard about VRF support (or VRF-lite like it is called in Documentation/network/vrf.txt) I wanted to start tinkering with it. Since the topic is currently only covered in the previously mentioned linux kernel documentation I thought it would be a good idea to post some notes.
It basically boils down to adding an VRF interface and creating two ip rule-entries.
I'm using a local VM with ArchLinux since the VRF feature seems to require a rather recent kernel. My experience with kernels below version 4.6 weren't that great.
$ ip -br link # this is where we are start off lo UNKNOWN 00:00:00:00:00:00 <LOOPBACK,UP,LOWER_UP> ens3 DOWN 52:54:00:12:34:56 <BROADCAST,MULTICAST>
Now are adding a new interface named vrf-customer1 with the table customer1 assigned to it. The table parameter is used to place routes from your devices within the VRF into the right routing table
$ ip link add vrf-customer1 type vrf table customer1 $ ip -d link show vrf-customer1 # verify that the interface indeed exists and has the correct table assigned to it 4: vrf-customer1: <NOARP,MASTER> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000 link/ether ca:22:59:ba:05:da brd ff:ff:ff:ff:ff:ff promiscuity 0 vrf table 100 addrgenmode eui64
Next: redirect the traffic from and to the vrf to the customer1 table and verify the rules are indeed as expected:
$ ip -4 rule add oif vrf-customer1 lookup customer1 $ ip -4 rule add iif vrf-customer1 lookup customer1 $ ip -6 rule add oif vrf-customer1 lookup customer1 $ ip -6 rule add iif vrf-customer1 lookup customer1 $ ip -4 rule 0: from all lookup local 32764: from all iif vrf-customer1 lookup customer1 32765: from all oif vrf-customer1 lookup customer1 32766: from all lookup main 32767: from all lookup default $ ip -6 rule 0: from all lookup local 32764: from all oif vrf-customer1 lookup customer1 32765: from all iif vrf-customer1 lookup customer1 32766: from all lookup main
To make any use of our VRF we will have to add an device to it. In my case I'll add the only available "physical" device ens3.
$ ip link set ens3 master vrf-customer1 $ # verify the interface is indeed a member of the VRF $ ip -br link show master vrf-customer1 ens3 DOWN 52:54:00:12:34:56 <BROADCAST,MULTICAST>
Now that we've an interface to receive send send packets with it we should consider adding an IP-Address to it. Since IPv6 is enabled per default we don't need to configure a LL-Address for that protocol.
$ # add an IP to the interface $ ip addr add 10.0.0.1/24 dev ens3 $ ip route show table customer1 local 10.0.0.1 dev ens3 proto kernel scope host src 10.0.0.1
Seeing a route like that might confuse the average linux user. Those routes usually exist within the local table which you can check via ip route show table local
The route to the /24 we've added is still missing from the interface. Why is that? You'll have to change the state of the interface to "UP":
$ ip link set ens3 up $ ip route show table customer1 broadcast 10.0.0.0 dev ens3 proto kernel scope link src 10.0.0.1 10.0.0.0/24 dev ens3 proto kernel scope link src 10.0.0.1 local 10.0.0.1 dev ens3 proto kernel scope host src 10.0.0.1 broadcast 10.0.0.255 dev ens3 proto kernel scope link src 10.0.0.1 $ ip -6 route show table customer1 local fe80::5054:ff:fe12:3456 dev lo proto none metric 0 pref medium fe80::/64 dev ens3 proto kernel metric 256 pref medium ff00::/8 dev vrf-customer1 metric 256 pref medium ff00::/8 dev ens3 metric 256 pref medium
suddenly routes \o/