Rețea IPeE tolerantă la erori folosind instrumente improvizate

Buna ziua. Aceasta înseamnă că există o rețea de 5 de clienți. Recent a apărut un moment nu foarte plăcut - în centrul rețelei avem un Brocade RX8 și a început să trimită o mulțime de pachete unicast necunoscute, deoarece rețeaua este împărțită în vlan-uri - aceasta nu este parțial o problemă, DAR există vlan-uri speciale pentru adrese albe etc. și sunt întinse în toate direcțiile rețelei. Așa că acum imaginați-vă un flux de intrare la adresa unui client care nu studiază ca student la graniță și acest flux zboară către o legătură radio către un anumit (sau tot) sat - canalul este înfundat - clienții sunt supărați - tristețe...

Scopul este de a transforma un bug într-o caracteristică. Mă gândeam în direcția q-in-q cu un client vlan cu drepturi depline, dar tot felul de hardware precum P3310, când dot1q este activat, nu mai lasă DHCP să treacă, nici ei nu știu cum să selecteze qinq și multe capcane de acest fel. Ce este ip-unnumbered și cum funcționează? Foarte pe scurt: adresa gateway + ruta pe interfață. Pentru sarcina noastră, trebuie să: tăiem modelul, să distribuim adrese clienților, să adăugăm rute către clienți prin anumite interfețe. Cum să faci toate astea? Shaper - lisg, dhcp - db2dhcp pe două servere independente, dhcprelay rulează pe serverele de acces, ucarp rulează și pe serverele de acces - pentru backup. Dar cum se adaugă rute? Puteți adăuga totul în avans cu un script mare - dar acest lucru nu este adevărat. Așa că vom face o cârjă auto-scrisă.

După o căutare amănunțită pe Internet, am găsit o bibliotecă minunată de nivel înalt pentru C++, care vă permite să adulmecați frumos traficul. Algoritmul pentru programul care adaugă rute este următorul - ascultăm cererile arp pe interfață, dacă avem o adresă pe interfața lo pe serverul care este solicitat, atunci adăugăm o rută prin această interfață și adăugăm un arp static inregistreaza pe acest ip - in general, cateva copy-pastes, putin adjectiv si gata

Surse ale „routerului”

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

scriptul de instalare libtins

#!/bin/bash

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

Comanda pentru a construi binarul

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

Cum să-l lansez?


start-stop-daemon --start --exec  /opt/ipoe/arp-routes/arp-rt -b -m -p /opt/ipoe/arp-routes/daemons/eth0.800.pid -- eth0.800

Da - va reconstrui tabelele pe baza semnalului HUP. De ce nu ai folosit netlink? Este doar lene și Linux este un script pe un script - așa că totul este bine. Ei bine, rutele sunt rute, ce urmează? În continuare, trebuie să trimitem rutele care sunt pe acest server la graniță - aici, din cauza aceluiași hardware învechit, am luat calea cu cea mai mică rezistență - am atribuit această sarcină BGP.

config bgpNume gazdă ******
parola *******
fișier jurnal /var/log/bgp.log
!
# Numărul AS, adresele și rețelele sunt fictive
router bgp 12345
bgp router-id 1.2.3.4
redistribui conectat
redistribui static
vecin 1.2.3.1 remote-as 12345
vecin 1.2.3.1 next-hop-self
vecin 1.2.3.1 ruta-hartă niciuna în
vecin 1.2.3.1 export out map rută
!
permis de export pentru lista de acces 1.2.3.0/24
!
permis de export pentru hărți de traseu 10
potriviți exportul adresei IP
!
refuza exportul hărții rutei 20

Hai sa continuăm. Pentru ca serverul să răspundă la solicitările arp, trebuie să activați proxy-ul arp.


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

Să mergem mai departe - ucarp. Scriem noi înșine scripturile de lansare pentru acest miracol.

Exemplu de rulare a unui 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"

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

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

Pentru ca dhcprelay să funcționeze pe o interfață, are nevoie de o adresă. Prin urmare, pe interfețele pe care le folosim vom adăuga adrese din stânga - de exemplu 10.255.255.1/32, 10.255.255.2/32 etc. Nu vă voi spune cum să configurați releul - totul este simplu.

Deci ce avem? Backup de gateway-uri, auto-configurare a rutelor, dhcp. Acesta este setul minim - de asemenea, lisg înfășoară totul în jurul lui și avem deja un modelator. De ce este totul atât de lung și complicat? Nu este mai ușor să iei accel-pppd și să folosești pppoe cu totul? Nu, nu este mai simplu - oamenii cu greu pot monta un cablu de corecție într-un router, ca să nu mai vorbim de pppoe. accel-ppp este un lucru grozav - dar nu a funcționat pentru noi - există o mulțime de erori în cod - se prăbușește, se taie strâmb, iar cel mai trist lucru este că, dacă s-a luminat - atunci oamenii trebuie să reîncarce totul - telefoanele sunt roșii - nu a funcționat deloc. Care este avantajul folosirii ucarpului, mai degrabă decât a keepalived? Da, în toate - există 100 de gateway-uri, keepalived și o eroare în config - totul nu funcționează. 1 gateway nu funcționează cu ucarp. În ceea ce privește securitatea, ei spun că cei din stânga își vor înregistra adrese și le vor folosi pe share - pentru a controla acest moment, am configurat dhcp-snooping + source-guard + inspecție arp pe toate switch-urile/olturile/bazele. Daca clientul nu are dhpc ci static - acces-list pe port.

De ce s-au făcut toate acestea? Pentru a distruge traficul nedorit. Acum fiecare switch are propriul lui vlan și unknown-unicast nu mai este înfricoșător, deoarece trebuie să meargă doar la un port și nu la toate... Ei bine, efectele secundare sunt o configurație standardizată a echipamentului, o eficiență mai mare în alocarea spațiului de adrese.

Cum să configurați lisg este un subiect separat. Link-uri către biblioteci sunt atașate. Poate că cele de mai sus vor ajuta pe cineva să-și atingă obiectivele. Versiunea 6 nu este încă implementată în rețeaua noastră - dar va fi o problemă - există planuri de rescriere a listei pentru versiunea 6 și va fi necesară corectarea programului care adaugă rute.

Linux ISG
DB2DHCP
Libtini

Sursa: www.habr.com

Adauga un comentariu