Fouttolerant IPeE-netwerk met behulp van geïmproviseerde tools

Hallo. Dit betekent dat er een netwerk van 5k klanten is. Onlangs deed zich een niet erg prettig moment voor - in het midden van het netwerk hebben we een Brocade RX8 en deze begon veel onbekende unicast-pakketten te verzenden, aangezien het netwerk is opgedeeld in vlans - dit is gedeeltelijk geen probleem, MAAR er zijn speciale vlans voor witte adressen, enz. en ze zijn uitgestrekt in alle richtingen van het netwerk. Stel je nu een inkomende stroom voor naar het adres van een cliënt die niet als grensstudent studeert en deze stroom vliegt richting een radioverbinding naar een (of heel) dorp - het kanaal is verstopt - de cliënten zijn boos - verdriet...

Het doel is om van een bug een feature te maken. Ik dacht in de richting van q-in-q met een volwaardige client-vlan, maar allerlei soorten hardware zoals P3310 laten, wanneer dot1q is ingeschakeld, DHCP niet meer door, ze weten ook niet hoe ze qinq moeten selecteren en veel dat soort valkuilen. Wat is ip-unnamed en hoe werkt het? Heel kort: gateway-adres + route op de interface. Voor onze taak moeten we: de shaper afsnijden, adressen naar klanten distribueren, routes naar klanten toevoegen via bepaalde interfaces. Hoe dit allemaal te doen? Shaper - lisg, dhcp - db2dhcp op twee onafhankelijke servers, dhcprelay draait op de toegangsservers, ucarp draait ook op de toegangsservers - voor back-up. Maar hoe voeg je routes toe? Je kunt alles vooraf toevoegen met een groot script, maar dit is niet waar. We gaan dus een zelfgeschreven kruk maken.

Na grondig zoeken op internet vond ik een prachtige bibliotheek op hoog niveau voor C++, waarmee je prachtig verkeer kunt opsnuiven. Het algoritme voor het programma dat routes toevoegt is als volgt: we luisteren naar arp-verzoeken op de interface, als we een adres hebben op de lo-interface op de server die wordt aangevraagd, dan voegen we een route toe via deze interface en voegen we een statische arp toe neem op naar dit ip - in het algemeen een paar copy-pastes, een klein bijvoeglijk naamwoord en je bent klaar

Bronnen van de 'router'

#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 installatiescript

#!/bin/bash

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

Commando om het binaire bestand te bouwen

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

Hoe het te lanceren?


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 - het zal de tabellen opnieuw opbouwen op basis van het HUP-signaal. Waarom heb je geen netlink gebruikt? Het is gewoon luiheid en Linux is een script op een script - dus alles is in orde. Nou ja, routes zijn routes, wat nu? Vervolgens moeten we de routes die op deze server staan ​​naar de grens sturen - hier hebben we vanwege dezelfde verouderde hardware de weg van de minste weerstand gekozen - we hebben deze taak aan BGP toegewezen.

bgp-configuratiehostnaam *******
wachtwoord *******
logbestand /var/log/bgp.log
!
# AS-nummer, adressen en netwerken zijn fictief
router-bgp 12345
bgp router-id 1.2.3.4
herverdelen verbonden
statisch herverdelen
buurman 1.2.3.1 op afstand-als 12345
buurman 1.2.3.1 next-hop-self
buur 1.2.3.1 routekaart geen in
buurman 1.2.3.1 routekaart exporteren
!
toegangslijst exportvergunning 1.2.3.0/24
!
routekaart exportvergunning 10
match ip-adres exporteren
!
export van routekaart weigeren 20

Laten we doorgaan. Om de server te laten reageren op arp-verzoeken, moet u de arp-proxy inschakelen.


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

Laten we verder gaan - ucarp. Wij schrijven zelf de lanceringsscripts voor dit wonder.

Voorbeeld van het uitvoeren van één daemon


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"

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

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

Om dhcprelay op een interface te laten werken, heeft het een adres nodig. Daarom zullen we op de interfaces die we gebruiken linkeradressen toevoegen - bijvoorbeeld 10.255.255.1/32, 10.255.255.2/32, enz. Ik zal je niet vertellen hoe je het relais moet configureren - alles is eenvoudig.

Dus wat hebben we? Back-up van gateways, automatische configuratie van routes, DHCP. Dit is de minimale set - lisg wikkelt er ook alles omheen en we hebben al een shaper. Waarom is alles zo lang en ingewikkeld? Is het niet eenvoudiger om accel-pppd te nemen en pppoe helemaal te gebruiken? Nee, het is niet eenvoudiger: mensen kunnen nauwelijks een patchkabel in een router passen, om nog maar te zwijgen van pppoe. accel-ppp is een cool ding - maar het werkte niet voor ons - er zitten veel fouten in de code - het brokkelt af, het snijdt scheef, en het meest trieste is dat als het helderder wordt - mensen opnieuw moeten laden alles - de telefoons zijn rood - het werkte helemaal niet. Wat is het voordeel van het gebruik van ucarp in plaats van keepalived? Ja, in alles - er zijn 100 gateways, keepalived en één fout in de configuratie - werkt alles niet. 1 gateway werkt niet met ucarp. Wat de veiligheid betreft, zeggen ze dat de linkse adressen voor zichzelf zullen registreren en deze op de share zullen gebruiken - om dit moment te controleren, hebben we dhcp-snooping + source-guard + arp inspection ingesteld op alle switches/olts/bases. Als de client geen dhpc maar statisch heeft: acces-list op de poort.

Waarom werd dit allemaal gedaan? Om ongewenst verkeer te vernietigen. Nu heeft elke switch zijn eigen vlan en is onbekend-unicast niet langer eng, omdat het maar naar één poort hoeft te gaan en niet naar alle... Welnu, de bijwerkingen zijn een gestandaardiseerde apparatuurconfiguratie, een grotere efficiëntie bij het toewijzen van adresruimte.

Hoe u lisg configureert is een apart onderwerp. Links naar bibliotheken zijn bijgevoegd. Misschien helpt het bovenstaande iemand bij het bereiken van zijn doelen. Versie 6 wordt nog niet op ons netwerk geïmplementeerd - maar er zal een probleem zijn: er zijn plannen om lisg te herschrijven voor versie 6, en het zal nodig zijn om het programma dat routes toevoegt te corrigeren.

Linux-ISG
DB2DHCP
Libtins

Bron: www.habr.com

Voeg een reactie