Fehlertolerantes IPeE-Netzwerk aus gebrÀuchlichen Materialien

Hallo. Es gibt also ein Netzwerk mit 5.000 Kunden. KĂŒrzlich gab es ein unangenehmes Problem — im Zentrum des Netzwerks steht ein Brocade RX8, der viele unknown-unicast-Pakete sendet. Da das Netzwerk in VLANs unterteilt ist, ist das teilweise kein Problem, aber es gibt spezielle VLANs fĂŒr weiße Adressen usw., die sich in alle Richtungen des Netzwerks erstrecken. Stellen Sie sich nun den eingehenden Datenstrom zu einer Kundenadresse vor, die nicht ĂŒber den Border-Router gelernt wird, und dieser Datenstrom fliegt in Richtung eines Funksenders in ein bestimmtes (und somit in alle) Dörfer — der Kanal ist ĂŒberlastet — die Kunden sind verĂ€rgert — Traurigkeit


Die Aufgabe besteht darin, einen Fehler in ein Feature zu verwandeln. Ich habe in Richtung q-in-q mit einem vollstĂ€ndigen Client-VLAN gedacht, aber verschiedene GerĂ€te wie P3310 hören auf, DHCP durchzulassen, wenn dot1q aktiviert ist. Sie unterstĂŒtzen auch kein selektives qinq und haben viele potenzielle Probleme dieser Art. Was ist ip-unumbered und wie funktioniert es? Kurz gesagt — Gateway-Adresse + Route auf dem Interface. FĂŒr unsere Aufgabe mĂŒssen wir Folgendes tun: Shaping durchfĂŒhren, Adressen an Kunden verteilen, Routen zu Kunden ĂŒber bestimmte Interfaces hinzufĂŒgen. Womit machen wir das? Shaping — lisg, DHCP — db2dhcp auf zwei unabhĂ€ngigen Servern. Auf den Zugangservern lĂ€uft dhcprelay, ebenso lĂ€uft ucarp auf den Zugangservern — als Backup. Aber wie fĂŒgt man Routen hinzu? Man könnte alles im Voraus mit einem großen Skript hinzufĂŒgen — aber das wĂ€re nicht wahrhaftig. Also werden wir eine eigene Lösung erstellen.

Nach grĂŒndlicher Recherche im Internet habe ich eine großartige High-Level-Bibliothek fĂŒr C++ gefunden, die es ermöglicht, den Datenverkehr elegant zu sniffen. Der Algorithmus des Programms, das Routen hinzufĂŒgt, funktioniert folgendermaßen: Wir hören auf ARP-Anfragen an der Schnittstelle und wenn wir auf dem Server eine Adresse an der Schnittstelle lo haben, die angefragt wird, fĂŒgen wir ĂŒber diese Schnittstelle eine Route hinzu und erstellen einen statischen ARP-Eintrag fĂŒr diese IP — im Grunde genommen ein paar Kopie-und-EinfĂŒge-Projekte, ein wenig Eigenes und fertig.

Quellcodes des 'Routenprogramms'

#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;
    }
}

Installationsskript fĂŒr libtins

#!/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 wird es gestartet?


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 — er passt die Tabellen bei HUP-Signalen an. Warum wurde kein Netlink verwendet? Einfach zu faul, und Linux ist ein Skript auf einem Skript — also ist alles in Ordnung. So, die Routen sind gesetzt, was kommt als NĂ€chstes? Weiterhin mĂŒssen wir die Routen, die auf diesem Server vorhanden sind, an den Border senden — hier sind wir aufgrund der veralteten Hardware den Weg des geringsten Widerstands gegangen und haben diese Aufgabe auf BGP gelegt.

BGP-Konfigurationhostname ******
password ******
log file /var/log/bgp.log
!
# ĐœĐŸĐŒĐ”Ń€ ас-ĐșĐž, аЎрДса Đž сДтО ĐČŃ‹ĐŽŃƒĐŒĐ°ĐœŃ‹
router bgp 12345
bgp router-id 1.2.3.4
redistribute connected
redistribute static
neighbor 1.2.3.1 remote-as 12345
neighbor 1.2.3.1 next-hop-self
neighbor 1.2.3.1 route-map none in
neighbor 1.2.3.1 route-map export out
!
access-list export permit 1.2.3.0/24
!
route-map export permit 10
match ip address export
!
route-map export deny 20

Wir machen weiter. Damit der Server auf ARP-Anfragen antwortet, muss Proxy ARP aktiviert werden.


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

Gehen wir weiter — ucarp. Die Startskripte fĂŒr dieses Wunder schreiben wir selbst.

Beispiel zum Starten 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

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

Damit dhcprelay auf der Schnittstelle funktioniert, benötigt es eine Adresse. Daher fĂŒgen wir auf den Schnittstellen, die wir verwenden, stellvertretende Adressen hinzu — zum Beispiel 10.255.255.1/32, 10.255.255.2/32 usw. Wie man den Relay konfiguriert, werde ich nicht erklĂ€ren — das ist ganz einfach.

Also, was haben wir? Backup-Gateways, automatische Routenanpassung, DHCP. Das ist das Minimum — zusĂ€tzlich kommt das LISG dazu und wir haben schon einen Shaper. Warum ist alles so langwierig und verwickelt? Ist es nicht einfacher, accel-pppd zu verwenden und einfach PPPoE zu nutzen? Nein, es ist nicht einfacher — die Leute haben schon Schwierigkeiten, ein Patchkabel in den Router zu stecken, geschweige denn bei PPPoE. Accel-PPP ist großartig — aber es hat uns nicht ĂŒberzeugt — viele Fehler im Code — es stĂŒrzt ab, schneidet schlecht und das Schlimmste ist, dass, wenn es abstĂŒrzt, die Leute alles neu starten mĂŒssen — die Telefone lĂ€uten rot — es kam einfach nicht in Frage. Was sind die Vorteile von ucarp im Vergleich zu keepalived? Überall — es gibt 100 Gateways, keepalived und ein Fehler in der Konfiguration — alles funktioniert nicht. Bei ucarp funktioniert ein Gateway nicht. Was die Sicherheit betrifft, dass sie sich falsche Adressen eintragen und auf den Netzteilen verwenden — zur Kontrolle dieses Aspekts aktivieren wir DHCP-Snooping + Source-Guard + ARP-Inspektion auf allen Switches/OLTs/Datenbanken. Wenn der Kunde kein DHCP hat, sondern eine statische IP — Access-List am Port.

Warum wurde das alles gemacht? Zur Zerschlagung des uns nicht genehmen Traffics. Jetzt hat jeder Switch sein VLAN und unknown-unicast ist kein Problem mehr, da es nur noch in einen einzelnen Port gehen muss und nicht mehr in alle... Und die Nebeneffekte – standardisierte Konfiguration der GerĂ€te, grĂ¶ĂŸere Effizienz in der Adressverteilung.

Wie man lisg einrichtet – das ist ein eigenes Thema. Links zu den Bibliotheken sind angefĂŒgt. Vielleicht hilft das oben Gesagte jemandem bei der Umsetzung seiner Aufgaben. Die Version 6 wird bei uns im Netz noch nicht implementiert – aber es wird ein Problem geben – es ist geplant, lisg fĂŒr Version 6 neu zu schreiben und das Programm, das Routen hinzufĂŒgt, muss ebenfalls angepasst werden.

Linux ISG
DB2DHCP
Libtins

Quelle: habr.com

ZuverlĂ€ssiges Hosting fĂŒr Websites mit DDoS-Schutz kaufen, VPS VDS Server đŸ”„ ZuverlĂ€ssiges Hosting fĂŒr Websites mit DDoS-Schutz kaufen, VPS VDS Server - ProHoster