Fehlertolerantes IPeE-Netz aus verfügbaren Mitteln

Hallo. Also, es gibt ein Netzwerk mit 5000 Kunden. Kürzlich gab es ein unerfreuliches Problem – im Zentrum des Netzwerks steht ein Brocade RX8, der viele Unknown-Unicast-Pakete verschickt. Da das Netzwerk in VLANs unterteilt ist, ist dies teilweise kein Problem. Aber es gibt spezielle VLANs für die öffentlichen Adressen usw., die sich überall im Netzwerk erstrecken. Jetzt stellen Sie sich den eingehenden Datenverkehr zu einer Kundenadresse vor, die vom Border-Router nicht erkannt wird, und dieser Datenverkehr geht in Richtung eines Funklinks zu irgendeinem (also zu allen) Dorf – der Kanal ist überlastet – die Kunden sind unzufrieden – Traurigkeit...

Die Aufgabe besteht darin, einen Bug in ein Feature umzuwandeln. Ich habe an q-in-q mit einem vollwertigen Client-VLAN gedacht, aber verschiedene Geräte wie der P3310 lassen beim Aktivieren von dot1q keine DHCP-Anfragen durch, und sie unterstützen auch kein selektives qinq sowie viele andere technische Einschränkungen. Was ist ip-unnumbered und wie funktioniert das? Kurz gesagt: Gateway-Adresse + Route auf dem Interface. Für unser Vorhaben müssen wir: den Shaper konfigurieren, IP-Adressen an die Kunden verteilen und Routen zu den Kunden über bestimmte Interfaces hinzufügen. Womit machen wir das? Shaper — lisg, DHCP — db2dhcp auf zwei unabhängigen Servern, auf den Zugangsservern läuft dhcprelay, ebenfalls wird ucarp — zur Backup-Übernahme — auf den Zugangsservern eingesetzt. Aber wie fügen wir Routen hinzu? Man könnte alles im Voraus mit einem großen Skript hinzufügen — aber das ist nicht die wahre Lösung. Also werden wir eine maßgeschneiderte Lösung entwickeln.

Nach gründlicher Recherche im Internet habe ich eine hervorragende High-Level-Bibliothek für C++ gefunden, die es ermöglicht, den Verkehr elegant abzuhören. Der Algorithmus der Software, die Routen hinzufügt, funktioniert wie folgt: Wir hören ARP-Anfragen auf dem Interface ab. Wenn wir eine Adresse auf dem Interface lo haben, die angefragt wird, fügen wir über dieses Interface eine Route hinzu und erstellen einen statischen ARP-Eintrag für diese IP – insgesamt einige Copy-Paste-Vorgänge, ein bisschen Eigenes und schon ist es fertig.

Quellcodes des 'Route-Händlers'

#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

Befehlt zum Erstellen der Binärdatei

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

Wie startet man ihn?


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 stellt die Tabellen bei einem HUP-Signal neu ein. Warum habe ich nicht netlink verwendet? Einfach zu faul und Linux ist ein Skript auf einem Skript – daher ist alles in Ordnung. Nun, die Routen sind die Routen, was kommt als Nächstes? Jetzt müssen wir die Routen, die auf diesem Server vorhanden sind, an den Border senden – hier, aufgrund derselben veralteten Hardware, haben wir den einfachsten Weg gewählt – wir haben diese Aufgabe BGP zugewiesen.

BGP-Konfigurationhostname *******
password *******
log file /var/log/bgp.log
!
# номер ас-ки, адреса и сети выдуманы
router bgp 12345
bgp router-id 1.2.3.4
redistribute connected
statische Stimmen verteilen
Nachbar 1.2.3.1 remote-as 12345
Nachbar 1.2.3.1 next-hop-self
Nachbar 1.2.3.1 route-map none in
Nachbar 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 reagiert, muss Proxy ARP aktiviert werden.


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

Weiter geht's — ucarp. Wir schreiben die Startskripte für dieses Wunder selbst.

Beispiel zum Start 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 am Interface funktioniert, braucht es eine Adresse. Daher werden wir an den Interfaces, die wir verwenden, Platzhalteradressen hinzufügen — 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.

Was haben wir also? Einen Backup-Gateway, automatische Routen-Konfiguration und DHCP. Das ist das Mindestmaß – und darauf kommt noch LISG, und schon haben wir ein Shaper. Warum ist das alles so kompliziert? Wäre es nicht einfacher, Accel-PPPD zu verwenden und einfach PPPoE zu nutzen? Nein, das wäre nicht einfacher – viele schaffen es kaum, das Patchkabel in den Router zu stecken, geschweige denn PPPoE. Accel-PPP ist eine großartige Sache – aber es lief bei uns nicht gut – viele Fehler im Code – es stürzt ab, arbeitet nicht richtig, und das Schlimmste ist, wenn es abstürzt – dann müssen die Leute alles neu starten – die Telefone sind rot – kurz gesagt, es hat nicht funktioniert. Was sind die Vorteile von UCarp im Vergleich zu Keepalived? Nun, bei Keepalived gibt es 100 Gateways und ein Fehler in der Konfiguration – schon funktioniert nichts mehr. Bei UCarp funktioniert nur ein Gateway. Zum Thema Sicherheit: Man könnte falsche Adressen eingeben und sich unbefugt Zugang verschaffen – um das zu kontrollieren, richten wir DHCP-Snooping + Source-Guard + ARP-Inspektion auf allen Switches/Router/Basics ein. Wenn der Kunde kein DHCP, sondern statische IPs hat – dann eine Access-List am Port.

Warum wurde das alles gemacht? Um unerwünschten Traffic zu blockieren. Jetzt hat jeder Switch sein eigenes VLAN, und Unknown-Unicast ist kein Problem mehr, da es nur noch einen Port nutzen muss und nicht mehr alle... Zudem haben wir einen standardisierten Konfigurationsprozess für die Hardware und eine effizientere Verteilung des Adressraums.

Wie man LISG einrichtet – das ist ein separates Thema. Links zu den Bibliotheken sind beigefügt. Vielleicht hilft das, was oben dargestellt ist, jemandem bei der Umsetzung seiner Aufgaben. Die Version 6 wird derzeit in unserem Netzwerk nicht implementiert – es wird jedoch ein Problem geben – es ist geplant, LISG für Version 6 neu zu schreiben, und wir müssen auch die Software anpassen, die die Routen hinzufügt.

Linux ISG
DB2DHCP
Libtins

Quelle: habr.com

Kaufen Sie zuverlässiges Hosting für Websites mit DDoS-Schutz, VPS VDS-Server 🔥 Kaufen Sie zuverlässiges Hosting für Websites mit DDoS-Schutz, VPS VDS-Server | ProHoster