KVM & BRCTL in Linux – bringing VLANs to the guests

I recently had the opportunity to setup a KVM machine running on a Centos machine. Tools such as Virtual Machine Manager (VMM) help immensely in the provisioning and administration of virtual machine guests within KVM, and bring the KVM solution on par with other solutions such as Xen, VMWare and VirtualBox.

Networking is one of my main tasks when setting up such an environment, and my particular scenario presented a bit of a challenge. Most articles regarding KVM and VLANs deal with having the guests in “access” mode – that is they can only access a single VLAN. In my particular scenario (a virtual guest hosting a pfsense install) I needed to preserve the VLAN tagging across the virtual bridge, in other words, having the guest in “trunking” mode, making it vlan-aware.

This article explores my understanding of the whole setup above using linux’s inbuilt uml-tools, a.k.a. brctl. I will go over the “normal” access mode that is presented in most articles, as well as a way to put the guests in trunking mode, which is very much less well documented on the web. When reading through the article pay particular attention to the order of the individual components that make up the solution, as when troubleshooting this will be invaluable (hence my many colorful, if inept, diagrams in this article.

I found some very good documentation on several sites, though it took a while to find what I needed, the following was of particular interest:

http://nickapedia.com/2011/11/28/now-for-something-completely-different-ubuntu-11-10-kvm-vlan-trunking/

I will be using CentOS rather than Ubuntu in my article. However, do make note of the following settings:

net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0
net.bridge.bridge-nf-call-arptables = 0
net.bridge.bridge-nf-filter-pppoe-tagged = 0
net.bridge.bridge-nf-filter-vlan-tagged = 0
These lines disable any filtering of frames on the bridge devices.

The above are found under /proc/net/bridge in Centos

Starting Point.

Our starting point will be simply bridging all physical and virtual interfaces together. Throughout this article we will be discussing 4 main components:

  • The physical network interface (eth0)
  • Two virtual network interfaces, attached to two virtual guests (vNIC1 and vNIC2)
  • A virtual bridge (BR0)

We would be aiming for something along the lines of:

brctl_starting

At this stage, the objective is to get eth0, vNIC1 and vNIC2 talking together. I wont focus on creating the actual KVM guests, plenty of documentation elsewhere on how to do that. We’ll keep our focus on the bridge, BR0. Creating the bridge is simple as is adding the member interfaces:

brctl add br0

brctl addif br0 eth0

brctl addif br0 vNIC1

brctl addif br0 vNIC2

With this setup, all three member interfaces can communicate together, like so:

brctl_simple

Adding VLANs to the mix – the usual guest access mode

Before continuing further, from this stage onwards make sure to have the 8021q module loaded in the kernel. At this stage, this is what we’re end up with:

brctl_access_guests

It’s a busy diagram so I’ll take some of your time to explain it. the physical interface, eth0, will be subdivided into vlan subinterfaces using the vconfig command. Tagged and untagged (native) traffic will enter on eth0. If a subinterface is defined for a particular vlan, that traffic is stripped of it’s vlan header, and presented untagged to the bridge. Any virtual machines which should have access to this vlan should be members of this bridge. So to achieve the above we first define the two eth0 vlan subinterfaces, for vlan 1 (eth0.1) and vlan 5 (eth0.5):

vconfig add eth0 1

vconfig add th0 5

ifconfig eth0.1 up

ifconfig eth0.5 up

You can check that the vlans have been correctly setup by running the command:

cat /proc/net/vlan/conf

Next we have the two virtual bridges, one for vlan1 traffic (BR01) and the other for vlan5 (BR05):

brctl add br01

brctl add br05

We then combine place the appropriate interfaces into the correct bridge:

brctl addif br01 eth0.1

brctl addif br01 vNIC1

brctl addif br05 eth0.5

brctl addif br05 vNIC2

After bringing up the interface, you should have KVM GUEST 1 as a member of vlan 1, and KVM GUEST 2 as a member of vlan 2

Homerun: going to guest trunk mode

Important, the method presented above, and the method about to be presented are mutually exclusive…. you must choose between one or the other. Having them both will give unexpected results (as I learnt the hard way). See the troubleshooting section below for some details.

We’ll be aiming for something like this:

brctl_tunking_guests

So, to explain the above: Tagged traffic comes through on eth0, which does not have any vlan subinterfaces defined as before. Traffic hits the virtual bridge, which is divided into vlan subinterfaces using vconfig. Basically, the vlan subinterfaces are defined directly on the bridge rather than on eth0.

The difference is that when subinterfaces are defined  on eth0, as noted previously Linux will strip the vlan tag, but when defined on the bridge, the vlan tags are kept. The vNICs are both members of the bridge, with the result that the tagged traffic is presented directly to them, with the VLAN tagging intact Smile Notice that if the bridge does not have a particular vlan subinterface (eg vlan 8 in my diagram above – there is no corresponding br0.8) that vlan traffic will be dropped.

To achieve the above first define the bridge:

brctl add br0

then we define the vlan subinterfaces on the bridge:

vconfig add br0 1

vconfig add br0 5

ifconfig br0.1 up

ifconfig br0.5 up

and we finally tie it all together

brctl add if br0 vNIC1

brctl add if br0 vNIC2

And now KVM GUEST 1 and 2 will be able to see tagged traffic from vlan 1 and 5. Of course the guest must be able to terminate the vlans themselves as they are now vlan aware.

Troubleshooting notes

While doing the above, if you run into problems, keep in mind the following:

1. Make sure all interfaces are up. Running ifconfig should show all interfaces, bridges and subinterfaces as “UP”

2. Traffic flow. Refer to the above diagrams. Though I only show the incoming traffic path (from the physical world to the VMs) in my diagrams, the outbound traffic flow (from the VMs to the physical world) is exactly the same, but in reverse.

This is essential so as to know where to run packet captures and where to know what is going wrong.

To give a real example of what happened in my case… I had defined all the above as in the second scenario presented above, but neglected to remove all the eth0 vlan subinterfaces that I had left there as a result of testing the first scenario. This resulted in tagged traffic leaving the VM, hitting the bridge correctly, and being sent out the physical world through eth0 correctly. But on it’s return, since the traffic was tagged and the kernel had eth0.1 defined, it was attempting to use those subinterfaces, and removing the vlan tags. Removing the eth0 subinterfaces meant that the tagged traffic could hit the bridge directly and the tagging was kept intact

3. TCPDUMP… when dumping vlan traffic remember that by default tcpdump will only monitor untagged traffic. To monitor tagged traffic from, say, vlan 5, you’d need:

tcpdump –i eth0 vlan 5