Multidomain Support

Preamble

There comes a time when a mesh network grows past sensible boundaries. As broadcast traffic grows, mesh networks experience scaling issues and using them becomes very unpleasant. An approach to solve this follows the well-known “divide and conquer” paradigm and splits a large network into multiple smaller networks. These smaller networks start with a dedicated layer 2 network each, which are interconnected via their gateways by layer 3 routing. Gluon is already field-tested handling a single domain and the multidomain feature allows for the reconfiguration of key parameters that decide which domain a node participates in, without the need of a distinct set of firmware images for each mesh domain.

Overview

Multidomain support allows to build a single firmware with multiple, switchable domain configurations. The nomenclature is as follows:

  • site: an aggregate over multiple domains
  • domain: mesh network with connectivity parameters that prevent accidental bridging with other domains
  • domain code: unique domain identifier
  • domain name: pretty name for a domain code

By default Gluon builds firmware with a single domain embedded into site.conf. To use multiple domains, enable it in site.mk:

GLUON_MULTIDOMAIN=1

In the site repository, create the domains/ directory, which will hold your domain configurations. Each domain configuration file is named after its primary domain_code, additional domain codes and names are supported.

site/
|-- site.conf
|-- site.mk
|-- i18n/
|-- domains/
  |-- alpha_centauri.conf
  |-- beta_centauri.conf
  |-- gamma_centauri.conf

The domain configuration alpha_centauri.conf could look like this.

{
  domain_names = {
    alpha_centauri = 'Alpha Centauri'
  },

  -- more domain specific config follows below
}

In this example “Alpha Centauri” is the user-visible domain_name for the domain_code alpha_centauri. Also note that the domain code alpha_centauri matches the filename alpha_centauri.conf.

Additional domain codes/names can be added to domain_names, which are treated as aliases for the their domain configuration. Aliases can be used to offer more fine-grained and well-recognizable domain choices to users. Having multiple aliases on a single domain is a helpful precursor to splitting the domain into even smaller blocks.

Furthermore you have to specify the default_domain in the site.conf. This domain is applied in following cases:

  • When the config mode is skipped.
  • When a domain is removed in a new firmware release, the default_domain will be chosen then.
  • When a user selects a wrong domain code via uci.

Please note, that this value is saved to uci, so changing the default_domain value in the site.conf in a new firmware release only affects the actual domain of a router, if and only if one of the above conditions matches.

Switching the domain

via commandline:

uci set gluon.core.domain="newdomaincode"
gluon-reconfigure
reboot

via config mode:

To allow switching the domain via config mode, config-mode-domain-select has to be added to GLUON_FEATURES in the site.mk.

image0

Allowed site variables

Internally the site variables are merged from the site.conf and the selected domain.conf, so the most variables are also allowed in site.conf and in domain.conf. But there are some exceptions, which do not make sense in a domain or site specific way. The following sections give an overview over variables that are only usable in either site or domain context.

site.conf only variables

  • Used in as initial default values, when the firmware was just flashed and/or the config mode is skipped, so they do not make sense in a domain specific way:
    • authorized_keys
    • default_domain
    • poe_passthrough
    • mesh_on_wan
    • mesh_on_lan
    • single_as_lan
    • setup_mode.skip
    • autoupdater.branch
    • mesh_vpn.enabled
    • mesh_vpn.pubkey_privacy
    • mesh_vpn.bandwidth_limit
    • mesh_vpn.bandwidth_limit.enabled
    • mesh_vpn.bandwidth_limit.ingress
    • mesh_vpn.bandwidth_limit.egress
  • Variables that influence the appearance of the config mode, domain-independent because they are relevant before a domain was selected.
    • config_mode.geo_location.show_altitude
    • config_mode.hostname.optional
    • config_mode.remote_login
    • config_mode.remote_login.show_password_form
    • config_mode.remote_login.min_password_length
    • hostname_prefix
    • mesh_vpn.fastd.configurable
    • roles.default
    • roles.list
  • Specific to a firmware build itself:
    • site_code
    • site_name
    • autoupdater.branches.*.name
    • autoupdater.branches.*.good_signatures
    • autoupdater.branches.*.pubkeys
  • We simply do not see any reason, why these variables could be helpful in a domain specific way:
    • mesh_vpn.fastd.syslog_level
    • timezone
    • regdom

domain.conf only variables

  • Obviously:
    • domain_names
      • a table of domain codes to domain names domain_names = { foo = 'Foo Domain', bar = 'Bar Domain', baz = 'Baz Domain' }
    • hide_domain
      • prevents a domain name(s) from appearing in config mode, either boolean or array of domain codes
        • true, false
        • { 'foo', 'bar' }
  • Because each domain is considered as an own layer 2 network, these values should be different in each domain:
    • next_node.ip4
    • next_node.ip6
    • next_node.name
    • prefix6
    • prefix4
    • extra_prefixes6
  • To prevent accidental bridging of different domains, all meshing technologies should be separated:
    • domain_seed (wired mesh)
      • must be a random value used to derive the vxlan id for wired meshing
    • wifi*.mesh.id
    • mesh_vpn.fastd.groups.*.peers.remotes
    • mesh_vpn.fastd.groups.*.peers.key
    • mesh_vpn.tunneldigger.brokers
  • Clients consider WiFi networks sharing the same ESSID as if they were the same L2 network and try to reconfirm and reuse previous addressing. If multiple neighbouring domains shared the same ESSID, the roaming experience of clients would degrade.
    • wifi*.ap.ssid
  • Some values should be only set in legacy domains and not in new domains.
    • mesh.vxlan
      • By default, this value is true. It should be only set to false for one legacy domain, since vxlan prevents accidental wired merges of domains. For old domains this value is still available to keep compatibility between all nodes in one domain.
    • next_node.mac
      • For new domains, the default value should be used, since there is no need for a special mac (or domain specific mac). For old domains this value is still available to keep compatibility between all nodes in one domain.

Example config

site.mk

##	gluon site.mk makefile example

##	GLUON_FEATURES
#		Specify Gluon features/packages to enable;
#		Gluon will automatically enable a set of packages
#		depending on the combination of features listed

GLUON_FEATURES := \
	autoupdater \
	ebtables-filter-multicast \
	ebtables-filter-ra-dhcp \
	ebtables-limit-arp \
	mesh-batman-adv-15 \
	mesh-vpn-fastd \
	respondd \
	status-page \
	web-advanced \
	web-wizard

##	GLUON_MULTIDOMAIN
#		Build gluon with multidomain support.

GLUON_MULTIDOMAIN=1

##	GLUON_SITE_PACKAGES
#		Specify additional Gluon/LEDE packages to include here;
#		A minus sign may be prepended to remove a packages from the
#		selection that would be enabled by default or due to the
#		chosen feature flags


GLUON_SITE_PACKAGES := iwinfo

##	DEFAULT_GLUON_RELEASE
#		version string to use for images
#		gluon relies on
#			opkg compare-versions "$1" '>>' "$2"
#		to decide if a version is newer or not.

DEFAULT_GLUON_RELEASE := 0.6+exp$(shell date '+%Y%m%d')

# Variables set with ?= can be overwritten from the command line

##	GLUON_RELEASE
#		call make with custom GLUON_RELEASE flag, to use your own release version scheme.
#		e.g.:
#			$ make images GLUON_RELEASE=23.42+5
#		would generate images named like this:
#			gluon-ff%site_code%-23.42+5-%router_model%.bin

GLUON_RELEASE ?= $(DEFAULT_GLUON_RELEASE)

# Default priority for updates.
GLUON_PRIORITY ?= 0

# Region code required for some images; supported values: us eu
GLUON_REGION ?= eu

# Languages to include
GLUON_LANGS ?= en de

# Do not build images for deprecated devices
GLUON_DEPRECATED ?= 0

site.conf

{
  site_name = 'Centauri Mesh',
  site_code = 'centauri',
  default_domain = 'alpha_centauri',

  timezone = 'CET-1CEST,M3.5.0,M10.5.0/3',
  ntp_server = {'ntp1.example.org', 'ntp2.example.org'},
  regdom = 'DE',

  wifi24 = {
    mesh = {
      mcast_rate = 12000,
    },
  },

  wifi5 = {
    mesh = {
      mcast_rate = 12000,
    },
  },

  mesh_vpn = {
    mtu = 1312,

    fastd = {
      methods = {'salsa2012+umac'},
    },

    bandwidth_limit = {
      enabled = false,
      egress = 200, -- kbit/s
      ingress = 3000, -- kbit/s
    },
  },

  autoupdater = {
    branch = 'stable',

    branches = {
      stable = {
        name = 'stable',
        mirrors = {'http://update.example.org/stable/sysupgrade'},
        good_signatures = 2,
        pubkeys = {
          'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', -- Alice
          'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', -- Bob
          'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', -- Mary
        },
      },
    },
  },
}

domains/alpha_centauri.conf

{
  -- multiple codes/names can be defined, the first one is the primary name
  -- additional aliases can be defined
  domain_names = {
    alpha_centauri = 'Alpha Centauri',
    rigil_kentaurus = 'Rigil Kentaurus',
    proxima_centauri = 'Proxima Centauri',
  },

  -- 32 byte random data in hexadecimal encoding
  -- This data must be unique among all sites and domains!
  -- Can be generated using: echo $(hexdump -v -n 32 -e '1/1 "%02x"' </dev/urandom)
  domain_seed = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',

  -- unique network prefixes per domain
  prefix4 = '10.xxx.0.0/20',
  prefix6 = 'fdxx:xxxx:xxxx:xxxx::/64',

  next_node = {
    ip4 = '10.xxx.yyy.zzz',
    ip6 = 'fdxx:xxxx:xxxx:xxxx::xxxx',
  },

  wifi24 = {
    ap = {
      ssid = "alpha-centauri.example.org",
      channel = 1,
    },
    mesh = {
      id = 'ueH3uXjdp', -- usually you don't want users to connect to this mesh-SSID, so use a cryptic id that no one will accidentally mistake for the client WiFi
    },
  },

  wifi5 = {
    ap = {
      ssid = "alpha-centauri.example.org",
      channel = 44,
    },
    mesh = {
      id = 'ueH3uXjdp',
    },
  },

  mesh = {
    batman_adv = {
      routing_algo = 'BATMAN_IV',
    },
  },

  mesh_vpn = {
    fastd = {
      groups = {
        backbone = {
          peers = {
            peer1 = {
              key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
              remotes = {'"peer1.example.org" port xxxxx'},
            },
            peer2 = {
              key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
              remotes = {'"peer2.example.org" port xxxxx'},
            },
          },
        },
      },
    },
  },
}

i18n/en.po

msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Project-Id-Version: PACKAGE VERSION\n"
"PO-Revision-Date: 2016-02-04 14:28+0100\n"
"Last-Translator: David Lutz <kpanic@hirnduenger.de>\n"
"Language-Team: English\n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

msgid "gluon-config-mode:welcome"
msgstr ""
"Welcome to the setup wizard of your new Freifunk Alpha Centauri node. Please "
"fill out the following form and submit it."

msgid "gluon-config-mode:domain"
msgstr "Domain"

msgid "gluon-config-mode:domain-select"
msgstr ""
"Here you have the possibility of selecting the mesh domain in which your node "
"is placed. Please keep in mind that your router only connects with the nodes "
"of the selected domain."

msgid "gluon-config-mode:pubkey"
msgstr ""
"<p>This is your Freifunk node's public key. The node won't be able to "
"connect to the mesh VPN until the key has been registered on the Freifunk "
"servers. To register, send the key together with your node's name "
"(<em><%=pcdata(hostname)%></em>) to "
"<a href=\"mailto:keys@alpha-centauri.freifunk.net?subject="
"<%= urlencode('Registration: ' .. hostname) %>&amp;body="
"<%= urlencode('# ' .. hostname .. '\n# ' .. sysconfig.primary_mac .. '\nkey ') %>"
"%22<%= pubkey %>%22;"
"<%= urlencode('\n\nI have taken note that the contact I entered in the ') %>"
"<%= urlencode('node is publicly available on the Internet and can be ') %>"
"<%= urlencode('used by any services (e.g. the meshviewer map).') %>"
"<%= urlencode('\n\nThanks, \n\n') %>"
"\">keys@alpha-centauri.freifunk.net</a>. Of course, your e-mail address will "
"be treated confidentially and will not be passed on.</p>"
"<div class=\"the-key\">"
" # <%= pcdata(hostname) %><br />"
"<%= pubkey %>"
"</div>"
"<p>Your node <em><%= pcdata(hostname) %></em> is currently rebooting and will "
"try to connect to other nearby Freifunk nodes via WLAN and to a VPN-gateway "
"via your internet connection after the reboot is finished.</p>"
"<p>Don't forget to plug the network cable from the LAN port to the WAN port."
"</p>"

msgid "gluon-config-mode:novpn"
msgstr ""
"<p>You have selected <strong>not</strong> to use the mesh VPN. "
"Your node will only be able to connect to the Freifunk network if other nodes "
"in reach already have a connection.</p>"
"Please send an e-mail with the name of your node "
"(<em><%=pcdata(hostname)%></em>) and some additional information to "
"<a href=\"mailto:keys@alpha-centauri.freifunk.net?subject="
"<%= urlencode('Registration: ' .. hostname) %>&amp;body="
"<%= urlencode('# ' .. hostname .. '\n# ' .. sysconfig.primary_mac .. '\nkey ') %>"
"%22<%= pubkey %>%22;"
"<%= urlencode('\n\nI have taken note that the contact I entered in the ') %>"
"<%= urlencode('node is publicly available on the Internet and can be ') %>"
"<%= urlencode('used by any services (e.g. the meshviewer map).') %>"
"<%= urlencode('\n\nThanks, \n\n') %>"
"\">keys@alpha-centauri.freifunk.net</a>. Of course, your e-mail address will "
"be treated confidentially and will not be passed on.</p>"
"<p>Your node <em><%= pcdata(hostname) %></em> is currently rebooting and will "
"try to connect to other nearby Freifunk nodes after that.</p>"

msgid "gluon-config-mode:reboot"
msgstr ""
"<p>For more information about the Freifunk community on Alpha Centauri, have a "
"look at <a href=\"https://alpha-centauri.freifunk.net/\" target=\"_blank\">our "
"homepage</a>.</p>"
"<p>To get back to this configuration interface, press the reset button for "
"about 10 seconds during normal operation. The device will then reboot into "
"config mode.</p>"
"<p>Have fun with your node and exploring of the Freifunk network!</p>"

# Leave empty to use the default text, which can be found in:
# package/gluon-config-mode-hostname/i18n/
msgid "gluon-config-mode:hostname-help"
msgstr ""

# Leave empty to use the default text, which can be found in:
# package/gluon-config-mode-geo-location/i18n/
msgid "gluon-config-mode:geo-location-help"
msgstr ""

msgid "gluon-config-mode:altitude-label"
msgstr ""

# Leave empty to use the default text, which can be found in:
# package/gluon-config-mode-contact-info/i18n/
msgid "gluon-config-mode:contact-help"
msgstr ""

msgid "gluon-config-mode:contact-note"
msgstr ""

i18n/de.po

msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Project-Id-Version: PACKAGE VERSION\n"
"PO-Revision-Date: 2015-03-19 20:28+0100\n"
"Last-Translator: Matthias Schiffer <mschiffer@universe-factory.net>\n"
"Language-Team: German\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"

msgid "gluon-config-mode:welcome"
msgstr ""
"Willkommen zum Einrichtungsassistenten für deinen neuen Alpha Centauri "
"Freifunk-Knoten. Fülle das folgende Formular deinen Vorstellungen "
"entsprechend aus und sende es ab."

msgid "gluon-config-mode:domain"
msgstr "Domäne"

msgid "gluon-config-mode:domain-select"
msgstr ""
"Hier hast du die Möglichkeit, die Mesh-Domäne, in der sich dein Knoten "
"befindet, auszuwählen. Bitte denke daran, dass sich dein Knoten nur mit den "
"Knoten der ausgewählten Domäne verbinden kann."

msgid "gluon-config-mode:pubkey"
msgstr ""
"<p>Dies ist der öffentliche Schlüssel deines Freifunk-Knotens. Erst nachdem "
"er auf den Servern des Freifunk-Projektes auf Alpha Centauri eingetragen "
"wurde, kann sich dein Knoten mit dem Mesh-VPN dort verbinden. Bitte schicke "
"dazu diesen Schlüssel und den Namen deines Knotens "
"(<em><%=pcdata(hostname)%></em>) an "
"<a href=\"mailto:keys@alpha-centauri.freifunk.net?subject="
"<%= urlencode('Anmeldung: ' .. hostname) %>&amp;body="
"<%= urlencode('# ' .. hostname .. '\n# ' .. sysconfig.primary_mac .. '\nkey ') %>"
"%22<%= pubkey %>%22;"
"<%= urlencode('\n\nIch habe zur Kenntnis genommen, dass der im ') %>"
"<%= urlencode('Knoten von mir eingetragene Kontakt im Meshnetz ') %>"
"<%= urlencode('öffentlich abfragbar ist und von beliebigen Diensten ') %>"
"<%= urlencode('(z.B. der Freifunk-Karte) veröffentlicht werden kann.') %>"
"<%= urlencode('\n\nGruß, \n\n') %>"
"\">keys@alpha-centauri.freifunk.net</a>. Deine E-Mail Adresse wird "
"selbstverständlich vertraulich behandelt und nicht weitergegeben."
"</p>"
"<div class=\"the-key\">"
"# <%= pcdata(hostname) %><br />"
"<%= pubkey %>"
"</div>"
"<p>Dein Knoten startet gerade neu und wird anschließend versuchen, sich mit "
"anderen Freifunkknoten in seiner Nähe über WLAN sowie über deine"
"Internetverbindung über das VPN-Gateway zu verbinden.</p>"
"<p>Vergiss nicht das Netzwerkkabel vom LAN Port in den WAN Port "
"umzustecken.</p>"

msgid "gluon-config-mode:novpn"
msgstr ""
"<p><strong>Du hast ausgewählt die Internetverbindung (Mesh-VPN) nicht zu "
"nutzen</strong>. Dein Knoten kann also nur dann eine Verbindung zum "
"Freifunk-Netz aufbauen, wenn andere Freifunk-Knoten in WLAN-Reichweite sind."
"<p>Bitte schicke uns eine E-Mail mit dem Namen deines Knotens "
"(<em><%= pcdata(hostname) %></em>) und ein paar Informationen an <a href="
"\"mailto:freifunk-keys@lists.in-kiel.de?"
"subject=<%= urlencode('Anmeldung: ' .. hostname) %>&amp;"
"body=<%= urlencode('# ' .. hostname .. '\n# ' .. sysconfig.primary_mac .. '\n# kein mesh-VPN') %>"
"<%= urlencode('\n\nIch habe zur Kenntnis genommen, dass der im ') %>"
"<%= urlencode('Knoten von mir eingetragene Kontakt im Meshnetz ') %>"
"<%= urlencode('öffentlich abfragbar ist und von beliebigen Diensten ') %>"
"<%= urlencode('(z.B. der Freifunk-Karte) veröffentlicht werden kann.') %>"
"<%= urlencode('\n\nGruß, \n\n') %>"
"\">kontakt@alpha-centauri.freifunk.net</a>. Deine E-Mail Adresse wird "
"selbstverständlich vertraulich behandelt und nicht weitergegeben.</p>"
"<p>Dein Knoten <em><%= pcdata(hostname) %></em> startet gerade neu und wird "
"anschließend versuchen, sich mit anderen Freifunkknoten in seiner Nähe über "
"WLAN zu verbinden.</p>"

msgid "gluon-config-mode:reboot"
msgstr ""
"<p>Weitere Informationen zur "
"Alpha Centauri Freifunk-Community findest du auf "
"<a href=\"https://alpha-centauri.freifunk.net/\" target=\"_blank\">unserer "
"Webseite</a>.</p>"
"<p>Um zu dieser Konfigurationsseite zurückzugelangen, drücke im normalen "
"Betrieb für ca. 10 Sekunden den Reset-Button. Das Gerät wird dann im Config "
"Mode neustarten.</p>"
"<p>Viel Spaß mit deinem Knoten und der Erkundung von Freifunk!</p>"

# Leave empty to use the default text, which can be found in:
# package/gluon-config-mode-hostname/i18n/
msgid "gluon-config-mode:hostname-help"
msgstr ""

# Leave empty to use the default text, which can be found in:
# package/gluon-config-mode-geo-location/i18n/
msgid "gluon-config-mode:geo-location-help"
msgstr ""

msgid "gluon-config-mode:altitude-label"
msgstr ""

# Leave empty to use the default text, which can be found in:
# package/gluon-config-mode-contact-info/i18n/
msgid "gluon-config-mode:contact-help"
msgstr ""

msgid "gluon-config-mode:contact-note"
msgstr ""

modules

# This file allows specifying additional repositories to use
# when building gluon.
#
# In most cases, it is not required so don't add it.

##	GLUON_SITE_FEEDS
#		for each feed name given, add the corresponding PACKAGES_* lines
#		documented below
#GLUON_SITE_FEEDS='my_own_packages'

##	PACKAGES_$feedname_REPO
#		the  git repository from where to clone the package feed
#PACKAGES_MY_OWN_PACKAGES_REPO=https://github.com/.../my-own-packages.git


##	PACKAGES_$feedname_COMMIT
#		the version/commit of the git repository to clone
#PACKAGES_MY_OWN_PACKAGES_COMMIT=123456789aabcda1a69b04278e4d38f2a3f57e49

##  PACKAGES_$feedname_BRANCH
#   the branch to check out
#PACKAGES_MY_OWN_PACKAGES_BRANCH=my_branch