Fehlertolerantes IPeE-Netzwerk mit improvisierten Tools

Guten Tag. Das bedeutet, dass es ein Netzwerk von 5 Clients gibt. Kürzlich kam es zu einem nicht sehr erfreulichen Moment: In der Mitte des Netzwerks haben wir einen Brocade RX8, der begann, viele unbekannte Unicast-Pakete zu senden, da das Netzwerk in VLANs unterteilt ist. Dies ist teilweise kein Problem, ABER es gibt sie spezielle VLANs für weiße Adressen usw. und sie erstrecken sich in alle Richtungen des Netzwerks. Stellen Sie sich nun einen ankommenden Fluss an die Adresse eines Kunden vor, der nicht als Grenzstudent studiert, und dieser Fluss fliegt auf eine Funkverbindung zu einem (oder allen) Dorf zu – der Kanal ist verstopft – die Kunden sind wütend – Traurigkeit …

Das Ziel besteht darin, einen Fehler in ein Feature umzuwandeln. Ich dachte an Q-in-Q mit einem vollwertigen Client-VLAN, aber alle Arten von Hardware wie P3310 lassen DHCP nicht mehr durch, wenn dot1q aktiviert ist. Sie wissen auch nicht, wie man Qinq und vieles mehr auswählt Fallstricke dieser Art. Was ist IP-Unnamed und wie funktioniert es? Ganz kurz: Gateway-Adresse + Route auf der Schnittstelle. Für unsere Aufgabe müssen wir: den Shaper ausschneiden, 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 läuft auf den Zugriffsservern, ucarp läuft auch auf den Zugriffsservern – zur Sicherung. Aber wie fügt man Routen hinzu? Man kann mit einem großen Skript alles im Voraus hinzufügen – das stimmt aber nicht. Also werden wir eine selbstgeschriebene Krücke machen.

Nach einer gründlichen Suche im Internet habe ich eine wunderbare High-Level-Bibliothek für C++ gefunden, mit der man den Datenverkehr wunderbar aufspüren kann. Der Algorithmus für das Programm, das Routen hinzufügt, lautet wie folgt: Wir hören auf ARP-Anfragen auf der Schnittstelle. Wenn wir eine Adresse auf der Lo-Schnittstelle des Servers haben, die angefordert wird, fügen wir eine Route über diese Schnittstelle hinzu und fügen einen statischen ARP hinzu Auf diese IP aufnehmen - im Allgemeinen ein paar Copy-Pastes, ein kleines Adjektiv und fertig

Quellen des „Routers“

#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

#!/bin/bash

git clone https://github.com/mfontanini/libtins.git
cd libtins
mkdir build
cd build
cmake ../
make
make install
ldconfig

Befehl zum Erstellen der Binärdatei

g++ main.cpp -o arp-rt -O3 -std=c++11 -lpthread -ltins

Wie starte ich 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 – die Tabellen werden basierend auf dem HUP-Signal neu erstellt. Warum haben Sie nicht Netlink verwendet? Es ist einfach Faulheit und Linux ist ein Skript auf einem Skript – also ist alles in Ordnung. Na ja, Routen sind Routen, wie geht es weiter? Als nächstes müssen wir die Routen, die sich auf diesem Server befinden, an die Grenze senden – hier haben wir aufgrund der gleichen veralteten Hardware den Weg des geringsten Widerstands gewählt – diese Aufgabe haben wir BGP zugewiesen.

BGP-KonfigurationHostname *******
Passwort *******
Protokolldatei /var/log/bgp.log
!
# AS-Nummer, Adressen und Netzwerke sind fiktiv
Router BGP 12345
BGP-Router-ID 1.2.3.4
verbunden umverteilen
statisch neu verteilen
Nachbar 1.2.3.1 remote-as 12345
Nachbar 1.2.3.1 Next-Hop-Selbst
Nachbar 1.2.3.1 Routenkarte keine in
Nachbar 1.2.3.1 Route-Map-Export
!
Zugriffslisten-Exportgenehmigung 1.2.3.0/24
!
Routenkarten-Ausfuhrgenehmigung 10
Match-IP-Adressexport
!
Routenkarten-Export verweigern 20

Lass uns weitermachen. 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

Lass uns weitermachen – ucarp. Wir schreiben die Startskripte für dieses Wunder selbst.

Beispiel für die Ausführung eines 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"

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

unten.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

Damit dhcprelay auf einer Schnittstelle funktioniert, benötigt es eine Adresse. Daher werden wir auf den von uns verwendeten Schnittstellen linke Adressen hinzufügen – zum Beispiel 10.255.255.1/32, 10.255.255.2/32 usw. Ich werde Ihnen nicht sagen, wie Sie das Relais konfigurieren – alles ist einfach.

Was haben wir also? Backup von Gateways, automatische Routenkonfiguration, DHCP. Dies ist der Mindestsatz – lisg wickelt auch alles darum und wir haben bereits einen Shaper. Warum ist alles so lang und kompliziert? Ist es nicht einfacher, accel-pppd zu nehmen und insgesamt PPPoE zu verwenden? Nein, es ist nicht einfacher – man kann kaum ein Patchkabel in einen Router einbauen, ganz zu schweigen von PPPoE. accel-ppp ist eine coole Sache – aber bei uns hat es nicht funktioniert – der Code enthält viele Fehler – er bröckelt, er schneidet schief und das Traurigste ist, dass die Leute neu laden müssen, wenn er heller wird Alles - die Telefone sind rot - es hat überhaupt nicht funktioniert. Was ist der Vorteil der Verwendung von Ucarp gegenüber Keepalived? Ja, in allem - es gibt 100 Gateways, Keepalived und einen Fehler in der Konfiguration - alles funktioniert nicht. 1 Gateway funktioniert nicht mit Ucarp. Bezüglich der Sicherheit sagen sie, dass die Linken Adressen für sich selbst registrieren und sie auf der Freigabe verwenden werden – um diesen Moment zu kontrollieren, haben wir DHCP-Snooping + Source-Guard + ARP-Inspektion auf allen Switches/OLTs/Basen eingerichtet. Wenn der Client kein DHCP, sondern eine statische Zugriffsliste für den Port hat.

Warum wurde das alles getan? Um unerwünschten Verkehr zu zerstören. Jetzt hat jeder Switch sein eigenes VLAN und Unknown-Unicast ist nicht mehr beängstigend, da es nur an einen Port und nicht an alle gehen muss ... Nun, die Nebenwirkungen sind eine standardisierte Gerätekonfiguration und eine höhere Effizienz bei der Zuweisung von Adressraum.

Wie man lisg konfiguriert, ist ein separates Thema. Links zu Bibliotheken sind beigefügt. Vielleicht hilft das oben Genannte jemandem dabei, seine Ziele zu erreichen. Version 6 wird in unserem Netzwerk noch nicht implementiert – aber es wird ein Problem geben – es gibt Pläne, lisg für Version 6 neu zu schreiben, und es wird notwendig sein, das Programm, das Routen hinzufügt, zu korrigieren.

Linux ISG
DB2DHCP
Libtins

Source: habr.com

Kommentar hinzufügen