Rețea IPeE rezistentă construită din resurse la îndemână

Bună ziua. Așadar, există o rețea de 5k clienți. Recent, a apărut o problemă neplăcută - în mijlocul rețelei avem un Brocade RX8 și acesta a început să transmită multe pachete unknown-unicast, deoarece rețeaua este împărțită pe VLAN-uri - parțial aceasta nu este o problemă, DAR avem VLAN-uri speciale pentru adresele albe etc. și acestea sunt întinse în toate direcțiile rețelei. Așadar, acum imaginați-vă fluxul de intrare către adresa clientului, care nu este contabilizat de bord și acest flux se îndreaptă spre un link radio către un anumit (sau toate) sat - canalul este blocat - clienții sunt nervoși - tristețe...

Sarcina - a transforma un bug într-o funcționalitate. M-am gândit la o soluție q-in-q cu un VLAN client complet, dar tot felul de echipamente precum P3310, atunci când activăm dot1q, nu mai permit trecerea DHCP, iar ele nu suportă qinq selectiv și multe alte soluții improvizate în acest sens. Ce este ip-unnumbered și cum funcționează? Dacă foarte pe scurt - adresa gateway-ului + ruta pe interfață. Pentru sarcina noastră, trebuie să: limităm șeiperul, să distribuim adrese clienților, să adăugăm rute către clienți prin anumite interfețe. Cu ce să facem toate acestea? Șeiper - lisg, dhcp - db2dhcp pe două servere independente, pe serverele de acces rulează dhcprelay, de asemenea pe serverele de acces funcționează ucarp - pentru backup. Dar cum adăugăm rute? Putem adăuga totul dintr-un script mare - dar asta nu este adevărat. Așadar, vom construi o soluție personalizată.

După o cercetare temeinică pe internet, am găsit o bibliotecă minunată de nivel înalt pentru c++, care permite capturarea elegantă a traficului. Algoritmul programului care adaugă rute este următorul - ascultăm pe interfață cereri ARP, dacă avem pe server o adresă pe interfața lo, care este solicitată, atunci adăugăm ruta prin această interfață și adăugăm o înregistrare ARP statică pentru această IP - în general, câteva copypaste-uri, puțin din imaginația proprie și gata.

Sursele 'route-ului'

#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 compilarea binarului

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

Cum se rulează?


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 — acesta reconfigurează tabelele la semnalul HUP. De ce nu am folosit link net? Pur și simplu mi-a fost lene și, în plus, Linux este un script pe un script — așa că este totul în regulă. Ei bine, rutele sunt rute, ce urmează? Următorul pas este să trimitem rutele de pe acest server către bordură — aici, din cauza acelorași echipamente învechite, am ales calea de rezistență minimă — am delegat această sarcină BGP-ului.

Configurație bgphostname *******
parolă *******
log file /var/log/bgp.log
!
# номер ас-ки, адреса и сети выдуманы
router bgp 12345
bgp router-id 1.2.3.4
redistribute connected
redistribute static
neighbor 1.2.3.1 remote-as 12345
neighbor 1.2.3.1 next-hop-self
neighbor 1.2.3.1 route-map none in
neighbor 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

Continuăm. Pentru ca serverul să răspundă la cererile ARP, trebuie să activăm proxy ARP.


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

Să mergem mai departe — ucarp. Scripturile de pornire pentru această minune le scriem noi.

Exemplu de pornire 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"

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

Pentru ca dhcprelay să funcționeze pe interfață, este necesară o adresă. Așa că pe interfețele pe care le folosim, vom adăuga adrese false — de exemplu, 10.255.255.1/32, 10.255.255.2/32 etc. Nu o să vă explic cum se configurează relay-ul — este totul destul de simplu.

Deci, ce avem? Backup gateway-uri, auto-configurare a rutelor, DHCP. Acesta este un set minim — pe lângă toate acestea, se adaugă lisg și deja avem și shaper. De ce este totul atât de lung și complicat? Nu ar fi mai simplu să luăm accel-pppd și să folosim pppoe? Nu, nu ar fi mai simplu — oamenii au dificultăți în a băga un pachet în router, cu atât mai puțin cu pppoe. accel-ppp este o chestie faină — dar nu ne-a mers — o grămadă de erori în cod — cedează, se oprește prost, și cel mai trist este că dacă s-a stricat — oamenii trebuie să reîncarce totul — telefoanele sună roșu — în general, nu a funcționat. Care este avantajul utilizării ucarp în loc de keepalived? Ei bine, la tot — sunt 100 de gateway-uri, keepalived și o eroare în configurație — totul nu funcționează. Cu ucarp nu funcționează un singur gateway. Despre securitate, de parcă și-ar putea scrie adrese false și ar folosi pe rețea — pentru a controla acest moment, pe toate switch-urile / olturi / baze, configurăm dhcp-snooping + source-guard + arp inspection. Dacă clientul nu are DHCP, ci static — acces-list pe port.

De ce s-a făcut tot asta? Pentru a elimina traficul care nu ne este pe plac. Acum, fiecare switch are VLAN-ul său, iar unknown-unicast nu mai este o problemă, deoarece trebuie să circule doar printr-un singur port și nu prin toate... Iar efectele secundare - o configurație standardizată a echipamentului, o eficiență mai mare a distribuției spațiului de adresare.

Cum să configurăm lisg - este un subiect separat. Linkuri către biblioteci sunt atașate. Poate că ceea ce am expus mai sus va ajuta pe cineva în realizarea sarcinilor sale. Versiunea 6 nu a fost implementată încă în rețeaua noastră - dar vor fi niște probleme - avem în plan să refacem lisg pentru versiunea 6 și va trebui să ajustăm programul care adaugă rutele.

Linux ISG
DB2DHCP
Libtins

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster