knb:raspberry

Offloader auf Basis eines Raspberry PI 4B (FASTD)

Bild: Freifunk München Logo

Achtung diese Anleitung ist veraltet und funktioniert nicht mehr mit Freifunk München! Bitte verwendet die Wireguard Anleitung

Disclaimer: Die Anleitung ist nur für Leute, die sich bereits mit dem Thema Linux auseinander gesetzt haben oder sich intensiv damit beschäftigen wollen.

Diese Anleitung funktioniert auf quasi allen Linux basierten Betriebssystemen, mit ein paar Anpassungen.

Mit dem Raspberry PI 4 ist es das erste Mal möglich, einen Offloader sinnvoll auf einem PI zu betreiben. Denn in dieser Version bietet der Ethernet Anschluss genug Durchsatz und die CPU ist leistungsfähig genug um eine ordentliche fastd Performance zu bieten. Dies ist bei den Vorgänger Modellen leider nicht der Fall.

Für kabelgebundene Clients und/oder Meshing per Kabel benötigt ihr einen VLAN-fähigen Switch!

Angeboten wird das PI in einer 1GB, 2GB und 4GB RAM Variante. Will man wirklich nur einen reinen Offloader ohne Zusatzfunktionen betreiben, reicht im Grunde die 1GB Version. Allerdings ist mehr RAM immer besser ;) und man weiß ja nicht, was man im Endeffekt noch alles darauf betreiben will.


Nachdem es kein fertiges Gluon Image für das Raspberry PI 4B gibt im Moment, installieren wir erstmal strikt nach Anleitung das aktuelle Raspbian.

Außerdem ermöglicht uns der Einsatz von Raspbian, dass wir alle normalen Anwendungen wie Webserver, Chatserver oder den Unifi-Controller einfach installieren können.

Auf dem FFBSeeCamp 2019 gab es auch einen Vortrag zum Thema.

Vom CCC gibt es dazu auch einen recent audio-only feed Raspberry PI4 als Offloader (tc19).

Eine Anleitung dazu findet ihr auf der offiziellen Raspbian Seite.

Am Ende der Prozedur, empfiehlt es sich auf der Boot Partition einfach eine leere Datei mit dem Namen „ssh“ anzulegen. Das aktiviert den SSH Daemon und man kann bequem per Putty, OpenSSH oder sonstigen Clients remote auf das PI zugreifen.

Der Benutzername lautet „pi“ und das Passwort „raspberry“. Unbedingt ändern! Sonst kommen Leute aus dem Internet auf euer Raspberry!

Möchte man vom Raspberry später auch ein WLAN ausstrahlen lassen so ist es zwingend von Nöten, dass WIFI explizit via rfkill freizugeben! Hier loggen wir uns per SSH ein und setzen folgenden Befehl ab:

 # rfkill unblock wifi

Anschließend starten wir den Raspberry 1x neu!

 # systemctl reboot

Wer nicht die nachfolgenden gut dokumentierten Schritte per Hand ausführen möchte, kann sich auch mit Hilfe von Ansible binnen 20 Minuten automatisiert einen Offloader Menügeführt und scriptiert erstellen lassen.

Das entsprechende HOWTO findet man in Djangos WIKI.

Nachdem ihr euch eingeloggt habt, wechseln wir zum Root User und laden das BATMAN Kernelmodul herunter. Das wird gebraucht um nachher das Routing innerhalb des Freifunknetzes zu übernehmen.

Am Besten schaut ihr vorher, welches die aktuelle BATMAN Version ist.

pi@raspberrypi:~ $ sudo su -
root@raspberrypi:~# cd /usr/src/
root@raspberrypi:/usr/src# wget https://downloads.open-mesh.org/batman/releases/batman-adv-2019.2/batman-adv-2019.2.tar.gz
root@raspberrypi:/usr/src# tar xzf batman-adv-2019.2.tar.gz

Nachdem das Kernelmodul manuell gebaut wird, wollen wir natürlich dass das auch Bestand hat, wenn ein Kernelupdate eingespielt wird. Dazu brauchen wir nun einige Pakete.

root@raspberrypi:/usr/src# apt update && apt install dkms raspberrypi-kernel-headers

Anschließend müssen wir auf Grund dessen, dass der Raspberry PI Kernel crosscompiled wurde die make Skripte neu generieren.

root@raspberrypi:/usr/src# cd linux-headers-$(uname -r)
root@raspberrypi:/usr/src/linux-headers-4.19.50-v7l+# make scripts

Es kann passieren, dass hier nach einiger Zeit mit der Fehlermeldung
„scripts/sortextable.c:31:10: fatal error: tools/be_byteshift.h: No such file or directory“
abgebrochen wird, das stellt aber kein Problem dar.

Nun nachdem die Vorbereitungen getroffen sind, zurück zu BATMAN. Und dem Anlegen der dkms.conf.

root@raspberrypi:/usr/src/linux-headers-4.19.50-v7l+# cd ../batman-adv-2019.2/
root@raspberrypi:/usr/src/batman-adv-2019.2# vi dkms.conf

Der Inhalt der dkms.conf sieht wie folgt aus:

PACKAGE_NAME=batman-adv
PACKAGE_VERSION=2019.2

DEST_MODULE_LOCATION=/extra
BUILT_MODULE_NAME=batman-adv
BUILT_MODULE_LOCATION=net/batman-adv

MAKE="'make'"
CLEAN="'make' clean"

AUTOINSTALL="yes"

Die Datei speichern und schon sind wir bereit für das erste Mal bauen des Kernelmodules.

root@raspberrypi:/usr/src/batman-adv-2019.2# dkms add -m batman-adv -v 2019.2
root@raspberrypi:/usr/src/batman-adv-2019.2# dkms build -m batman-adv -v 2019.2
root@raspberrypi:/usr/src/batman-adv-2019.2# dkms install -m batman-adv -v 2019.2

Als nächstes müssen wir dafür sorgen, dass das Kernelmodul beim Boot auch geladen wird. Dazu müssen wir die Datei „/etc/modules-load.d/batman-adv.module.conf“ wie folgt anpassen.

root@raspberrypi:/usr/src/batman-adv-2019.2# vi /etc/modules-load.d/batman-adv.module.conf
#
# Load batman-adv module on system boot
#
batman-adv
dummy

Nun kann man entweder rebooten oder die Module manuell laden.

root@raspberrypi:/usr/src/batman-adv-2019.2# modprobe dummy 
root@raspberrypi:/usr/src/batman-adv-2019.2# modprobe batman_adv

Um BATMAN verwalten zu können müssen wir nun noch „batctl“ installieren.

root@raspberrypi:/usr/src/batman-adv-2019.2# apt install batctl

Dann überprüfen wir ob alles korrekt geladen ist.

root@raspberrypi:/usr/src/batman-adv-2019.2# batctl ra
Active routing protocol configuration:

Selected routing algorithm (used when next batX interface is created):
 => BATMAN_IV

Available routing algorithms:
 * BATMAN_IV
 * BATMAN_V

Nachdem wir BATMAN_V bei uns verwenden, sehen wir dass aktuell der falsche Routing Algorithmus ausgewählt ist. Das korrigieren einmal sofort manuell.

root@raspberrypi:/usr/src/batman-adv-2019.2# batctl ra BATMAN_V

Nun kommen wir zur Interface Konfiguration. In diesem Beispiel, werden wir das Raspberry PI im Segment „welt“ ansiedeln, deswegen heißen auch die Interfaces entsprechend.

root@raspberrypi:/usr/src/batman-adv-2019.2# apt install bridge-utils
/etc/network/interfaces
# interfaces(5) file used by ifup(8) and ifdown(8)
 
# Please note that this file is written to be used with dhcpcd
# For static IP, consult /etc/dhcpcd.conf and 'man dhcpcd.conf'
 
# Include files from /etc/network/interfaces.d:
 
auto eth0
iface eth0 inet dhcp
 
auto br-welt
iface br-welt inet dhcp
        bridge-ports bat-welt
        pre-up /usr/sbin/batctl ra BATMAN_V
        pre-up /sbin/ip link add dummy-welt type dummy
        pre-up /sbin/ip link set address $(ip -br l | grep eth0 | egrep -o '([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})' | head -1) dev dummy-welt
        pre-up /sbin/ip link set dummy-welt up
        pre-up /usr/sbin/batctl -m bat-welt if add dummy-welt
        pre-up /sbin/ip link set bat-welt up
        pre-up /usr/sbin/batctl -m bat-welt gw_mode client
        pre-up /sbin/ip link set address $(ip -br l | grep eth0 | egrep -o '([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})' | head -1) dev bat-welt
        post-up /sbin/ip link set address $(ip -br l | grep eth0 | egrep -o '([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})' | head -1) dev br-welt

Im Beispiel setzen wir die MAC Addresse für BATMAN fest auf die MAC von eth0, damit wir später auch Statistiken etc abrufen können.

Bitte informiert euch vorher in welches Segment ihr wollt, dementsprechend muss der FASTD Port angepasst werden!

Domain FASTD Port
ffmuc_muc_cty 30002
ffmuc_muc_nord 30003
ffmuc_muc_ost 30004
ffmuc_muc_sued 30005
ffmuc_muc_west 30006
ffmuc_uml_nord 30007
ffmuc_uml_ost 30008
ffmuc_uml_sued 30009
ffmuc_uml_west 30010
ffmuc_gauting 30012
ffmuc_freising 30013
ffmuc_welt 30011

Um zum Freifunk München Netz verbinden zu können, brauchen wir nun noch fastd.

Diesen installieren wir einfach per apt

root@raspberrypi:~# apt install fastd

Und anschließend generieren wir uns einen Key, den wir zum Verbinden und verschlüsseln benötigen.

root@raspberrypi:~# fastd --generate-key
2019-07-05 10:57:41 +0100 --- Info: Reading 32 bytes from /dev/random...
Secret: 1843fd17494d265d65f9563e3655728dbf1c8f7fb04365a9ab733f4debdb8f51
Public: 7703d5f1766c11cc701cc039af1913faef216ced7b33dd18f87e57c172e0324e

Wir benötigen davon nur den Secret Key. Anschließend erzeugen wir die Konfiguration.

root@raspberrypi:~# mkdir /etc/fastd/welt
root@raspberrypi:~# vi /etc/fastd/welt/fastd.conf
 # vi /etc/fastd/welt/fastd.conf
/etc/fastd/welt/fastd.conf
#
# welt FASTd configuration
#
 
log to syslog level info;
 
interface "fastd-welt";
 
method "salsa2012+umac";
method "null";
 
secret "1843fd17494d265d65f9563e3655728dbf1c8f7fb04365a9ab733f4debdb8f51";
mtu 1406;
 
status socket "/var/run/fastd.welt.sock";
 
on up "
        batctl ra BATMAN_V
        ip link set $INTERFACE down
        ip link set address $(ip -br l | grep eth0 | egrep -o '([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})' | head -1) dev $INTERFACE
        ip link set $INTERFACE up
        batctl -m bat-welt if add $INTERFACE
 
";
 
on down "
        batctl -m bat-welt if del $INTERFACE
";
peer group "ffmuc" {
        peer limit 1;
 
         peer "gw04.in.ffmuc.net" {
                key "179c91d4b3b6676d032b622809bbdfa563c70f8873fb2b8c1e62d61020acd550";
                remote "gw04.ext.ffmuc.net" port <Port siehe oben - z.B. 30004>;
        }
 
        peer "gw05.in.ffmuc.net" {
                key "f26f70fea16e0708c1899ac373b24881b16b9cbfba9d24703617e98a260abb72";
                remote "gw05.ext.ffmuc.net" port <Port siehe oben - z.B. 30004>;
        }
}

Nun aktivieren wir „fastd“ für den Boot.

root@raspberrypi:~# systemctl enable fastd@welt

Dieser Teil ist wichtig, da es sein kann dass ihr gegen die Nutzungsbedingungen einiger Communities verstoßt, wenn ihr euch nicht als Knoten im Netz meldet. Außerdem trägt es generell zur Netzhygiene bei, wenn alle Knoten in den Statistiken auftauchen.

Nachdem wir auch auf der Knotenkarte auftauchen wollen, installieren wir auch noch ext-respondd

root@raspberrypi:~# apt install git python3-netifaces
root@raspberrypi:~# git clone https://github.com/freifunkMUC/ext-respondd /opt/ext-respondd/
root@raspberrypi:~# cp /opt/ext-respondd/ext-respondd.service.example /etc/systemd/system/ext-respondd.service
root@raspberrypi:~# systemctl daemon-reload
root@raspberrypi:~# systemctl enable ext-respondd

Dann müssen wir auch noch die Konfigurationsdateien von ext-respondd anpassen.

Bitte tragt die Daten inklusive dem Segmentnamen korrekt ein. Eine Übersicht der Segmentnamen gibt es hier.

root@raspberrypi:~# vi /opt/ext-respondd/alias.json

Die Dateien sind selbsterklärend, einfach die nötigen Infos eintragen.

{
  "nodeinfo": {
    "hostname": "aw-raspi-4",
    "hardware": {
      "model": "Raspberry Pi 4B"
    },
    "owner": {
      "contact": "awlnx @ chat.ffmuc.net"
    },
    "system": {
      "site_code": "<segmentname>",
      "role": "client"
    }
  },
  "firstseen": "2015-04-12T15:41:01"
}
root@raspberrypi:~# vi /opt/ext-respondd/config.json
{
  "batman": "bat-welt",
  "bridge": "br-welt",
  "mesh-vpn": [ "fastd-welt" ],
  "wan": "eth0",
  "rate_limit": 30,
  "rate_limit_burst": 10
}

Anschließend rebooten wir einmal und wenn alles stimmt sollte der Knoten auf der Karte erscheinen und „batctl“ folgendes liefern, der Neighbor kann je nach GW ein anderer sein:

root@raspberrypi:~# batctl -m bat-welt n
[B.A.T.M.A.N. adv 2019.1, MainIF/MAC: dummy-welt/dc:a6:32:00:6b:59 (bat-welt/86:6e:aa:be:bb:94 BATMAN_V)]
IF             Neighbor              last-seen
f2:00:22:10:00:00    0.060s (        1.0) [fastd-welt]

Nun sollet ihr eine IPv6 Adresse aus dem gewählten Segment auf eurem Interface sehen.

root@raspberrypi:~# ip -br a | grep br-welt
br-welt          UP             10.80.203.128/21 2001:608:a01:109:dea6:32ff:fe00:6b59/64 fe80::dea6:32ff:fe00:6b59/64 

Falls ihr per Kabel meshen wollt, ist dies nur sinnvoll per VLAN möglich.

Dazu müsst ihr zuerst ein VLAN Interface in /etc/network/interfaces anlegen und dieses an das BATMAN Interface hängen.

/etc/network/interfaces
auto eth0.666
iface eth0.666 inet manual
      post-up /usr/sbin/batctl ra BATMAN_V
      post-up /usr/sbin/batctl -m bat-welt if add eth0.666

Beachtet bitte, das Setup funktioniert nur in Umgebungen ohne VXLAN Meshing. Mit VXLAN Meshing braucht es auch noch ein VXLAN Interface.

Mehr dazu siehe unten.

Für andere Communities benötigt ihr die entsprechenden VXLAN IDs zum Meshen

Um meshing per Kabel nutzen zu können, muss bei manchen Communities (wie bei FFMUC) VXLAN benutzt werden.

Bei uns gibt es momentan folgende VXLAN IDs, diese werden von GLUON dynamisch aus dem domain_seed erzeugt.

Domain vxlan id
ffmuc_muc_cty 10758607
ffmuc_muc_nord 15521492
ffmuc_muc_ost 2948862
ffmuc_muc_sued 8599288
ffmuc_muc_west 7318933
ffmuc_uml_nord 5705961
ffmuc_uml_ost 4892713
ffmuc_uml_sued 16544703
ffmuc_uml_west 16677749
ffmuc_gauting 16175732
ffmuc_freising 12937858
ffmuc_welt 16306234

Um die VXLAN ID aus einem domain_seed zu berechnen haben wir ein kleines Python Skript geschrieben.

python3 get_vxlan_id_from_domain_seed.py domain_seed

Die Konfiguration in /etc/network/interfaces sieht so aus, um per VLAN666 VXLAN Meshing für das Segment Welt zu machen. Wie ihr seht, wird dafür die ID aus der obigen Tabelle benötigt.

/etc/network/interfaces
auto eth0.666
iface eth0.666 inet manual
        pre-up /sbin/ip link add vxlan-mesh type vxlan id 16306234 group ff02::15c dstport 4789 port 32768 61000 no udpcsum udp6zerocsumtx udp6zerocsumrx dev eth0.666 || true
        up /sbin/ip link set vxlan-mesh up
        post-up /usr/sbin/batctl ra BATMAN_V
        post-up /usr/sbin/batctl -m bat-welt if add vxlan-mesh
        down ip link set vxlan-mesh down
        post-down ip link del vxlan-mesh || true

Damit das vxlan-mesh Interface auch der bevorzugte Meshingpoint in BATMAN_V wird noch folgendes in die /etc/rc.local hinzufügen.

/etc/rc.local
echo 1000000 > /sys/devices/virtual/net/vxlan-mesh/batman_adv/throughput_override 

Danach rebooten oder das Interface hochfahren. Wenn alles geklappt hat und ihr bereits einen Router zum Meshen in diesem VLAN habt, sollte danach folgendes in batctl zu sehen sein.

root@raspberrypi:~# batctl -m bat-welt n
[B.A.T.M.A.N. adv 2018.3, MainIF/MAC: vxlan-mesh/4a:af:0b:bf:44:0c (bat-welt/dc:a6:32:00:6b:59 BATMAN_V)]
IF             Neighbor              last-seen
52:98:0e:9b:5f:9c    0.470s (        1.0) [vxlan-mesh]
f2:00:22:10:00:00    0.420s (        1.0) [fastd-welt]

Sollte sich kein Client am Mesh VLAN befinden, wird die vxlan-mesh Zeile nicht angezeigt.

Außerdem sollten die Interfaces wie folgt aussehen:

root@ffoff:~# batctl -m bat-welt if
dummy-welt: active
vxlan-mesh: active
fastd-welt: active

Leider ist der Link zum Gateway auf der Map nicht zu sehen.

Dazu müssen wir hostapd installieren und konfigurieren; bei der Vergabe der SSID ist zu beachten:

WICHTIG: Das Abändern der zum Segment passenden SSID ist gemäß der Nutzungsbedingungen nicht gestattet!

  • 5. Lokale Zusätze für Freifunk München
    Das Betreiben von Routern in Verbindung mit der Freifunk-München Netzwerkinfrastruktur ist nur gestattet, solange die folgenden zwei Bedingungen erfüllt sind:
    • die SSID des Client-Netzes auf “muenchen.freifunk.net/(Segmentname)” lautet
    • die Mesh-BSSID/SSID denen, der in den offiziellen Firmware-Images veröffentlichten, entspricht

Die Liste der SSIDs findet ihr hier:

Aktuell1) gibt es folgende Segmente:

SSID Segment-Name IPv4-Prefix IPv6-Prefix Wien IPv6-Prefix München
muenchen.freifunk.net/muc_cty ffmuc_muc_cty 10.80.128.0/21 2001:678:e68:100::/64 2001:678:ed0:100::/64
muenchen.freifunk.net/muc_nord ffmuc_muc_nord 10.80.136.0/21 2001:678:e68:101::/64 2001:678:ed0:101::/64
muenchen.freifunk.net/muc_ost ffmuc_muc_ost 10.80.144.0/21 2001:678:e68:102::/64 2001:678:ed0:102::/64
muenchen.freifunk.net/muc_sued ffmuc_muc_sued 10.80.152.0/21 2001:678:e68:103::/64 2001:678:ed0:103::/64
muenchen.freifunk.net/muc_west ffmuc_muc_west 10.80.160.0/21 2001:678:e68:104::/64 2001:678:ed0:104::/64
muenchen.freifunk.net/uml_nord ffmuc_uml_nord 10.80.168.0/21 2001:678:e68:105::/64 2001:678:ed0:105::/64
muenchen.freifunk.net/uml_ost ffmuc_uml_ost 10.80.176.0/21 2001:678:e68:106::/64 2001:678:ed0:106::/64
muenchen.freifunk.net/uml_sued ffmuc_uml_sued 10.80.184.0/21 2001:678:e68:107::/64 2001:678:ed0:107::/64
muenchen.freifunk.net/uml_west ffmuc_uml_west 10.80.192.0/21 2001:678:e68:108::/64 2001:678:ed0:108::/64
muenchen.freifunk.net/welt ffmuc_welt 10.80.200.0/21 2001:678:e68:109::/64 2001:678:ed0:109::/64
muenchen.freifunk.net/gauting ffmuc_gauting 10.80.208.0/21 2001:678:e68:10a::/64 2001:678:ed0:10a::/64
muenchen.freifunk.net/freising ffmuc_freising 10.80.216.0/21 2001:678:e68:10b::/64 2001:678:ed0:10b::/64
muenchen.freifunk.net/augsburg ffmuc_augsburg 10.80.224.0/21 2001:678:e68:10c::/64 2001:678:ed0:10c::/64
root@raspberrypi:~# apt install hostapd
root@raspberrypi:~# echo 'DAEMON_OPTS="-d"' >> /etc/default/hostapd 
root@raspberrypi:~# vi /etc/hostapd/hostapd.conf
 vi /etc/hostapd/hostapd.conf
/etc/hostapd/hostapd.conf
ssid=muenchen.freifunk.net/welt
 
country_code=DE
 
interface=wlan0
driver=nl80211
 
macaddr_acl=0
 
logger_syslog=0
logger_syslog_level=4
logger_stdout=-1
logger_stdout_level=0
 
hw_mode=a
wmm_enabled=1
 
# N
ieee80211n=1
require_ht=1
ht_capab=[MAX-AMSDU-3839][HT40+][SHORT-GI-20][SHORT-GI-40][DSSS_CCK-40]
 
# AC
ieee80211ac=1
require_vht=1
ieee80211d=0
ieee80211h=0
vht_capab=[MAX-AMSDU-3839][SHORT-GI-80]
vht_oper_chwidth=1
channel=36
vht_oper_centr_freq_seg0_idx=42

Danach hostapd für den Systemstart aktivieren und starten.

root@raspberrypi:~# systemctl unmask hostapd
root@raspberrypi:~# systemctl enable hostapd
root@raspberrypi:~# systemctl start hostapd

Das Wlan0 muss nun noch in die Bridge gepackt werden. Dazu passen wir die /etc/rc.local an.

#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
  printf "My IP address is %s\n" "$_IP"
fi
sleep 10; /sbin/brctl addif br-welt wlan0
exit 0

Am Besten nochmal rebooten um sicher zugehen dass alles passt, danach einfach mit dem ClientWifi verbinden.

Diese sind im Grunde ganz einfach, ihr erstellt ein VLAN Interface in der /etc/network/interfaces. Und verbindet dieses mit der Bridge, schon könnt ihr per getaggtem VLAN euer Clientnetz bereitstellen.

Hier ein Beispiel mit VLAN333, erst das VLAN Interface definieren und dann in der vorhandenen Bridge hinzufügen.

 vi /etc/network/interfaces
/etc/network/interfaces
auto eth0.333
iface eth0.333 inet manual
 
auto br-welt
iface br-welt inet dhcp
        bridge-ports bat-welt eth0.333

Nun zur wichtigsten Frage, welche Performance kann man erwarten.

Hier ein paar Speedtest Ergebnisse:

WiFi Client an VDSL 100:

Kabel Client an VDSL 100:

Kabel Client an 1Gbit/s Glas:

 $ wget -O /dev/null https://speed.hetzner.de/10GB.bin --report-speed=bits
--2019-07-01 09:12:45-- https://speed.hetzner.de/10GB.bin
Resolving speed.hetzner.de (speed.hetzner.de)... 88.198.248.254
Connecting to speed.hetzner.de (speed.hetzner.de)|88.198.248.254|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 10485760000 (9.8G) [application/octet-stream]
Saving to: ‘/dev/null’
/dev/null            2%[===>           ] 288.96M   115Mb/s    eta 14m 34s

Hat man mehr als einen Raspberry in seinem Netzwerk am laufen, so kann es mitunter sehr schnell unübersichtlich werden, wenn alle Geräte den gleichen Hostnamen verwenden und z.B. mit diesen an einem zentralen DHCP-Server melden und eine IP-Adresse beziehen. Wir wollen daher den Hostnamen auf den wer setzen, den wir für unseren Offloader beim Setzen des Nodenamen zuvor bei der Definition des ext-respondd beim Parameter site_code gesetzt hatten. In nachfolgendem Konfigurationsbeispiel verwenden wir mal den Site_code/Hostname ff-django-raspi.

pi@raspberrypi:~ $ sudo su -
root@raspberrypi:~# hostnamectl set-hostname ff-django-raspi
root@raspberrypi:~# vi /etc/hosts
127.0.0.1	localhost
::1		localhost ip6-localhost ip6-loopback
ff02::1		ip6-allnodes
ff02::2		ip6-allrouters
 
127.0.1.1	ff-django-raspi
root@raspberrypi:~# hostnamectl
   Static hostname: ff-django-raspi
         Icon name: computer
        Machine ID: b08567d7855541cf8110f09e88638c58
           Boot ID: b35eb62018fc4446b8252eb2f2ae9ff5
  Operating System: Raspbian GNU/Linux 10 (buster)
            Kernel: Linux 4.19.66-v7l+
      Architecture: arm

Wenn wir uns nun neu mit unserem Offloader verbinden, wird der nunmehr richtige Freifunk-Node-Name auch als Hostname verwendet.

pi@ff-django-raspi:~ $

Nebenbei gibt es noch die Möglichkeit, Tethering mit iOS Geräten zu machen. Dazu muss usbmuxd installiert werden. Und in der /etc/network/interfaces ein Interface angelegt werden.

root@raspberrypi:~# apt install usbmuxd libimobiledevice6
root@raspberrypi:~# vi /etc/network/interfaces

/etc/network/interfaces

allow-hotplug eth1
iface eth1 inet dhcp

Am Besten rebootet ihr jetzt einmal.

Danach steckt ihr euer iOS Gerät per USB an während ihr im Hotspot Menü seid und werdet gefragt, ob das PI Zugriff bekommen kann. Das beantwortet ihr mit „Ja“.

Euer PI bezieht sich nun eine IP per DHCP vom iOS Gerät und kann darüber Verbindungen zu den Freifunk Gateways aufbauen.

Und so sieht ein Mobile PI mit iPad als Uplink aus:

Bild: Raspberry Reise-Offloader Bild: Testaufbau des Raspberry mit dem OLED

In dem Beispiel verwenden wir ein OLED Display von AZDelivery mit 128×64 Pixeln. Das schöne an dem Display ist, es funktioniert auch mit 5V und ist damit noch einfacher anzuschließen.

Angeschlossen wird das Ganze wie folgt:

OLED Pin   GPIO Pin  Notes
Vcc45V
Gnd6Ground
SCL5I2C SCL
SDA3I2C SCA

Als nächstes aktivieren wir die I2C Schnittstelle und installieren die notwendige Software.

root@raspberrypi:~# echo i2c-bcm2708 >> /etc/modules
root@raspberrypi:~# echo i2c-dev >> /etc/modules
root@raspberrypi:~# apt install python3-dev python3-smbus i2c-tools python3-pil python3-pip python3-setuptools python3-rpi.gpio

Bevor wir nun einen Reboot durchführen kontrollieren wir noch, ob in der /boot/config.txt das I2C-Interface auch gestartet wird.

 root@raspberrypi:~# vim /boot/config.txt
...

# Uncomment some or all of these to enable the optional hardware interfaces
# Django : 2019-09-11 - für OLED Display Support aktiviert
# default: # dtparam=i2c_arm=on
dtparam=i2c_arm=on

...

Nun am Besten einmal rebooten.

 root@raspberrypi:~# systemctl reboot

Danach überprüfen wir ob das Display erkannt wird.

root@raspberrypi:~# i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- 3c -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- --   

Wie man sieht meldet sich das Display unter der Adresse 3c.

Jetzt installieren wir die notwendige Python Library um das Display ansteuern zu können.

root@raspberrypi:~# apt install git fonts-freefont-ttf
root@raspberrypi:~# cd /usr/local/src/
root@raspberrypi:~# git clone https://github.com/adafruit/Adafruit_Python_SSD1306.git
root@raspberrypi:~# cd Adafruit_Python_SSD1306
root@raspberrypi:~# python3 setup.py install

Anschließend können wir mit ein paar Beispielen probieren ob das Display korrekt funktioniert.

root@raspberrypi:~# cd examples
root@raspberrypi:~# python3 /usr/local/src/Adafruit_Python_SSD1306/examples/stats.py

Auf dem Display werden ein paar Systemdaten angezeigt:

Bild: Anzeige von Systemdaten auf dem OLED nach Start des Scripts stats.py

Nachdem alles alles soweit funktioniert, können wir als nächstes das Bandbreiten Skript installieren, welches auch gleichzeitig die verbundenen BATMAN Clients anzeigt.

Anschließend das Python Skript, welches das Display steuert installieren und anpassen.

root@raspberrypi:~# cd /usr/local/src
root@raspberrypi:~# git clone https://github.com/awlx/raspberry-oled-bandwidth
root@raspberrypi:~# cd raspberry-oled-bandwidth

Nun passen wir in dem Script folgende Variabelen unseren Gegebenheiten an:

  • wifi : Die definierte Schnittstelle unseres WiFi-Devices
  • primary_mac : MAC des Ethernet-Ports unseres Raspberry 4 # ip addr show eth0 | grep link/ether
root@raspberrypi:~# vim /usr/local/src/raspberry-oled-bandwidth/bandwidth.py
/usr/local/src/raspberry-oled-bandwidth/bandwidth.py
#
# Inspired by https://github.com/DarrenBeck/rpi-oled-bandwidth and https://photochirp.com/r-pi/use-raspberry-pi-oled-bandwidth-monitor/
#
# Maintained by awlnx - [email protected]
#
 
import subprocess
import time
import re
import Adafruit_GPIO.SPI as SPI
import Adafruit_SSD1306
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
import math
 
# Adjust to your needs
wifi = 'wlan0'
vpn = 'fastd-welt'
batman = 'bat-welt'
# Django : 2019-09-14
# default: primary_mac = 'dc:a6:32:00:6b:59'
primary_mac = 'dc:a6:32:01:7f:f0'
 
# We assume 100mbit/s max bandwidth
maxRateIn = 10000000
maxRateOut = 10000000
PImaxRateIn = 10000000
PImaxRateOut = 10000000
 
### DO NOT EDIT BELOW THIS POINT ###
 
# Raspberry Pi pin configuration:
RST = 24
 
# 128x64 display with hardware I2C:
disp = Adafruit_SSD1306.SSD1306_128_64(rst=RST)
 
# Initialize library.
disp.begin()
 
# Clear display.
disp.clear()
disp.display()
 
# Create blank image for drawing.
# Make sure to create image with mode '1' for 1-bit color.
width = disp.width
height = disp.height
image = Image.new('1', (width, height))
 
# Get drawing object to draw on image.
draw = ImageDraw.Draw(image)
 
font = ImageFont.truetype('/usr/share/fonts/truetype/freefont/FreeSans.ttf', 12)
fontsmall = ImageFont.truetype('/usr/share/fonts/truetype/freefont/FreeSans.ttf', 10)
fontverysmall = ImageFont.truetype('/usr/share/fonts/truetype/freefont/FreeSans.ttf', 8)
fontmedium = ImageFont.truetype('/usr/share/fonts/truetype/freefont/FreeSans.ttf', 12)
 
#Display Image
disp.image(image)
disp.display()
 
#Functions
 
def get_network_bytes(interface):
    for line in open('/proc/net/dev', 'r'):
        if interface in line:
            data = line.split('%s:' % interface)[1].split()
            rx_bytes, tx_bytes = (data[0], data[8])
            return (int(rx_bytes), int(tx_bytes))
 
def drawBar (x, barHeight):
    # parameters are x, y, end x, end y
    # draw.rectangle ((x, height - barHeight, x + 10, height -1), outline=255, fill=255)
    draw.rectangle ((x, 32 - barHeight, x + 8, height - 32), outline=255, fill=255)
 
def drawBarLOW (x, barLOWHeight):
        # parameters are x, y, end x, end y
        draw.rectangle ((x, 32 + barLOWHeight, x + 8, height - 32), outline=255, fill=255)
 
def textRate(rate):
    # rate -> raw bitrate
    # Returns: SI formatted bitrate
    if rate == 0:
        return "0B"
    rate = rate * 8
    size_name = (
        "b/s", "kb/s", "mb/s", "gb/s", "tb/s", "pb/s", "eb/s")
    i = int(math.floor(math.log(rate , 1024)))
    p = math.pow(1024, i)
    s = round(rate / p, 1)
    return "%s %s" % (s, size_name[i])
 
lastInBytes = get_network_bytes(vpn)[0];
lastOutBytes = get_network_bytes(vpn)[1];
lastPIInBytes = get_network_bytes(wifi)[0];
lastPIOutBytes = get_network_bytes(wifi)[1];
lastTime = time.time()
 
#timed array vars
timerTime = time.time()
highestSpeedIn = 0
highestSpeedOut = 0
PIhighestSpeedIn = 0
PIhighestSpeedOut = 0
speedArrayIn = []
speedArrayOut = []
PIspeedArrayIn = []
PIspeedArrayOut = []
inMax = 0
outMax = 0
PIinMax = 0
PIoutMax = 0
 
while (1):
    time.sleep(2)
    draw.rectangle((0, 0, width, height), outline=0, fill=0)
 
    now = time.time()
    elapsed = now - lastTime
    lastTime = now
 
    #calculate rates in and out
    inBytes = get_network_bytes(vpn)[0]
    currInBytes = (inBytes - lastInBytes) / elapsed
    lastInBytes = inBytes
 
    outBytes = get_network_bytes(vpn)[1]
    currOutBytes = (outBytes - lastOutBytes) / elapsed
    lastOutBytes = outBytes
 
    PIinBytes = get_network_bytes(wifi)[0]
    currPIInBytes = (PIinBytes - lastPIInBytes) / elapsed
    lastPIInBytes = PIinBytes
 
    PIoutBytes = get_network_bytes(wifi)[1]
    currPIOutBytes = (PIoutBytes - lastPIOutBytes) / elapsed
    lastPIOutBytes = PIoutBytes
 
 
    #max rate last 24 hours calculations
 
    if currInBytes > highestSpeedIn:
        highestSpeedIn = currInBytes
    if currOutBytes > highestSpeedOut:
        highestSpeedOut = currOutBytes
    if currPIInBytes > PIhighestSpeedIn:
        PIhighestSpeedIn = currPIInBytes
    if currPIOutBytes > PIhighestSpeedOut:
        PIhighestSpeedOut = currPIOutBytes
 
    if now > timerTime + 3600:
        print('-----------------------------------------------------------------  time expired')
        timerTime = now
 
        speedArrayIn.append (highestSpeedIn)
        if len (speedArrayIn) > 23:
            del speedArrayIn[0]
        inMax = max(speedArrayIn)
 
        speedArrayOut.append (highestSpeedOut)
        if len (speedArrayOut) > 23:
            del speedArrayOut[0]
        outMax = max(speedArrayOut)
 
        highestSpeedIn = 0
        highestSpeedOut = 0
 
        PIspeedArrayIn.append (PIhighestSpeedIn)
        if len (PIspeedArrayIn) > 23:
            del PIspeedArrayIn[0]
        PIinMax = max(PIspeedArrayIn)
 
        PIspeedArrayOut.append (PIhighestSpeedOut)
        if len (PIspeedArrayOut) > 23:
            del PIspeedArrayOut[0]
        PIoutMax = max(PIspeedArrayOut)
 
        PIhighestSpeedIn = 0
        PIhighestSpeedOut = 0
 
    #adjust these in each loop in case we find a faster speed
    inMax = max(inMax, highestSpeedIn)
    outMax = max(outMax, highestSpeedOut)
    PIinMax = max(PIinMax, PIhighestSpeedIn)
    PIoutMax = max(PIoutMax, PIhighestSpeedOut)
 
    #draw graph
    inHeight = 0.0
    outHeight = 0.0
    PIinHeight = 0.0
    PIoutHeight = 0.0
 
    if currInBytes > 0:
        inHeight = float(currInBytes / maxRateIn) * 32 
 
    if currOutBytes > 0:
        outHeight = float(currOutBytes / maxRateOut) * 32
 
    if currPIInBytes > 0:
        PIinHeight = float(currPIInBytes / PImaxRateIn) * 32
 
    if currPIOutBytes > 0:
        PIoutHeight = float(currPIOutBytes / PImaxRateOut) * 32
 
    drawBar (0, inHeight)
    drawBar (10, PIinHeight)
    drawBarLOW (0, outHeight)
    drawBarLOW (10, PIoutHeight)
    #write rates
    draw.text((26,38), textRate(currInBytes), font=font, fill=255)
    draw.text((26,50), textRate(currOutBytes), font=font, fill=255)
 
    draw.text((81,38), textRate(currPIInBytes), font=font, fill=255)
    draw.text((81,50), textRate(currPIOutBytes), font=font, fill=255)
 
    # Batman Clients
    clients = subprocess.check_output("batctl -m " + batman + " tl | egrep -v '(MainIF|" + primary_mac + ")' | egrep -o '([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})' | wc -l", shell=True).split().pop()
    draw.text((0,48), "Clients", font=fontverysmall, fill=255)
    draw.text((10,55), clients.decode("utf-8"), font=fontverysmall, fill=255)
 
    #max rates
    draw.text((36,0), "VPN", font=fontsmall, fill=255)
    draw.text((26,10), textRate(inMax), font=fontsmall, fill=255)
    draw.text((26,20), textRate(outMax), font=fontsmall, fill=255)
 
    draw.text((90,0), "Wifi", font=fontsmall, fill=255)
    draw.text((81,10), textRate(PIinMax), font=fontsmall, fill=255)
    draw.text((81,20), textRate(PIoutMax), font=fontsmall, fill=255)
 
    disp.image(image)
    disp.display()

Nun können das Script erst einmal manuell anstarten und prüfen ob die gewünschten Informationen richtig angezeigt werden.

root@raspberrypi:~# /usr/bin/python3 /usr/local/src/raspberry-oled-bandwidth/bandwidth.py

Bild: OLED mit Anzeige der Verbindungsdaten

Um nun nicht jedes mal beim Starten des Offloaders/Gateways manuell starten müssen, legen wir uns noch ein systemd-Script an.

 root@raspberrypi:~# vim /etc/systemd/system/oled-bandwidth.service
/etc/systemd/system/oled-bandwidth.service
# Django : 2019-09-11
[Unit]
After=network.target
 
[Service]
ExecStart=/usr/bin/python3 /usr/local/src/raspberry-oled-bandwidth/bandwidth.py
 
[Install]
WantedBy=default.target

Anschließend Informieren wir unser System über unser definiertes Daemon-Startscript und aktivieren dies auch gleich noch.

 root@raspberrypi:~# systemctl daemon-reload
 root@raspberrypi:~# systemctl enable oled-bandwidth.service

Zu guter letzte rebooten wir nun unseren Rechner.

 root@raspberrypi:~# systemctl reboot

Bild: OLED in Betrieb


1)
Stand Ende Oktober 2021
  • knb/raspberry.txt
  • Zuletzt geändert: 2021/04/06 19:42
  • von awickert