Die offensichtlichste und "einfachste" Variante dürfte sein, die vorhandene Netzwerkkarte gegen eine schnellere auszutauschen. Das bringt bei Ethernet gleich die (theoretisch) 10-fache Geschwindigkeit, man lädt den passenden Treiber und fertig. Oder auch nicht, denn man braucht schliesslich auch eine passende Gegenstelle (also einen Hub oder einen Switch), an die man diese Netzwerkkarte anschliessen kann. Fast Ethernet mit 100 MBit/s ist heutzutage günstig zu haben und weit verbreitet. Wer noch mit 10 MBit Ethernet arbeitet, sollte wirklich diese Variante wählen. Beim Wechsel von Fast auf Gigabit Ethernet sieht die Sache hingegen noch anders aus. Sowohl die Netzwerkkarten als auch besonders die Switches sind ein gutes Stück teurer. Hat man, wie z.B. auf LAN-Parties, keinen Einfluss auf das Netzwerk, steht man weiter vor dem Problem, ob überhaupt passende Ports zur Verfügung stehen und ob diese einen zur Netzwerkkarte passenden Anschluss (TP oder Glasfaser) haben.
Das führt uns zur zweiten Variante: stecken wir doch einfach eine (oder mehrere) weitere Netzwerkkarte(n) in den Rechner! Dies bringt uns freilich nur die doppelte (oder entsprechend vielfache) Bandbreite, was aber ja schon ausreichend sein kann, zumal viele Rechner eine Gigabit-Karte eh nicht auslasten können. Vorausgesetzt natürlich, man hat noch ausreichend PCI-Slots auf dem Mainboard frei. Ansonsten bliebe einem noch die Möglichkeit, zu einer Netzwerkkarte zu greifen, die mehrere Anschlüsse auf einer Karte bietet.
Wir haben uns jetzt also für eine zweite Netzwerkkarte entschieden (die übrigens nicht vom selben Modell oder Hersteller wie die erste sein muss). Beide Karten stecken im Rechner, der/die Treiber ist/sind geladen und beide Karten haben eine IP. Egal, zu welcher IP wir verbinden, gehen die Daten aber immer nur über die zweite Karte wieder raus.
Der Grund für dieses Verhalten liegt in der Routingtabelle. Anhand der Routingtabelle entscheidet das Betriebssystem, wohin es ein IP-Paket schicken soll.
In den folgenden Beispielen verwende ich folgende Daten:
| Karte | Interface | MAC-Adresse | IP-Adresse | Subnetzmaske |
|---|---|---|---|---|
| 1 | eth0 | 00:11:22:33:44:55 | 192.168.1.1 | 255.255.255.0 |
| 2 | eth1 | AA:BB:CC:DD:EE:FF | 192.168.1.2 | 255.255.255.0 |
(Die Liste lässt sich mit weiteren Karten beliebig fortsetzen.)
Die Routingtabelle sollte jetzt ungefähr so aussehen:
root@server:~ # route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1 192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo root@server:~ #
Der Kernel geht die Tabelle von oben nach unten durch und nimmt den ersten passenden Eintrag. Und schon haben wir das Problem ... alles, was an 192.168.1.* gehen soll, wird also an eth1 geschickt.
Indem man das Routing "verbessert" :-)
Wer noch nie einen Kernel selber kompiliert hat sollte an dieser Stelle das Handbuch zur Distribution, ein anderes HOWTO oder ähnliches zu Rate ziehen.
In der Kernelkonfiguration (bevorzugt mit make menuconfig bzw. make xconfig) findet man unter Networking Options die Option IP: advanced router. Aktiviert man diese, so erscheinen eine Reihe weiterer Optionen, unter anderem:
Weiter unten findet man noch eine Option, die sich auch mit der Lastverteilung beschäftigt:
Die Hilfe verrät einem dazu, dass man damit so schicke Sachen machen kann, wie das Routing anhand der Quell-IP-Adresse (IP: policy routing) oder das abwechselnde Routing über unterschiedliche, gleichwertige Pfade (IP: equal cost multipath und ähnlich TEQL queue).
Im weiteren werde ich nur auf das IP: policy routing eingehen. Dieses hat den netten Effekt, dass eine IP einer Netzwerkkarte fest zugeordnet wird und man sich so als Client die Karte "aussuchen" kann (z.B. auch, um bequem die Performance zweier unterschiedlicher Netzwerkkarten zu vergleichen. :-) )
Mit IP: equal cost multipath liesse sich eine automatische Verteilung der ausgehenden Verbindungen auf beide Karten erreichen. Zumindest im Kernel 2.2.x läuft dies aber nur IP bezogen, d.h. wenn der Kernel zum ersten Mal an eine bestimmte IP Daten liefert, dann entscheidet er sich einmal für eine der Karten/Routen und behält diese dann bei.
Die TEQL queue sorgt auch für eine Verteilung der Daten auf mehrere Leitungen, aber für jedes Paket einzeln. Dies klingt zwar erstmal wunderbar, aufgrund der Art, wie dies erreicht wird (ein neues Device teql0, dem die Netzwerkkarten zugeordnet werden), ergeben sich jedoch Probleme bei den ARP Anfragen (siehe unten), die ich mit der neuen arp_filter Option nicht lösen konnte.
Wir kompilieren also einen neuen Kernel mit aktiviertem IP: policy routing. Wer es selber eventuell mal probieren will, kann auch die anderen beiden Optionen aktivieren. Ausser einem etwas grösseren Kernel hat dies keine weiteren Auswirkungen, so lange man es nicht mit einer entsprechenden Konfiguration später aktiviert.
Leider nicht. :-)
Nachdem der neue Kernel kompiliert, installiert und geladen wurde, muss unser neues Feature erst einmal konfiguriert werden. Dazu brauchen wir das iproute-Paket. Sofern dies nicht schon der verwendeten Distribution beiliegt, muss man es sich von hier runterladen und selber kompilieren. Ein einfaches
root@server:/usr/src/iproute2 # make
sollte zum kompilieren ausreichen. Installiert werden muss per Hand mit
root@server:/usr/src/iproute2 # cp ip/ip ip/rtmon tc/tc /usr/sbin/
Das Programm ip ist das eigentliche Programm, was für die folgende Konfiguration benötigt wird.
Es ist schon fast geschafft. :-)
Noch die Routingtabelle in Erinnerung?
root@server:~ # route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth1 192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo
Ein Eintrag ist eh zu viel. Den löschen wir jetzt einfach raus:
root@server:~ # route del -net 192.168.1.0 netmask 255.255.255.0 dev eth1
Wer noch mehr Karten hat, wiederholt dies für alle bis auf eine. Dieser letzte Eintrag bleibt als "Standard"-Route für dieses Subnetz bestehen. In jedem Fall sollte die Routingtabelle nun (zumindest was das 192.168.1.0-Netz anbelangt) so aussehen:
root@server:~ # route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 192.168.1.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo
Nun geht es an die eigentliche Konfiguration des "policy routing". Wir legen 2 neue Routingtabellen an:
root@server:~ # ip route add 192.168.1.0/24 table 100 dev eth0 src 192.168.1.1 protocol static root@server:~ # ip route add 192.168.1.0/24 table 101 dev eth1 src 192.168.1.2 protocol static
Tabelle 100 routet nun Pakete an 192.168.1.0/24 über das Interface eth0, wobei noch als "bevorzugt zu verwendende" eigene IP-Adresse 192.168.1.1 eingetragen wird. Tabelle 101 macht dasselbe für Interface eth1 und dessen IP 192.168.1.2.
Nun muss dem Kernel aber auch gesagt werden, wann er welche der Tabellen verwenden soll. Dafür werden 2 entsprechende Regeln angelegt:
root@server:~ # ip rule add from 192.168.1.1 table 100 root@server:~ # ip rule add from 192.168.1.2 table 101
Voilà. Alles, was jetzt von der IP 192.168.1.1 kommt wird in Tabelle 100 geschickt und von dort (sofern das Ziel auch im 192.168.1.0/24 Netz liegt) über eth0 rausgeschickt. Ein Paket mit der Quell-IP 192.168.1.2 geht hingegen in Tabelle 101 und wird, oh Freude, wie gewünscht über eth1 weitergeleitet.
Nun sollte noch der Routing-Cache gelöscht werden:
root@server:~ # ip route flush cache
und schon sind wir mit der Konfiguration des Routing fertig. :-)
Pakete, die aus irgendeinem Grund (weitere IPs auf einem Interface, die nicht in der Routingtabelle stehen?) keine dieser IPs haben, nehmen die "Standard"-Route über eth0, die wir oben noch in der ursprünglichen Routingtabelle stehengelassen haben.
Mit dem ip Programm lassen sich alle Tabellen und Regeln auch ausgeben.
Die Liste der Routingtabellen müsste jetzt so beginnen:
root@server:~ # ip route list table all 192.168.1.0/24 dev eth0 table 100 proto static scope link src 192.168.1.1 192.168.1.0/24 dev eth1 table 101 proto static scope link src 192.168.1.2 192.168.1.0/24 dev eth0 scope link 127.0.0.0/8 dev lo scope link [....]
Die ersten beiden Zeilen sind die beiden neu angelegten Tabellen, darunter kommen die Einträge aus der "normalen" Routingtabelle (siehe route -n). Anschliessend folgt noch eine Reihe weiterer Einträge, die hier nicht weiter wichtig sind.
Die Regelliste müsste schliesslich noch dies enthalten:
root@server:~ # ip rule list 0: from all lookup local 32764: from 192.168.1.2 lookup 101 32765: from 192.168.1.1 lookup 100 32766: from all lookup main 32767: from all lookup default
Das ist nun noch ein anderes "Problemchen". Im Ethernet können die Pakete nicht anhand der IP-Adresse, sondern nur über die MAC Adresse der Netzwerkkarte zugestellt werden. Dazu wird das Address Resolution Protocol (ARP) verwendet, über das die Rechner die zu einer IP gehörige MAC-Adresse erfragen. Rechner A fragt also über ein Broadcast "Wer hat die IP 192.168.1.1?" und der entsprechende Rechner B antwortet darauf, so dass A jetzt dessen MAC-Adresse hat. Da unser Rechner mehrere Netzwerkkarten im gleichen Subnetz hat, empfängt er diesen Broadcast über alle diese Karten.
Nun unterscheidet Linux in der Voreinstellung (für uns: "leider") nicht, ob die IP-Adresse auch zu der Netzwerkkarte gehört, über die dieser Broadcast empfangen wurde und schickt jetzt auf jeder Netzwerkkarte eine Antwort mit der entsprechenden MAC-Adresse der jeweiligen Karte raus. Je nach, in welcher Reihenfolge die Pakete jetzt vom anfragenden Rechner empfangen werden, erhält dieser jetzt irgendeine der MAC-Adressen von Rechner B. Das Ergebnis könnte nun auf Rechner A (dem Client!) so aussehen:
root@client:~ # arp -n Address HWtype HWaddress Flags Mask Iface 192.168.1.1 ether 00:11:22:33:44:55 C eth0 192.168.1.2 ether 00:11:22:33:44:55 C eth0
Egal, an welche IP der Client nun sendet, das Paket wird in jedem Fall an die MAC-Adresse von eth0 (siehe Beispiel am Anfang, Achtung: das "eth0" in dieser Ausgabe ist die Netzwerkkarte des Client!).
Aber wie gesagt: dies ist nur die Voreinstellung und Dank /proc-Filesystem lässt sich das schnell ändern. Hier hat sich nur leider über die verschiedenen Kernelversion etwas geändert und ich kann nicht genau sagen, was ab welcher Version vorhanden ist. Es ist also ein Blick in /proc/sys/net/ipv4/conf/all/ nötig, ob sich dort eine dieser Dateien befindet:
Kernel 2.4.9 hat z.B. nur die erste, Kernel 2.2.19 hat beide und Kernel 2.2.14 hat nur die zweite ... wenn vorhanden, sollte man daher wohl arp_filter wählen, so dass die Befehle lauten:
root@server:~ # echo 1 >/proc/sys/net/ipv4/conf/all/arp_filter root@server:~ # echo 1 >/proc/sys/net/ipv4/conf/eth0/arp_filter root@server:~ # echo 1 >/proc/sys/net/ipv4/conf/eth1/arp_filter
Wenn arp_filter nicht vorhanden ist, so ist dies hier durch hidden zu ersetzen.
In beiden Fällen schaltet die erste Zeile die Funktion an sich frei und die folgenden Zeilen aktivieren es schliesslich für alle betroffenen Netzwerkkarten. Auf eth0 werden nun nur noch ARP-Anfragen beantwortet, die auch an eine IP von eth0 gerichtet sind usw.
Ja! Es ist geschafft! :-)
Wenn nichts schiefgelaufen ist, dann sollten sämtliche Daten von und zu 192.168.1.1 nur über eth0 und alle Daten von und zu 192.168.1.2 nur über eth1 laufen.