Die Installation des Gateways ist noch nicht abgeschlossen, deswegen ist dieser Wiki-Eintrag noch nicht fertiggestellt. Änderungen sind möglich, ebenso können noch Fehler enthalten sein.


<>

Voraussetzungen

Im Folgenden wird das Aufsetzen eines Gateways (gw1) beschrieben. Beispielhaft verwenden wir Gentoo, dabei wird von Grundinstallation laut Gentoo Handbuch ausgegangen. Empfohlen wird die Nutzung eines Hardened Profiles.

Die einzelnen Konfigurationsdateien werden zentral in einem Git-Repository und können dort eingesehen werden.Dort befindet sich auch ein Gentoo-Portage-Overlay für Freifunk-spezifische Ebuilds. Diese wird wie folgt eingebunden:

emerge -av layman

Nach der Layman-Installation wird angezeigt, wie dies in der Portage-Konfiguration (/etc/portage/make.conf) integriert wird. Danach wird das Overlay eingebunden:

# cd /etc/layman/overlays
# wget https://raw.githubusercontent.com/ffggrz/ff-overlay/master/ff-overlay.xml
# layman -L
# layman -a ff-overlay

Grundkonfiguration

Hostname

Der Hostname wird in /etc/conf.d/hostname gesetzt:

# Set to the hostname of this machine
hostname="gw1"

/etc/hosts sollte etwa so aussehen:

# IPv4 and IPv6 localhost aliases

#
127.0.0.1                       localhost
::1                             localhost ip6-localhost ip6-loopback
ff02::1                         ip6-allnodes
ff02::2                         ip6-allrouters

# own external addresses
148.251.158.22                  gw1.freifunk-gera-greiz.de gw1
2a01:4f8:210:5132:201::22       gw1.freifunk-gera-greiz.de gw1

# Freifunk external
78.46.192.49                    gw2.freifunk-gera-greiz.de gw2
2a01:4f8:c17:229::12            gw2.freifunk-gera-greiz.de gw2

 # Freifunk internal

10.181.0.1                      nextnode.ffggrz
fdb5:078b:64cc::1               nextnode.ffggrz

10.181.0.11                     gw1.ffggrz 1.ntp.services.ffggrz 1.updates.services.ffggrz
fdb5:78b:64cc::11               gw1.ffggrz 1.ntp.services.ffggrz 1.updates.services.ffggrz

10.181.0.12                     gw2.ffggrz 2.ntp.services.ffggrz 2.updates.services.ffggrz
fdb5:78b:64cc::12               gw2.ffggrz 2.ntp.services.ffggrz 2.updates.services.ffggrz

10.181.0.101                    raspbx.ffggrz
fdb5:78b:64cc::101              raspbx.ffggrz

Hier werden also, jeweils für IPv4 und IPv6, die Namen für die localhost-Adressen, die externen Adressen im Internet und die Freifunk-internen Adressen definiert. Am Ende folgen noch Einträge für alle Host, welche der später per DNSmasq arbeitende DNS-Server auflösen soll.

Routing

/etc/iproute2/rt_tables:

#
# reserved values
#
255     local
254     main
253     default
0       unspec
#
# local
#
# local community tables
181     ggrz
#
# icvpn table
42      icvpn
#
# ffrl table (Exit per FFRL)
83      ffrl

Hier wurden separate Routing-Tabellen für das lokale Mesh-Netzwerk (181, ggrz), das Intercity-VPN (42, icvpn) und den Exit per Freifunk Rheinland e.V. (83, ffrl) definiert. Nur so kann später erreicht werden, daß dieses Gateway als Default Gateway für alle Clients fungiert und deren Traffic nicht über das "normale" Internet routet, sondern über ein Exit-VPN per Tunnel ins Ausland oder zu einem der Freifunk-Vereine mit Provider-Status.

Kernelparameter

Unter Gentoo bauen wir den Kernel selbst. Neben der Grundkonfiguration, um die verwendete Hardware bzw. den Virtualisierungs-Host oder Grundfunktionen wie Netzwerk mit IPv6 zu unterstützen, brauchen wir folgende Optionen:

[*] Networking support --->
   Networking options --->
      <*> TCP/IP networking
      [*] IP: multicasting
      [*] IP: advanced router
      [*] IP: policy routing
      <*> The IPv6 protocol --->
             [*] IPv6: multiple routing tables
      <*> 802.1d Ethernet Bridging

    Device Drivers  --->
      [*] Network device support  --->
             [*]   Network core driver support
             <*>     Universal TUN/TAP device driver support

Sicherlich fehlen hier noch viele andere erforderliche Parameter. Ein Beispiel einer funktionierenden (aber sicher auch nicht optimalen) Konfiguration findet sich hier: linux-3.18.9-hardened.config. Die aktuelle Konfiguration ist im Git-Repository zu finden.

Folgende Einstellungen in /etc/sysctl.conf sind erforderlich:

# Freifunk specific settings
net.ipv4.ip_forward = 1

net.ipv4.conf.default.rp_filter = 2
net.ipv4.conf.all.rp_filter = 2

net.bridge.bridge-nf-call-arptables = 0
net.bridge.bridge-nf-call-ip6tables = 0
net.bridge.bridge-nf-call-iptables = 0

net.ipv6.conf.all.forwarding = 1

net.ipv6.conf.all.autoconf = 0
net.ipv6.conf.default.autoconf = 0
net.ipv6.conf.eth0.autoconf = 0

net.ipv6.conf.all.accept_ra = 0
net.ipv6.conf.default.accept_ra = 0
net.ipv6.conf.eth0.accept_ra = 0

Damit wird das Routing (Forwarding) für IPv4 und IPv6 aktiviert, Bridge-Traffic wird nicht von der Firewall (iptables) verarbeitet, die IPv6-Autoconfiguration wird deaktiviert und Router Advertisements werden nicht ausgewertet.

Netzwerk

Externe Anbindung

Das Gateway in diesem Beispiel läuft als Virtuelle maschine auf einem Root-Server bei Hetzner unter KVM. Der Root-Server verfügt über ein zusätzliches Subnetz mit 8 IPv4-Adressen sowie ein IPv6-Subnetz. Um die wertvollen IPv4-Adressen alle nutzen zu können und diese nicht als Netzwerk- und Broadcast-Adresse "zu verbraten", wird eine etwas spezielle Netzwerk-Konfiguration verwendet:

modules="iproute2"

# Natives IPv6-Netz (/64)
v6net="2a01:4f8:210:5132"

config_eth0="148.251.158.22 peer 148.251.152.51
             ${v6net}:201::22/128"
routes_eth0="default via 148.251.152.51
             default via fe80::1"

dns_domain="freifunk-gera-greiz.de"
dns_servers="213.133.100.100 213.133.99.99 213.133.98.98"

Auf gw3 (Rootserver Kimsuf1 KS-2 SSD) sieht das so aus:

modules="iproute2"

###############################
# Externe Anbindung
###############################
config_eth0="5.196.74.176/24
             2001:41d0:e:8b0::1/64"
routes_eth0="default via 5.196.74.254
             2001:41d0:e:8ff:ff:ff:ff:ff
             default via 2001:41d0:e:8ff:ff:ff:ff:ff"

dns_domain="freifunk-gera-greiz.de"
dns_servers="213.186.33.99
             2001:41d0:3:163::1"

Bridge

Eine Bridge brauchen wir im Gateway nicht unbedingt. Sie erleichtert aber das Leben, da sie praktisch ohne Abhängigkeiten immer aktiv bleibt. Die Bridge erhält die Freifunk-internen IP-Adressen des Gateways, auch diese sind damit immer verfügbar. Daran können wir dann unsere internen Dienste (DNS, DHCP, NTP...) binden. Würden wir auf die Bridge verzichten, müßte das B.A.T.M.A.N.-Interface die IP-Adressen erhalten. Müßte dieses z.B. neu gestartet werden, müßten wir auch alle Dienste neu starten.Die Bridge wird in /etc/conf.d/net wie folgt konfiguriert:

###############################
# Bridge
###############################
# Dummy-Interface "ggrzDummy" für die Bridge "ggrzBR". Dieses Interface bekommt
# die niedrigste Mac-Adresse aller Interfaces an der Bridge, welche dann auch
# von der Bridge übernommen wird.

tuntap_ggrzDummy="tap"
config_ggrzDummy="null"
rc_net_ggrzBR_need="net.ggrzDummy"

bridge_ggrzBR="ggrzDummy"
config_ggrzBR="10.181.0.11 netmask 255.255.192.0 brd 10.181.63.255
               fdb5:078b:64cc::11/64"
brctl_ggrzBR="setfd 0
sethello 10
stp off"

routes_ggrzBR="10.181.0.0/18 proto kernel src 10.181.0.11 table ggrz
               fe80::/64 proto kernel table ggrz
               fdb5:078b:64cc::/64 proto kernel table ggrz
               10.181.0.0/18 proto kernel src 10.181.0.11 table icvpn
               fe80::/64 proto kernel table icvpn
               fdb5:078b:64cc::/64 proto kernel table icvpn"
rules_ggrzBR="iif ggrzBR table ggrz priority 1810"
rules6_ggrzBR="iif ggrzBR table ggrz priority 1810"

###############################
# Up-/Down-Scripts
###############################
postup() {
    if  $IFACE == ggrzDummy  ; then
        /bin/ip link set address de:ad:be:ef:01:00 dev $IFACE
    elif  $IFACE == ggrzBR  ; then
        /bin/ip route add unreachable default table ggrz
    fi
    return 0
}

predown() {
    if  $IFACE == ggrzBR  ; then
        /bin/ip route del unreachable default table ggrz
    fi
    return 0
}

Zuerst wird also ein Dummy-Interface angelegt, welches als erstes an die Bridge gebunden wird. Von diesem Interface übernimmt die Bridge die Mac-Adresse, welche im postup-Script gesetzt wird. In den Rules zur Bridge wird festgelegt, daß alle über die Bridge eingehenden Pakete über die Tabelle ggrz geroutet werden.

Da die zusätzlichen Routing-Tables nicht automatisch Device-Rpouten erhalten, werden diese in der Konfiguration hinzugefügt (routes_ggrzBR).

Die Interfaces werden nach Anlegen der erforderlichen Symlinks gestartet und in den Runlevel default aufgenommen:

# cd /etc/init.d
# ln -s net.lo net.ggrzDummy
# ln -s net.lo net.ggrzBR
# rc-service net.ggrzDummy start
# rc-service net.ggrzBR start
# rc-update add net.ggrzDummy default
# rc-update add net.ggrzBR default

GRETAP-Tunnel

Zwischen den Gateways werden GRETAP-Tunnel angelegt, worüber diese auf Layer2-Ebene verbunden werden. Die Tunnel werden in /etc/conf.d/net wie folgt konfiguriert:

###############################
# Tunnel
###############################
# ggrzTUN1: gw1 <-> gw2
# ggrzTUN2: gw1 <-> gw3
# ggrzTUN3: gw2 <-> gw3
config_ggrzTUN2="null"
config_ggrzTUN3="null"

###############################
# Up-/Down-Scripts
###############################
preup() {
    if [...]
    elif $IFACE == ggrzTUN1 ; then
        /bin/ip link add $IFACE type gretap remote 193.28.153.11 local 148.251.158.22 key 1
        /bin/ip link set address de:ad:be:ef:01:03 dev $IFACE
    elif $IFACE == ggrzTUN2 ; then
        /bin/ip link add $IFACE type gretap remote 5.196.74.176 local 148.251.158.22 key 2
        /bin/ip link set address de:ad:be:ef:01:04 dev $IFACE
    fi
    return 0
}

postdown() {
    if [...]
    elif $IFACE == ggrzTUN1 ; then
        /bin/ip link del $IFACE type gretap remote 193.28.153.11 local 148.251.158.22 key 1
    elif $IFACE == ggrzTUN2 ; then
        /bin/ip link del $IFACE type gretap remote 5.196.74.176 local 148.251.158.22 key 2
    fi
    return 0
}

Auch für diese Tunnel-Interfaces werden wieder Symlinks angelegt, sie werden gestartet und in den Default-Runlevel aufgenommen:

# cd /etc/init.d
# ln -s net.lo net.ggrzTUN1
# ln -s net.lo net.ggrzTUN2
# rc-service net.TUN1 start
# rc-service net.TUN2 start
# rc-update add net.TUN1 default
# rc-update add net.TUN2 default

B.A.T.M.A.N.

Als Kernel-Modul nutzen wir nicht das direkt im Kernel integrierte, da wir dort nicht dessen Version beeinflussen können. Zur Steuerung benötigen wir dann noch batctl:

# echo "net-misc/batman-adv-2015.1" >> /etc/portage/package.keywords/Freifunk
# echo "net-misc/batctl-2015.1" >> /etc/portage/package.keywords/Freifunk
# echo "net-misc/batman-adv bla dat debug mcast nc" >> /etc/portage/package.use/Freifunk
# emerge -av batman-adv batctl

Die Konfiguration in /etc/conf.d/net:

###############################
# Mesh-Interfaces
###############################
config_ggrzBAT="null"
rc_net_ggrzBAT_need="net.ggrzBR net.ggrzTUN1 net.ggrzTUN2"

###############################
# Up-/Down-Scripts
###############################
preup() {
    if  $IFACE == ggrzBAT  ; then
        /sbin/modprobe batman-adv
        /usr/sbin/batctl -m $IFACE if add ggrzTUN1
        /usr/sbin/batctl -m $IFACE if add ggrzTUN2
        /usr/sbin/batctl -m $IFACE gw server 96mbit/96mbit
        /usr/sbin/batctl -m $IFACE it 10000
        /bin/ip link set address de:ad:be:ef:01:02 dev $IFACE
        echo 60 > /sys/class/net/$IFACE/mesh/hop_penalty
        /sbin/brctl addif ggrzBR $IFACE
    fi
    return 0
}

postdown() {
    if  $IFACE == ggrzBAT  ; then
        /sbin/brctl delif ggrzBR $IFACE
        /usr/sbin/batctl -m $IFACE if del ggrzTUN1
        /usr/sbin/batctl -m $IFACE if del ggrzTUN2
    fi
    return 0
}

Das Mesh-Interface wird also nach Bridge- und GRETAP-Tunnels gestartet. Nach dem Start des Mesh-Interfaces werden die Tunnel ans Mesh-Interface gekoppelt und dieses in der Gateway-Mode versetzt. Anschließend wird das Mesh-Interface an die Bridge gehängt.

Der Start erfolgt wie gewohnt:

# cd /etc/init.d
# ln -s net.lo net.ggrzBAT
# rc-service net.BAT start
# rc-update add net.BAT default

FastD

Der "Fast and secure tunneling Daemon" erstellt die Tunnel zwischen den Routern der Nodes und dem Gateway. Dafür gibt es keine fertigen Ebilds im Portage-Tree, aber sie sind bereits im Bugtracker gelandet: dev-libs/libuecc und net-misc/fastd. An diesen beiden Bugeinträgen hängen funktionierende Ebuilds, welche wir angepaßt und in unser Portage-Overlay integriert haben. Anschließend erfolgt die Installation mittels:

# echo "dev-libs/libuecc" >> /etc/portage/package.keywords/Freifunk
# echo "net-misc/fastd" >> /etc/portage/package.keywords/Freifunk
# echo "sys-devel/bison" >> /etc/portage/package.keywords/Freifunk
# emerge -av fastd

Die Konfiguration für unsere Community Gera-Greiz wird in folgender Datei vorgenommen:

/etc/fastd/ggrz/fastd.conf

log to syslog level verbose;

hide ip addresses yes;
hide mac addresses yes;

interface "ggrzVPN";

method "aes128-gcm";
method "salsa2012+umac";
method "null+salsa2012+umac";

# Bind auf externe v4 (genattet) and v6 Adressen
bind 172.31.1.100:10181;
bind [2a01:4f8:c17:229::12]:10181;

mode tap;

include "secret.conf";
mtu 1406; # 1492 - IPv4/IPv6 Header - fastd Header...

include peers from "peers";

status socket "/var/run/fastd-gera-greiz.status";

on up "
    /bin/ip link set address de:ad:be:ef:03:01 dev $INTERFACE up
    /usr/sbin/batctl -m ggrzBAT if add $INTERFACE
";

on down "
    /usr/sbin/batctl -m ggrzBAT if del $INTERFACE
    /bin/ip link set dev $INTERFACE down
"; 

on verify "
    /etc/fastd/ggrz/fastd-blacklist.sh $PEER_KEY
";

Das "on verify" Script wird bei der Verbindungsaufnahme unbekannter Clients aufgerufen. Es hat folgenden Inhalt:

/etc/fastd/ggrz/fastd-blacklist.sh

#!/bin/bash

if /bin/grep -Fq $PEER_KEY /etc/fastd/ggrz/fastd-blacklist.json; then
        exit 1
else
        exit 0
fi

Ist der Schlüssel des Clients in /etc/fastd/ggrz/fastd-blacklist.json aufgeführt, wird die Verbindung durch Rückgabe einer "1" abgewiesen, ansonsten werden Verbindungen unbekannter Knoten generell erlaubt (Rückgabe von "0").

Als nächstes werden die Keys erzeugt (die angezeigten Keys sind nur Beispiele):

# fastd --generate-key
2015-04-02 14:58:03 +0200 --- Info: Reading 32 bytes from /dev/random...
Secret: 8897599cc0298ee31cff6e83315f68207b0c95cf03678da4b4b8b5661c9d1568
Public: 5818e1e0c322c2f414ec13b4ab536ac0696d571eebb0327ba2ce8beb5d159244

Der geheime Schlüssel wird wie folgt in secret.conf eingetragen, als Kommentar ist hier auch der zugehörige public Key hinterlegt:

/etc/fastd/ggrz/secret.conf

secret "8897599cc0298ee31cff6e83315f68207b0c95cf03678da4b4b8b5661c9d1568";
# 
# (public: a8fe6ec0e7d7a1bb125a02c06b8caa8b06405752667e346e85d81ee48f358109)

Der öffentliche Schlüssel muß dann noch in die site.conf zum Erstellen der Gluon-Firmware eingetragen werden, damit sich die Knoten-Router mit dem Gateway verbinden können.

Die öffentlichen Schlüssel der Knoten werden im Git-Repository "peers-ffggrz"eingetragen. Obwohl wir derzeit auch alle Verbindungen von unbekannten Knoten zulassen, geben wir die Knoten trotzdem unserem Gateway bekannt. So könnten wir ohne großen Aufwand bei Bedarf nur noch Verbindungen bekannter Knoten akzeptieren. Deswegen laden wir nun dieses Repository auf das Gateway:

# cd /etc/fastd/ggrz
# git clone https://github.com/ffggrz/peers-ffggrz.git peers

Mit dem folgenden Script wird dafür gesorgt, daß die Peers aktuell bleiben:

/etc/fastd/update-fastd-peers

# fastd configuration directory
FASTD_CFG=/etc/fastd

# FF Communities
COMMUNITIES="ggrz"

function getCurrentVersion() {
# Get hash from latest revision
git log --format=format:%H -1
}

cd $FASTD_CFG/$COMMUNITIES/peers

# Get current version hash
GIT_REVISION=$(getCurrentVersion)

# Automagically commit local changes
# This preserves local changes
git commit -m "CRON: auto commit"

# Pull latest changes from upstream
git fetch
git merge origin/master -m "Auto Merge"

# Get new version hash
GIT_NEW_REVISION=$(getCurrentVersion)

if [ $GIT_REVISION != $GIT_NEW_REVISION ]
then
  # Reload updated configuration
  echo "Reload fastd configuration."
  kill -HUP $(pidof fastd.$COMMUNITIES)
fi

Das Script muß ausführbar sein:

# chmod 750 /etc/fastd/update-fastd-peers

Nun muß dieses Script noch regelmäßig per Cron aufgerufen werden:

/etc/cron.d/update-fastd-peers

# Global variables
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

# Update fastd peers every 15 minutes.
*/15 * * * * root /etc/fastd/update-fastd-peers > /dev/null

Zuletzt legen wir noch einen Symlink des Startscriptes für die FastD-Instanz der Community an, starten diese und nehmen sie in den Default-Runlevel auf:

# cd /etc/init.d
# ln -s fastd /etc/init.d/fastd.ggrz
# rc-service fastd.ggrz start
# rc-update add fastd.ggrz default

Exit-VPN

Zur Vermeidung der Störerhaftungs-Problematik erfolgt der Internet-Zugang ausschließlich per VPN über einen Server im Ausland oder den Server eines Vereines mit Provider-Status. In den meisten Fällen wird dabei ein Tunnel mit OpenVPN aufgebaut. Das installieren wir deswegen:

# echo "net-misc/openvpn iproute2 passwordsave" >> /etc/portage/package.use/Freifunk
# emerge -av openvpn

In /etc/conf.d/openvpn muß die Zeile PEER_DNS="yes" ersetzt werden durch PEER_DNS="no".

Oft erhält man vom VPN-Anbieter eine fertige Konfiguration, diese wird nach /etc/openvpn/.conf kopiert.

Für Mullvad sieht die Konfiguration z.B. so aus:

/etc/openvpn/mullvad.conf

client
remote se.mullvad.net 1300
##remote nl.mullvad.net 1300
cipher AES-256-CBC
dev exitVPN
dev-type tun
proto udp
tun-ipv6

resolv-retry infinite
route-noexec
persist-key
persist-tun
nobind ping 5
ping-restart 60
ping-timer-rem
explicit-exit-notify 2
script-security 2
remote-cert-tls server
route-delay 5
##tun-mtu 1500
##fragment 1300
##mssfix 1300
verb 4
comp-lzo

ca /etc/openvpn/mullvad/ca.crt
cert /etc/openvpn/mullvad/mullvad.crt
key /etc/openvpn/mullvad/mullvad.key

crl-verify /etc/openvpn/mullvad/crl.pem
# Limit range of possible TLS cipher-suites
tls-cipher TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA:TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA:TLS-DHE-RSA-WITH-AES-128-CBC-SHA:TLS-DHE-RSA-WITH-SEED-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SH

Das CA-Zertifikat von Mullvad, das eigene Zertifikat und der zugehörige private key sowie die CRL gehören also nach /etc/openvpn/mullvad.

Weiterhin benötigen wir Up- und Down-Scripte, in denen Routen und Regeln gesetzt werden:

/etc/openvpn/openvpn.mullvad-up.sh

#!/bin/sh
# ip rules for maintenance
ip rule add from $ifconfig_local table ggrz priority 9181

# ipv4 default route into appropriate rt_table
ip route replace 0.0.0.0/1 via $route_vpn_gateway dev $dev table ggrz
ip route replace 128.0.0.0/1 via $route_vpn_gateway dev $dev table ggrz

# ipv6 default route into appropriate rt_table
ip -6 route replace 2000::/3 dev $dev table ggrz

exit 0

/etc/openvpn/openvpn.mullvad-down.sh

#!/bin/sh
# ip rules for maintenance
ip rule del from $ifconfig_local table ggrz priority 9181

Zuletzt wird wieder ein Symlink für den Initscript erstellt, dieser gestartet und in den Default-Runlevel aufgenommen:

# nano -w /etc/openvpn/.conf
# cd /etc/init.d
# ln -s openvpn /etc/init.d/openvpn.
# rc-service openvpn. start
# rc-update add openvpn. default

IC-VPN

Installation

# echo "net-misc/bird" >> /etc/portage/package.keywords/Freifunk
# echo "net-misc/tinc" >> /etc/portage/package.keywords/Freifunk
# emerge -av bird tinc

tinc

Wesentliche Community-übergreifende Konfigurationen liegen in einem Git-Repository, welches wir klonen:

# mkdir /etc/tinc/icVPN
# git clone https://github.com/freifunk/icvpn /etc/tinc/icVPN/

Zur Absicherung des Tunnels wird ein Schlüsselpaar erzeugt:

# tincd -n icvpn -K

Bei den Vorgaben für den Speicherort wird der Verzeichnisname icvpn durch icVPN ersetzt. Also /etc/tinc/icVPN/rsa_key.priv und /etc/tinc/icVPN/rsa_key.pub.

Anschließend erstellen wir den Kopf der Konfiguration von tinc:

/etc/tinc/icVPN/tinc.conf.header:

Name = gera_greiz1
PrivateKeyFile = /etc/tinc/icVPN/rsa_key.priv
Mode = Switch
PingTimeout = 30
Port = 10781
Hostnames = yes
Interface = icVPN

Diesen Header kopieren wir in die eigentliche tinc-Konfiguration:

# cp /etc/tinc/icVPN/tinc.conf.header /etc/tinc/icVPN/tinc.conf

Nun benötigen wir Scripts zur Netzwerk-Konfiguration, welches tinc nach dem Verbindungsauf- und -abbau ausführt:

/etc/tinc/icVPN/tinc-up:

#!/bin/sh
# Interface aktivieren und IP-Adressen setzen
/bin/ip link set dev $INTERFACE up
/bin/ip addr add dev $INTERFACE 10.207.0.181/16 broadcast 10.207.255.255 scope link
/bin/ip -6 addr add dev $INTERFACE fec0::a:cf:0:b5/96 preferred_lft 0

# Device-Routen in den anderen Routing-Tabellen
/bin/ip -4 route add 10.207.0.0/16 proto kernel dev $INTERFACE src 10.207.0.181 table ggrz
/bin/ip -6 route add fec0::a:cf:0:0/96 proto kernel dev $INTERFACE table ggrz
/bin/ip -6 route add fe80::/64 proto kernel dev $INTERFACE table ggrz
/bin/ip -4 route add 10.207.0.0/16 proto kernel dev $INTERFACE src 10.207.0.181 table icvpn
/bin/ip -6 route add fec0::a:cf:0:0/96 proto kernel dev $INTERFACE table icvpn
/bin/ip -6 route add fe80::/64 proto kernel dev $INTERFACE table icvpn

### Default-Route zu vpn03 Berlin
##ip route replace 0.0.0.0/1 via 10.207.0.6 dev $INTERFACE table ggrz
##ip route replace 128.0.0.0/1 via 10.207.0.6 dev $INTERFACE table ggrz

# ip rules
/bin/ip rule add priority 31000 iif $INTERFACE table icvpn
/bin/ip rule add priority 31001 iif $INTERFACE unreachable
/bin/ip -6 rule add priority 31000 iif $INTERFACE table icvpn
/bin/ip -6 rule add priority 31001 iif $INTERFACE unreachable

/etc/tinc/icVPN/tinc-down:

#!/bin/sh
/bin/ip addr del dev $INTERFACE 10.207.0.181/16 broadcast 10.207.255.255
/bin/ip -6 addr del dev $INTERFACE fec0::a:cf:0:b5/96

# ip rules
/bin/ip rule del priority 31000 iif $INTERFACE table icvpn
/bin/ip rule del priority 31001 iif $INTERFACE unreachable
/bin/ip -6 rule del priority 31000 iif $INTERFACE table icvpn
/bin/ip -6 rule del priority 31001 iif $INTERFACE unreachable

# shutdown interface
/bin/ip link set dev $INTERFACE down

Beide Scripts müssen ausführbar sein:

# chmod 755 /etc/tinc/icVPN/tinc-*

Nun wird noch ein Script benötigt, welches die Konfiguration um die aktuellen Peer-Einträge ergänzt:

/etc/tinc/icVPN/update-tincd-icvpn:

#!/bin/bash
# Simple script for update of tinc IC-VPN hosts from git
# upstream and automated creation of tincd configuration.

# Tinc IC-VPN configuration directory
TINC_ICVPN=/etc/tinc/icVPN

# IC-VPN configuration file
TINC_CONF=tinc.conf

# IC-VPN configuration file header
TINC_CONF_HEADER=tinc.conf.header

# IC-VPN host file directory
TINC_HOSTS=hosts
function getCurrentVersion() {
# Get hash from latest revision
git log --format=format:%H -1
}

cd $TINC_ICVPN

# Get current version hash
GIT_REVISION=$(getCurrentVersion)

# Automagically commit local changes
# This preserves local changes
git commit -m "CRON: auto commit"

# Pull latest changes from upstream
git fetch
git merge origin/master -m "Auto Merge"

# Get new version hash
GIT_NEW_REVISION=$(getCurrentVersion)

if [ $GIT_REVISION != $GIT_NEW_REVISION ]
then
  # Start with header file for tinc configuration
  cp $TINC_CONF_HEADER $TINC_CONF

  # Iterate through hosts in metanodes
  while read HOST; do
    # search correspondending host with address
    grep -iq '^Address' -- hosts/"$HOST" || continue
    # Add ConnectTo line for current host
    echo "ConnectTo = $HOST" >> $TINC_CONF
  done < metanodes

  # Reload updated configuration
  echo "Reload tincd configuration."
  kill -HUP $(pidof tincd)
fi

Auch dieses Script muß ausführbar sein:

# chmod 750 /etc/tinc/icVPN/update-tincd-icvpn

Um dann stündlich abgearbeitet zu werden, legen wir für cron folgende Datei an:

/etc/cron.d/update-tincd-icvpn

# Global variables
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

# Update tincd IC-VPN peers every hour.
42 * * * * root /etc/tinc/icVPN/update-tincd-icvpn > /dev/null

Wichtig sind die richtigen Berechtigungen und ein Neustart von Cron:

# chmod 644 /etc/cron.d/update-tincd-icvpn
# /etc/init.d/vixie-cron restart

Nun müssen wir unser Gateway im Git hinterlegen und damit den anderen Gateways bekanntgeben. Dazu wird ein Push-Request auf https://github.com/freifunk/icvpn mit folgender Datei im Verzeichnis hosts erstellt:

gera_greiz1:

Address = gw1.freifunk-gera-greiz.de
Port = 10781

-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEArLhMleQyfCgADO1vPEHK1hTJ8ZW/NljeiFsZcvTHCRKnnfF0AP1S
XfcviGlQCV3sfhXLdKABe0QZiLeHwbJY2R/n0PJxG/UxfscAKH+vRCa6qA4PqVI7
CLShMe+E/S66UPvYzYYwBpfLBFYlaMzSz+IhvhiRwoCWgk/0+YfPJt85mFSUHNxN
XGiBLJBSRGnGi9BFSw+GbrN86lmknV/fnL4uypFw94TJYF0Q7TexgP7aRtFSOciH
2tDCbzzJh7lmw4XE1Wtz7QWUP/R34zqlOELXlmoilbkTO71y4qlCeTHmaWDOG9/f
VScV3K/v1mrofNlK35GJzfcOp9ueijIbZwIDAQAB
-----END RSA PUBLIC KEY-----

Der public Key ist der Inhalt von /etc/tinc/icVPN/rsa_key.pub.

Zum Abschluß wird tinc gestartet:

# cd /etc/init.d
# ln -s tincd /etc/init.d/tincd.icVPN
# rc-service tincd.icVPN start
# rc-config add tincd.icVPN default

Nun können wir noch kontrollieren, ob alles wie gewünscht funktioniert. Das Interface:

# ifconfig icVPN
icVPN: flags=4163  mtu 1500
        inet 10.207.0.181  netmask 255.255.0.0  broadcast 10.207.255.255
        inet6 fe80::3001:89ff:fe38:e613  prefixlen 64  scopeid 0x20
        inet6 fec0::a:cf:0:b5  prefixlen 96  scopeid 0x40
        ether 32:01:89:38:e6:13  txqueuelen 500  (Ethernet)
        RX packets 49766  bytes 2922522 (2.7 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 749  bytes 50702 (49.5 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

Erreichbarbeit anderer Peers, z.B. berlin2:

# ping -c2 10.207.0.6
PING 10.207.0.6 (10.207.0.6) 56(84) bytes of data.
64 bytes from 10.207.0.6: icmp_seq=1 ttl=64 time=1.53 ms
64 bytes from 10.207.0.6: icmp_seq=2 ttl=64 time=1.17 ms

--- 10.207.0.130 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 1.171/1.354/1.537/0.183 ms

# ping6 -c2 fec0::a:cf:0:6
PING fec0::a:cf:0:6(fec0::a:cf:0:6) 56 data bytes
64 bytes from fec0::a:cf:0:6: icmp_seq=1 ttl=64 time=1.10 ms
64 bytes from fec0::a:cf:0:6: icmp_seq=2 ttl=64 time=2.20 ms

--- fec0::a:cf:0:82 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 1.108/1.656/2.204/0.548 ms

bird

# mkdir /etc/bird
# mkdir /etc/bird/bird.d
# mkdir /etc/bird/bird6.d
# touch /etc/bird/bird.conf
# touch /etc/bird/bird6.conf
# ln -s bird/bird.conf /etc/bird.conf
# ln -s bird/bird6.conf /etc/bird6.conf

Nun erstellen bzw. editieren wir folgende Konfigurations-Dateien:

/etc/bird/local.conf:

# lokale Parameter (verschieden für jedes Gateway unserer Community)

router id 10.207.0.181;                 # Router-Id (entspricht üblicherweise v4-IP-Adresse)
define addr_ic = 10.207.0.181;          # eigene Gateway-Adresse im IC-VPN (Tinc-Tunnel)
define addr_br = 10.181.0.11;           # eigene Mesh-Bridge-IP
define addr_neighbor1 = 10.181.0.12;    # interner Peer 1
define addr_neighbor2 = 10.181.0.13;    # interner Peer 2

/etc/bird/bird.conf:

timeformat protocol iso long;

# lokale Konfigurationsdaten einbinden
include "/etc/bird/local.conf";

# AS
define ggrz_as = 65181; # AS Gera-Greiz

# Bird Routingtabellen
table ggrz;                                     # internes Mesh
table icvpn;                                    # ICVPN

# ROA
roa table roa_icvpn {
    include "/etc/bird/bird.d/icvpn-roa";
}

#####################
### F U N C T I O N S
#####################
#
#   alle Freifunk-Netze
function is_freifunk() {
    return net ~ [
        10.0.0.0/8+
    ];
}

#   alle DN42-Netze
function is_dn42() {
    return net ~ [
        172.20.0.0/16+,
        172.22.0.0/15+
    ];
}

#   alle Chaos-VPN-Netze
function is_chaosvpn() {
    return net ~ [
        172.31.0.0/16+
    ];
}

#   Mesh-Netze Gera-Greiz
function is_ggrz_self_net() {
    return net ~ [
        10.181.0.0/16+
    ];
}

#####################
### F I L T E R S
#####################
#
#   Import-Filter für alle ICVPN-Peers
filter ebgp_icvpn_import_filter {
    if is_ggrz_self_net() then reject;          # eigene Netze zurückweisen
    if is_freifunk() then accept;               # interne Freifunk-Netze erlauben
    if is_dn42() then accept;                   # DN42-Netze erlauben
    if is_chaosvpn() then accept;               # CaosVPN-Netze erlauben
#    if roa_check(roa_icvpn) = ROA_VALID then { # Test Route Origin Authorization für Freifunk-Netze
#        accept;
#    } else {
#        print "ROA check failed for ", net, " ASN ", bgp_path.last;
#    }
    reject;                                     # alles andere ablehnen
}

#####################
### P R O T O C O L S
#####################
#
#   Interfaces abfragen
protocol device {
    scan time 30;
};

#   Device-Routen in Bird-Table icvpn aufnehmen
protocol direct ggrz_subnets {
    interface 10.181.0.0/16;
    table icvpn;
};

#   Bird-Tabelle ICVPN nach GGRZ kopieren
protocol pipe icvpn2ggrz {
    import none;                                # nichts von ggrz nach icvpn importieren
    export all;                                 # alles von icvpn nach ggrz exportieren
    table icvpn;                                # Quelle: icvpn
    peer table ggrz;                            # Ziel: ggrz
};

#   Kernel-Routing-Table fürs Mesh-Netz Gera-Greiz
protocol kernel kernel_ggrz {
    scan time 30;
    import none;                                # nichts von Kernel in Bird importieren
    export filter {
        if is_ggrz_self_net() then              # eigene Netze nicht zum Kernel exportieren
            reject;
        krt_prefsrc = addr_br;
        accept;                                 # alles andere Bird-ggrz-Tabelle kommt in Kernel
    };
    table ggrz;                                 # Bird-Tabelle icvpn
    kernel table 181;                           # Kernel-Tabelle icvpn (s. etc/iproute2/rt_tables)
};

#   Kernel-Routing-Table fürs IC-VPN
protocol kernel kernel_icvpn {
    scan time 30;
    import none;                                # nichts von Kernel in Bird importieren
    export filter {
        if is_ggrz_self_net() then              # eigene Netze nicht zum Kernel exportieren
            reject;
        krt_prefsrc = addr_ic;
        accept;                                 # alles andere vom IC-VPN kommt in Kernel
    };
    table icvpn;                                # Bird-Tabelle icvpn
    kernel table 42;                            # Kernel-Tabelle icvpn (s. etc/iproute2/rt_tables)
};

#####################
### T E M P L A T E S
#####################
#
#   Template für iBGP (interne Peers)
template bgp ibgp {
    local addr_br as ggrz_as;           # als eigene IP die der Mesh-Bridge verwenden
    table icvpn;                        # Bird-Tabelle icvpn (korrekt???)
    import keep filtered on;            # gefilterte Routen nicht löschen,
                                        # sondern verstecken und nicht propagieren
                                        # können dadurch mit "show route filtered" angesehen werden
    import all;  # EXPERIMENT !!!!!
    export where source = RTS_BGP;      # alles, was über BGP hereingekommen ist, exportieren
    direct;                             # Nachbar ist direkt verbunden
    gateway direct;                     # Next Hop wird nicht durch Lookup der Routing Table ermittelt,
                                        # sondern der bgp_next_hop genommen (falls direkt erreichbar,
                                        # sonst die Adresse des Neighbor
};

#   Template für eBGP (Peers im IC-VPN)
template bgp peers {
    local addr_ic as ggrz_as;           # als eigene IP die des Tinc-Tunnels verwenden
    table icvpn;                        # Bird-Tabelle icvpn
    import keep filtered on;            # gefilterte Routen nicht löschen,
                                        # sondern verstecken und nicht propagieren
                                        # können dadurch mit "show route filtered" angesehen werden
    import filter ebgp_icvpn_import_filter;
    export filter {
        if is_ggrz_self_net() then {    # interne Netze Gera-Greiz
            accept;                     # exportieren
        }
        if source = RTS_BGP then {      # wenn über BGP gelernt und
            if is_freifunk() || is_dn42() || is_chaosvpn() then {
                                        # wenn es sich um ein Freifunk-, DN42- oder Chaos-Netz handelt
                accept;                 # exportieren
            }
        }
        reject;                         # alle anderen Routen nicht exportieren
    };
    direct;                             # Nachbar ist direkt verbunden
};

#####################
### P E E R I N G S
#####################
#
#   iBGP (interne Peers)
##protocol bgp neighbor1 from ibgp {
##    neighbor addr_neighbor1 as ggrz_as;
##};
##protocol bgp neighbor2 from ibgp {
##    neighbor addr_neighbor2 as ggrz_as;
##};

# eBGP IC-VPN Peers
# erzeugt mit /etc/cron.d/update-icvpn-meta
# ruft auf: /etc/icvpn/update-icvpn-meta
# Parameter BIRD_TEMPLATE muß dem Templatenamen (peers) entsprechen
include "/etc/bird/bird.d/icvpn";

/etc/bird/local6.conf

# lokale Parameter (verschieden für jedes Gateway unserer Community)

router id 10.207.0.183;                         # Router-Id (entspricht üblicherweise v4-IP-Adresse)
define addr_ic = fec0::a:cf:0:b5;               # eigene Gateway-Adresse im IC-VPN (Tinc-Tunnel)
define addr_br = fdb5:78b:64cc::11;             # eigene Mesh-Bridge-IP
define addr_neighbor1 = fdb5:78b:64cc::12;      # interner Peer 1
define addr_neighbor2 = fdb5:78b:64cc::13;      # interner Peer 2

/etc/bird/bird6.conf

timeformat protocol iso long;

# lokale Konfigurationsdaten einbinden
include "/etc/bird/local6.conf";

# AS
define ggrz_as = 65181; # AS Gera-Greiz

# Bird Routingtabellen
table ggrz;                                     # internes Mesh
table icvpn;                                    # ICVPN

# ROA
roa table roa_icvpn {
    include "/etc/bird/bird6.d/icvpn-roa";
}

#####################
### F U N C T I O N S
#####################
#
#   ULA Adressen
function is_ula() {
    return net ~ [
        fc00::/7{48,64}
    ];
}

# Mesh-Netze Gera-Greiz
function is_ggrz_self_net() {
    return net ~ [
        fdb5:078b:64cc::/48+
    ];
}

#####################
### F I L T E R S
#####################
#
#   Import-Filter für alle ICVPN-Peers
filter ebgp_icvpn_import_filter {
    if is_ggrz_self_net() then reject;          # eigene Netze zurückweisen
    if is_ula() then accept;                    # Netze mit ULA-Adressen erlauben
#    if roa_check(roa_icvpn) = ROA_VALID then { # Test Route Origin Authorization für Freifunk-Netze
#        accept;
#    } else {
#        print "ROA check failed for ", net, " ASN ", bgp_path.last;
#    }
    reject;                                     # alles andere ablehnen
}

#####################
### P R O T O C O L S
#####################
#
#   Interfaces abfragen
protocol device {
    scan time 30;
};

#   Device-Routen in Bird-Table icvpn aufnehmen
protocol direct ggrz_subnets {
    interface fdb5:078b:64cc::/48;
    table icvpn;
};

#   Bird-Tabelle ICVPN nach GGRZ kopieren
protocol pipe icvpn2ggrz {
    import none;                                # nichts von ggrz nach icvpn importieren
    export all;                                 # alles von icvpn nach ggrz exportieren
    table icvpn;                                # Quelle: icvpn
    peer table ggrz;                            # Ziel: ggrz
};

#   Kernel-Routing-Table fürs Mesh-Netz Gera-Greiz
protocol kernel kernel_ggrz {
    scan time 30;
    import none;                                # nichts von Kernel in Bird importieren
    export filter {
        if is_ggrz_self_net() then              # eigene Netze nicht zum Kernel exportieren
            reject;
        krt_prefsrc = addr_br;
        accept;                                 # alles andere Bird-ggrz-Tabelle kommt in Kernel
    };
    table ggrz;                                 # Bird-Tabelle icvpn
    kernel table 181;                           # Kernel-Tabelle icvpn (s. etc/iproute2/rt_tables)
};

#   Kernel-Routing-Table fürs IC-VPN
protocol kernel kernel_icvpn {
    scan time 30;
    import none;                                # nichts von Kernel in Bird importieren
    export filter {
        if is_ggrz_self_net() then              # eigene Netze nicht zum Kernel exportieren
            reject;
        krt_prefsrc = addr_ic;
        accept;                                 # alles andere vom IC-VPN kommt in Kernel
    };
    table icvpn;                                # Bird-Tabelle icvpn
    kernel table 42;                            # Kernel-Tabelle icvpn (s. etc/iproute2/rt_tables)
};

#####################
### T E M P L A T E S
#####################
#
#   Template für iBGP (interne Peers)
template bgp ibgp {
    local addr_br as ggrz_as;           # als eigene IP die der Mesh-Bridge verwenden
    table icvpn;                        # Bird-Tabelle icvpn (korrekt???)
    import keep filtered on;            # gefilterte Routen nicht löschen,
                                        # sondern verstecken und nicht propagieren
                                        # können dadurch mit "show route filtered" angesehen werden
    import all;  # EXPERIMENT !!!!!
    export where source = RTS_BGP;      # alles, was über BGP hereingekommen ist, exportieren
    direct;                             # Nachbar ist direkt verbunden
    gateway direct;                     # Next Hop wird nicht durch Lookup der Routing Table ermittelt,
                                        # sondern der bgp_next_hop genommen (falls direkt erreichbar,
                                        # sonst die Adresse des Neighbor
};

#   Template für eBGP (Peers im IC-VPN)
template bgp peers {
    local addr_ic as ggrz_as;           # als eigene IP die des Tinc-Tunnels verwenden
    table icvpn;                        # Bird-Tabelle icvpn
    import keep filtered on;            # gefilterte Routen nicht löschen,
                                        # sondern verstecken und nicht propagieren
                                        # können dadurch mit "show route filtered" angesehen werden
    import filter ebgp_icvpn_import_filter;
    export filter {
        if is_ggrz_self_net() then {    # interne Netze Gera-Greiz
            accept;                     # exportieren
        }
        if source = RTS_BGP then {      # wenn über BGP gelernt und
            if is_ula() then {          # wenn es sich um Netze mit ULA-Adressen handelt
                accept;                 # exportieren
            }
        }
        reject;                         # alle anderen Routen nicht exportieren
    };
    direct;                             # Nachbar ist direkt verbunden
};

#####################
### P E E R I N G S
#####################
#
#   iBGP (interne Peers)
##protocol bgp neighbor1 from ibgp {
##    neighbor addr_neighbor1 as ggrz_as;
##};
##protocol bgp neighbor2 from ibgp {
##    neighbor addr_neighbor2 as ggrz_as;
##};

# eBGP IC-VPN Peers
# erzeugt mit /etc/cron.d/update-icvpn-meta
# ruft auf: /etc/icvpn/update-icvpn-meta
# Parameter BIRD_TEMPLATE muß dem Templatenamen (peers) entsprechen
include "/etc/bird/bird6.d/icvpn";

Der Austauch der Konfirurationsparameter der einzelnen Communities geschieht wieder per Git. Es gibt ein Repository mit den Daten (icvpn-meta) und eines mit Scripts zur Erzeugung der Konfigurationen (icvpn-scripts). Für die Scripts wird python-yaml benötigt. Das installieren wir, anschließend clonen wir die beiden Repositories:

# emerge -av pyyaml
# mkdir /etc/icvpn
# cd /etc/icvpn
# git clone https://github.com/freifunk/icvpn-meta.git meta
# git clone https://github.com/freifunk/icvpn-scripts.git scripts

Unsere eigene Community müssen wir auch bekanntgeben. Dazu wird ein Push-Request auf https://github.com/freifunk/icvpn-meta mit folgender Datei erstellt:

gera-greiz:

tech-c:
  - Diese E-Mail-Adresse ist vor Spambots geschützt! Zur Anzeige muss JavaScript eingeschaltet sein.
asn: 65181
networks:
  ipv4:
    - 10.181.0.0/16
  ipv6:
    - fdb5:078b:64cc::/48
bgp:
  gera_greiz1:
    ipv4: 10.207.0.181
    ipv6: fec0::a:cf:0:b5
domains:
  - ffggrz
  - 181.10.in-addr.arpa
  - c.c.4.6.b.8.7.0.5.b.d.f.ip6.arpa
nameservers:
  - 10.181.0.11
  - fdb5:78b:64cc::11

Jetzt benötigen wir ein Script, welches das lokale Git-Repository aktualisiert und daraus die Konfigurationen für bird und dnsmasq erzeugt:

/etc/icvpn/update-icvpn-meta:

****!/bin/bash
# Simple script for update of IC-VPN meta information from git upstream
# and automated creation of derived bird and dnsmasq configuration.
# Note that scripts are not automatically updated for security reasons.

# Local icvpn-meta and icvpn-script clones
ICVPN_META=/etc/icvpn/meta
ICVPN_SCRIPTS=/etc/icvpn/scripts

# Name of own community (as used in icvpn-meta repository)
OWN_COMMUNITY=gera-greiz

# Bird peer configuration (IPv4 and IPv6)
BIRD_CONF=/etc/bird/bird.d/icvpn
BIRD6_CONF=/etc/bird/bird6.d/icvpn

# Bird roa configuration (IPv4 and IPv6)
BIRD_ROA=/etc/bird/bird.d/icvpn-roa
BIRD6_ROA=/etc/bird/bird6.d/icvpn-roa
BIRD_ROA_LEN=24
BIRD6_ROA_LEN=64

# Bird templates used for peers (IPv4 and IPv6)
BIRD_TEMPLATE=peers
BIRD6_TEMPLATE=peers

# Dnsmasq configuration
DNSMASQ_CONF=/etc/dnsmasq.d/icvpn

function getCurrentVersion() {
  # Get hash from latest revision
  git log --format=format:%H -1
}

cd $ICVPN_META

# Get current version hash
GIT_REVISION=$(getCurrentVersion)

# Automagically commit local changes
# This preserves local changes
git commit -m "CRON: auto commit"

# Pull latest changes from upstream
git fetch
git merge origin/master -m "Auto Merge"

# Get new version hash
GIT_NEW_REVISION=$(getCurrentVersion)

# Check whether content of icvpn-meta has changed
if [ $GIT_REVISION != $GIT_NEW_REVISION ]
then
  cd $ICVPN_SCRIPTS
  # Create bird configuration
  ./mkbgp -4 -f bird -s $ICVPN_META -d $BIRD_TEMPLATE -x $OWN_COMMUNITY > $BIRD_CONF
  ./mkbgp -6 -f bird -s $ICVPN_META -d $BIRD6_TEMPLATE -x $OWN_COMMUNITY > $BIRD6_CONF
  ./mkroa -4 -f bird -s $ICVPN_META -m $BIRD_ROA_LEN -x $OWN_COMMUNITY > $BIRD_ROA
  ./mkroa -6 -f bird -s $ICVPN_META -m $BIRD6_ROA_LEN -x $OWN_COMMUNITY > $BIRD6_ROA
  # Restrict access
  ##chown bird.bird $BIRD_CONF $BIRD6_CONF $BIRD_ROA $BIRD6_ROA
  chmod 640 $BIRD_CONF $BIRD6_CONF $BIRD_ROA $BIRD6_ROA

  # Create dnsmasq configuration
  ./mkdns -f dnsmasq -s $ICVPN_META -x OWN_COMMUNITY > $DNSMASQ_CONF

  # Version has changed we need to update
  echo "Reload bird configuration."
  kill -HUP $(pidof bird)
  kill -HUP $(pidof bird6)
  echo "Reload dnsmasq configuration."
  /sbin/rc-service dnsmasq restart
fi

Die Programme zum Erstellen der Bird- und Dnsmasq-Konfiguration benötigen ein Python 2.x, kein 3.x. Ansonsten bleiben beim Aufruf per Cron (warum auch immer?) die erzeugten Konfigurationen leer. Deswegen wird Python 2.7 als System-Python eingestellt (mittels "eselect python set x").

Das Script sollte ausführbar sein:

# chmod 750 /etc/icvpn/update-icvpn-meta

Um dann stündlich abgearbeitet zu werden, legen wir für cron folgende Datei an:

/etc/cron.d/update-icvpn-meta

# Global variables
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/

# Update tincd IC-VPN peers every hour.
37 * * * * root /etc/icvpn/update-icvpn-meta > /dev/null

Die Cron-Datei muß die richtigen Rechte bekommen und Cron neu gestartet werden:

# chmod 644 /etc/cron.d/update-icvpn-meta
# /etc/init.d/vixie-cron restart

ZumSchluß wird bird gestartet und in den Default-Runlevel aufgenommen:

# /etc/init.d/bird start
# /etc/init.d/bird6 start
# rc-update add bird default
# rc-update add bird6 default

Zur Kontrolle werden die Routing-Tabellen angezeigt, diese müssen jede Menge Einträge enthalten:

# ip route show table icvpn
[...]
# ip route show table ggrz
[...]
# ip -6 route show table icvpn
[...]
# ip -6 route show table ggrz
[...]

Ein paar weitere Tests:

Eine Schwierigkeit ist die Ermittlung eines möglichen Ping-Ziels im ICVPN. Am wahrsccheinlichsten ist es, daß man einen der DNS-Server anderer Communities erreichen kann. Diese kann man wie folgt auflisten:

# cat /etc/dnsmasq.d/icvpn

In den folgenden Beispielen nutzen wir die 10.56.0.7.

Diese sollte man dann von einem Client aus unserem Freifunk-Netz anpingen können. Für einen Test direkt vom Gateway muß man mit einer "ip rule" noch dafür sorgen, daß wie für die Clients die Routing-Table "ggrz" verwendet wird:

# ip rule add to 10.56.0.7 table ggrz priority 32000
# ping 10.56.0.7
# ip rule del to 10.56.0.7 table ggrz priority 32000

Das korrekte Routing kann auch schneller wir folgt getestet werden:

# ip route get to 10.56.0.7 from 10.181.0.22 iif ggrzBAT
10.56.0.7 from 10.181.0.22 via 10.207.0.56 dev icVPN
    cache  iif ggrzBAT

Backbone Rheinland

Gre-Tunnel

Der Freifunk Rheinland e.V. ermöglicht uns den Exit ins Internet. Dazu werden GRE-Tunnel zu den dortigen Gateways erstellt. Die Konfiguration in /etc/conf.d/net:

###############################
# Tunnel
###############################

# ffrl-a-ak-ber: Backbone Rheinland (A), Berlin
# ffrl-a-ix-dus: Backbone Rheinland (A), Düsseldorf
# ffrl-b-ak-ber: Backbone Rheinland (B), Berlin
# ffrl-b-ix-dus: Backbone Rheinland (B), Düsseldorf
#
iptunnel_ffrl_a_ak_ber="mode gre local 148.251.158.22 remote 185.66.195.0 ttl 64"
config_ffrl_a_ak_ber="100.64.3.167 peer 100.64.3.166
                      185.66.194.27/32
                      2a03:2260:0:1dd::2/64"
mtu_ffrl_a_ak_ber="1400"
#
iptunnel_ffrl_a_ix_dus="mode gre local 148.251.158.22 remote 185.66.193.0 ttl 64"
config_ffrl_a_ix_dus="100.64.3.169 peer 100.64.3.168
                      185.66.194.27/32
                      2a03:2260:0:1de::2/64"
mtu_ffrl_a_ix_dus="1400"
#
iptunnel_ffrl_b_ak_ber="mode gre local 148.251.158.22 remote 185.66.195.1 ttl 64"
config_ffrl_b_ak_ber="100.64.3.171 peer 100.64.3.170
                      185.66.194.27/32
                      2a03:2260:0:1df::2/64"
mtu_ffrl_b_ak_ber="1400"
#
iptunnel_ffrl_b_ix_dus="mode gre local 148.251.158.22 remote 185.66.193.1 ttl 64"
config_ffrl_b_ix_dus="100.64.3.173 peer 100.64.3.172
                      185.66.194.27/32
                      2a03:2260:0:1e0::2/64"
mtu_ffrl_b_ix_dus="1400"

Danach werden die Symlinks der Init-Scripts der einzelnen Interfaces erstellt, die Tunnel gestartet und in den Default-Runlevel aufgenommen:

# cd /etc/init.d
# ln -s net.lo net.ffrl-a-ak-ber
# ln -s net.lo net.ffrl-a-ix-dus
# ln -s net.lo net.ffrl-b-ak-ber
# ln -s net.lo net.ffrl-b-ix-dus
# rc-service net.ffrl-a-ak-ber start
# rc-service net.ffrl-a-ix-dus start
# rc-service net.ffrl-b-ak-ber start
# rc-service net.ffrl-b-ix-dus start
# rc-update add net.ffrl-a-ak-ber default
# rc-update add net.ffrl-a-ix-dus default
# rc-update add net.ffrl-b-ak-ber default
# rc-update add net.ffrl-b-ix-dus default

Firewall

Layer 3

Primär dient die Firewall an dieser Stelle dazu, die Pakete aus der Freifunk-Mesh Richtung Exit-VPN zu natten und zu markieren, um sie an die spezielle Routing-Tabelle (181, ggrz) zu übergeben. Dazu legen wir die folgenden Regeln an:

IPv4:

# iptables -t filter -A INPUT -i eth0 -p tcp --dport 179 -j REJECT
# iptables -t filter -A INPUT -i exitVPN -p tcp --dport 179 -j REJECT
# iptables -t filter -A INPUT -i ggrzBR -p tcp --dport 179 -j REJECT

# iptables -A FORWARD -i ggrzBR -o eth0 -p tcp --dport 587 -m connlimit --connlimit-above 5 -j REJECT

# iptables -t mangle -A PREROUTING -i ggrzBR -j MARK --set-xmark 0x1/0xffffffff
# iptables -t mangle -A PREROUTING -i ggrzBR -p tcp --dport 587 -j MARK --set-mark 0x0/0xffffffff

# iptables -t nat -A POSTROUTING -o exitVPN -j MASQUERADE
# iptables -t nat -A POSTROUTING -o eth0 -p tcp --dport 587 -j MASQUERADE

# iptables -t nat -N ffrl-nat
# iptables -t nat -A ffrl-nat -s 100.64.3.166/31 -o ffrl+ -j RETURN
# iptables -t nat -A ffrl-nat -s 100.64.3.168/31 -o ffrl+ -j RETURN
# iptables -t nat -A ffrl-nat -s 100.64.3.170/31 -o ffrl+ -j RETURN
# iptables -t nat -A ffrl-nat -s 100.64.3.172/31 -o ffrl+ -j RETURN
# iptables -t nat -A ffrl-nat -s 10.181.0.0/16 -o ffrl+ -j SNAT --to-source 185.66.194.27
# iptables -t nat -A POSTROUTING -s 10.181.0.0/16 -o ffrl+ -j ffrl-nat

# iptables -A FORWARD -i ggrzBR -o ffrl+ -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
# iptables -A FORWARD -i ffrl+ -o ggrzBR -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

# iptables -t raw -A PREROUTING -d 5.196.74.176/32 -i ggrzBR -p udp -m multiport --dports 10181,20181 -m comment --comment "drop VPN from mesh" -j DROP
# iptables -t raw -A PREROUTING -d 193.28.153.11/32 -i ggrzBR -p udp -m multiport --dports 10181,20181 -m comment --comment "drop VPN from mesh" -j DROP
# iptables -t raw -A PREROUTING -d 136.243.179.182/32 -i ggrzBR -p udp -m multiport --dports 10181,20181 -m comment --comment "drop VPN from mesh" -j DROP

IPv6:

# ip6tables -t filter -A INPUT -i eth0 -p tcp --dport 179 -j REJECT
# ip6tables -t filter -A INPUT -i exitVPN -p tcp --dport 179 -j REJECT
# ip6tables -t filter -A INPUT -i ggrzBR -p tcp --dport 179 -j REJECT

# ip6tables -t nat -A POSTROUTING -o exitVPN -j MASQUERADE

# ip6tables -A FORWARD -i ggrzBR -o ffrl+ -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
# ip6tables -A FORWARD -i ffrl+ -o ggrzBR -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

# ip6tables -t raw -A PREROUTING -d 2a01:4f8:172:19ea:201::182/128 -i ggrzBR -p udp -m multiport --dports 10181,20181 -m comment --comment "drop VPN from mesh" -j DROP
# ip6tables -t raw -A PREROUTING -d 2a02:ff80:1003:2::2/128 -i ggrzBR -p udp -m multiport --dports 10181,20181 -m comment --comment "drop VPN from mesh" -j DROP
# ip6tables -t raw -A PREROUTING -d 2001:41d0:e:8b0::1/128 -i ggrzBR -p udp -m multiport --dports 10181,20181 -m comment --comment "drop VPN from mesh" -j DROP

Die Zeilen mit --dport 587 bewirken, daß der SMTP-Submission-Port nicht über das VPN geroutet wird. Das ist erforderlich, da die meisten VPN-Anbieter zur Spam-Verhinderung die Ports 25, 465 und 587 sperren.

Im Verzeichnis /etc/conf.d wird in den Dateien iptables und ip6tables die Zeile SAVE_ON_STOP="yes" geändert in SAVE_ON_STOP="no". Anschließend werden die angelegten Regeln gespeichert und iptables in den Runlevel default aufgenommen:

# rc-service iptables save
# rc-service iptables start
# rc-update add iptables default
# rc-service ip6tables save
# rc-service ip6tables start
# rc-update add ip6tables default

Layer 2

B.A.T.M.A.N. verfügt über die sinnvolle Funktionatität, im Server-Mode DHCP-Broadcasts von Clients nicht weiterzuleiten. Diese werden damit nur vom lokalen DHCP-Server beantwortet, welcher den Clients sich selbst als Default Gateway und DNS-Server nennt. Für IPv6 fehlt in B.A.T.M.A.N. eine analoge Funktionalität für die Router Advertisements. Damit sehen die Clients alle Gateways einer Mesh-Wolke als mögliches Default Gateway und nicht das nächste (wo der FastD-Tunnel des Knotens endet. Um das zu verhindern, wird "ebtables" genutzt:

# emerge -av ebtables

Analog zu iptables wird in /etc/conf.d/ebtables die Zeile SAVE_ON_STOP="yes" geändert in SAVE_ON_STOP="no". Anschließend wird die Konfiguration erzeugt, gespeicher und in den Default Runlevel aufgenommen:

# ebtables -A FORWARD -p IPv6 -i ggrzBAT --ip6-proto ipv6-icmp --ip6-icmp-type router-advertisement -j DROP
# rc-service ebtables save
# rc-service ebtables start
# rc-update add ebtables default

Dienste

NTP

Bei Verschlüsselung, fürs Logging u.a. ist eine genaue Zeit wichtig. Dafür wird NTP installiert:

# emerge -av ntp

Die Konfiguration wird dann wie folgt angepaßt:

/etc/ntp.conf:

# /etc/ntp.conf, configuration for ntpd; see ntp.conf(5) for help

driftfile /var/lib/ntp/ntp.drift

# listen at localhost and mesh only
interface ignore wildcard
interface listen 127.0.0.1
interface listen ::1
interface listen 10.181.0.11
interface listen fdb5:78b:64cc::11

# only use local time, synced with kvm host time
tos orphan 1

# By default, exchange time with everybody, but don't allow configuration.
restrict -4 default kod notrap nomodify nopeer noquery
restrict -6 default kod notrap nomodify nopeer noquery

# Local users may interrogate the ntp server more closely.
restrict 127.0.0.1
restrict ::1

Ohne die Angabe von "server", stattdessen mit "tos orphan 1" werden keine externen Zeitserver abgefragt, sondern nur die Systemzeit an andere NTP-Clients verteilt. Das ist sinnvoll, da dieses Gateway in einer virtuellen Maschine auf einem KVM-Host läuft. Dieser Host gibt seine per NTP aktuell gehaltene Zeit an die VMs weiter.

Damit der NTP-Daemon erst startet, wenn das Mesh-Interface vorhanden ist (daran soll er sich ja binden), wird folg. ergänzt:

/etc/conf.d/ntpd:

# soll sich an die Mesh-Interfaces binden,
# die sollten also da sein
rc_need="net.ggrzBAT"

Abschließend wird der Daemon gestartet:

# /etc/init.d/ntpd start
# rc-update add ntpd default

Dnsmasq

Als Server für DNS und DHCP verwenden wir Dnsmasq:

# echo "net-dns/dnsmasq auth-dns dhcp-tools" >> /etc/portage/package.use/Freifunk
# emerge -av dnsmasq

Die Konfiguration sollte dann so aussehen

/etc/dnsmasq.conf:

### Allgemeines
conf-dir=/etc/dnsmasq.d
bogus-priv
no-resolv
no-poll
expand-hosts
domain-needed
cache-size=4096
strict-order
interface=ggrzBAT
bind-interfaces

### DNS

# Hetzner DNS Server (später evtl. andere nehmen)
server=213.133.100.100
server=213.133.99.99
server=213.133.98.98

local=/ffggrz/
domain=ffggrz

### DHCP / RA

dhcp-authoritative
#log-dhcp

dhcp-range=10.181.1.1,10.181.2.254,3m
dhcp-option=option:router,10.181.0.11
dhcp-option=option:dns-server,10.181.0.11,8.8.8.8
dhcp-option=option:ntp-server,10.181.0.11
dhcp-option=option:domain-search,ffggrz
### MTU 1280
##dhcp-option-force=26,1280

dhcp-range=set:ggrzv6,::,constructor:ggrzBAT,slaac,ra-only,3m
dhcp-option=tag:ffsv6,option6:dns-server,[fdb5:78b:64cc::11]
dhcp-option=tag:ffsv6,option6:ntp-server,[fdb5:78b:64cc::11]
dhcp-option=tag:ffsv6,option6:domain-search,ffggrz

enable-ra
##ra-param=ggrzBAT,low,0,0
ra-param=ggrzBAT,high,60,1200

Falls das Verzeichnis /etc/dnsmasq.d noch nicht existiert, wird dieses jetzt angelegt:

# mkdir /etc/dnsmasq.d

Auch Dnsmasq soll erst starten, wenn das Mesh-Interface vorhanden ist:

/etc/conf.d/dnsmasq:

# soll sich an die Mesh-Interfaces binden,
# die sollten also da sein
rc_need="net.ggrzBAT"

Nun wird Dnsmasq noch gestartet und in den Default Runlevel aufgenommen:

# /etc/init.d/dnsmasq start
# rc-update add dnsmasq default

Karte, Listen

Backend

Zur Darstellung aktiver Nodes und Gateway in einer Karte bzw. Liste erfolgt die Sammlung und Aufbereitung der nötigen Daten mittels A.L.F.R.E.D.:

# emerge -av alfred

A.L.F.R.E.D. wird das zu nutzende Interface mitgeteilt, außerdem soll der Start erst nach dem Mesh-Interface erfolgen:

/etc/conf.d/alfred:

# B.A.T.M.A.N. interface
ALFRED_IFCBAT="ggrzBAT"

# soll sich an die Mesh-Interfaces binden,
# die sollten also da sein
rc_need="net.ggrzBAT"

Nun wird A.L.F.R.E.D. noch gestartet und in den Default Runlevel aufgenommen:

# /etc/init.d/alfred start
# rc-update add alfred default

Das Auslesen der Daten und die Übergabe an den Mapviewer erfolgt über das ffmap-Backend:

# emerge -av ffmap-backend

Dieses wird dann mittels Cron-Job minütlich abgearbeitet (s. /etc/cron.d/ffmap-backend). Die erzeugten Daten landen in /var/www/localhost/htdocs/data und werden dort vom Webserver (Nginx) für das Frontend zur Verfügung gestellt.

Frontend

Als Frontend verwenden wir den Meshviewer. Dieser wird nicht auf dem Gateway, sondern auf den Server für das Webportal installiert. Die folgenden Schritte werden also dort durchgeführt.

# cd /root
# git clone https://github.com/tcatm/meshviewer.git
# cd meshviewer
# npm install
# npm install bower grunt-cli
# node_modules/.bin/bower --allow-root install
# node_modules/.bin/grunt

Anschließend erfolgt die Konfiguration. Dazu wird die Beispieldatei  config.json.example nach build/config.json kopiert und angepaßt.