Introduction & Quick-start

Most of us are into free wireless mesh networks. We’re fascinated by the idea of deploying a bunch of OpenWRT routers, that magically connect to each other to span large mesh cloud.

Unfortunately, doing so is very hard - the various different options for mesh routing protocols and network designs can be overwhelming.

Many community networks deploy complex configurations. They release custom firmware blobs using self-written scripts to customize OpenWRT images. Usually, these firmware blobs are tight to a specific network by including network addresses or wifi identifiers.

Some Freifunk firmware files even include vpn-server-addresses, rendering them useless for other communities or people living just a few kilometers away.

Here, configuration becomes opaque and thus hard to understand: It’s buried in binary images using custom software and different, sometimes hackish startup-scripts.

Build systems such as Gluon provide a heavy-weight abstraction to OpenWRT. By creating a Domain-Specific-Language (DSL), all relevant OpenWRT settings are duplicated into a site-model, which is then used to describe a specific community network. Although not having explicit restrictions, this site-model focuses on backend-server driven networks, somewhat missing the point of a wifi mesh network. Very little changes to the configuration require a build using the full OpenWRT buildroot.

Node-config is trying to jump in here by providing a comprehensible configuration. Without having a shiny user interface in its core, node-config is focusing on network enthusiasts. It explains all necessary OpenWRT settings for a simple wifi mesh, while providing a complete and easy-to-use setup.

This documentation is devided into tree chapters: Introduction & Quick-start provides an overview including a hands-on. It helps to get node-config running in a few minutes. The chapter Configuration in node-config takes a deep dive into the configuration: It outlines all things done to an OpenWRT router by node-config. The last chapter Advanced Topics highlights the background. It illustrates node-config’s design considerations and explains the concepts in use. Building packages and firmware is also addressed in the last chapter.

Let’s get started.

1. What node-config is

Node-config is just a bunch of uci configuration files including a scripts to apply them. It is copied to plain OpenWRT routers running OpenWRT 18.06 and merged into the existing configuration. It configures routing, network- and wireless interfaces as well as network services such as dhcp and radv.

It spans a mesh network using both babel and batman-adv to provide roaming and hierarchical routing capabilities. The network is IPv4 and IPv6 dual-stack.

You can use and understand node-config as:
  • An elaborate example for an OpenWRT mesh configuration

  • A kickstarter or template for wifi mesh networks without a central vpn (i.e. servers)

  • A way for building supernodish nodes in gluon based Freifunk networks to get rid of servers

  • A lean baseline for testing new architectures and services (i.e. l3roamd)

A git-repository is available at - for help and discussions please subscribe to the wlanware mailing list.

2. Quick-start

You need OpenWRT 18.06 installed on a router and enough free flash space (~2 MB). The router has to be connected to the internet (wan port) an to your computer (pc or laptop using the lan port)

It helps to be familiar with a unix terminal (i.e. Mac OS, Linux) since you need to use ssh to connect to your router. In addition, git is required:

On your computer execute:
git clone
cd node-config; scp -r freifunk root@
ssh root@ /lib/freifunk/

It is assumed, that your router is reachable at (OpenWRT default). Using git, scp and ssh node-config is cloned and then copied to your router. It is applied using [_install_sh].

  1. Set a password - your router will be reachble using ssh.

  2. When executing /lib/freifunk/ you are asked for one IPv4 (/24) and one IPv6 (/64) network. These networks are distributed using dhcp and radv and have to be unique. You must not share a subnet among routers. Using distinct RFC1918 ranges and IPv6 unique local addresses (ULA) is usually the way to go. Please mind bookkeeping.

3. Using packages

Packages can be used instead of copying the filesystem tree using git and scp.

For testing, opkg packages are available at: Packages are built using an additional project integrated into GitLab CI.

To install the packages open a ssh session to your router and execute:
wget -O /etc/opkg/keys/cca6643a8ac2f277
echo "src/gz nc" >> /etc/opkg/customfeeds.conf
opkg update
opkg install node-config node-config-openvpn node-config-pptp

Reboot the router, after installing the packages. For setting IPv4 and IPv6 networks open a new ssh session execute /lib/freifunk/ When executing opkg install node-config, core components are installed, only - openvpn and pptp are not available.

  1. Set a password - your router will be reachable using ssh

  2. Make sure, that /etc/opkg/keys/cca6643a8ac2f277 matches the key available at - the download is not using TLS.

  3. The public key for cca6643a8ac2f277 is availble in our Gitlab CI and may be easily become compromised. Consider using you own key when using packages. For details see Makefiles.

4. Internet Sharing

4.1. Direct Exit

To share the routers WAN uplink in the mesh cloud, you have to enable it.

Open a ssh session to your router and execute:
uci set network.internet_share.disabled=0
uci set network.internet_share6.disabled=0
uci firewall.freifunk_internet.dest='wan'
uci commit firewall
uci commet network
/etc/init.d/firewall restart
/etc/init.d/network restart
By enabling internet_share and internet_share6, WAN routes will be copied to the mesh routing table. uci firewall.freifunk_internet.dest='wan' enables forwarding.

4.2. Using a VPN provider

To use a vpn tunnel (i.e. mullvad), you can use a configuration in /lib/freifunk/vpn and activate it by editing /etc/config/openvpn. See /etc/config/openvpn for details.

If you want to use a provider not included in /lib/freifunk/vpn, you can place your provider’s configuration there. Mind adding route-nopull, script-security 2 and up /lib/freifunk/vpn/ for default route handling. Have a look at existing VPN configurations for example. Also mind setting dev vpn-nat to use the correct network interface.

5. Git directory structure

All configuration can be found in /freifunk/initial_configuration. Other directories contain scripts, build files and documentation.




Directory root, including readme’s, the project’s Makefile and CI-configuration


Documentation including the asciidoc source of this manual


Configuration including scripts


OpenWRT configuration (uci)


Shared routines for scripts


OpenVPN exit configuration for different providers


Makefiles and other files for creating OpenWRT packages

Configuration in node-config

Hold your breath - we’ll take a deep dive into node-config’s Configuration. Don’t get lost in details - try to see the big picture.

The configuration is documented on a per-file basis - each OpenWRT configuration is explained individually. Figure 1 shows the system from a birds eye perspective.

interface configuration
Figure 1. Interface configuration - bird’s eye perspective

Node-config creates a hand full of interfaces: wlan0, wlan1, bat0, br-freifunk as well as vpn interfaces like tap0 and ppp0. They are configured as follows:

External interfaces (switch ports, radio)
  • wlan0 and wlan1 are set up by node-config

    • wlan0 is running in master mode. It’s used by all clients.

    • wlan1 is running in ad-hoc (or 802.11s) mode. This interface is used for meshing.

  • ethX (WAN) and ethY (LAN) are created by OpenWRT. They are not configured by node-config.

    • The LAN interface is used for a management (SSH / LuCI).

    • The WAN interface connects to the internet. It can be used for internet sharing.

    • Optionally, a VPN (openvpn, pptp, wiregard) can tunnel internet traffic.

Internal interfaces (bridges, vpn)
  • bat0 is the batman-adv interface of the system. It utilizes the ad-hoc interface for meshing.

  • br-freifunk is central: It bridges all clients (wlan0) into the roaming domain (wlan1).

  • Other vpn interfaces (fastd: tap0, l2tp: l2tpeth0) are optional: They created and assigned to batman-adv and / or babel to mesh and roam using wires or directed wireless links.

Some interfaces are used by services to provide connectivity:

Miscellous interfaces (not shown)include
  • DHCP and radv services are running on br-freifunk. They hand out ip-addresses to clients. However, these dropped on bat0 using ebtables to reduce broadcast / multicast traffic.

  • babel is running on the wlan1 interface and all vpn interfaces used for mesh routing.

Mind not to bind the optional vpn interfaces to br-freifunk / bat0. It is easy to have a catch-22.

Some interfaces are missing in the picture - have a look at the actual system:

Additional interfaces
  • Most interfaces have an IPv6 counterpart. These duplicates are needed by OpenWRT

  • internet_share is a duplicate of the WAN interfaces. It’s used for copying WAN routes into the freifunk routing tables.

  • By default, node-config creates two optional interfaces using fastd. One is for Routing within Freifunk communities (enabled), while the other is part of the Freifunk / Gluon supernode template (disabled)

6. /etc/config/wireless

The wireless configuration is generated using a shell script Additional information in wireless configuration can be found in the OpenWRT wiki. the OpenWRT wiki.

Please adjust if needed

Content of
if [ -z $(uci get wireless.radio0) ]; then
	echo "No wifi device found"

radio0_disabled=$(uci get wireless.radio0.disabled)
radio0_default=$(uci get wireless.default_radio0)

if [ "$radio0_disabled" == "1" ] && [ "$radio0_default" == "wifi-iface" ];then
	echo "Disabled adapter with default configuration found - disabling default configuration"
	uci set wireless.default_radio0.disabled='1'

echo "Creating wifi interface on radio0 - Assuming 2.4 Ghz - Using channel 1"
uci -q batch <<EOF
	set wireless.radio0.disabled='0'
	set'1'                 # Radio settings
        set wireless.radio0.htmode='HT20'
        set wireless.wifi_freifunk='wifi-iface'         # 1. wifi: Accesspoint
        set wireless.wifi_freifunk.device='radio0'
        set wireless.wifi_freifunk.mode='ap'
        set wireless.wifi_freifunk.ssid='Freifunk'

        set wireless.wifi_mesh='wifi-iface'             # 2. wifi: ad-Hoc mesh
        set wireless.wifi_mesh.device='radio0'
        set'mesh babel_mesh'
        set wireless.wifi_mesh.mode='adhoc'
        set wireless.wifi_mesh.ssid='42:42:42:42:42:42'
        set wireless.wifi_mesh.bssid='42:42:42:42:42:42'
        set wireless.wifi_mesh.mcast_rate='12000'

radio1=$(uci get wireless.radio1)
if [ $radio1 ];then
	echo "Found 2nd wifi-interface - Assuming 5Ghz - Using channel 36"
	radio1_disabled=$(uci get wireless.radio1.disabled)
	radio1_default=$(uci get wireless.default_radio1)

	if [ "$radio1_disabled" == "1" ] && [ "$radio1_default" == "wifi-iface" ];then
        	echo "Disabled adapter with default configuration found - disabling default configuration"
        	uci set wireless.default_radio1.disabled='1'
	uci -q batch <<EOB
        	set wireless.radio1.disabled='0'
		set'36'                 # radio settings
        	set wireless.radio1.htmode='HT40'
		set wireless.wifi_freifunk5='wifi-iface'         # 1. wifi: accesspoint
        	set wireless.wifi_freifunk5.device='radio1'
        	set wireless.wifi_freifunk5.mode='ap'
        	set wireless.wifi_freifunk5.ssid='Freifunk'

        	set wireless.wifi_mesh5='wifi-iface'             # 2. wifi: ad-Hoc mesh
        	set wireless.wifi_mesh5.device='radio1'
        	set'mesh5 babel_mesh5'
        	set wireless.wifi_mesh5.mode='adhoc'
        	set wireless.wifi_mesh5.ssid='42:42:42:42:42:42'
        	set wireless.wifi_mesh5.bssid='42:42:42:42:42:42'
        	set wireless.wifi_mesh5.mcast_rate='12000'



uci commit wireless
Three things are done by
  1. It detects how many radios are available

    • If there’s a radio, 2.4 Ghz is assumed. It’s locked to channel 1 and enabled

    • If there’s a second radio, 5 Ghz is assumed. It’s locked to channel 36 and enabled.

    • Other radios are ignored

  2. If unused OpenWRT default configurations are present on a radio, they are disabled.

  3. An ad-hoc interface with SSID 42:42:42:42:42:42 and an access point interface with SSID Freifunk is created on all radios detected before. All ad-hoc interfaces use a broadcast rate of 12 MBit/s. While the 5 Ghz radio is using 40 Mhz, the 2.4 GHz has 20 MHz.

6.1. Naming the Service Set ID (SSID):

You can adjust the BSSID and SSID according to your needs. Creating separated networks requires using different SSIDs. For roaming, all SSID of a network must be the same:

On the one hand, moving to an accesspoint with a different SSID will reinitialize the wifi card - all active connections will be lost. On the other hand, all mesh stations need the same SSID to connect to each other. The network will fall apart, if they differ.

Keep in mind, that the Extended Service Set ID (ESSID) is called SSID in OpenWRT. It is the network name shown on clients. The Basic Service Set ID (BSSID) defines the technical identifier of the ad-hoc segment. By its design, ad-hoc networks can fall apart by mistakenly generating different BSSIDs for the same logical segment. Thus the BSSID needs set to fixed value. The issue is addressed by IEEE 802.11s.

For more details on service sets have a look at wikipedia.

6.2. Using IEEE 802.11s

You can use the new IEEE 802.11s mesh mode instead of the new ad-hoc. To do so, you need to modify

Modication to - example for radio0 (first radio, 2.4 Ghz usually).
# ...
        set wireless.wifi_mesh='wifi-iface'
        set wireless.wifi_mesh.device='radio0'
        set'mesh babel_mesh'
        set wireless.wifi_mesh.mode='mesh'
        set wireless.wifi_mesh.mesh_id='42:42:42:42:42:42'
        set wireless.wifi_mesh_fwding='0'
        set wireless.wifi_mesh.mcast_rate='12000'
# ...

When modifying the 5 Ghz network, use wifi_mesh5 instead of wifi_mesh.

Setting mesh_fwding='0' disables forwarding in the IEEE 802.11s mesh network. Forwarding is disabled, because Babel and batman-adv need to see the topology on their own. Forwarding would hide the structure of network from both babel and batman-adv. In addition, it is redundant to batman-adv.

If experimenting with IEEE 802.11s mesh forwarding:
  1. Disable batman-adv - directly attach the mesh interfaces to the Freifunk bridge

  2. Try to make babeld using the overlay metric

  3. Avoid re-transmitting babel messages - IEEE 802.11s will distribute them anyway.

  4. Happy hacking :-).

7. /etc/config/batman-adv

Batman-adv is used for roaming. It uses all wifi ad-hoc interface (2.4 Ghz and 5 Ghz). In addition, VPN (l2tp, fastd) interfaces can be added to improving roaming when no radio contact is available.

The configuration does not contain any network interfaces. The mapping is done in /etc/config/network. More details configuration options are listed in the Open-Mesh wiki.

batman-adv.uci creates a mesh (bat0) using these options.
package batman-adv

# Batman-adv is used for roaming
# Every node is a batman-adv gateway, since it runs a dhcp service
# DAT, aggregated_ogms and bridge_loop_avoidance are enabled.
config mesh 'bat0'
	option orig_interval '5000'
	option bridge_loop_avoidance '1'
	option gw_mode 'server'
	option fragmentation '1'
	option aggregated_ogms '1'
	option distributed_arp_table '1'

Having hardly any mobility in a Freifunk-style network, an originator interval of 5 seconds is enough. Bridge loop avoidance, the distributed arp table and ogm aggregation is enabled.

Fragmentation is enabled for roaming using wired interfaces. Usually, ethernet, l2tp or fastd connections cannot transmit a complete batman-adv frame using up to 1532 bytes without fragmentation.

8. /etc/config/network

The network configuration is probably the most complex part of node-config. Be brave, you’ll make it :-).

Have a look at the for additional help on OpenWRT’s network configuration.

8.1. Network Interfaces

Let’s look at the network interfaces first (cf.

These network interfaces are created in /etc/config/network
# Freifunk-Interface - main interface of the node
# This bridge has the configured IPv4 and IPv6 networks
# It's using the Freifunk routing table having a dual-stack configuration

# Using ip6assign und ip6class the ula space and DHCPv6 PD prefixes are used for delegation
config interface 'freifunk'
        option disabled '1'
	option ifname 'bat0'
	option type 'bridge'
	option auto '1'
	option proto 'static'
	option ipaddr ''
	option netmask ''
	option ip6prefix 'fdd3:5d16:b5dd:f064::/64'
	option ip6assign '64'
	list ip6class 'vpn6'
	list ip6class 'freifunk'
	list ip6class 'internet_share6'

# bat0 interface - used in the br-freifunk bridge to access batman-adv
config interface 'bat0'
	option proto 'none'
	option ifname 'bat0'

# The fastd interface is used for Freifunk Routing. Being included in babel but not in fastd,
# Other Mesh-segments are reachable using a vpn.
# For IPv4 the node's IP address is used with a /32 netmask
# For IPv6 only host routes are used
# It's disabled by default an enabled, when IP-addresses are set (i.e. using
config interface 'fastd'
    option disabled '1'
	option ifname 'tap-icvpn'
	option proto 'static'
	option ipaddr ''
	option netmask ''
	option ip4table '66'
	option ip6table '66'

# The VPN is used for sharing internet using a VPN tunnel. Both IPv4 and IPv6 configurations are provided
config interface 'vpn'
	option proto 'none'
	option ifname 'vpn-nat'
	option ip4table '66'
	option ip6table '66'

config interface 'vpn6'
	option proto 'dhcpv6'
	option ifname 'vpn-nat'
	option ip6table '66'

# Batman-adv meshes have ot be configured for 2.4 and 5 Ghz sperately.
# An ad-hoc wifi interface is mapped to each interface definition
# 2.4 GHz
config interface 'mesh'
	option proto 'batadv'
	option mtu '1532'
	option mesh 'bat0'

# 5 Ghz
config interface 'mesh5'
        option proto 'batadv'
        option mtu '1532'
        option mesh 'bat0'

# When used as a Freifunk/Gluon supernode, an additional fastd interfaces has to be added to babel.
# It is disabled by default
config interface 'mesh_supernode'
    option disabled '1'
    option proto 'batadv'
    option mtu '1532'
    option mesh 'bat0'
	option ifname 'tap-supernode'

# The share the internet direct, the wan interfaces has to be re-definied using a different routing table
# This copies all WAN routes to table 66
# This has to be done for IPv4 and IPv6 separately
config interface 'internet_share'
        option ifname '@wan'
        option proto 'dhcp'
        option ip4table '66'
        option disabled '1'

config interface 'internet_share6'
        option ifname '@wan'
        option ip6table '66'
        option disabled '1'
        option proto 'dhcpv6'

That is it :-). Try matching these interfaces with bird’s eye perspective picture in the beginning (Figure 1).

You might need to adjust some interfaces according to your local setup (i.e. internet_share - use proto 'static' instead of proto 'dhcp'). All vpn and fastd interfaces can be removed, if vpn connections are not in use.

8.2. Routes & Rules

Node-config uses a dedicated routing table (66). This allows separating the mesh from uplink and management networks. To build this table, all relevant IPv4 and IPv6 routes are duplicated. In addition, IP rules assign mesh traffic to this table.

Have a look at the Linux Advanced Routing & Traffic Control HOWTO for more information on policy based routing.

Routes and Rules in network.uci
# The configured IPv4 network is reachable in the freifunk bridge
config route 'route4_node_subnet'
	option interface 'freifunk'
	option target ''
	option netmask ''
	option gateway ''
	option table '66'

# The configured IPv6 network is reachable in the freifunk bridge
config route6 'route_6_node_subnet'
	option interface 'freifunk'
	option target 'fdd3:5d16:b5dd:f064::/64'
	option table '66'

# For both babel interfaces (2.4 GHz, 5 GHz) there's a host route for the interface address
config interface 'babel_mesh'
        option disabled '1'
        option proto 'static'
        option ipaddr ''
        option netmask ''
        option ip4table '66'
        option ip6table '66'

config interface 'babel_mesh5'
        option disabled '1'
        option proto 'static'
        option ipaddr ''
        option netmask ''
        option ip4table '66'
        option ip6table '66'

# Use table 66 for all Freifunk traffic
config rule 'rule4_freifunk_out'
   	option out     'freifunk'
	option lookup '66'
	option priority '66'

config rule6 'rule6_freifunk_out'
        option out     'freifunk'
	option lookup '66'
        option priority '66'

config rule 'rule4_freifunk_in'
        option in     'freifunk'
        option lookup '66'
        option priority '66'

config rule6 'rule6_freifunk_in'
        option in     'freifunk'
        option lookup '66'
        option priority '66'

Unfortunately, OpenWRT cannot create all rules using /etc/config/network. Thus they’re created using the firewall script /etc/firewall.user. Being part of node-config modies the the firewall script. || true prevents firewall.user from failing, if an interface is not available, yet.

  • When adding new interfaces to babel (i.e. l2tp connections), new rules have to be added accordingly.

  • The firewall is reload, when new interfaces become available.

Rules added by firewall.user
mv /etc/rc.local /tmp/rc.local

echo "

# Hack:
# Since /etc/config/network cannot be used for intering rules, the firewall script is used

# Pref 66 is important. It prevents rules with priority 0 (for local)

ip rule del iif wlan0 lookup 66 pref 66 || true
ip rule del oif wlan0 lookup 66 pref 66 || true
ip rule del iif wlan1 lookup 66 pref 66 || true
ip rule del oif wlan1 lookup 66 pref 66 || true
ip rule del iif tap-icvpn lookup 66 pref 66 || true
ip rule del oif tap-icvpn lookup 66 pref 66 || true

ip rule add iif wlan0 lookup 66 pref 66
ip rule add oif wlan0 lookup 66 pref 66
ip rule add iif wlan1 lookup 66 pref 66
ip rule add oif wlan1 lookup 66 pref 66
ip rule add iif tap-icvpn lookup 66 pref 66
ip rule add oif tap-icvpn lookup 66 pref 66

ip -6 rule del iif wlan0 lookup 66 pref 66 || true
ip -6 rule del oif wlan0 lookup 66 pref 66 || true
ip -6 rule del iif wlan1 lookup 66 pref 66 || true
ip -6 rule del oif wlan1 lookup 66 pref 66 || true
ip -6 rule del iif tap-icvpn lookup 66 pref 66 || true
ip -6 rule del oif tap-icvpn lookup 66 pref 66 || true

ip -6 rule add iif wlan0 lookup 66 pref 66
ip -6 rule add oif wlan0 lookup 66 pref 66
ip -6 rule add iif tap-icvpn lookup 66 pref 66
ip -6 rule add oif tap-icvpn lookup 66 pref 66

ip -6 rule add iif wlan1 lookup 66 pref 66
ip -6 rule add oif wlan1 lookup 66 pref 66
ip -6 rule add iif tap-icvpn lookup 66 pref 66
ip -6 rule add oif tap-icvpn lookup 66 pref 66

" > /etc/rc.local
chmod 755 /etc/rc.local
cat /tmp/rc.local >> /etc/rc.local

8.3. PPtP configuration

network_pptp.uci has additional interfaces for PPtP. Due to this separation, PPtP interfaces are not created, when using the node-config opkg-package, only.

PPtP interface from /etc/config/network_pptp.uci
# VPN - if pptp is used
# Servers are
# -
# -
# Accounts: please mail
config interface 'vpn_pptp'
        option disabled '1'
        option proto 'pptp'
        option server ''
        option username 'user'
        option password 'password'
        option ip4table '66'
        option ip6table '66'

config interface 'vpn_pptp6'
        option proto 'dhcpv6'
        option ifname '@vpn_pptp'
        option ip6table '66'

9. /etc/config/babeld

Babeld is used for routing. It uses the ad-hoc wifi interfaces. The optional interface tap-icvpn is included for Freifunk routing.

/freifunk/initial_configuration/babeld.uci defines these options:
package babeld

# Babel uses the mesh routing table (66) for import and export
# Routes are created with a low priority (2048); thus they're not considered local.
# By that, a default route on a (Open)VPN interface has higher precedence.
config general
	option ipv6_subtrees 'true'
	option export_table '66'
	option import_table '66'
	option kernel_priority '2048'

# Export all static routes from the freifunk tables
# This is the configured IPv4 and IPv6 network of the router
config filter 'redistribute_static'
	option type 'redistribute'
	option proto '4'

# Do not export local routes (LAN-IP, address, etc.)
config filter
	option type 'redistribute'
	option local 'true'
	option action 'deny'

# Bind to both ad-hoc (802.11s) interfaces
config interface
	option ifname 'wlan0'
	option type 'wireless'

config interface
        option ifname 'wlan1'
        option type 'wireless'

# Optional fastd-interface for Freifunk Routing
config interface
	option ifname 'tap-icvpn'
	option type 'tunnel'

10. /etc/config/dhcp

OpenWRT uses dnsmasq and odhcpd to distribute prefixes. Node-config adds new definitions to. The OpenWRT wiki explains the DHCP options in use: [1, 2]

/etc/config/dhcp is used to
  • Hand out IPv4 address configured manually (DHCP)

  • Announce the IPv6 network configured manually (radv)

Some configuration is done in /etc/config/network
  • Re-distributing prefixes assigned automatically to the internet uplink (DHCPv6 PD).

  • Annoucing the manually assigned /64 range for SLAAC.

These stanzas are added by dhcp.uci
package dhcp

# A DHCP and radvd service is configured for the Freifunk interface
# The 254 address are used out the the manual prefix.

# For radvd, a default route is annouced. This enables routing freifunk traffic
# Subnet delegation is inactive. Subnets are delegated using the mesh interface, only

# Annoucing the configured /64 network is already part of /etc/network/interfaces.
# It doesn't hae to be configured, here
config dhcp 'freifunk'
	option interface 'freifunk'
	option start '2'
	option limit '254'
	option leasetime '10m'
	option dhcpv6 'none'
	option ra 'server'
	option dhcp_option '6,,,'
	option ra_offlink '1'
	option ra_default '1'

11. /etc/config/fastd

Fastd is a simple, decentralized vpn software. By default, fastd does neither switching nor routing. Both is handled by batman-adv and babel.

VPN definitions fastd.uci
package fastd

#fastd is used for Freifunk routing and supernode-stuff

# Test peer for Freifunk routing.
# Better: Setup your own peer and connect it to the icvpn usign BGP
config peer 'icvpn_test'
	option remote 'ipv4 "" port 11003'
	option enabled '1'
	option net 'backbone'
	option key 'fa824f8d39adcb3ad6232660bda439020d3c2e61dd56466439fc66c0ee159b41'

# Network definition for Freifunk routing / disabled by default
config fastd 'backbone'
	option enabled '0'
	option syslog_level 'warn'
	option mode 'tap'
	option interface 'tap-icvpn'
	option mtu '1280'
	option forward '0'
	option secure_handshakes '1'
	list method 'salsa2012+umac'
#	list bind 'any:10000' # Bind is done using shell
	option secret 'generate'

# Template for Gluon supernodes / disabled by default
config fastd 'supernode'
	option enabled '0'
	option syslog_level 'warn'
	option mode 'tap'
	option interface 'tap-supernode'
	option mtu '1312'
	option forward '0'
	option secure_handshakes '1'
	list method 'salsa2012+umac'
	list method 'null'
#	list bind 'any:10001' # Bind is done using shell
	option secret 'generate'

fastd is bound to the lan and lan interface, only. It makes no sense to use the mesh to tunnel mesh packages. Unfortunately, interfaces cannot be referenced by its OpenWRT name - thus the binding is done using shell.

fastd binding:

wan_if=$(uci get network.wan.ifname 2> /dev/null) # Ignore errors => No interface anyway
lan_if=$(uci get network.lan.ifname 2> /dev/null) # Ignore errors => No interface anyway

# Make sure, that fastd is using only lan and wan interfaces to avoid tunnel-in-tunnel situations
if [ $wan_if ];then
	uci add_list fastd.backbone.bind="any:10000 interface \"$wan_if\""
	uci add_list fastd.supernode.bind="any:10001 interface \"$wan_if\""

if [ $lan_if ];then
	uci add_list fastd.backbone.bind="any:10000 interface \"$lan_if\""
        uci add_list fastd.supernode.bind="any:10001 interface \"$lan_if\""
uci commit fastd

12. /etc/config/firewall & firewall.user

At quick glance, the firewall is used to seperate the mesh from other networks. Firewalling is done using the OpenWRT firewall and ebtables. See OpenWRT → Firewall for more information on OpenWRT’s firewalling options.

The firewall
  • Creates a vpn and a freifunk zone. Forwarding from freifunk to vpn is allowed.

  • Exposes the fastd vpn on the WAN interface (meshing, roaming via vpn)

  • Makes the router accessible from the freifunk zone

  • Takes care of NAT internet traffic

  • Blocks broadcast / anycast traffic to the batman-adv mesh

Set a password on your router. It is accessible from the mesh network.
OpenWRT firewall configuration
package firewall

# The firewall seperates the mesh from other networkss such es wan or lan.

# Mind, that this configuration is not trusted
# your mesh router can still be compromies i.e. by exploiting babeld.
# This allows unfiltered access to your wan network.

# Allow: fastd from wan (roaming, meshing using vpn)
config rule
	option name 'fastd'
	option src 'wan'
	option dest_port '10000'
	option proto 'udp'
	option target 'ACCEPT'
	option family 'ipv4'

# Freifunk zone definition
# It has all the interfaces used for mesh routing (babel) and the central freifunk brdige.
config zone 'freifunk'
	option forward 'ACCEPT'
	option output 'ACCEPT'
	option input 'REJECT'
	option name 'freifunk'
	list network 'freifunk'
	list network 'babel_mesh'
	list network 'babel_mesh5'
	list network 'fastd'
	option mtu_fix '1'

# In the freifunk zone, the router can be access by
# 1st Babel
config rule
	option name 'Babel'
	option src 'freifunk'
	option proto 'udp'
	option dest_port '6696'
	option limit '1000/sec'
	option target 'ACCEPT'

# 2nd IGMP
config rule
	option name 'Allow IGMP (Freifunk)'
	option src 'freifunk'
	option proto 'igmp'
	option family 'ipv4'
	option target 'ACCEPT'

# 3rd ICMP
config rule
	option name 'Allow ICMP (Freifunk)'
	option src 'freifunk'
	option proto 'icmp'
	option family 'ipv4'
	option target 'ACCEPT'

# 4th DHCP
config rule
	option name 'Allow DHCP request (Freifunk)'
	option src 'freifunk'
	option dest_port '67-68'
	option proto 'udp'
	option target 'ACCEPT'
	option family 'ipv4'

# 5th ICMP v6
config rule
	option name 'Allow-ICMPv6-Input (Freifunk)'
	option src 'freifunk'
	option proto 'icmp'
	option limit '1000/sec'
	option family 'ipv6'
	option target 'ACCEPT'

# 6th SSH (Mind setting a password)
config rule
	option name 'Allow SSH (Freifunk)'
	option src 'freifunk'
	option dest_port '22'
	option proto 'tcp'
	option target 'ACCEPT'

# Internet exit through VPNs need another zone, since it has to by masquerade
config zone
	option forward 'ACCEPT'
	option output 'ACCEPT'
	option input 'REJECT'
	option name 'vpn'
	option network 'vpn vpn6 vpn_pptp vpn_pptp6'
	option masq '1'
	option mtu_fix '1'

# In the VPN zone, some incoming traffic is allowed
# 1st ICMPv6 incoming
config rule
        option name 'Allow-ICMPv6-Input (VPN)'
        option src 'vpn'
        option proto 'icmp'
        option limit '1000/sec'
        option family 'ipv6'
        option target 'ACCEPT'

# 1st ICMPv6 outgoing
config rule
        option name 'Allow-ICMPv6-Output (VPN)'
        option dest 'vpn'
        option proto 'icmp'
        option limit '1000/sec'
        option family 'ipv6'
        option target 'ACCEPT'

# This allows accessing the internet to a vpn tunnel
config forwarding 'freifunk_internet'
	option dest 'vpn'
	option src 'freifunk'

# To allow direct access, change it to:

# config forwarding 'freifunk_internet'
#	option dest 'wan'
#	option src 'freifunk'

# Mind handling abuse

# To prevent direct exit traffic accessing your uplink network,
# RFC1918 and ULA addresses are blocked.
# Please mind, that accessing public IP addresses is allowed, still.
config rule
        option name 'Do not forward freifunk traffic to RFC1918 address in WAN zone'
        option src 'freifunk'
        option dest 'wan'
        list dest_ip ''
        list dest_ip ''
        list dest_ip ''
        option family 'ipv4'
        option target 'REJECT'
        option proto 'all'

config rule
        option name 'Do not forward freifunk traffic to local ipv6-addresses in WAN zone'
        option src 'freifunk'
        option dest 'wan'
        list dest_ip 'fc00::/7'
        list dest_ip 'fec0::/10'
        option family 'ipv6'
        option target 'REJECT'
        option proto 'all'

Broadcast / anycast traffic is blocked using ebtables. ebtables rules are not exposed via uci. takes care of adding this rules to the firewall.user script.

Contents of

# ebtables Multicast-filter - not exposed to uci
# reject multicast / broadcast within the batman-adv mesh
# allow DHCP and ARP - it is translated into unicast
echo "
ebtables -F
ebtables -A FORWARD -p ARP -j ACCEPT
ebtables -A FORWARD -p IPv4 --ip-protocol udp --ip-destination-port 67:68 -j ACCEPT
ebtables -A FORWARD -d Multicast -j DROP

#" > /etc/firewall.user

For policy routing, additional ip-rules are added to See Routes & Rules for details.

13. /etc/config/openvpn

OpenVPN is used to tunnel internet traffic. Using external configuration files, the different providers are referenced, only.

package openvpn

# OpenVPN-configuration for tunnel providers
# Beispiel fuer mullvad - hier enabled (vgl.
config openvpn 'mullvad'
	option enabled '0'
	option config '/lib/freifunk/vpn/mullvad/mullvad.conf'

# Freifunk-KBU Exit-VPN
# More information:
# -
config openvpn 'freifunk_kbu'
	option enabled '0'
	option config '/lib/freifunk/vpn/freifunk_kbu/client.conf'

# yanosz' Freifunk-Exit-VPN
# Used for testing
# Please for access.
config openvpn 'yanosz'
	option enabled '0'
	option config '/lib/freifunk/vpn/yanosz/client.conf'

Here is an example for an external configuration. It can be used as a template for other providers. Refer to [the OpenVPn website] for information on different OpenVPN options.

# yanosz Freifunk-Exit-VPN
# used for testing
# Plase mail for access

# Path - adjust if needed

# OpenVPN must not configure routing - it cannot use a different routing table such as 66
# Routes are set using a custom scrip
up /lib/freifunk/vpn/

remote 1194
proto udp

# As an alternative
#remote 443
#proto tcp

# OpenVPN-specifics
client                  # Its a client
dev vpn-nat
dev-type tun            # Keep aligned with /etc/config/network
cipher AES-256-CBC      # 256-Bit AES encryption is used
resolv-retry infinite   # Try to establish a connection continuously
remote-cert-tls server
ping-restart 60         # 60 Sekunden Ping Restart Timer
script-security 2       # Scripts - needed to execute
ca ca.crt
ping 10
link-mtu 1350			# Small MTU (tunneling through adsl lines has some offset)
When setting up a new vpn provider, some settings need to be added to the provider’s configuration:
  1. route no-pull prevents OpenVPN from installing routes in the default routing table

  2. up /lib/freifunk/vpn/ makes OpenVPN installing a default route in the freifunk table (66), when the interface is going up.

  3. script-security 2 allows OpenVPN to execute scripts such as

  4. dev vpn-nat uses the correspondig interface defined in /etc/config/network

Advanced Topics

You made a long way. The documentation has many complex details and its quite easy to get lost. Anyway, you made it :-).

This chapter focuses on advanced topics. It explains how to use node-config to build packages, create a firmware and to do configuration management.

Illustrating underlaying concepts such as multihomming, roaming an Freifunk routing is also part of this chapter.

14. Building node-config

Node-config uses different Makefiles to generate packages and documentation. When building node-config, architecture independent opkg packages ("all") are generated using the OpenWRT SDSK.

Also, this chapter describes the underlaying network architecture and propose working methods for node-config.

14.1. Makefiles

Node-config is built using Makefiles. The project contains two Makefiles

  • ./Makefile is the global makefile. Packages and Documentation is generated using make dist. Have a look at the makefile for other targets

  • ./lede_build/node-config/Makefile defines the actual opkg packages. By default, node-config is split in node-config (base part), node-config-openvpn (openvpn configuration and dependencies) and node-config-pptp (pptp configuration and dependencies).

15. Generating a OpenWRT package repository (feed)

Running make dist creates a opkg package repository (feed) at dist/feed.

To use this repository on an OpenWRT router, all packages must be signed using a public/private keypair. The private key is used to create a signature and has to be present on the router. Keys are generating using usign, that is part of the OpenWRT SDK.

Generating a new keypair
./sdk_home$ staging_dir/host/bin/usign -G -s secret.key -p public.key
./sdk_home$ cat public.key
untrusted comment: public key 9cf140f9fda134d8
./sdk_home$ mv public.key 9cf140f9fda134d8 # Change accordingly.

In this example, the key id is 9cf140f9fda134d8. Thus the file public.key has to be renamed to 9cf140f9fda134d8. To use the key on OpenWRT, it has to be placed at /etc/opkg/keys/9cf140f9fda134d8.

To create a signed feed, execute:
$  make dist BUILD_KEY=/full/path/to/secret.key
$ cat dist/feed/Packages.sig
untrusted comment: signed by key 9cf140f9fda134d8

Copy dist/feed to a webserver and place a reference at /etc/opkg/feeds.conf.

Consider make repository accessibly via http and https: opkg doesn’t support SSL/TLS - the imagebuilder does not require the key to present but supports TLS.

16. A Graphical User Interface (GUI).

A Freifung-styled AngularJS based WebUI is available at It’s still under active development. Contributors welcome :-).

17. Creating Firmware & Configuration Management

Although node-config is not focussing on creating a firmware blob, OpenWRT’s imagebuilder. can be used for doing so.

Creating a firmware for ar71xx devices without pptp
$ wget
$ tar xf openwrt-imagebuilder-18.06.1-ar71xx-generic.Linux-x86_64.tar.xz
$ cd openwrt-imagebuilder-18.06.1-ar71xx-generic.Linux-x86_64
$ echo "src/gz nodeconfig >> repositories.conf
$ make image PROFILE="Default" PACKAGES="node-config node-config-openvpn ip-full luci-ssl"
A gitlab-ci job building firmware including the GUI is available at

TBD: Configuration management using mesh testbed generator

18. Underlaying concepts

18.1. Roaming

Roaming concerns clients moving from one accesspoint to another. To keep all connections alive, traffic must be redirected.

Roaming from A to B, the wifi association is updated: In the beginning, all data is transmitted to node A. When changing the association, data goes via B instead. This is illustrated in Figure 2. Due to node A and B using the same ESSID, the client interfaces is not reset.

babel roam
Figure 2. Roaming

In this scenario, packets need to be handled by a different nodes: While node A handle’s the clients packets in the beginning, node B is responsible in the end. This is, where things get messy: Network state needs to be updated. IP-packets need to find a new way. However, some ideas exists:

  1. Creating a large, ethernet segment using batman-adv: As done in classical ethernet networks, a client’s location is updated using broadcast / multicast continuously behind the scenes. In this a flat, MAC-based address scheme, routes cannot be aggregated. In Freifunk communities, these networks are VPN based and scale up to 1000 ~ 2000 nodes.

  2. Update IP routing on roaming: Instead of creating a large ethernet segment, the network is split into one segment per node. One dedicated subnet is assigned per segment. IP-Packets are routed between those segments using traditional IP-routing. Whenever a client roams, a host route is added to the routing table. This approach may reduce the size of the routing table. But in order to so, each node needs to know all IP-addresses used by clients roaming into its segment. Getting only a mac address from the wifi association, this task is very challenging. l3roamd tries to go this way, but it is not ready for production, yet.

  3. Mobile-IP like approaches. In Mobile IP traffic is handled by a home node and passed along the network. It takes care of maintaining reachability information and passing packets to its clients. As a downside, routing through a home node may increases the forwarding path. In addition, management traffic for both batman-adv and babel has to be handled.

Combining babel and batman-adv, node-config includes configuration for a mobile IP like system, while not being restricted to it. Its functionality is illustrated in the next sections:

At quick glance, using routing two routing protocols (babel and batman-adv) at the same-time seems counter-intuitive: It’s just duplicating the management effort, isn’t it? Of course, an ideal mesh protocol would take care of routing and roaming; propagating reachability information on MAC and IP-addresses alongside. Unfortunately, no such protocol exist.

In an Mobile IP like setup, the forwarding path doesn’t increase necessarily. In order to prevent source ip spoofing and to maintain NAT state in a multi homed network, external traffic is routed via fixed "home-ish" gateway node in any situation.

18.1.1. Roaming in node-config using IPv4

Figure 2 illustrates roaming using IPv4. A client roams from node A to B. The traffic-flow is green, while network connections are red.

The batman-adv mesh cloud connects all segments in transmission range. It acts as a huge network switch.

babel ipv4 roam
Figure 3. IPv4 Roaming

The client is using from A’s dhcp range. By that, node A is the home node of the client. (Read this as: is set a gateway using dhcp).

When roaming, node A is responsible for handling all the traffic. The client reaches node A ( using the batman-adv cloud (and vice versa). Having batmn-adv’s distributed ARP table enabled, ARP broadcasts won’t hurt the mesh. Thus - as in classical ethernet - both are talking ARP using their MAC-addresses.

Keep in mind, that ethernet segments are limited by the transmission range of the nodes. There is no large ethernet segments connecting all nodes using a VPN.

18.1.2. Roaming in node-config using IPv6

For IPv6 the situation is more complex. In contrast to IPv4, clients have multiple addresses and gateways provided by the radv-protocol.

In node-config, a distinct default route is announced by every router (distinct: unique link local address). When a clients roams to a different node, it has two default routes, at least. This is illustrated in figure 4.

babel ipv6 roam
Figure 4. IPv6 Roaming

As before, the clients roams from node A to B. Before connecting to node B it is reachable at fd00:1::42. This address is allocated using slaac in node A’s segment. Node A is used for routing (route #1)

When moving to node B, the client receives an additional route (route #2) and address (not shown) from the node B. Having two default routes, the client can access other networks via node A (route #1) and node B (route #2).

  • route #1:

    • next-hop: link local address of node A.

    • When selected, the client tries to reach node A by its MAC-address.

    • mac-address: reachable via batman-adv

  • route #2:

    • next-hop: link local address of node B

    • mac-address: based in the infrastructure network (no batman-adv).

Mind, that the traffic (inbound vs. outbound) is slightly asymmetric.

From the client’s point of view:
  • Incoming traffic arrives via node A, only. To reach the client, it is send trough the batman-adv cloud.

  • Outgoing traffic is somewhat asymmetric:

    • If the client chooses the old route, traffic is reaching node A through the batman-adv cloud

    • If the client chooses the new route, traffic may by routed via node A using babel; external traffic (i.e. internet) always leaves the mesh via node A due to source specific routing.

Although babel and batman-adv are operating in the same topology, they use different metrics. Thus babel and batman-adv may choose different paths from B to A.

Have in mind, that node-config blocks all multicast traffic in batman-adv including IPv6 Neighbor Solicitation (NS). By doing so, Node A has no chance to discover the client’s mac address after roaming. Node config assumes:

ICMPv6 NS assumptions:
  1. All MAC addresses are cached while relevant.

  2. Address collisions won’t occur, since nodes have just a few clients.

  3. A client learning a routers mac from a radv-message doesn’t need NS to find the correspondig router at all time.

18.1.3. Roaming in wifi range vs. planned roaming

If nodes are in in transmission range, roaming will be easy: packets from B to A are sent using wifi, after the client moved from A to B. This seems to be realistic: When a client roams, it is connected to the wifi all time long: If the signal is lost, the network interface will loose its association and the connection is reset.

Unfortunately, this doesn’t work out in any case:

Problematic situations:
  1. There’re obstacles around (i.e. walls) shadowing one node: one node won’t be able to reach the other - packets will get lost.

  2. Retransmitting packets uses twice the airtime (at least). This reduces the overall performance.

  • Shadowing by obstacles causes hidden and exposed terminals (wikipedia). Try to avoid this, by putting some effort into placing the nodes (i.e. line of sight).

  • By default, all radios in the mesh are using the same radio frequencies to connect to each other. This is good for sparse networks. For dense networks it is better to have devices with multiple radios, operating on different frequencies. This involvces frequency planning (wikipedia).

In these situations you can offload roaming traffic i.e. by using wired or directed links. In many situations, its good to use a fastd based vpn (TODO yanosz: add a fastd example configuration for offloading in node-config). While fastd is easy to setup and fits for many situations, other technolgies (ethernet, l2tp) provide a better performance:

To offload traffic on a layer2 broadcast medium:
  1. Choose a medium for off-loading (i.e. ethernet, p2p-wifi, l2tp).

  2. Create a corresponding OpenWRT configuration

  3. All all interfaces to babel in /etc/config/babel

  4. Add all interfaces to the freifunk firewall-zone in /etc/config/firewall

  5. Add policy-routing for these interfaces using table 66 in /etc/firewall.user

  6. Add a batman-adv mesh definition for all interfaces in /etc/config/network.

  7. Modify /lib/freifunk/ to set and IPv4-address (/32) on all related interfaces, execute it, reboot the node.

Unfortunately, policy routing and firewalling makes it very complex to add a new link for offloading. This is the downside of having a Freifunk-style exit-vpn using an arbitrary provider. When removing this functionality, you can skip steps 4 and 5. Using fastd for all links somewhat simplifies this situation, because - unlike l2tp - only one tap interface definition is needed for any number offloading peers.

Have in mind:
  1. Having a shared network (i.e. ethernet switch, tinc in switched mode) creates a full mesh. Management traffic increases quadratic in the number of nodes. Consider star or snowflake topologies using p2p links like fastd or l2tp.

  2. babel and batman-adv use broadcast / multicast traffic for management. This is an issue when using directed wifi point-to-point links: By specification, this kind of traffic is sent using a very low datarate to reach all stations in transmission range, drastically reducing the capacity of the wireless link. Consider encapsulating this traffic using p2p-vpns like l2tp or fastd.

18.2. “Multihoming” & Prefix Delegation

A network run by node-config may have multiple internet uplinks. Typically, internet uplinks are provided by different providers using different IPv4 and IPv6 networks.

This is illustrated in figure 5. By design, each node acts as a router:

  • It routes packets using babel

  • It runs in batman-adv gateway mode, handing out IP addresses.

Figure 5. Multihoming

Routing packets internally is easy: Having distinct non-overlapping IPv4 and IPv6 networks per node, every network is homed at an unique node. Each node announces its prefixes using babel - packets can be routed. If a node is connected to the internet (uplink), it will announce a default route in addition.

Originally, the term multihoming refers to a situation in BGP: A private autonomous system (AS) announces its prefix via different upstream providers. Having the private AS-number removed from the path, all networks appear to be originating from all providers; hence they are home to multiple places.

18.2.1. IPv4 & Network Address Translation (NAT)

By default, node-config uses private IPv4 space. To access the internet, network addresses need to be translated (NAT).

multihome ipv4
Figure 6. IPv4 multihoming

In figure 6 node A and node B are directly connected to the internet. Node A is using - Node B is using In node-config, both A and B take care of NAT: When leaving the mesh network, the sender address is changed to ( resp.).

This design assumes, that the route selection in babel is stable: From the perspective of node D, both A and B have usable default routes; the choice is arbitrary. But if D’s selection changes (i.e. from A to B), the NAT will break: By turning over, the sender address switches from to This breaks all the TCP session of all clients at D.

Babeld’s stability is defined by the half-time parameter - see man babeld for details.

18.2.2. IPv6, source specific routing & Prefix Delegation

The situation is different for IPv6. Instead of translating IP-addresses, routers with internet connectivity provide public address space to all other routers.

IPv6 is probably not mature, yet. Altough, node-config only use OpenWRT’s core components and follows IPv6’s best practices by using prefix delegation, some details are likely to be broken.

In addition, node-config’s IPv6 configuration is largely untested: Only a few vpn providers have support IPv6 prefix delegation.

Address space is assigned using DHCP v6 prefix delegation. See rfc3633 for details. In figure 7, 2001:DB8:AAAA::/56 is assigned to node A by its provider. Node B got 2001:DB8:BBBB::/56.

To use these networks, corresponding addresses must be assigned to clients at nodes C,D,E and F. In order to do so, at least one /64 network must be assigned to each node. Node-config is using the ad-hoc (or 802.11s) network for prefix delegation. Here’s an example:

multihome ipv6
Figure 7. IPv6 multihoming
Example configuration using DHCPv6 PD (dynamic).
  • Node C is connected to A. It gets 2001:DB8:AAAA:1::/64

  • Node D is connected to A and B. It gets 2001:DB8:AAAA:2::/64 and 2001:DB8:BBBB:1::/64

  • Node E is connectd to B. It gets 2001:DB8:BBBB:2::/64

Mind, that this configuration is dynamic: C,D and E may choose to request more than one prefix. In addition, they can request a larger subnet (i.e. /60) for redistribution.

However, there’s catch: If a client uses 2001:DB8:AAAA:2::42, it must use node A to access the internet - node B’s provider will probably reject the packet due to address spoofing (wikipedia). Here source-specific routing comes into action. Node A and B announce a default route for their range, only. Babel takes care of using the correct route

Most internet protocols (i.e. BGP) follow the destination based forwarding paradigm: The route of a packet is determined by its destination address, only.

Source routing is an alternative to destination based forwarding. Here, the path is defined at the sender. This approach is done by some mesh protocols, implemented in IPv4, but hardly used.

Source specific routing somewhat follows the destination based forwarding paradigm. The route is determined by the sender address, only. In addition, routes are restricted to a specified source range, limiting the set for destination based lookups.

Babel is using source specific routing, to comply with the destination based forwarding paradigm, while taking care of multiple IPv6 networks in the same mesh. It’s sad, that most slaac-implemtations do not restrict routes like source specific routing does.

Unfortunately, node-config’s IPv6 functionality is hardly tested.

Here’ll be dragons
  1. What happens, if node A crashes?

    • Will D detect this?

    • Will D stop announcing 2001:DB8:AAAA:1::/64?

    • Will D’s client use 2001:DB8:BBBB:2::/64, only?

  2. Is DHCPv6 proxying better than redistribution?

    • What’s a good redistribution strategy? (density?)

A way out: Static IPv6 assignments.

As an alternative to prefix delegation you can assign IPv6 networks, statically. By providing a public IPv6-network instead of an ULA-network (i.e. when using

This can be done using provider independent (PI) address space or space assigned by large tunnel providers (i.e.

While this is fine for networks under a central administration, the Freifunk philosophy rejects the idea of a central administration or uplink. See Design considerations for details.

A different way out? Network Prefix Translation

Network prefix translation can be used as an alternative to prefix delegation. Instead of handing out public address space, ULA addresses are translated to access the internet. (i.e. from fdc3:337e:00c0:3f04::42 to 2001:DB8:AAAA:5271::42). This is done on all border routers with IPv6 connectivity (i.e. nodes A and B).

Using ULA addresses removes the necessity of maintaining state for network delegations (mind the dragons of Prefix Delegation). Unfortunately, this approach has some drawbacks

Issues with network prefix translation:
  1. End-to-end addressing is a fundamental building block for IPv6 networks. Address translation is violating this paradigm. Expect issues with protocols expecting compliance.

  2. As in IPv4 (c.f. figure 5) changing the path also changes the sender address and resets all TCP connections. Unlike IPv4, you cannot set a fixed default gateway using DHCPv6 (DHCPv6 Route Options never made it an internet standard), thus we have to consider roaming, again:

    1. You can live with it

    2. You can translate all addresses to a statically assigned IPv6 prefix - but then you can do static IPv6 assignments anyway. There’s no need to translate addresses, anymore.

    3. You can use DHCPv6 Prefix Delegation for ULA addresses and babel’s source specific routing. While this looks attractive in the first place (unexpected changes in external delegations won’t ruin your day), the dragons of Prefix Delegation will still be there
      (…​ what happens, if node A crashes …​)

19. Freifunk Networking

Let’s have a look at Freifunk: has a good introduction into the Freifunk community, its motivation, goals and structure.

In contrast to a green-field mesh vpn, integration into existing community networks is an issue - especially, if you want to use your router in an existing Freifunk network. As of today, most Freifunk networks are based on Gluon or connect via the Intercity-VPN (ICVPN).

19.1. Gluon integration / migration

Node-config was developed with Gluon networks in mind. Also, it tries to overcome some limitations of gluon networks. See <<#_Design considerations>> for details.

In gluon networks, node-config can be used as a node, supernode, or vpn-offloader. Although all options are explained at this section, doing so somewhat looses the focus of the points node-config tries to address.

Probably the best way to integrate a node-config router into a Gluon network is to operate it side-by-side.

19.2. Side-by-side operation

In side-by-side operation a node-config router is using the same SSIDs meshing on layer-2. To so do edit /etc/config/wireless according to your Freifunk community.

It also interesting, to connect all node-config routers and supernodes using a routed vpn speaking babel. /etc/config/fastd provides a icvpn template for doing so. This allows end-to-end adressing.

Consider planned romaing (i.e. fastd from node-config to supernode), if and only if a node-config router is in the vicinity of gluon node. The load will be challanging. See <<#_Design_considerations>> for details.

19.3. Gluon Supernode

Node-config’s internals are very similar to a gluon supernode: It speeks batman-adv over fastd and provides DHCP as well as routing and ravd services. In fact, some supernodes are connected using a backbone vpn using babel. There’re some smaller differences, still.

Most OpenWRT routers probably lack the horsepower needed for a supernode - even for a mediocre sized network.

Differences between node-config and gluon supernodes
  • Gluon supernodes to not speak babel to other (non-super)-nodes in the mesh.

  • Usually, a DHCP pool of 250 addresses is not enough for a freifunk network.

Node-config contains a fastd supernode template. Enabling it goes like this:

Starting a local supernode
uci set fastd.supernode.enabled=1
uci commit
/etc/init.d/fastd restart
/etc/init.d/fastd show_key supernode

By that, a fastd instance is started on 10001/udp. It’s accessible using the WAN and LAN interfaces. fastd’s public key is printing using the show_key command. It can to be integrated into the Gluon site configuration - for more information on configuring fastd see

To increase the DHCP pool, you need to change one line in /etc/config/network and /etc/config/dhcp.

/etc/config/network - increase subnet mask:
config interface 'freifunk'
    # .. some other options ..
    option netmask ''
/etc/config/network - increase pool:
config dhcp 'freifunk'
    #   .. some other options ..
    option limit '10000'

19.4. Gluon node / VPN-Offloader

A gluon node is very lean: It connects to a supernode using fastd and speaks batman-adv. It’s not running any other service (i.e. babel, dhcp, routing).

Technically, there’s hardly any difference between a gluon node and a vpn-offloader. Boiling it down, a vpn-offloader is a node also accepting incoming fastd connections.

Typically, vpn-offloaders are more powerful gluon nodes, deployed at large sites. Here, only the vpn-offloader maintains a connection to the supernode. All other nodes connect to the offloader instead. Having only one connection to the supernode (instead of one connection per node), brodcast / multicast traffic is sent only once. By that, traffic is reduced.

To run a node of vpn-offloader
  1. Enable the supernode fastd instance uci set fastd.supernode.enabled=1

  2. Disable the batman-adv gateway mode uci set batman-adv.bat0.gw_mode='client'

  3. Disable babel: /etc/init.d/babeld disable

  4. Disable DHCP: uci set dhcp.freifunk.ignore=1

  5. Commit & restart: uci commit

  6. Change SSID and channel in /etc/config/wireless according to your Freifunk community.

Reboot the router, when you’re done.

When running a node instead of an offloader, mind firewalling the fastd-instance by commenting out the correspondig rule in /etc/config/firewall.

19.5. ICVPN routing

The Intercity-VPN network (ICVPN) is a tinc and BGP based network for connecting all Freifunk communities. See (german) for details.

Unfortunately, the design of ICVPN makes it almost impossible to connect using a small SoHo-router, only. The configuration (BGP, DNS) is generated using python scripts operating on a cloned git repository. Way to much for a small node. Thus ICVPN connectivity using DNS and BGP is outside the scope of node-config.

Looking at the big picture, BGP and DNS can be handeled on a different platform (i.e. RaPi running Debian), exposing its connectivity using babel. Then, node-config can:

  • Use routes learned via babel to connecting to ICPN hosts.

  • Use a IPv6 anycast address for DNS.

Unfortunately, the Freifunk community rejected the idea of a DNS-TLD-naming convention, thus delegations for all Freifunk DNS TLDs must be configured individually. Generating a suitable dnsmasq-configuration is also part of the pyhton scripts provided by ICVPN. This manual won’t go into detail here. See or for more information.

IPv4 is futile. Use IPv6, only.

Still, node-config has a fastd-instance for ICVPN routing. It’s integrated into babel but not into batman-adv. In addition, there’s a test peer providing basic ICVPN connectivity.

Enabling ICVPN connectivity
uci set fastd.backbone.enabled=1
uci commit fastd
/etc/init.d/fastd restart

20. Design considerations

Up to now, we were looking technical aspects. That’s ok. You’re reading a technical documentation and - no regrets - it’s supposed to be like that. On the other hand, most design decisions are based on specific goals or requirements. Here, at the end, it’s time to connect the dots. These requirements are key to node-configs design:

  1. You don’t need any servers: Some Freifunk network designs promote data center based servers (supernodes) to provide basic network functionality (i.e. routing, dhcp). This needs extra funding, requires a reliable internet connection as well as a data center and introduces additional complexity. As a requirement for node-config, basic functionality has to be provided by the nodes.

  2. Use vanilla OpenWRT releases: Many community networks are based on custom OpenWRT builds or forks. To start a network, OpenWRT has to be compiled or forked at first. Usually, only a few people know how to maintain or compile the software needed to run the network. Node-config is required to use OpenWRT as-it-is; using released images and packages, only.

  3. Share your local internet connection: Freifunk and other networks are built with the idea of having one or many internet gateways providing connectivity. Unfortunately, many networks depend on an external infrastructure (i.e. a fixed vpn provider). For node-config, the need for a designated vpn provider has to be eliminated. Node-config must allow direct internet sharing or using an arbitrary vpn provider.

  4. Wifi & OpenWRT configuration is all you need to know: Introducing servers or operating vpn providers requires additional knowledge in a community. For instance, supernodes require management using ansible or puppet, connecting to autonomous systems (AS) (i.e. Freifunk Rheinland e.V.) via vpn puts the Border Gateway Protocol (BGP) on the table. In its core, that knowledge is outside of running wifi mesh networks and raises the entrance barrier. Using node-config must not require any additional knowledge (except OpenWRT configuration and wifi).

  5. Overcoming scaling limitations seen in Gluon based networks. Many proposed Gluon design scale up to 1000 ~ 2000 nodes per mesh. Basically, this is due to broadcast / anycast and batman-adv management traffic sent to all nodes. Roaming management traffic is distributed across regions without any reasonable wifi coverage. Node-config is required to scale better by integrating hierarchical routing and to limit roaming management traffic to definable regions.

  6. Be decentralized - no administration authority. Node-config is desinged to implement decentralisation (c.f. Hacker Ethic (wikipedia)). It is required, that no administration authority governs the network. Node-config is required to:

    • Eliminate the need for network administrators (i.e. for servers or BGP routers)

    • Empower people by providing a simple and accessible design as well as documentation.

    • Lower the entrance barrier (funding, infrastructure, know-how) for new communities.