Réseau IPoE tolérant aux pannes à portée de main

Bonjour Il existe donc un réseau de 5 000 clients. Récemment, un moment pas si agréable est sorti - au centre du réseau et nous avons Brocade RX8 et il a commencé à envoyer beaucoup de paquets de monodiffusion inconnue, car le réseau est divisé en vlans - ce n'est en partie pas un problème MAIS il existe des vlans spéciaux pour les adresses blanches, etc. et ils sont étirés dans toutes les directions du réseau. Imaginez maintenant un flux entrant à l'adresse d'un client qui n'étudie pas en tant que pensionnaire et ce flux vole vers la liaison radio vers un village (et ainsi de suite) - la chaîne est obstruée - les clients sont mauvais - tristes ...


La tâche consiste à transformer le bogue en fonctionnalité. J'ai pensé en direction de q-in-q avec un client-vlan complet, mais toutes sortes de morceaux de fer comme P3310 lorsque j'allume dot1q arrêtent de passer DHCP, ils ne savent toujours pas à quel point le qinq sélectif et beaucoup de béquilles sous-marines comme ça. Qu'est-ce que ip-unnambered et comment ça marche? Si très brièvement - l'adresse de la passerelle + la route sur l'interface. Pour notre tâche, nous devons: couper les shapers, distribuer les adresses aux clients, ajouter des routes aux clients via des interfaces spécifiques. Comment faire tout ça? Shaper - lisg, dhcp - db2dhcp sur deux serveurs indépendants, dhcprelay fonctionne sur des serveurs d'accès, ucarp fonctionne également sur des serveurs d'accès - pour la sauvegarde. Mais comment ajouter des itinéraires? Vous pouvez tout ajouter à l'avance dans un grand script - mais ce n'est pas vrai. Nous allons donc clôturer une béquille faite maison.


Après avoir soigneusement fouillé sur Internet, j'ai trouvé une merveilleuse bibliothèque de haut niveau pour c ++ qui vous permet de flairer magnifiquement le trafic. L'algorithme du programme qui ajoute des routes est le suivant - nous écoutons les demandes d'arp sur l'interface, si nous avons une adresse de serveur sur l'interface lo que nous demandons, ajoutez la route via cette interface et ajoutez une entrée d'arp statique à cette ip - en général, quelques copies-pâtes, un petit gag et vous avez terminé


Sources du «minibus»
#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; } } 


Script d'installation de Libtins
 #!/bin/bash git clone https://github.com/mfontanini/libtins.git cd libtins mkdir build cd build cmake ../ make make install ldconfig 


La commande pour construire le binaire
 g++ main.cpp -o arp-rt -O3 -std=c++11 -lpthread -ltins 


Comment l'exécuter?
 start-stop-daemon --start --exec /opt/ipoe/arp-routes/arp-rt -b -m -p /opt/ipoe/arp-routes/daemons/eth0.800.pid -- eth0.800 


Oui - il remplace les tables au signal HUP. Pourquoi n'avez-vous pas utilisé netlink? La paresse est juste oui, et Linux est un script sur un script - de sorte que tout va bien. Eh bien routes itinéraires, quelle est la prochaine? Ensuite, nous devons envoyer les routes qui se trouvent sur ce serveur à la frontière - ici, en raison du même morceau de fer obsolète, nous avons suivi le chemin avec le moins de résistance - nous avons mis cette tâche sur BGP.


Configuration BGP
nom d'hôte *******
mot de passe *******
fichier journal /var/log/bgp.log
!
# numéro d'adresse, adresse et réseau inventé
routeur bgp 12345
bgp router-id 1.2.3.4
redistribuer connecté
redistribuer l'électricité statique
voisin 1.2.3.1 distant-as 12345
voisin 1.2.3.1 next-hop-self
voisin 1.2.3.1 route-map aucun dans
voisin 1.2.3.1 exportation de la carte d'itinéraire
!
autorisation d'exportation de la liste d'accès 1.2.3.0/24
!
permis d'exportation de carte routière 10
faire correspondre l'exportation d'adresse IP
!
exportation de carte d'itinéraire refus 20

Nous continuons. Pour que le serveur réponde aux requêtes arp, vous devez activer le proxy arp.



 echo 1 > /proc/sys/net/ipv4/conf/eth0.800/proxy_arp 

Allez-y - ucarp. Scripts pour lancer ce miracle que nous écrivons nous-mêmes


Exemple de démarrage d'un seul démon
 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" 


up.sh
 #!/bin/bash iface=$1 addr=$2 gw=$3 vlan=`echo $1 | sed "s/eth0.//"` ip ad ad $addr/32 dev lo ip ro add blackhole $gw echo 1 > /proc/sys/net/ipv4/conf/$iface/proxy_arp killall -9 dhcrelay /etc/init.d/dhcrelay zap /etc/init.d/dhcrelay start killall -HUP arp-rt 


down.sh
 #!/bin/bash iface=$1 addr=$2 gw=$3 ip ad d $addr/32 dev lo ip ro de blackhole $gw echo 0 > /proc/sys/net/ipv4/conf/$iface/proxy_arp killall -9 dhcrelay /etc/init.d/dhcrelay zap /etc/init.d/dhcrelay start 


Pour que dhcprelay fonctionne sur une interface, il a besoin d'une adresse. Par conséquent, sur les interfaces que nous utilisons, nous ajouterons des adresses de gauche - par exemple, 10.255.255.1/32, 10.255.255.2/32, etc. Je ne vous dirai pas comment configurer les relais - tout est simple là-bas.


Alors ce que nous avons. Passerelles de sauvegarde, routes de réglage automatique, DHCP. C'est le set minimum - même lisg est vissé dessus et nous avons déjà un shaper. Pourquoi tout est-il si long et cool? N'est-il pas plus facile de prendre accel-pppd et d'utiliser généralement pppoe? Non, ce n'est pas plus facile - les gens peuvent à peine insérer un cordon de raccordement dans un routeur, sans parler de pppoe. accel-ppp est une chose sympa - mais cela n'a pas fonctionné pour nous - un tas d'erreurs dans le code - il roule, coupe de façon tordue, et la chose triste est que s'il est éclairé, les gens doivent tout redémarrer - les téléphones sont rouges - en général, cela ne correspondait pas. Quel est l'avantage d'utiliser ucarp plutôt que keepalived? Oui, en tout - il y a 100 passerelles, keepalived et une erreur dans la config - tout ne fonctionne pas. 1 passerelle ne fonctionne pas avec ucarp. En ce qui concerne la sécurité, ils disent que les adresses seront gauchers et seront utilisées sur la balle - pour contrôler ce moment, nous configurons dhcp-snooping + source-guard + arp inspection sur tous les commutateurs / alt / bases. Si le client n'a pas de dhpc mais de la statique - liste d'accès sur le port.


Pourquoi tout cela a-t-il été fait? Pour détruire le trafic, nous n'aimons pas. Maintenant, chaque commutateur a son propre vlan et unknown-unicast n'a plus peur, car il n'a besoin d'aller que sur un seul port et pas sur la totalité ... Eh bien, les effets secondaires sont une configuration matérielle standardisée et une efficacité élevée d'allocation d'espace d'adressage.


Comment configurer lisg est un sujet distinct. Des liens vers les bibliothèques sont joints. Peut-être que quelqu'un les aidera dans la mise en œuvre de leurs tâches. Nous n'introduisons pas encore la version 6 sur notre réseau - mais il y aura un problème - il est prévu de réécrire lisg pour la version 6 et, bien, il sera nécessaire de corriger le programme, qui ajoute des routes.


Linux ISG
DB2DHCP
Libtins

UPD

Tout s'est avéré un peu plus compliqué ... J'ai dû écrire un démon plus ou moins normal. Et sans arp proxy. Maintenant, mon démon répond à arp, il ajoute / supprime également des routes sur les sous-réseaux vers les clients, j'ai également dû apprendre à travailler avec netlink. Et une caractéristique est devenue claire - lorsque Linux reconnaît l'arp à une certaine adresse, puis après avoir trouvé l'interface - il prend la première adresse qu'il rencontre de la dernière - à laquelle certains clients ne répondent pas - est résolue à l'aide d'arptables.

En général, il y a déjà une option normale pour le lien - là, soit dit en passant, l'écoute des changements de routes et d'adresses et l'ajout d'une suppression de route via netlink sont implémentés (avec ce dernier, le mal de tête était terrible)

github

Source: https://habr.com/ru/post/fr462885/


All Articles