Aber die Nutzung dieser Möglichkeit gestaltete sich doch deutlich schwieriger als ich gedacht hatte. Einen Kabelrouter mit Link Aggregation gibt es leider noch nicht, also muss man ein "autarkes" Load Balancing basteln, welches keine Zusammenarbeit vom Router benötigt. Ich habe zwei Tage intensiver Internet-Recherche und Tüftelei gebraucht, bis ich mir dieses Script erarbeitet hatte, um Ubuntu 16.04 LTS zu überreden, beide LAN-Verbindungen zu nutzen:
Code: Alles auswählen
#!/bin/bash
# load balancing configuration script for Ubuntu/Linux 16.04 LTS
# currently only IPv4 and only in downstream direction
# the interfaces to be load-balanced
IF1=eno1
IF2=eno2
function clear_routes() {
local RT=$1
while read ROUTE
do
sudo ip route del $ROUTE table $RT
done < <(ip route list table $RT)
}
function copy_routes() {
local RT=$1
local IF=$2
local IP=0.0.0.0
while read ROUTE
do
sudo ip route add $ROUTE table $RT
if [[ "$ROUTE" == *" src "* ]]; then
IP=`echo $ROUTE | grep -o "src \S*" | cut -d' ' -f2`
fi
done < <(ip route list table main | grep " dev $IF ")
echo $IP
}
function clear_rules() {
local RT=$1
while read RULE
do
sudo ip rule del table $RT
done < <(ip rule list | grep "^$RT:")
}
function setup_route_table() {
local RT=$1
local IF=$2
clear_routes $RT
IP=$(copy_routes $RT $IF)
clear_rules $RT
sudo ip rule add from $IP prio $RT table $RT
sudo sysctl -q -w net.ipv4.conf.$IF.arp_filter=1
sudo sysctl -q -w net.ipv4.conf.$IF.rp_filter=2
echo $IP
}
function clear_route_table() {
local RT=$1
local IF=$2
sudo sysctl -q -w net.ipv4.conf.$IF.arp_filter=0
sudo sysctl -q -w net.ipv4.conf.$IF.rp_filter=1
clear_rules $RT
clear_routes $RT
}
function enable_load_balancing() {
# set up ip routes
IP1=$(setup_route_table 1 $IF1)
IP2=$(setup_route_table 2 $IF2)
sudo ip route flush cache
# set up iptables to randomly swap the source IP addresses
sudo iptables -F -t nat
sudo iptables -A POSTROUTING -t nat -o $IF1 -m statistic --mode random --probability 0.5 -j SNAT --to-source $IP2
sudo iptables -A POSTROUTING -t nat -o $IF2 -m statistic --mode random --probability 0.5 -j SNAT --to-source $IP1
}
function disable_load_balancing() {
sudo iptables -F -t nat
clear_route_table 1 $IF1
clear_route_table 2 $IF2
sudo ip route flush cache
}
function show_load_balancing() {
echo "Load balancing statistics (pkts bytes):"
sudo iptables -L POSTROUTING -t nat -v | grep "statistic"
}
set -e
case $1 in
on|enable)
enable_load_balancing
;;
off|disable)
disable_load_balancing
;;
"")
show_load_balancing
;;
*)
echo "Usage: $0 [on|off] - enable or disable load balancing over $IF1 and $IF2"
;;
esac
Außerdem sollte man IPv6 für beide LAN-Verbindungen deaktivieren. Das kann man in Ubuntu über die GUI machen, indem man die Verbindungen editiert und unter dem Reiter "IPv6" oben "Ignore" auswählt.
Dann kann man das Script, wenn man es z.B. als "load_balancing" gespeichert und ausführbar gemacht hat so nutzen:
load_balancing on - konfiguriert load-balancing
load_balancing off - entfernt die load-balancing Konfiguration
load_balancing - zeigt etwas Status von iptables an. Eigentlich wenig interessant
Übrigens sind keine von den Änderungen persistent! Wenn das Script einem also die Netzwerkkonfiguration "zerschiessen" sollte und man gar kein Internet mehr hat, einfach neu booten und alles ist wie vorher.
Zur Historie und Funktionsweise:
Das erste Problem ist, dass beide LAN-Verbindungen im selben Subnetz liegen und denselben Gateway haben. Damit kann Linux so gar nichts anfangen, das kennt immer nur einen Pfad zu einem Gateway. Entsprechend legt es von sich aus Routingeinträge mit unterschiedlicher "metric" an - was dazu führt, dass eine LAN-Verbindung immer genutzt wird und die andere nie (bzw. erst, wenn man die genutzte trennt). Es ist auch nicht möglich, beide auf die gleiche "metric" zu setzen.
Um also die beiden LAN-Verbindungen überhaupt nutzen zu können, muss man erst mal das Routing umstricken: Linux erlaubt zwar nicht zwei Pfade zum gleichen Gateway in einer Routingtabelle - aber es erlaubt mehrere Routingtabellen! Also legt die Funktion setup_route_table() für je eine LAN-Verbindung eine eigene Routingtabelle an, wo nur eines der beiden Netzwerkinterfaces vorkommt. Dazu wird ein "ip rule" angelegt, was festlegt, dass Pakete, welche die IP-Adresse dieses Netzwerkinterfaces haben, über dessen eigene Routingtabelle zu routen sind - fertig ist das sogenannte "source based routing".
Nun kann man mit "ping -I <Netzwerkinterface|dessen zugewiesene IP-Adresse> <Ziel>" über die eine oder andere LAN-Verbindung pingen.
Nur wählen lokale Anwendungen typischerweise das "Default" Netzwerkinterface für ihre Kommunikation aus, und entsprechend geht sämtlicher Datenverkehr immer noch nur über eine LAN-Verbindung (nämlich immer noch die mit der anfangs erwähnten niedrigeren "metric").
Dem wird mit den mächtigen "iptables" Abhilfe geleistet, mit welchen sich Routingalgorithmen quasi "programmieren" lassen. Da ich iptables aber gerade mal Ansatzweise verstehe, habe ich etwas eigentlich recht Simples eingerichtet: Mit 50%iger Wahrscheinlichkeit wird die Quelladresse eines über ein Netzwerkinterface gesendeten Pakets mit der Quelladresse des anderen Netzwerkinterfaces ausgetauscht. Streng genommen ist das "IP spoofing", aber meinen Kabelrouter hat es nicht gestört Nötig wäre das eigentlich nur für das eine Netzwerkinterface, über das sämtlicher Datenverkehr geht, aber da man nicht vorhersagen kann, welches von den beiden ist (das hängt von der Initialisierungsreihenfolge oder der Reihenfolge beim Einstecken der LAN-Kabel ab und kann bei einem Reboot auch mal wechseln), wird es für beide aufgesetzt - angewendet wird stets nur eins von beiden.
Und damit kommen wir auch schon zu den berühmten
Known Issues:
1. Es funktioniert nur für IPv4
2. Es funktioniert nur für den Downstream, der Upstream läuft stets komplett über das Netzwerkinterface mit der niedrigeren "metric".
3. Es funktioniert nur "statisch", d.h. man muss vor dem Einschalten des load-balancing beide LAN-Verbindungen hergestellt haben, und sollte es auch besser abschalten, wenn man eine der beiden Verbindungen trennen möchte.
Punkt 2 und auch das unschöne "IP spoofing" könnte man beheben, indem man ein virtuelles Netzwerkinterface aufsetzt und das zum Default macht (durch Setzen der Defaultroute mit der niedrigsten "metric"), und dann iptables so einrichtet, dass es eine Art "inverses NAT" auf die beiden echten Netzwerkinterfaces macht. Das sollte eigentlich machbar sein.
Punkt 3 könnte man auch beheben, indem man up/down Hook-Scripte für die beiden Netzwerkinterfaces baut, die dann entsprechend das Routing umkonfigurieren, wenn eine LAN-Verbindung getrennt wird oder hinzukommt.
Also wer mag, und sich vielleicht mit iptables besser auskennt als ich, darf gerne an dem Script weiter tüfteln. Viel Spaß!