Veerkrachtig IPeE-netwerk met beschikbare middelen

Hallo. Dus er is een netwerk van 5k klanten. Onlangs is er een minder prettige situatie ontstaan — in het midden van het netwerk staat een Brocade RX8 en deze begint veel unknown-unicast pakketten te versturen, omdat het netwerk is verdeeld in VLAN's — dat is gedeeltelijk geen probleem. MAAR er zijn speciale VLAN's voor witte adressen, enz., en deze zijn over het hele netwerk verspreid. Stel je nu de inkomende stroom voor naar het adres van een klant, dat niet door de border wordt geleerd, en deze stroom vliegt in de richting van een radio link naar een of andere (en dus naar allemaal) dorp — de kanaal is vol — klanten zijn boos — verdriet…

De taak is om een bug in een feature om te zetten. Ik dacht aan q-in-q met een volwaardig client-VLAN, maar allerlei apparaten zoals de P3310 stoppen bij het inschakelen van dot1q met het doorlaten van DHCP. Bovendien kunnen ze geen selective qinq en zijn er veel onderwaterproblemen van die aard. Wat is ip-unnumbered en hoe werkt het? In het kort — het adres van de gateway + route op de interface. Voor onze taak moeten we: de shaper knippen, adressen aan klanten toekennen, routes aan klanten toevoegen via specifieke interfaces. Wat te gebruiken? Shaper — lisg, dhcp — db2dhcp op twee onafhankelijke servers, op de toegangservers draait dhcprelay, ook draait ucarp op de toegangservers — voor de backup. Maar hoe routes toe te voegen? We kunnen een groot script van tevoren alles toevoegen — maar dat is niet echt de bedoeling. Dus we gaan een zelfgeschreven oplossing creëren.

Na grondig te hebben gezocht op het internet, vond ik een geweldige high-level bibliotheek voor c++, die het mogelijk maakt om verkeer mooi te sniffen. Het algoritme van het programma dat routes toevoegt is als volgt — we luisteren naar ARP-verzoeken op de interface, als we op de server een adres hebben op de interface lo dat wordt opgevraagd, voegen we de route via deze interface toe en voegen we een statische ARP-invoer voor dit ip toe — in principe een paar copy-pastes, een beetje eigen inbreng en klaar.

Broncode van de 'routeerder'

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

Installatiescript voor libtins

#!/bin/bash

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

Commando om de binaire te bouwen

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

Hoe start je het?


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 - hij herziet de tabellen bij een HUP-signaal. Waarom geen netlink gebruikt? Gewoon lui zijn en Linux is een script op een script - dus alles is in orde. Routes zijn routes, wat nu? Nu moeten we de routes die op deze server staan naar de border sturen - hier, gezien de verouderde hardware, hebben we de weg van de minste weerstand genomen - we hebben deze taak aan BGP toevertrouwd.

BGP-configuratiehostname *******
wachtwoord *******
logbestand /var/log/bgp.log
!
# номер ас-ки, адреса и сети выдуманы
router bgp 12345
bgp router-id 1.2.3.4
herdistribueer verbonden
herdistribueer statisch
buur 1.2.3.1 remote-as 12345
buur 1.2.3.1 next-hop-self
buur 1.2.3.1 route-map none in
buur 1.2.3.1 route-map export out
!
toegangscontrolelijst exporteer sta toe 1.2.3.0/24
!
route-map exporteer sta toe 10
match ip-adres exporteer
!
route-map exporteer sta af 20

Laten we doorgaan. Om ervoor te zorgen dat de server op ARP-verzoeken reageert, moet proxy ARP worden ingeschakeld.


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

Laten we verder gaan - ucarp. We schrijven zelf de opstartscripts voor dit wonder.

Voorbeeld van het starten 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"

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

Om dhcprelay op de interface te laten werken - heeft het een adres nodig. Daarom voegen we op de interfaces die we gebruiken neutrale adressen toe - bijvoorbeeld 10.255.255.1/32, 10.255.255.2/32, enzovoort. Hoe je relays instelt, zal ik niet uitleggen - het is allemaal eenvoudig.

Dus, wat hebben we? Backup van gateways, automatische routerconfiguratie, DHCP. Dit is de minimale set - daarbij komt nog eens LISP en we hebben al een shaper. Waarom is dit alles zo lang en ingewikkeld? Zou het niet eenvoudiger zijn om accel-pppd te nemen en gewoon PPPoE te gebruiken? Nee, het is niet eenvoudiger - mensen hebben al moeite om de pincode in de router te krijgen, laat staan PPPoE. Accel-ppp is een geweldige tool - maar wij hebben het niet geschikt gevonden - veel fouten in de code - het crasht, het snijdt krom, en het ergste is dat als het crasht - mensen alles opnieuw moeten opstarten - de telefoons zijn rood - het kwam gewoon niet aan. Wat zijn de voordelen van het gebruik van ucarp in plaats van keepalived? Overal - bij 100 gateways, keepalived en één fout in de configuratie - werkt alles niet. Met ucarp werkt er niet 1 gateway. Wat betreft de beveiliging, dat ze valse adressen instellen en op het netwerk gebruik maken - om dit te controleren stellen we DHCP snooping + source guard + ARP inspectie in op alle switches/olt’s/databases. Als de klant geen DHCP heeft maar statisch - dan een access-list op de poort.

Waarom is dit allemaal gedaan? Voor de vernietiging van ongewenst verkeer. Nu heeft elke switch zijn eigen VLAN en is unknown-unicast niet langer een probleem, omdat het nu slechts één poort hoeft te gebruiken in plaats van allemaal... En de neveneffecten zijn een gestandaardiseerde configuratie van de apparatuur en een grotere efficiëntie in de toewijzing van adresruimte.

Hoe lisg in te stellen — dat is een apart onderwerp. Links naar de bibliotheken zijn bijgevoegd. Misschien helpt bovenstaande informatie iemand bij het uitvoeren van hun taken. Versie 6 wordt voorlopig niet in ons netwerk geïmplementeerd — maar daar zal een probleem ontstaan — het is de bedoeling om lisg opnieuw te schrijven voor versie 6 en dan moet het programma dat routes toevoegt, ook worden aangepast.

Linux ISG
DB2DHCP
Libtins

Bron: habr.com

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers 🔥 Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster