Change VLAN Priority using Linux bridge

Following my objective of increasing home FTTH bandwith by removing ISP set-top-box, I needed to change the VLAN priority of the DHCP packets (priority 6).

In previous articles, I patched the DHCP client of OpenWRT which uses raw sockets to use priority 6.

Now my objective is to use a standard router such as Mikrotik, which do not support the DHCP packet CoS by default.

I decided to use my Macchiatobin running a custom kernel and OpenWRT and configure it as a bridge which modifies the VLAN priority of specific packets.

This was actually easier than expected!

When creating a bridge, it is important to understand that we are operating at layer 2. A typical bridge is not inspecting packets at higher layers (3 and up), and as such not interacting at IP level.

There is however an option to change this behavior, and to force the processing of packets by the kernel at IP layer!

Just try the following command to enable the processing of bridge packets through iptables:

echo "1" > /proc/sys/net/bridge/bridge-nf-call-iptables

This change can be made persistent by altering sysctl configuration file in /etc/sysctl.d/.

net.bridge.bridge-nf-call-iptables=1

Now that the packets are being processed by iptables, the steps are the following:

  1. Define VLAN interfaces
  2. Create the bridge and attach interfaces
  3. Create iptables rule to change the VLAN priority of selected packets

Defining VLAN interfaces

Why defining every VLAN, instead of just bridging interface as a trunk ? If you do that, the kernel will not have any knowledge about packets, and especially that 802.1Q is in use. And as VLAN priority is a feature that comes with 802.1Q – no VLAN = no priority !

Declare VLAN on the interfaces you wish to bridge, and specify the egress map, to indicate that packets with CoS set to 6 will be leaving the interface with a VLAN priority set to 6:

ip link add link eth0 name eth0.832 type vlan id 832
ip link set eth0.832 type vlan egress 6:6

ip link add link eth3 name eth3.832 type vlan id 832
ip link set eth3.832 type vlan egress 6:6

Create the bridge

Now, it is time to create the bridge and add the interfaces to it. Note that we don’t need to define an IP configuration for this interface (br0) as we just want the device to be a “passive” bridge.

brctl addbr br0
brctl addif br0 eth0.832
brctl addif br0 eth3.832

Create iptables rule to change the CoS of selected packets

The only packets we want to alter is DHCP configuration. Those packets typically have a predefined source and destination port, making it easy.

iptables -A FORWARD -p udp --sport 68 --dport 67 -j CLASSIFY --set-class 0:6

Here we are! At this stage, the device is operating as a bridge, bridging vlan 832 and changing the priorty of DHCP packets to 6.

Persisting this configuration with OpenWRT

If you use OpenWRT and want to persist this configuration, here are the changes.

# /etc/config/network

config device
	option type '8021q'
	option ifname 'eth3'
	option vid '832'
	option name 'eth3.832'
	option ipv6 '0'
	list egress_qos_mapping '6:6'
	list egress_qos_mapping '0:0'
	list egress_qos_mapping '1:0'
	list egress_qos_mapping '2:0'
	list egress_qos_mapping '3:0'
	list egress_qos_mapping '4:0'
	list egress_qos_mapping '5:0'

config device
	option type '8021q'
	option ifname 'eth0'
	option vid '832'
	option name 'eth0.832'
	option ipv6 '0'
        list egress_qos_mapping '6:6'
        list egress_qos_mapping '0:0'
        list egress_qos_mapping '1:0'
        list egress_qos_mapping '2:0'
        list egress_qos_mapping '3:0'
        list egress_qos_mapping '4:0'
        list egress_qos_mapping '5:0'

config interface 'br0'
	option type 'bridge'
	option ifname 'eth3.832 eth0.832'
# /etc/sysctl.d/11-br-netfilter.conf

net.bridge.bridge-nf-call-arptables=0
net.bridge.bridge-nf-call-ip6tables=0
net.bridge.bridge-nf-call-iptables=1
# /etc/firewall.user

iptables -A FORWARD -p udp --sport 68 --dport 67 -j CLASSIFY --set-class 0:6