❮ 2020-03-30
Paket basiertes Routing mit Iptables
Teil 2 vom Routing Projekt
Probleme mit reinem IP Routing
Mit dem "alten" Script bin ich nun abseits von einigen Verbesserungen ca. 1 Jahr gut zurecht gekommen, jedoch mit 2 nervigen Einschränkungen:
- Durch einen Logik-Fehler musste Ich das Script immer manuell ausführen
Ich habe einen Fehler gemacht, und das Script auch per UDEV-Regel gestartet. Udev hat aber mit dem Initialisieren der eigentlichen Ethernet-Verbidnung nichts mehr zu tun, und macht dann dementsprechend nichts mehr, wenn man z.B. am Handy den Hotsport ausstellt, oder die Netzwerkverbindung anderweitig down und wieder up geht, dann verschwinden zwar die Routen aus den Tabellen, das Script wird dann jedoch nicht mehr aktiv.
Das zweite Problem war eine Art Racing Condition. Das eigentliche Einrichten der Netwerk-Adapter übernimmt unter Debian-Jessie das "ifupdown" Package. Dieses hat immer genau dann losgelegt, wärend gleichzeitig mein Script von UDEV ausgeführt wurde. Manchmal war dann einfach der Netzwerkadapter noch nicht komplett hochgefahren, und der ifstate auf DOWN, dann konnte mein Script natürlich noch keine Routen hinzufügen.
Als Workaround habe ich den ifup-Service, den ifupdown über System-D bereitstellt, überschrieben, sodass immer wenn ein NIC angeschlossen wird mein Script aufgerufen wird. Zwar muss mein Script sich nun um alles selbst kümmern (IP-Adresse zuweisen, Interface "Uppen"), aber man hat so endlich keine hässlichen Racing-Conditions mehr, und die volle Kontrolle darüber, was gerade passiert.
Slice=system.slice
ExecStart=/bin/sh
ExecStop=/bin/sh
#ExecStop=/sbin/ifdown %I
#ExecStart=/bin/sh -ec 'ifup --allow=hotplug %I; ifquery --state %I; /opt/net/setup_with_log'
RemainAfterExit=true
TimeoutStartSec=5min
/etc/systemd/system/ifup@.service
Mit der Änderung, dass nur noch auf die Existenz des NIC geprüft wird, und nicht auf den State funktioniert nun alles endlich komplett automatisch.
Dies könnte jedoch zu Problemen bei fest integrieten NICs kommen. USB Geräte verschwinden einfach komplett, wenn sie abgesteckt werden, eth0 z.B. wechselt dann einfach auf den Down-State, das Script würde dann das NIC als present bewerten und dann nicht funktionierende Routen erstellen. Da ich gerade die Festnetzleitung immer benutze, macht dies momentan noch keine Probleme.
- Es wurden nur bestimmte IP-Adressen oder Subnetze, also auf IP-Ebene geroutet, was auch einige Nachzeile nach sich zog
Zum einen musste Ich die Subnetze für jeden Online-Dienst herausfinden, den ich anders geroutet haben wollte. Dies an sich ist schon ein zeitintensives, und fehlerbehaftetes Unterfangen.
Auch sämtlichen Traffic nur basierend auf der IP Adresse zu routen ist in meinem Fall nicht zielführend, da man nicht differenzieren kann, welchen Traffic man über LTE routen möchte, und welchen nicht. Beispielsweise gibt es bei Online-Spielen, oder Konferenzsystemen die Unterscheidung von zeitkritischen Funktionen, wie der Game-Traffic oder Sprachpaketen, und weniger Zeitkritischen, aber "Teuren" Traffic, wie Patches oder Assets, die Übertragen werden. Beim reinen IP-Routing würde so im schlimmsten Fall sämtlicher Traffic über eine Volumen-LTE-Verbindung geroutet, und würde das Volumen in kürzester Zeit aufbrauchen.
Bei League of Legends besteht das Problem nicht, da die Game-Server keine Patches ausliefern, bei anderen Spielen müsste man das Verhalten erst analysieren, die Gameserver-IPs herausfinden, und diese dann ggf. mit in das Script aufnehmen, dies ist aber ein Kampf gegen Windmühlen.
Einfacher ist es, den Traffic nach Protokoll aufzuschlüsseln, und diesen dann demenstprechend zu routen, zeitkritischer Traffic wird meist über UDP-Pakete verschickt, diesen kann man mit Iptables markieren und über eine gesonderte Routing-Tabelle routen. Ein Vorteil ist, dass man so Dienstübergreifend sämtlichen Echtzeittraffic von Bandbreitenintensiven Traffic trennen kann, in der UDP-Routing-Tabelle kann man dann noch spezifischere Fälle abdecken.
Meine Routing Idee sieht gerade so aus:
Ich route z.B. so Teamspeak und Discord über meine Festnetz-Leitung, und League of Legends priorisiert über meinen 1GB LTE Tarif von Congstar, da ich weiß, dass eine Runde LoL sehr wenig Bandbreite verbraucht, und der Ping meistens konstanter ist als beim Festnetz DSL. Sämtlicher anderer UDP Traffic läuft so über die Leitung, die das beste Verhältnis zwischen Stabilität und Datenvolumen hat.
Realisierung
abgespecktes routing-flowchartDas Ausführen des setup-Scripts wurde von den Udev-Rules entfernt, jetzt werden hier nur die Namen der NICs festgelegt.
SUBSYSTEM=="net", ACTION=="add", ATTR{address}==xx:xx:xx:xx:xx:xx", NAME="funk"
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="xx:xx:xx:xx:xx:xx", NAME="vodafone"
SUBSYSTEM=="net", ACTION=="add", ATTR{address}=="xx:xx:xx:xx:xx:xx", NAME="congstar"
/etc/udev/rules.d/70-persistent-net.rules
Im setupNetworking Script wird analog zu den IP-Routen noch eine Iptables Regel erstellt, sowie eine neue Routing-Tabelle angelegt, die sich um das Routing der UDP Pakete kümmert:
Mit diesem Befehl wird eine neue Routing-Tabelle angelegt.
Mit diesem Iptables Befehl werden sämtliche UDP-Pakete, die aus dem Heimnetz-Subnetz kommen mit einer Markierung versehen. Das muss unbedingt in der PREROUTING Chain passieren, damit die Pakete danach basierend auf dem Ergebnis geroutet werden können.
Mit diesen beiden iproute-Befehlen werden die markierten Pakete dann unter Berücksichtigung der udp_routing_table Tabelle geroutet.
Das Ergebnis:
alter vs. neuer ping bei discordDer Ping unter Discord ist nun deutlich konstanter, weil nun der UDP Traffic über die Festnetz Leitung läuft, sätmliche Updates, Bilder/Videos in Chats oder anderer TCP-Verkehr wird trotzdem noch über die schnelle Freenet-Verbindung geroutet.
Todo
Das Hauptproblem ist so gelöst, alle zeitkritischen Dienste laufen nun über eine Leitung mit geringer Latenz.
Doch wie stark ist die monatliche Belastung für Volumentarife, wenn jetzt nicht nur ein Online-Spiel über die Handy-Leitung geroutet wird, sondern auch Voip, und andere Online-Spiele, die eventuell mehr Nutzdaten übertragen?
Mit Iptables lässt sich dies herausfinden, indem man die markierten Pakete zählen kann. Nach einem Monat kann man dann genau ablesen, wie viel Traffic erzeugt wurde, und welchen LTE Tarif, man dann bestellen sollte, oder ob man bestimmte Dienste nicht über LTE routen sollte.
Ein potentielles Problem wären dann noch VPN-Dienste, die den Traffic über UDP verschicken, diese müssten noch explizit mit einer anderen iptables-Regel anders markiert werden, sodass diese nicht über eine eventuell gecappte Verbindung fließen, doch damit habe ich mich noch nicht beschäftigt.
Script
Das komplette Script:
#!/bin/bash
#Prios
#League IPs = congstar, vodafone, eth0
#Twitch IPs = eth0, funk
#Russendisco = eth0 (wegen nat)
#TS = vodafone, funk, eth0
#Default Route = funk, eth0
USE_UDP_ROUTING=1
NET_DIR=/sys/class/net/
APPLE_GATEWAY=172.20.10.1
FRITZ_BOX_GATEWAY=192.168.178.1
HOME_SUBNET=192.168.178.0
#Die Netzwerkadapter, die ich definiert habe
CONGSTAR_NIC=congstar
FUNK_NIC=funk
VODAFONE_NIC=vodafone
WIRE_NIC=eth0
#Zuweisung der Standardgateways für die definierten NICs
#Diese kann man dann als key-value Wert auslesen
configs[]=
configs[]=
configs[]=
configs[]=
#Zuweisung der statischen IP Adressen für die NICs
ips[]="172.20.10.5/28"
ips[]="172.20.10.2/28"
ips[]="172.20.10.4/28"
ips[]="192.168.178.46/24"
#In diese Variable wird der Output für Telegram geschrieben
telegramString=""
#NICS
interfaces=(
\
\
\
)
#Priorities for udp
udprpios=(
\
\
\
)
#IP Ranges for routing
league=(
"162.249.72.0/24" \
"162.249.73.0/24" \
"162.249.74.0/24" \
"162.249.75.0/24" \
"162.249.76.0/24" \
"162.249.77.0/24" \
"162.249.78.0/24" \
"162.249.79.0/24" \
"185.40.64.0/24" \
"185.40.65.0/24" \
"185.40.66.0/24" \
"185.40.67.0/24" \
)
twitch=(
185.42.204.33
)
teamspeak=(
84.200.93.247 \
)
default_route_array=(
default
)
# Priority Maps
priority_league=(
\
\
\
)
priority_teamspeak=(
\
\
\
)
priority_twitch=(
\
)
priority_route=(
\
)
#Hier wird das NIC aktiviert, falls es innerhalb von 20 Versuchen nicht funktioniert bricht das Script ab.
#Hier wird die Routing-Tabelle mit den überreichten Einträgen befüllt
# $1 = NICs, $2 = Array With IPs, $3 = Name, $4 = Place in udp routing table
# $1 = Array, $2 = Array With IPs, $3 = Name, $4 = Place in udp route
#Removes all routing entries previously set
# Check if network adapter is currently plugged in
#Falls das script schon läuft, die ältere Insanz abbrechen
for; do
if [; then
fi
done
#Assigns the passed interface an ip address, if it does not already have one,
for; do
if [; then
fi
|
if [
then
fi
done
if [; then
else
fi
default=eth0
#Default Route
#Russendisco
#echo "Route RUSSENDISCO over WIRE"
#ip route add $RUSSENDISCO via $FRITZ_BOX_GATEWAY dev eth0
#League
#Twitch
#Teamspeak
# setUpRoute priority_teamspeak[@] teamspeak[@] "Teamspeak"
entrycount=