Feltolerant IPeE-nätverk med improviserade verktyg

Hallå. Det betyder att det finns ett nätverk av 5k klienter. Nyligen dök ett inte särskilt trevligt ögonblick upp - i mitten av nätverket har vi en Brocade RX8 och den började skicka en massa okända-unicast-paket, eftersom nätverket är uppdelat i vlans - detta är delvis inte ett problem, MEN det finns speciella vlans för vita adresser osv. och de sträcks åt alla håll i nätverket. Så föreställ dig nu ett inkommande flöde till adressen till en klient som inte studerar som gränsstudent och detta flöde flyger mot en radiolänk till någon (eller hela) by - kanalen är igensatt - klienterna är arga - sorg...

Målet är att förvandla en bugg till en funktion. Jag tänkte i riktning mot q-in-q med ett fullfjädrat klient-vlan, men all möjlig hårdvara som P3310, när dot1q är aktiverad, slutar släppa igenom DHCP, de vet inte heller hur man selektiv qinq och många fallgropar av det slaget. Vad är ip-numrerat och hur fungerar det? Mycket kortfattat: gatewayadress + rutt på gränssnittet. För vår uppgift behöver vi: skära ut formaren, distribuera adresser till klienter, lägga till rutter till klienter genom vissa gränssnitt. Hur gör man allt detta? Shaper - lisg, dhcp - db2dhcp på två oberoende servrar, dhcprelay körs på åtkomstservrarna, ucarp körs också på åtkomstservrarna - för säkerhetskopiering. Men hur lägger man till rutter? Du kan lägga till allt i förväg med ett stort skript - men det är inte sant. Så vi ska göra en egenskriven krycka.

Efter en grundlig sökning på Internet hittade jag ett underbart högnivåbibliotek för C++, som låter dig sniffa trafik på ett vackert sätt. Algoritmen för programmet som lägger till rutter är som följer - vi lyssnar på arp-förfrågningar på gränssnittet, om vi har en adress på lo-gränssnittet på servern som efterfrågas, så lägger vi till en rutt genom detta gränssnitt och lägger till en statisk arp spela in till denna ip - i allmänhet några copy-pastes, ett litet adjektiv och du är klar

Källor till "routern"

#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

Kommando för att bygga binären

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

Hur startar man den?


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 - det kommer att bygga om tabellerna baserat på HUP-signalen. Varför använde du inte netlink? Det är bara lathet och Linux är ett skript på ett script - så allt är bra. Nåväl, rutter är rutter, vad händer härnäst? Därefter måste vi skicka rutterna som finns på den här servern till gränsen - här, på grund av samma föråldrade hårdvara, tog vi vägen för minsta motstånd - vi tilldelade denna uppgift till BGP.

bgp configvärdnamn *******
Lösenord *******
loggfilen /var/log/bgp.log
!
# AS-nummer, adresser och nätverk är fiktiva
router bgp 12345
bgp router-id 1.2.3.4
omfördela ansluten
omfördela statisk
granne 1.2.3.1 fjärr-som 12345
granne 1.2.3.1 nästa-hopp-jag
granne 1.2.3.1 ruttkarta ingen i
granne 1.2.3.1 ruttkarta export ut
!
exporttillstånd för åtkomstlista 1.2.3.0/24
!
ruttkarta exporttillstånd 10
matcha ip-adressexport
!
ruttkarta export neka 20

Låt oss fortsätta. För att servern ska kunna svara på arp-förfrågningar måste du aktivera arp-proxyn.


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

Låt oss gå vidare - ucarp. Vi skriver själva lanseringsskripten för detta mirakel.

Exempel på att köra en demon


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

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

För att dhcprelay ska fungera på ett gränssnitt behöver det en adress. Därför kommer vi att lägga till vänsteradresser på gränssnitten som vi använder - till exempel 10.255.255.1/32, 10.255.255.2/32, etc. Jag kommer inte att berätta hur du konfigurerar reläet - allt är enkelt.

Så vad har vi? Säkerhetskopiering av gateways, automatisk konfiguration av rutter, dhcp. Detta är minimiuppsättningen - lisg sveper också allt runt sig och vi har redan en shaper. Varför är allt så långt och komplicerat? Är det inte lättare att ta accel-pppd och använda pppoe helt och hållet? Nej, det är inte enklare - folk kan knappast passa en patchkabel i en router, för att inte tala om pppoe. accel-ppp är en cool grej - men det fungerade inte för oss - det finns många fel i koden - den smulas sönder, den skär snett, och det tråkigaste är att om det ljusnade - så måste folk ladda om allt - telefonerna är röda - det fungerade inte alls. Vad är fördelen med att använda ucarp istället för att hålla liv? Ja, i allt - det finns 100 gateways, keepalived och ett fel i konfigurationen - allt fungerar inte. 1 gateway fungerar inte med ucarp. Angående säkerheten så säger de att de vänstra kommer att registrera adresser åt sig själva och använda dem på aktien - för att kontrollera detta moment ställer vi upp dhcp-snooping + source-guard + arp-inspektion på alla switchar/olts/baser. Om klienten inte har dhpc utan statisk - åtkomstlista på porten.

Varför gjordes allt detta? För att förstöra oönskad trafik. Nu har varje switch sitt eget vlan och unknown-unicast är inte längre skrämmande, eftersom den bara behöver gå till en port och inte till alla... Tja, biverkningarna är en standardiserad utrustningskonfiguration, större effektivitet vid allokering av adressutrymme.

Hur man konfigurerar lisg är ett separat ämne. Länkar till bibliotek bifogas. Kanske kommer ovanstående att hjälpa någon att nå sina mål. Version 6 implementeras inte på vårt nätverk ännu - men det kommer att bli ett problem - det finns planer på att skriva om lisg för version 6, och det kommer att bli nödvändigt att korrigera programmet som lägger till rutter.

Linux ISG
DB2DHCP
Libtins

Källa: will.com

Lägg en kommentar