Guten Tag. Es gibt also ein Netzwerk von 5.000 Clients. Kürzlich kam ein nicht so angenehmer Moment heraus - in der Mitte des Netzwerks und wir haben Brocade RX8 und es begann viele unbekannte Unicast-Pakete zu senden, da das Netzwerk in VLANs unterteilt ist - dies ist teilweise kein Problem, ABER es gibt spezielle VLANs für weiße Adressen usw. und sie sind in alle Richtungen des Netzwerks gestreckt. Stellen Sie sich nun einen eingehenden Stream an die Adresse eines Kunden vor, der nicht als Internatsschüler studiert, und dieser Stream fliegt in Richtung Funkverbindung zu einem (und so weiter) Dorf - der Kanal ist verstopft - Kunden sind böse - traurig ...
Die Aufgabe besteht darin, den Fehler in eine Funktion umzuwandeln. Ich dachte in Richtung Q-in-Q mit einem vollständigen Client-VLAN, aber alle Arten von Eisenstücken wie P3310, wenn ich dot1q einschalte, hören auf, DHCP zu passieren. Sie wissen immer noch nicht, wie selektiv Qinq und viele solche Unterwasserkrücken sind. Was ist IP-unbenannt und wie funktioniert es? Wenn ganz kurz - die Gateway-Adresse + Route auf der Schnittstelle. Für unsere Aufgabe müssen wir: Shaper schneiden, Adressen an Clients verteilen, Routen zu Clients über bestimmte Schnittstellen hinzufügen. Wie macht man das alles? Shaper - lisg, dhcp - db2dhcp auf zwei unabhängigen Servern, dhcprelay wird auf Zugriffsservern ausgeführt, ucarp funktioniert auch auf Zugriffsservern - zur Sicherung. Aber wie füge ich Routen hinzu? Sie können alles in einem großen Skript im Voraus hinzufügen - dies ist jedoch nicht der Fall. Also werden wir eine selbstgemachte Krücke umzäunen.
Nachdem ich mich im Internet gründlich umgesehen hatte, fand ich eine wunderbare High-Level-Bibliothek für C ++, mit der Sie den Datenverkehr auf wundervolle Weise abhören können. Der Algorithmus des Programms, das Routen hinzufügt, lautet wie folgt: Wir hören die Arp-Anforderungen auf der Schnittstelle ab. Wenn wir eine Serveradresse auf der von uns angeforderten lo-Schnittstelle haben, fügen Sie die Route über diese Schnittstelle hinzu und fügen Sie dieser IP einen statischen Arp-Eintrag hinzu - im Allgemeinen ein paar Kopierpasten, ein wenig Gag und fertig
Quellen des Kleinbusses#include <stdio.h> #include <sys/types.h> #include <ifaddrs.h> #include <netinet/in.h> #include <string.h> #include <arpa/inet.h> #include <tins/tins.h> #include <map> #include <iostream> #include <functional> #include <sstream> using std::cout; using std::endl; using std::map; using std::bind; using std::string; using std::stringstream; using namespace Tins; class arp_monitor { public: void run(Sniffer &sniffer); void reroute(); void makegws(); string iface; map <string, string> gws; private: bool callback(const PDU &pdu); map <string, string> route_map; map <string, string> mac_map; map <IPv4Address, HWAddress<6>> addresses; }; void arp_monitor::makegws() { struct ifaddrs *ifAddrStruct = NULL; struct ifaddrs *ifa = NULL; void *tmpAddrPtr = NULL; gws.clear(); getifaddrs(&ifAddrStruct); for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next) { if (!ifa->ifa_addr) { continue; } string ifName = ifa->ifa_name; if (ifName == "lo") { char addressBuffer[INET_ADDRSTRLEN]; if (ifa->ifa_addr->sa_family == AF_INET) { // check it is IP4 // is a valid IP4 Address tmpAddrPtr = &((struct sockaddr_in *) ifa->ifa_addr)->sin_addr; inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN); } else if (ifa->ifa_addr->sa_family == AF_INET6) { // check it is IP6 // is a valid IP6 Address tmpAddrPtr = &((struct sockaddr_in6 *) ifa->ifa_addr)->sin6_addr; inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN); } else { continue; } gws[addressBuffer] = addressBuffer; cout << "GW " << addressBuffer << " is added" << endl; } } if (ifAddrStruct != NULL) freeifaddrs(ifAddrStruct); } void arp_monitor::run(Sniffer &sniffer) { cout << "RUNNED" << endl; sniffer.sniff_loop( bind( &arp_monitor::callback, this, std::placeholders::_1 ) ); } void arp_monitor::reroute() { cout << "REROUTING" << endl; map<string, string>::iterator it; for ( it = route_map.begin(); it != route_map.end(); it++ ) { if (this->gws.count(it->second) && !this->gws.count(it->second)) { string cmd = "ip route replace "; cmd += it->first; cmd += " dev " + this->iface; cmd += " src " + it->second; cmd += " proto static"; cout << cmd << std::endl; cout << "REROUTE " << it->first << " SRC " << it->second << endl; system(cmd.c_str()); cmd = "arp -s "; cmd += it->first; cmd += " "; cmd += mac_map[it->first]; cout << cmd << endl; system(cmd.c_str()); } } for ( it = gws.begin(); it != gws.end(); it++ ) { string cmd = "arping -U -s "; cmd += it->first; cmd += " -I "; cmd += this->iface; cmd += " -b -c 1 "; cmd += it->first; system(cmd.c_str()); } cout << "REROUTED" << endl; } bool arp_monitor::callback(const PDU &pdu) { // Retrieve the ARP layer const ARP &arp = pdu.rfind_pdu<ARP>(); if (arp.opcode() == ARP::REQUEST) { string target = arp.target_ip_addr().to_string(); string sender = arp.sender_ip_addr().to_string(); this->route_map[sender] = target; this->mac_map[sender] = arp.sender_hw_addr().to_string(); cout << "save sender " << sender << ":" << this->mac_map[sender] << " want taregt " << target << endl; if (this->gws.count(target) && !this->gws.count(sender)) { string cmd = "ip route replace "; cmd += sender; cmd += " dev " + this->iface; cmd += " src " + target; cmd += " proto static"; // cout << cmd << std::endl; /* cout << "ARP REQUEST FROM " << arp.sender_ip_addr() << " for address " << arp.target_ip_addr() << " sender hw address " << arp.sender_hw_addr() << std::endl << " run cmd: " << cmd << endl;*/ system(cmd.c_str()); cmd = "arp -s "; cmd += arp.sender_ip_addr().to_string(); cmd += " "; cmd += arp.sender_hw_addr().to_string(); cout << cmd << endl; system(cmd.c_str()); } } return true; } arp_monitor monitor; void reroute(int signum) { monitor.makegws(); monitor.reroute(); } int main(int argc, char *argv[]) { string test; cout << sizeof(string) << endl; if (argc != 2) { cout << "Usage: " << *argv << " <interface>" << endl; return 1; } signal(SIGHUP, reroute); monitor.iface = argv[1]; // Sniffer configuration SnifferConfiguration config; config.set_promisc_mode(true); config.set_filter("arp"); monitor.makegws(); try { // Sniff on the provided interface in promiscuous mode Sniffer sniffer(argv[1], config); // Only capture arp packets monitor.run(sniffer); } catch (std::exception &ex) { std::cerr << "Error: " << ex.what() << std::endl; } }
Libtins Installationsskript Der Befehl zum Erstellen der Binärdatei g++ main.cpp -o arp-rt -O3 -std=c++11 -lpthread -ltins
Wie läuft es? start-stop-daemon --start --exec /opt/ipoe/arp-routes/arp-rt -b -m -p /opt/ipoe/arp-routes/daemons/eth0.800.pid -- eth0.800
Ja - es überschreibt die Tabellen beim HUP-Signal. Warum nicht netlink verwendet? Faulheit ist nur ja, und Linux ist ein Skript auf einem Skript - so dass alles in Ordnung ist. Nun Routen Routen, was kommt als nächstes? Als nächstes müssen wir die Routen, die sich auf diesem Server befinden, an die Grenze senden - hier haben wir aufgrund des gleichen veralteten Stücks Eisen den Weg mit dem geringsten Widerstand gegangen - wir haben diese Aufgabe auf BGP übertragen.
Bgp configHostname *******
Passwort *******
Protokolldatei /var/log/bgp.log
!
# Anzahl der erfundenen Adressen, Adressen und Netzwerke
Router BGP 12345
bgp router-id 1.2.3.4
neu verteilen verbunden
statische neu verteilen
Nachbar 1.2.3.1 remote-as 12345
Nachbar 1.2.3.1 Next-Hop-Self
Nachbar 1.2.3.1 Routenkarte keine in
Nachbar 1.2.3.1 Routenkartenexport aus
!
Exportgenehmigung für Zugriffsliste 1.2.3.0/24
!
Ausfuhrgenehmigung für Streckenkarte 10
Export der IP-Adresse übereinstimmen
!
Route-Map-Export verweigern 20
Wir fahren fort. Damit der Server auf Arp-Anfragen antworten kann, müssen Sie den Arp-Proxy aktivieren.
echo 1 > /proc/sys/net/ipv4/conf/eth0.800/proxy_arp
Mach weiter - ucarp. Skripte zum Starten dieses Wunders schreiben wir selbst
Beispiel für das Starten eines einzelnen Daemons start-stop-daemon --start --exec /usr/sbin/ucarp -b -m -p /opt/ipoe/ucarp-gen2/daemons/$iface.$vhid.$virtualaddr.pid -- --interface=eth0.800 --srcip=1.2.3.4 --vhid=1 --pass=carpasword --addr=10.10.10.1 --upscript=/opt/ipoe/ucarp-gen2/up.sh --downscript=/opt/ipoe/ucarp-gen2/down.sh -z -k 10 -P --xparam="10.10.10.0/24"
Damit dhcprelay an einer Schnittstelle funktioniert, benötigt es eine Adresse. Daher fügen wir auf den von uns verwendeten Schnittstellen linke Adressen hinzu, z. B. 10.255.255.1/32, 10.255.255.2/32 usw. Ich werde Ihnen nicht sagen, wie Sie Relais einrichten - dort ist alles einfach.
Also was wir haben. Backup-Gateways, Routen zur automatischen Optimierung, DHCP. Dies ist der minimale Satz - sogar Lisg ist darauf geschraubt und wir haben bereits einen Shaper. Warum ist alles so lang und cool? Ist es nicht einfacher, accel-pppd einzunehmen und generell pppoe zu verwenden? Nein, es ist nicht einfacher - die Leute können kaum ein Patchkabel in einen Router stecken, ganz zu schweigen von pppoe. accel-ppp ist eine coole Sache - aber es hat bei uns nicht funktioniert - eine Reihe von Fehlern im Code - es rollt ein, schneidet schief und das Traurige ist, dass wenn es aufgehellt wird, die Leute alles neu starten müssen - die Telefone sind rot - im Allgemeinen passte es nicht. Was ist der Vorteil der Verwendung von ucarp anstelle von Keepalived? Ja, in allem - es gibt 100 Gateways, Keepalived und einen Fehler in der Konfiguration - funktioniert nicht alles. 1 Gateway funktioniert nicht mit ucarp. In Bezug auf die Sicherheit heißt es, dass die Adressen Linkshänder sind und am Ball verwendet werden. Um diesen Moment zu steuern, konfigurieren wir DHCP-Snooping + Source-Guard + Arp-Inspektion für alle Switches / Alt / Bases. Wenn der Client nicht über DHCC, sondern über Statik verfügt - Zugriffsliste auf dem Port.
Warum wurde das alles gemacht? Den Verkehr zu zerstören mögen wir nicht. Jetzt hat jeder Switch sein eigenes VLAN und Unbekannt-Unicast hat keine Angst mehr, da er nur an einen Port und nicht an alle gehen muss ... Nun, die Nebenwirkungen sind eine standardisierte Hardwarekonfiguration und eine hohe Effizienz bei der Adressraumzuweisung.
Das Konfigurieren von lisg ist ein separates Thema. Links zu Bibliotheken sind beigefügt. Vielleicht hilft jemand bei der Umsetzung seiner Aufgaben. Wir führen Version 6 noch nicht in unserem Netzwerk ein - aber es wird ein Problem geben - es gibt Pläne, lisg für Version 6 gut umzuschreiben, und Sie müssen das Programm optimieren, das Routen hinzufügt.
Linux ISGDB2DHCPLibtinsUPD
Alles stellte sich als etwas komplizierter heraus ... Ich musste einen mehr oder weniger normalen Dämon schreiben. Und verzichten Sie auf Proxy Arp. Jetzt antwortet mein Daemon auf arp. Außerdem werden Routen in Subnetzen zu Clients hinzugefügt / entfernt. Außerdem musste ich lernen, wie man mit Netlink arbeitet. Und es stellte sich heraus, dass eine Funktion - wenn Linux den Arp an einer bestimmten Adresse erkennt und nach dem Auffinden der Schnittstelle die erste Adresse verwendet, die von der letzten stammt - auf die einige Kunden nicht antworten - mithilfe von Arptables gelöst wird.
Im Allgemeinen gibt es bereits eine normale Option für die Verbindung - dort wird übrigens das Abhören von Änderungen an Routen und Adressen und das Hinzufügen einer Routenentfernung über Netlink implementiert (bei letzteren waren die Kopfschmerzen schrecklich).
Github