Fault-tolerant IPeE-netwurk mei ymprovisearre ark

Hallo. Dit betsjut dat d'r in netwurk is fan 5k kliïnten. Koartlyn kaam in net heul noflik momint op - yn it sintrum fan it netwurk hawwe wy in Brocade RX8 en it begon in protte ûnbekende-unicast-pakketten te ferstjoeren, om't it netwurk is ferdield yn vlans - dit is foar in part gjin probleem, MAAR d'r binne spesjale vlans foar wite adressen, etc. en se wurde spand yn alle rjochtingen fan it netwurk. Stel jo no dus in ynkommende stream foar nei it adres fan in kliïnt dy't net studearret as grinsstudint en dizze stream fljocht nei in radiolink nei ien of oar (of allegear) doarp - it kanaal is ferstoppe - de kliïnten binne lilk - fertriet...

It doel is om in bug te feroarjen yn in funksje. Ik tocht yn 'e rjochting fan q-in-q mei in folweardige client vlan, mar allerhanne hardware lykas P3310, doe't dot1q is ynskeakele, stopt it litten fan DHCP troch, se witte ek net hoe selektyf qinq en in protte pitfalls fan dat soarte. Wat is ip-ûnnamme en hoe wurket it? Hiel koart: gateway adres + rûte op de ynterface. Foar ús taak moatte wy: de shaper snije, adressen oan kliïnten ferspriede, rûtes tafoegje oan kliïnten fia bepaalde ynterfaces. Hoe dit alles te dwaan? Shaper - lisg, dhcp - db2dhcp op twa ûnôfhinklike tsjinners, dhcprelay rint op de tagong tsjinners, ucarp ek rint op de tagong tsjinners - foar reservekopy. Mar hoe kinne jo rûtes tafoegje? Jo kinne alles foarôf tafoegje mei in grut skript - mar dit is net wier. Sa sille wy in selsskreaune kruk meitsje.

Nei in yngeande syktocht op it ynternet fûn ik in prachtige bibleteek op hege nivo foar C++, wêrmei jo ferkeard ferkear kinne snuffelje. It algoritme foar it programma dat rûtes tafoegje is as folget - wy harkje nei arp-oanfragen op 'e ynterface, as wy in adres hawwe op' e lo-ynterface op 'e tsjinner dy't frege wurdt, dan foegje wy in rûte ta troch dizze ynterface en foegje in statyske arp ta record nei dizze ip - yn 't algemien, in pear copy-pastes, in bytsje eigenskipswurd en jo binne klear

Boarnen fan 'e 'router'

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

#!/bin/bash

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

Kommando om de binêre te bouwen

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

Hoe kinne jo it lansearje?


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 - it sil de tabellen opnij opbouwe op basis fan it HUP-sinjaal. Wêrom hawwe jo netlink net brûkt? It is gewoan luiheid en Linux is in skript op in skript - dus alles is goed. No, rûtes binne rûtes, wat is de folgjende? Dêrnei moatte wy de rûtes dy't op dizze tsjinner binne nei de grins stjoere - hjir, fanwege deselde ferâldere hardware, namen wy it paad fan minste wjerstân - wy hawwe dizze taak oan BGP tawiisd.

bgp konfiguraasjehostnamme *******
wachtwurd *******
log triem /var/log/bgp.log
!
# AS-nûmer, adressen en netwurken binne fiktyf
router bgp 12345
bgp router-id 1.2.3.4
redistribute ferbûn
redistribute statyske
buorman 1.2.3.1 remote-as 12345
buorman 1.2.3.1 folgjende-hop-sels
buorman 1.2.3.1 rûte-map gjin yn
buorman 1.2.3.1 rûte-map eksportearje út
!
tagong-list eksportfergunning 1.2.3.0/24
!
rûte-kaart eksportfergunning 10
oerien mei ip-adres eksportearje
!
rûte-kaart eksportearje wegerje 20

Lit ús trochgean. Om de tsjinner te reagearjen op arp-oanfragen, moatte jo de arp-proxy ynskeakelje.


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

Litte wy fierder gean - ucarp. Wy skriuwe sels de lansearringsskripts foar dit wûnder.

Foarbyld fan run one 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

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

Foar dhcprelay om oan in ynterface te wurkjen, hat it in adres nedich. Dêrom sille wy op 'e ynterfaces dy't wy brûke sille linkse adressen tafoegje - bygelyks 10.255.255.1/32, 10.255.255.2/32, ensfh. Ik sil jo net fertelle hoe't jo it estafette konfigurearje - alles is ienfâldich.

Dus wat hawwe wy? Reservekopy fan poarten, auto-konfiguraasje fan rûtes, dhcp. Dit is de minimale set - lisg slacht ek alles om en wy hawwe al in shaper. Wêrom is alles sa lang en betiizjend? Is it net makliker om accel-pppd te nimmen en pppoe hielendal te brûken? Nee, it is net ienfâldiger - minsken kinne amper in patchcord yn in router passe, om net te sprekken fan pppoe. accel-ppp is in cool ding - mar it wurke net foar ús - d'r binne in protte flaters yn 'e koade - it brokkelt, it snijt krom, en it treurichste is dat as it opheldere - dan moatte minsken opnij laden alles - de tillefoans binne read - it wurke hielendal net. Wat is it foardiel fan it brûken fan ucarp ynstee fan keepalived? Ja, yn alles - d'r binne 100 gateways, keepalived en ien flater yn 'e konfiguraasje - alles wurket net. 1 gateway wurket net mei ucarp. Oangeande feiligens sizze se dat de linker adressen foar harsels registrearje en se brûke op it oandiel - om dit momint te kontrolearjen, sette wy dhcp-snooping + source-guard + arp-ynspeksje op alle skeakels / olten / bases. As de kliïnt gjin dhpc hat, mar statysk - tagongslist op 'e haven.

Wêrom is dit alles dien? Om net winske ferkear te ferneatigjen. No hat elke skeakel in eigen vlan en ûnbekend-unicast is net langer skriklik, om't it allinich nei ien haven moat gean en net nei allegear ... No, de side-effekten binne in standerdisearre apparatuerkonfiguraasje, gruttere effisjinsje by it allocearjen fan adresromte.

Hoe't jo lisg konfigurearje is in apart ûnderwerp. Links nei biblioteken binne taheakke. Miskien sil it boppesteande immen helpe by it berikken fan har doelen. Ferzje 6 wurdt noch net ymplementearre op ús netwurk - mar d'r sil in probleem wêze - d'r binne plannen om lisg foar ferzje 6 te herskriuwen, en it sil nedich wêze om it programma te korrigearjen dat rûtes tafoeget.

Linux ISG
DB2DHCP
Libtins

Boarne: www.habr.com

Add a comment