Vigade vältimisega IPeE võrk käsitsi valmistatud vahenditest

Tere. Oleme võrgu 5k kliendiga. Hiljuti ilmus ebasoodne hetk — meie võrgu keskuses on Brocade RX8 ja see hakkas saatma palju unknown-unicast pakette, kuna võrk on jagatud VLAN-ideks — see ei ole osaliselt probleem, kuid on spetsiaalsed VLAN-id valgete aadresside jaoks jne, ja need on laiali üle kogu võrgu. Kujutage nüüd ette sissetulevat liiklust kliendi aadressile, mida piiraja ei õpi, ja see liiklus suundub mingi (ja seetõttu kõikide) külasse — kanal on ummistunud — kliendid on pahased — kurb...

Ülesanne — muuta viga omaduseks. Mõtlesin q-in-q suunal, kus oleks täielik kliendi-VLAN, kuid igasugused seadmed nagu P3310 lakkavad dot1q sisse lülitamisel DHCP-d edastamast, nad ei oska ka selective qinq ja palju allpoolse tiibadega vahepealseid lahendusi. Mis on ip-unumbered ja kuidas see töötab? Kui väga lühidalt — saadetava värava aadress + marsruut liidese peal. Meie ülesande jaoks on vajalik: kärpida šeiperi, jagada aadresse klientidele, lisada marsruute klientidele läbi teatud liideste. Mida kõik sellega teha? Šeiper — lisg, dhcp — db2dhcp kahel sõltumatul serveril, juurdepääsuserverites töötab dhcprelay, samuti töötab juurdepääsuserverites ucarp — varukoopiaks. Aga kuidas marsruute lisada? Suure skriptiga saab kõik ette ära lisada — aga see ei ole tõene. Seega hakkame ise lahendama.

Põhjalikult internetis uurides leidsin suurepärase kõrgema taseme raamatukogu c++ jaoks, mis võimaldab ilusasti liiklust nuuskida. Programm, mis marsruute lisab, töötab järgmiselt: kuulame liidese peal arp päringuid, kui meil on serveris aadress liidese lo peal, mida küsitakse, siis lisame marsruudi selle liidese kaudu ja lisame selle ip kohta staatilise arp kirje — üldiselt paar copy-paste, natuke oma mõtteid ja asi valmis.

Marsuuri 'marsruutija' lähtekoodid

#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 installimise skript

#!/bin/bash

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

Käsk binaari kompileerimiseks

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

Kuidas seda käivitada?


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

Jah — ta konfigureerib HUP signaaliga tabelid. Miks ei kasutanud netlinki? Lihtsalt laiskus ja Linux on script kõige peal — nii et kõik on normaalne. Noh, marsruudid on marsruutideks, mis edasi? Edasi peame saatma piiril marsruudid, mis on sellel serveril — siin, arvestades samu vanu riistu, läksime lihtsamat teed — panime selle ülesande BGP-le.

bgp konfihostname *******
password *******
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

Jätkame. Selleks, et server vastaks ARP päringutele, tuleb sisse lülitada proxy ARP.


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

Edasi — ucarp. Kirjutame ise selle imelise skripti käivitamiseks skriptid

Näide ühe daemon'i käivitamisest


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

Kuna dhcprelay töötab liideses — tal on vajalik aadress. Seetõttu lisame liidestele, mida kasutame, valeadresse — näiteks 10.255.255.1/32, 10.255.255.2/32 jne. Kuidas seadistada releed, ei hakka rääkima — seal on kõik lihtne.

Nii, mida me omame. Kaitse väravatele, automaatne marsruutide seadistamine, dhcp. See on minimaalne komplekt — erinevalt sellest lisame lisaks lisg'i ning meil on juba olemas ka shaper. Miks kõik on nii pikk ja keeruline? Kas poleks lihtsam võtta accel-pppd ja kasutada üldse pppoe? Ei, see pole lihtsam — inimesed ei suuda isegi patche kasti ruuterisse suruda, rääkimata pppoe'st. accel-ppp on äge asi — aga meie jaoks ei sobinud — palju vigu koodis — kukuvad kokku, lõikavad valesti, ja kõige kurvem on see, et kui see kokku kukub — peavad inimesed kõik uuesti käivitama — telefonid punased — igal juhul ei sobinud. Miks valida ucarp mitte keepalived? Jah, kõiges — on 100 väravat, keepalived ja üks viga konfis — kõik ei tööta. Ucarp-näidikuga ei tööta 1 värav. Mis puudutab turvalisust, et nad kirjutavad endale vale aadresse ja kasutavad neid — selle kontrollimiseks seadistame kõigis switchides / oltdes / andmebaasides dhcp-snooping + source-guard + arp inspection. Kui kliendil pole dhcp-d, vaid staatika — access-list sadamates.

Miks see kõik tehti? Ebaausate liiklusvoogude hävitamiseks. Nüüd on igal lülitil oma VLAN ja unknown-unicast ei ole enam hirmutav, kuna see peab minema ainult ühte porti, mitte kõikidesse… Veelgi enam, kõrvalmõjud — standardiseeritud seadme konfiguratsioon, suurem aadressiruumi jaotamise tõhusus.

Kuidas konfigureerida lisg — see on eraldi teema. Raamatukogude lingid on lisatud. Võib-olla aitab see kellelegi ülesannete lahendamisel. Versioon 6 ei ole meil veel võrgus rakendatud — kuid seal on probleem — plaanis on kirjutada lisg versioon 6 jaoks ümber ning tuleb ka programmi, mis marsruute lisab, veidi kohendada.

Linux ISG
DB2DHCP
Libtins

Allikas: habr.com

Osta usaldusväärne veebimajutus DDoS-kaitsega veebisaitidele, VPS VDS serverid 🔥 Osta usaldusväärne veebimajutus DDoS-kaitsega veebisaitidele, VPS VDS serverid - ProHoster