Ich habe mir vor Kurzem einen BananaPi bestellt und wollte diesen probeweise als WLAN & LAN Router einrichten, wobei WLAN und LAN sich gemeinsam ein Netzwerk teilen.
Mit den Programmen hostapd, dnsmasq und Arch-Linux lässt sich das ohne größere Schwierigkeiten umsetzen.
Hardware & Betriebssystem
Wie schon angedeutet, handelt es sich um ein Banana Pi. Das Ding ist so ähnlich wie ein Raspberry Pi, nur kommt es mit einer DualCore CPU und 1 GB RAM, sowie einer 1GBit Netzwerkschnitstelle.
Die Wahl des Betriebssystems fiel auf das Arch Linux v2.0 Image. Dieses kann man, wie gewohnt, einfach herunterladen und auf die SD Karte spielen.
Des Weiteren benötigt man noch einen USB-Ethernet Adapter, sowie einen USB-WLAN Adapter. Die folgenden Produkte hatte ich bei mir noch rumliegen und können out-of-the-box genutzt werden.
- LogiLink UA0025C
- TP Link TL-WN722N
Ziel
Wenn wir die folgende Konfiguration annehmen:
- eth0 : Schnittstelle zum Internet (WAN)
- eth1 : Schnittstelle zum LAN
- wlan0 : Schnittstelle zum WLAN
Dann möchten wir folgendes Setup erreichen:
- eth0 bekommt seine IP per DHCP
- eth1 & wlan0 teilen sich das Subnet 192.168.4.0/24
- Die IPs aus dem Subnet werden per DHCP verteilt
- Dem Subnet wird ein DNS Server zur Verfügung gestellt
Um das zu erreichen, werden wir das Setup in drei größere Konfigurationsabschnitte unterteilen:
- Netzwerkschnittstellen konfigurieren (systemd-networkd)
- Iptables konfigurieren
- Dnsmasq & Hostapd konfigurieren
Kernel vs. Systemd
Die Kernelversion des Images ist bisher bei 3.4.x stehen geblieben. Jedoch werden die Systemd Pakete weiter aktualisiert. Ab systemd-216 führt dies zu Problemen beim Laden von Treibern. Systemd erwartet, dass der Kernel diese Funktion übernimmt, jedoch ist diese in dieser älteren Version noch nicht implementiert.
Folgende Fehler können auf dieses Problem hinweisen:
[ 119.592292] usb 1-1.2: ath9k_htc: Firmware htc_9271.fw requested [ 180.172266] usb 1-1.2: ath9k_htc: Failed to get firmware htc_9271.fw [ 180.302130] usb 1-1.2: ath9k_htc: USB layer deinitialized
Natürlich sollte die Datei „htc_9271.fw“ in „/lib/firmware“ bzw. „/usr/lib/firmware“ vorhanden sein.
Die Lösung des Problems besteht darin, dass man Updates für folgende drei Pakete zurückhält:
- libsystemd
- systemd
- systemd-sysvcompat
Diese drei Paketenamen trägt man hinter „IgnorePkg=“ in der „/etc/pacman.conf“ ein.
Danach kann man sich von Archlinux-Rollback die drei Pakete in der Version „216-3“ herunterladen und mittels „pacman -U <Paket>“ installieren.
Netzwerkschnittstellen konfigurieren
Der erste Schritt besteht darin dem System mitzuteilen, wie wir unsere Interfaces konfiguriert haben möchten. Ein Interface soll eine dynamische IP haben und die zwei verbleibenden Schnittstellen sollen ein virtuelles Device bilden.
Hierzu stellen wir erstmal sicher, dass die Voraussetzungen gegeben sind, in dem wir „systemd-networkd“ aktivieren und „netctl“ (ggf. andere Netzwerkmanager) deaktivieren.
systemctl enable systemd-networkd systemctl stop netctl systemctl disable netctl
Für die dynamischen IP Adressen benötigen wir dann noch das Paket „dhcpcd“ und entsprechend den aktivierten Dienst:
pacman -S dhcpcd systemctl enable dhcpcd.service systemctl start dhcpcd.service
Als nächstes wechselt man in das Verzeichnis „/etc/systemd/network/“. Dort legen wir die Datei für unser WAN-Interface an. Die Datei nennt man „eth0.network“.
[Match] Name=eth0 [Network] DHCP=yes
Als nächstes ist unser WLAN-Device dran. Dieses bekommt keine IP zugewiesen, da sich hostapd darum kümmern wird. Die Datei „wlan0.network“ enthält folgendes:
[Match] Name=wlan0 [Network] DHCP=no
Als nächstes definieren wirin der Datei „br0.netdev“ die virtuelle Netzwerkbrücke:
[NetDev] Name=br0 Kind=bridge
Dieser weisen wir unser Subnetz zu (br0.network):
[Match] Name=br0 [Network] Address=192.168.4.1/24 DHCP=no
Zuletzt wird noch direkt unser LAN-Interface an die virtuelle Netzwerkschnittstelle gebunden (eth1.network):
[Match] Name=eth1 [Network] Bridge=br0
Nun sollte die Konfiguration soweit abgeschlossen sein. Das lässt sich so überprüfen:
systemctl restart systemd-networkd #Netzwerk neustarten
Die Ausgabe von „ip a s“ sollte so ähnlich aussehen:
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 link/ether 02:86:08:82:75:2e brd ff:ff:ff:ff:ff:ff inet 192.168.2.208/24 brd 192.168.2.255 scope global eth0 4: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast master br0 state UNKNOWN group default qlen 1000 link/ether 00:60:6e:42:57:d9 brd ff:ff:ff:ff:ff:ff 5: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq master br0 state UP group default qlen 1000 link/ether f8:1a:67:17:66:e5 brd ff:ff:ff:ff:ff:ff 6: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 00:60:6e:42:57:d9 brd ff:ff:ff:ff:ff:ff inet 192.168.4.1/24 brd 192.168.4.255 scope global br0
Wie man sieht, hat eth0 automatisch eine IP von meinem Router bekommen. Das virtuelle Interface br0 hat das Subnetz 192.168.4.1/24 zugewiesen bekommen. Eth0 ist bereits ein Teil vom virtuellen Interface.
Iptables konfigurieren
Damit der Traffic nicht unkontrolliert zwischen den Interfaces fließt bzw. das NAT konfiguriert werden kann, müssen die Iptables genutzt werden.
Normalerweise sind diese schon installiert. Ansonsten muss man das gleichnamige Paket nachinstallieren.
Die im Arch Wiki vorgestellte Konfiguration sieht ungefähr so aus:
# Generated by iptables-save v1.4.21 on Sat Feb 28 02:05:21 2015 *nat :PREROUTING ACCEPT [5:231] :INPUT ACCEPT [1:61] :OUTPUT ACCEPT [2:122] :POSTROUTING ACCEPT [2:122] -A POSTROUTING -s 192.168.4.0/24 -o eth0 -j MASQUERADE COMMIT # Completed on Sat Feb 28 02:05:21 2015 # Generated by iptables-save v1.4.21 on Sat Feb 28 02:05:21 2015 *filter :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [55:4554] :TCP - [0:0] :UDP - [0:0] :fw-interfaces - [0:0] -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -m conntrack --ctstate INVALID -j DROP -A INPUT -p icmp -m icmp --icmp-type 8 -m conntrack --ctstate NEW -j ACCEPT -A INPUT -p udp -m conntrack --ctstate NEW -j UDP -A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j TCP -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable -A INPUT -p tcp -j REJECT --reject-with tcp-reset -A INPUT -j REJECT --reject-with icmp-proto-unreachable -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT -A FORWARD -j fw-interfaces -A FORWARD -j REJECT --reject-with icmp-host-unreachable -A TCP -p tcp -m tcp --dport 22 -j ACCEPT -A TCP -i br0 -p tcp -m tcp --dport 53 -j ACCEPT -A TCP -i br0 -p tcp -m tcp --dport 67 -j ACCEPT -A UDP -i br0 -p udp -m udp --dport 53 -j ACCEPT -A UDP -i br0 -p udp -m udp --dport 67 -j ACCEPT -A fw-interfaces -i br0 -j ACCEPT COMMIT # Completed on Sat Feb 28 02:05:21 2015
Das speichert man sich in „/etc/iptables/iptables.rules“ und lädt die Regeln neu:
systemctl restart iptables.service systemctl enable iptables.service
Die Iptables kann man bei diesem Schritt direkt für den Autostart markieren.
Die Regeln sind so aufgebaut, dass in der Input-Chain der Traffic in UDP/TCP unterteilt wird. In diesen Unter-Chains kann man dann Traffic zulassen oder ablehnen. Wir werden später udp/tcp Port 53,67 nutzen, um darüber DHCP/DNS zu verschicken.
Die Forward-Chain „fw-interfaces“ regelt, von welchen Netzwerken Traffic wohin weitergeleitet werden darf. Hier sagen wir ganz grob, dass alles vom br0 weitergeleitet werden darf.
Ansonsten kümmert sich das conntrack-Modul um die Verwaltung der Verbindungen, in dem es die Pakete zu einer „neuen/ähnlichen/verbundenen“ Verbindung zugeordnet wird.
Ganz wichtig ist die POSTROUTING-Regel im oberen Bereich. Diese kümmert sich um das NAT, in dem alle ausgehenden Pakete vom Subnetz 192.168.4.0/24 die IP vom eth0 verpasst bekommen.
Eine relativ gute Übersicht über alle Regeln bekommt man mit:
sudo iptables -L -vn
Zusätzlich muss das IP-Forwarding im System aktiviert werden:
sudo sysctl -w net.ipv4.ip_forward=1
Dnsmasq & Hostapd konfigurieren
Kommen wir nun zum letzten Teil. Zunächst installieren wir die nötigen Pakete:
sudo pacman -S dnsmasq hostapd
Dnsmasq ist ein kleiner DHCP/DNS Server und hostapd ein Tool zum Erstellen von Access Points.
Dnsmasq
Für Dnsmasq editieren wir die Datei „/etc/dnsmasq.conf“ und setzen dort diese Optionen:
interface=br0 dhcp-range=192.168.4.100,192.168.4.200,24h
Für einen erfolgreichen Betrieb reichen die o.g. Einstellungen aus. Allerdings sollte man sich die Konfigurationsdatei durchlesen, um ggf. Sicherheitsvorkehrungen zu treffen oder weitere Features zu aktivieren.
Mit der ersten Zeile wird festgelegt, dass nur Anfragen vom Interface br0 verarbeitet werden. Die zweite Zeile legt den zu vergebenden IP-Bereich bzw. die Leasetime fest.
Danach muss man dnsmasq neustarten und im Autostart verankern:
systemctl enable dnsmasq.service systemctl restart dnsmasq.service
Hostapd
Auch hier müssen wir eine Konfigurationsdatei bearbeiten. Beispiele und eine ausführliche Dokumentation finden sich diesmal hier: „/usr/share/doc/hostapd/*“
Die Einstellungen landen dann in der „/etc/hostapd/hostapd.conf“. Diese kann zum Beispiel so aussehen:
interface=wlan0 bridge=br0 driver=nl80211 ssid=PiWiFi channel=1 ignore_broadcast_ssid=0 country_code=DE ieee80211d=1 hw_mode=g beacon_int=100 dtim_period=2 macaddr_acl=0 max_num_sta=20 rts_threshold=2347 fragm_threshold=2346 logger_syslog=-1 logger_syslog_level=2 logger_stdout=-1 logger_stdout_level=2 dump_file=/tmp/hostapd.dump ctrl_interface=/var/run/hostapd ctrl_interface_group=0 auth_algs=3 wmm_enabled=0 wpa=2 rsn_preauth=1 rsn_preauth_interfaces=wlan0 wpa_key_mgmt=WPA-PSK rsn_pairwise=CCMP wpa_group_rekey=600 wpa_ptk_rekey=600 wpa_gmk_rekey=86400 wpa_passphrase=DASPASSWORT!
Wichtig hierbei ist nur, dass der „Interface=wlan0“ und der „Bridge=br0“ Eintrag vorhanden sind.
Zuletzt muss hostapd noch neugestartet und im Autostart verankert werden:
systemctl restart hostapd.service systemctl enable hostapd.service
Abschließende Worte
Das von uns definierte Ziel ist nun erreicht. Wir haben einen funktionierenden kleinen Router.
Ich hatte noch probiert „traffic shaping“ umzusetzen, jedoch schien das der Treiber bzw. der Kernel nicht zu unterstützen.
~ Sebastian