[Solved] vlan trunks via mesh 802.11s (backup) + ethernet (main)

I'm asking for help setting up vlan trunks via 802.11s. At the moment I have a router and 2 repeaters. They are connected via PLC. I want to make a backup connection to them via 5+2 GHz, and also connect two more repeaters with swconfig at 2 GHz without a cable. @bluewavenet suggested that this can be done using vxlan, but I have not found similar examples of how to do this. At the moment, I have made a script that creates nftables rules to protect against loops in the bridges, assigned mesh_hwmp_rootmode 4/0, mesh_gate_annuncements 1/0 to the router and repeaters, created 802.11s access points, but did not attach them to any network. The current network configurations is as follows:

#router network
config interface 'loopback'
	option device 'lo'
	option proto 'static'
	option ipaddr '127.0.0.1'
	option netmask '255.0.0.0'

config globals 'globals'
	option ula_prefix 'fdef:7753:fd90::/48'
	option packet_steering '1'

config device
	option name 'br-lan'
	option type 'bridge'
	list ports 'lan1'
	list ports 'lan2'
	list ports 'lan3'

config interface 'lan'
	option device 'br-lan.1'
	option proto 'static'
	option ipaddr '192.168.1.1'
	option netmask '255.255.255.0'
	option ip6assign '60'

config interface 'wan'
	option device 'wan'
	option proto 'dhcp'

config interface 'wan6'
	option device 'wan'
	option proto 'dhcpv6'

config interface 'wwan'
	option proto 'qmi'
	option device '/dev/cdc-wdm0'
	option auth 'none'
	option pdptype 'ipv4'

config bridge-vlan
	option device 'br-lan'
	option vlan '1'
	list ports 'lan1'
	list ports 'lan2'
	list ports 'lan3'

config bridge-vlan
	option device 'br-lan'
	option vlan '3'
	list ports 'lan1:t'
	list ports 'lan2:t'
	list ports 'lan3:t'

config interface 'gosti'
	option proto 'static'
	option device 'br-lan.3'
	option ipaddr '192.168.3.1'
	option netmask '255.255.255.0'
	option ip6assign '60'
#repeater1 network
config interface 'loopback'
	option device 'lo'
	option proto 'static'
	option ipaddr '127.0.0.1'
	option netmask '255.0.0.0'

config globals 'globals'
	option ula_prefix 'fd42:93d2:5a22::/48'
	option packet_steering '1'

config device
	option name 'br-lan'
	option type 'bridge'
	list ports 'lan1'
	list ports 'lan2'
	list ports 'lan3'
	list ports 'lan4'

config interface 'lan'
	option device 'br-lan.1'
	option proto 'static'
	option ipaddr '192.168.1.2'
	option netmask '255.255.255.0'
	option gateway '192.168.1.1'
	list dns '192.168.1.1'
	option delegate '0'

config interface 'wan'
	option device 'wan'
	option proto 'dhcp'

config bridge-vlan
	option device 'br-lan'
	option vlan '1'
	list ports 'lan1'
	list ports 'lan2'
	list ports 'lan3'
	list ports 'lan4'

config bridge-vlan
	option device 'br-lan'
	option vlan '3'
	list ports 'lan1:t'
	list ports 'lan2:t'
	list ports 'lan3:t'
	list ports 'lan4:t'

config interface 'gosti'
	option proto 'none'
	option device 'br-lan.3'

config interface 'wan6'
	option proto 'dhcpv6'
	option device 'wan'
	option reqaddress 'try'
	option reqprefix 'auto'
	option norelease '1'
#repeater2 network
config interface 'loopback'
	option device 'lo'
	option proto 'static'
	option ipaddr '127.0.0.1'
	option netmask '255.0.0.0'

config globals 'globals'
	option ula_prefix 'fd95:84c2:fc2d::/48'
	option packet_steering '1'

config device
	option name 'br-lan'
	option type 'bridge'
	list ports 'eth0.1'

config interface 'lan'
	option device 'br-lan'
	option proto 'static'
	option ipaddr '192.168.1.3'
	option netmask '255.255.255.0'
	option gateway '192.168.1.1'
	list dns '192.168.1.1'
	option delegate '0'

config interface 'wan'
	option device 'eth0.2'
	option proto 'dhcp'

config interface 'wan6'
	option device 'eth0.2'
	option proto 'dhcpv6'

config switch
	option name 'switch0'
	option reset '1'
	option enable_vlan '1'

config switch_vlan
	option device 'switch0'
	option vlan '1'
	option ports '6t 1 2 3'

config switch_vlan
	option device 'switch0'
	option vlan '2'
	option ports '6t 0'

config device
	option name 'br-gosti'
	option type 'bridge'
	list ports 'eth0.3'

config interface 'gosti'
	option proto 'none'
	option device 'br-gosti'

config switch_vlan
	option device 'switch0'
	option ports '6t 1t 2t 3t'
	option vlan '3'

I think I figured out how to do it. For now, at least, everything is working. All I had to do was get up at 4 a.m. :wink: That's what I got. I installed the luci-proto-vxlan, ip-full (not sure if it was needed), kmod-nft-bridge packages. I created the vxlan interface for the guest network, disabled vlan filtering on the bridge, enabled stp and igmp snooping, created a new bridge for the guest network, added the vxlan interface of the guest network to it. 802.11s tied to the lan.

#router
config interface 'loopback'
	option device 'lo'
	option proto 'static'
	option ipaddr '127.0.0.1'
	option netmask '255.0.0.0'

config globals 'globals'
	option ula_prefix 'fdef:7753:fd90::/48'
	option packet_steering '1'

config device
	option name 'br-lan'
	option type 'bridge'
	list ports 'lan1'
	list ports 'lan2'
	list ports 'lan3'
	option stp '1'
	option hello_time '4'
	option igmp_snooping '1'

config interface 'lan'
	option device 'br-lan'
	option proto 'static'
	option ipaddr '192.168.1.1'
	option netmask '255.255.255.0'
	option ip6assign '60'

config interface 'wan'
	option device 'wan'
	option proto 'dhcp'

config interface 'wan6'
	option device 'wan'
	option proto 'dhcpv6'

config interface 'wwan'
	option proto 'qmi'
	option device '/dev/cdc-wdm0'
	option auth 'none'
	option pdptype 'ipv4'

config interface 'gosti'
	option proto 'static'
	option device 'br-gosti'
	option ipaddr '192.168.3.1'
	option netmask '255.255.255.0'
	option ip6assign '60'

config interface 'vxlan3_gosti'
	option proto 'vxlan'
	option peeraddr '239.1.3.1'
	option port '4789'
	option vid '3'
	option tunlink 'lan'

config device
	option type 'bridge'
	option name 'br-gosti'
	list ports 'vxlan3_gosti'
	option bridge_empty '1'
	option stp '1'
	option hello_time '4'
	option igmp_snooping '1'
#repeater1
config interface 'loopback'
	option device 'lo'
	option proto 'static'
	option ipaddr '127.0.0.1'
	option netmask '255.0.0.0'

config globals 'globals'
	option ula_prefix 'fd42:93d2:5a22::/48'
	option packet_steering '1'

config device
	option name 'br-lan'
	option type 'bridge'
	list ports 'lan1'
	list ports 'lan2'
	list ports 'lan3'
	list ports 'lan4'
	option stp '1'
	option hello_time '4'
	option igmp_snooping '1'

config interface 'lan'
	option device 'br-lan'
	option proto 'static'
	option ipaddr '192.168.1.2'
	option netmask '255.255.255.0'
	option gateway '192.168.1.1'
	list dns '192.168.1.1'
	option delegate '0'

config interface 'wan'
	option device 'wan'
	option proto 'dhcp'

config interface 'gosti'
	option proto 'none'
	option device 'br-gosti'

config interface 'wan6'
	option proto 'dhcpv6'
	option device 'wan'
	option reqaddress 'try'
	option reqprefix 'auto'
	option norelease '1'

config interface 'vxlan3_gosti'
	option proto 'vxlan'
	option peeraddr '239.1.3.1'
	option port '4789'
	option vid '3'
	option tunlink 'lan'

config device
	option type 'bridge'
	option name 'br-gosti'
	list ports 'vxlan3_gosti'
	option bridge_empty '1'
	option stp '1'
	option hello_time '4'
	option igmp_snooping '1'
#repeater2
config interface 'loopback'
	option device 'lo'
	option proto 'static'
	option ipaddr '127.0.0.1'
	option netmask '255.0.0.0'

config globals 'globals'
	option ula_prefix 'fd95:84c2:fc2d::/48'
	option packet_steering '1'

config device
	option name 'br-lan'
	option type 'bridge'
	list ports 'eth0.1'
	option stp '1'
	option hello_time '4'
	option igmp_snooping '1'

config interface 'lan'
	option device 'br-lan'
	option proto 'static'
	option ipaddr '192.168.1.3'
	option netmask '255.255.255.0'
	option gateway '192.168.1.1'
	list dns '192.168.1.1'
	option delegate '0'

config interface 'wan'
	option device 'eth0.2'
	option proto 'dhcp'

config interface 'wan6'
	option device 'eth0.2'
	option proto 'dhcpv6'

config switch
	option name 'switch0'
	option reset '1'
	option enable_vlan '1'

config switch_vlan
	option device 'switch0'
	option vlan '1'
	option ports '6t 1 2 3'

config switch_vlan
	option device 'switch0'
	option vlan '2'
	option ports '6t 0'

config device
	option name 'br-gosti'
	option type 'bridge'
	list ports 'vxlan3_gosti'
	option stp '1'
	option hello_time '4'
	option igmp_snooping '1'

config interface 'gosti'
	option proto 'none'
	option device 'br-gosti'

config interface 'vxlan3_gosti'
	option proto 'vxlan'
	option peeraddr '239.1.3.1'
	option port '4789'
	option vid '3'
	option tunlink 'lan'

I added it to the autorun of the router:

iw dev "phy0-mesh0" set mesh_param mesh_hwmp_rootmode 4
iw dev "phy0-mesh0" set mesh_param mesh_gate_announcements 1
iw dev "phy1-mesh0" set mesh_param mesh_hwmp_rootmode 4
iw dev "phy1-mesh0" set mesh_param mesh_gate_announcements 1
brctl setpathcost br-lan phy0-mesh0 181
echo 1 > /proc/sys/net/ipv4/conf/phy0-mesh0/proxy_arp_pvlan
echo 1 > /proc/sys/net/ipv4/conf/phy1-mesh0/proxy_arp_pvlan

And for the repeaters:

iw dev "phy0-mesh0" set mesh_param mesh_hwmp_rootmode 0
iw dev "phy0-mesh0" set mesh_param mesh_gate_announcements 0
iw dev "phy1-mesh0" set mesh_param mesh_hwmp_rootmode 0
iw dev "phy1-mesh0" set mesh_param mesh_gate_announcements 0
brctl setpathcost br-lan phy0-mesh0 181
echo 1 > /proc/sys/net/ipv4/conf/phy0-mesh0/proxy_arp_pvlan
echo 1 > /proc/sys/net/ipv4/conf/phy1-mesh0/proxy_arp_pvlan

and script for all devices that generates nftables rules to protect against loops in the bridge (I also don't know if I need it.)

Thank you @psherman and @bluewavenet for the tip and help.

Cool! I am guessing you read the code in mesh11sd then :nerd_face:

You need the loop protection if you have any cabled segments in the backhaul.

Any particular reason for setting mesh path cost to 181?
Normally you would set it to a very high value to prioritise using the cable segment over a possible mesh link. Likewise a very low value to prioritise using the mesh link.

You also might need to enable VLAN Aggregation, aka mac forced forwarding, aka proxy_arp_pvlan, for ipv4 on the mesh interface, otherwise you can end up with tcp links breaking etc

1 Like

Yes, it all started with studying the code. When I started using mesh11d, I came across the fact that 2 wifi bands cannot be used simultaneously. This was the reason why I started studying the code and configuring it manually.

I calculated this as the default path cost phy0-mesh 100 + (default path cost phy0-mesh 100 - default path cost ethernet 100mbps 19). I have not found information about what default values different interfaces can take under different conditions in order to more accurately select this value.

Thank you for your help. I'll study how to do it.

It can be set to any value from 0 to 65534

To be clear, this is two mesh interfaces on separate bands at the same time.
To want more than one mesh interface is pretty much an edge case. Mesh11sd detects mesh capable hardware but stops short of enabling any more than one, based on its config.
If you do have more than one mesh interface on the backhaul, you WILL have loop problems unless you have the nft bridge rules and STP config in place to prevent it.
Note: STP by itself id not sufficient because the loop occurs through the mesh interface at kernel level, before STP has a chance to do anything.

It is planned in a future version to enable multiple separate backhaul support in mesh11sd, allowing bridging between two or more independent backhauls. This functionality will be very convenient in large Community or WISP deployments, particularly from the point of view of traffic partitioning, resilience etc., but it is worth noting that in most regions, use of 5GHz channels outdoors is severely restricted.

As you have gone to the effort of doing some reverse engineering of the code, you are welcome to contribute to the project if you wish. Feature requests and PRs welcome:

1 Like

script (kmod-nft-bridge package required):

#!/bin/sh

# Function to get the MAC address of the bridge
get_bridge_mac() {
  local device="$1"
  ip link show dev "$device" | grep "link/ether" | awk '{printf "%s", $2}'
}

# Function to get the list of interfaces in the bridge
get_bridge_interfaces() {
  local device="$1"
  brctl showstp "$device" | grep -B 1 -w "port id" | grep "(" | awk '{printf "%s ", $1}'
}

# Function to add nftables rules for the bridge
configure_bridge_loop_protect() {
  local device="$1"
  local mac=$(get_bridge_mac "$device")

  # Check if the MAC address was obtained
  if [ -z "$mac" ]; then
    echo "Error: Failed to get MAC address for bridge $device"
    return 1
  fi

  nft add table bridge "backhaul_loop_protect" 2>/dev/null
  nft add chain bridge "backhaul_loop_protect" "backhaul_loop_protect_prerouting" { type filter hook prerouting priority -350\; } 2>/dev/null

  # Add rules for each interface in the bridge
  for interface in $(get_bridge_interfaces "$device"); do
    nft add rule bridge "backhaul_loop_protect" "backhaul_loop_protect_prerouting" meta iifname "$interface" ether saddr "$mac" counter drop 2>/dev/null
    nft add rule bridge "backhaul_loop_protect" "backhaul_loop_protect_prerouting" meta iifname "$interface" counter accept 2>/dev/null
    echo "Rules added for interface $interface in bridge $device"
  done
}

# Find all bridge interfaces (assuming names contain "br-")
for device in $(brctl show | awk 'NR>1 {print $1}' | grep '^br-'); do
  echo "Configuring loop protection for bridge $device"
  configure_bridge_loop_protect "$device"
done

exit 0

Thank you, but it's not easy for me. I'm not ready for that yet.

You seem quite capable of reverse engineering the source code, so I would suggest you are ready :nerd_face:

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.