From dd69724837b2aebefa116f41e64bda2c7ddd6de0 Mon Sep 17 00:00:00 2001 From: Johannes Kimmel Date: Wed, 20 Jan 2021 09:01:00 +0100 Subject: [PATCH 1/3] fff-layer3-vxmesh: join multple client nets with vxlan Easily span a layer 2 network over a small group of, not necessarily adjacent, routers. This patch introduces a new `vxmesh` configuration section. It takes care of setting up a shared client network using vxlan among all configured routers. As a result, clients can finally roam between the routers of the mesh. It integrates well with the underlying layer 3 routing to make best use of the infrastructure available. This way the network is not depending on a contiguous layer 2 spanning tree topology and can make use of all fallback mechanisms of the layer 3 underlay. This example config ... ``` config gateway option peer_ip6 '2001:0db8::1' # required ... config vxmesh option proto 'vxlan|vxlan6' # required option vid '42' # required # ... # any vxlan options can be included # ... # list of peers for headend replication list peer '2001:0db8::1' # list can include the ip of the router list peer '2001:0db8::2' # this way the complete config section list peer '2001:0db8::3' # can be copied to all routers in the list peer '2001:0db8::4' # group ... ``` ... will generate ... ``` config interface client ... # append and remove the vxmesh0 entry # depending on whether a vxmesh is configured ifname '... vxmesh0' ... ... config interface 'vxmesh0' option proto 'vxlan6' option vid '42' config vxlan_peer option vxlan 'vxmesh0' option dst '2001:0db8::2' config vxlan_peer option vxlan 'vxmesh0' option dst '2001:0db8::3' config vxlan_peer option vxlan 'vxmesh0' option dst '2001:0db8::4' ``` It will also take care and configure the `dhcp.@dnsmasq[0].authoritative` setting depending on whether a vxmesh is enabled or not. Signed-off-by: Johannes Kimmel --- src/packages/fff/fff-layer3-vxmesh/Makefile | 29 +++++ .../files/etc/layer3.d/60-vxmesh | 111 ++++++++++++++++++ src/packages/fff/fff-layer3/Makefile | 4 +- 3 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 src/packages/fff/fff-layer3-vxmesh/Makefile create mode 100644 src/packages/fff/fff-layer3-vxmesh/files/etc/layer3.d/60-vxmesh diff --git a/src/packages/fff/fff-layer3-vxmesh/Makefile b/src/packages/fff/fff-layer3-vxmesh/Makefile new file mode 100644 index 00000000..b748c198 --- /dev/null +++ b/src/packages/fff/fff-layer3-vxmesh/Makefile @@ -0,0 +1,29 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=fff-layer3-vxmesh +PKG_RELEASE:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/fff-layer3-vxmesh + SECTION:=base + CATEGORY:=Freifunk + TITLE:=Freifunk-Franken layer3 configuration for mesh via vxlan + URL:=http://www.freifunk-franken.de + DEPENDS:=+vxlan +endef + +define Package/fff-layer3-vxmesh/description + Adds a simple configuration to connect multple routers into one layer 2 + network via vxlan. +endef + +define Build/Compile + # nothing +endef + +define Package/fff-layer3-vxmesh/install + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,fff-layer3-vxmesh)) diff --git a/src/packages/fff/fff-layer3-vxmesh/files/etc/layer3.d/60-vxmesh b/src/packages/fff/fff-layer3-vxmesh/files/etc/layer3.d/60-vxmesh new file mode 100644 index 00000000..7018a1ef --- /dev/null +++ b/src/packages/fff/fff-layer3-vxmesh/files/etc/layer3.d/60-vxmesh @@ -0,0 +1,111 @@ +#!/bin/sh + +configure() { + local proto + local peerip + local otherpeers + local fields=" + proto + port + vid + ttl + tos + mtu + macaddr + zone + rxcsum + txcsum + srcportmin + srcportmax + ageing + maxaddress + learning + rsc + proxy + l2miss + l3miss + gbp + tunlink + ipaddr + ip6addr + " + + # cleanup old vxmesh and peer entries + uci -q delete network.vxmesh0 + while uci -q delete network.@vxlan_peer[-1]; do :; done + + # remove vxmesh0 entry from the client bridge and remove extra whitespaces + uci set network.client.ifname="$(uci -q get network.client.ifname | sed s/vxmesh0// | xargs)" + + # reset dns to authorative + uci set dhcp.@dnsmasq[0].authoritative="1" + + # check if a vxmesh config is available, otherwise quit + uci -q get gateway.@vxmesh[0] > /dev/null || return 0 + + # check if proto is set and probe for the correct peer ip + proto="$(uci -q get gateway.@vxmesh[0].proto)" + case "$proto" in + vxlan6) + peerip="$(uci -q get gateway.@gateway[0].peer_ip6)" + ;; + vxlan) + peerip="$(uci -q get gateway.@gateway[0].peer_ip)" + ;; + *) + echo "FATAL: vxmesh: option proto 'vxlan|vxlan6' required!" + return 1 + ;; + esac + + # vxmesh needs a separate peer ip as the ip on the client interface ip + # might be shared over multiple devices + [ -z "$peerip" ] && { + echo "FATAL: vxmesh: peer_ip|peer_ip6 required!" + return 1 + } + + uci -q get gateway.@vxmesh[0].vid > /dev/null || { + echo "FATAL: vxmesh: missing vid!" + return 1 + } + + # copy main options over + uci set network.vxmesh0="interface" + for option in $fields; do + uci set network.vxmesh0."$option"="$(uci -q get gateway.@vxmesh[0]."$option")" + done + + # exclude peerip from the local router, so packets aren't sent to itself + otherpeers=$(uci -q get gateway.@vxmesh[0].peer | xargs -n1 | grep -v -e "$peerip") + + for peer in $otherpeers; do + # create peer sections + if ! uci -q batch > /dev/null; then + echo "FATAL: vxmesh: error setting up peer!" + echo " peer: \"$peer\"" + return 1 + fi <<- EOF + add network vxlan_peer + set network.@vxlan_peer[-1].vxlan="vxmesh0" + set network.@vxlan_peer[-1].dst="$peer" + EOF + done + + # with multiple routers in the network, there shouldn't be an authoritative + # dhcp server + uci set dhcp.@dnsmasq[0].authoritative="0" + + # add the vxlan interface to the client bridge + uci set network.client.ifname="$(uci -q get network.client.ifname) vxmesh0" +} + +apply() { + uci commit network + uci commit dhcp +} + +revert() { + uci revert network + uci revert dhcp +} diff --git a/src/packages/fff/fff-layer3/Makefile b/src/packages/fff/fff-layer3/Makefile index ce3d2002..ea9183a7 100644 --- a/src/packages/fff/fff-layer3/Makefile +++ b/src/packages/fff/fff-layer3/Makefile @@ -1,7 +1,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=fff-layer3 -PKG_RELEASE:=8 +PKG_RELEASE:=9 include $(INCLUDE_DIR)/package.mk @@ -36,7 +36,7 @@ define Package/fff-layer3 +mtr \ +tc \ +tcpdump \ - +vxlan + +fff-layer3-vxmesh endef define Package/fff-layer3/description -- 2.39.2 From 27e074b1ead1ec42ea5a2c82bfed20159b91bab6 Mon Sep 17 00:00:00 2001 From: Johannes Kimmel Date: Thu, 21 Jan 2021 12:34:02 +0100 Subject: [PATCH 2/3] fff-network: exclude vxmesh fdb entries in client count This avoids counting a single client multiple times. Neighbours behind the vxlan device are actually local to another router and should only be counted there. Signed-off-by: Johannes Kimmel --- src/packages/fff/fff-network/Makefile | 2 +- .../fff/fff-network/files/usr/lib/nodewatcher.d/50-clients.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/packages/fff/fff-network/Makefile b/src/packages/fff/fff-network/Makefile index c97a816a..21af16af 100644 --- a/src/packages/fff/fff-network/Makefile +++ b/src/packages/fff/fff-network/Makefile @@ -1,7 +1,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=fff-network -PKG_RELEASE:=31 +PKG_RELEASE:=32 include $(INCLUDE_DIR)/package.mk diff --git a/src/packages/fff/fff-network/files/usr/lib/nodewatcher.d/50-clients.sh b/src/packages/fff/fff-network/files/usr/lib/nodewatcher.d/50-clients.sh index 1d5cd832..98e9f92d 100755 --- a/src/packages/fff/fff-network/files/usr/lib/nodewatcher.d/50-clients.sh +++ b/src/packages/fff/fff-network/files/usr/lib/nodewatcher.d/50-clients.sh @@ -12,7 +12,7 @@ debug "Collecting information about connected clients" client_count=0 dataclient="" -CLIENT_INTERFACES=$(ls "/sys/class/net/$MESH_INTERFACE/brif" | grep -v '^bat') +CLIENT_INTERFACES=$(ls "/sys/class/net/$MESH_INTERFACE/brif" | grep -v -e '^bat' -v -e '^vxmesh') for clientif in ${CLIENT_INTERFACES}; do cc=$(bridge fdb show br "$MESH_INTERFACE" brport "$clientif" | grep -v self | grep -v permanent -c) client_count=$((client_count + cc)) -- 2.39.2 From d50feeab07f2a0f81a50a985b70b6e7295d74350 Mon Sep 17 00:00:00 2001 From: Johannes Kimmel Date: Tue, 16 Feb 2021 07:56:44 +0100 Subject: [PATCH 3/3] fff-layer3-vxmesh: add babel MTU filters A VXLAN Tunnel (IPv6, without inner VLAN tag) adds 70 bytes of overhead to each packet. To support bridging our client interfaces with an MTU of 1500, VXLAN packets require a link with an MTU of 1570. To avoid sending too large packets to peers over a link with an MTU less than 1570, this patch adds filters to babel to ignore routes to peers announced over small links. Signed-off-by: Johannes Kimmel --- .../files/etc/layer3.d/60-vxmesh | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/packages/fff/fff-layer3-vxmesh/files/etc/layer3.d/60-vxmesh b/src/packages/fff/fff-layer3-vxmesh/files/etc/layer3.d/60-vxmesh index 7018a1ef..f6deeb80 100644 --- a/src/packages/fff/fff-layer3-vxmesh/files/etc/layer3.d/60-vxmesh +++ b/src/packages/fff/fff-layer3-vxmesh/files/etc/layer3.d/60-vxmesh @@ -1,5 +1,7 @@ #!/bin/sh +. /lib/functions.sh + configure() { local proto local peerip @@ -92,6 +94,52 @@ configure() { EOF done + # learn routes to other peers only over babel interfaces with sufficient mtu + # - according to the rfc, vxlan packets must not be fragmented by a VTEP + # - the vxlan tunnel requires an mtu of 1570 when using ipv6 + # + # -> to avoid sending too large packets over an interface with too small mtu, + # don't learn a route to a peer over that interface in the first place + babel_filter_mtu() { + local config="$1" + local otherpeers="$2" + local mtu + local ifname + + case $config in + babelpeer*) ;; + wireguardpeer*) ;; + *) return ;; + esac + + config_get mtu "$config" mtu + config_get ifname "$config" ifname + + [ "${mtu:-0}" -ge "1570" ] && return + [ -z "${ifname}" ] && { + echo "WARNING: could not determine ifname from \"$config\"" + return + } + for peer in $otherpeers; do + if ! uci -q batch > /dev/null; then + echo "FATAL: error adding babel filter for vxlan peer!" + echo " peer: \"$peer\"" + echo " interface: \"$ifname\"" + return 1 + fi <<- EOF + add babeld filter + set babeld.@filter[-1].type="in" + set babeld.@filter[-1].ip="$peer" + set babeld.@filter[-1].if="$ifname" + set babeld.@filter[-1].addedbyautoconfig="true" + set babeld.@filter[-1].action="deny" + EOF + done + } + + config_load network + config_foreach babel_filter_mtu interface "$otherpeers" + # with multiple routers in the network, there shouldn't be an authoritative # dhcp server uci set dhcp.@dnsmasq[0].authoritative="0" @@ -103,9 +151,11 @@ configure() { apply() { uci commit network uci commit dhcp + uci commit babeld } revert() { uci revert network uci revert dhcp + uci revert babeld } -- 2.39.2