Erar-tolerema IPeE-reto uzante improvizitajn ilojn

Saluton. Ĉi tio signifas, ke ekzistas reto de 5k klientoj. Lastatempe venis ne tre agrabla momento - en la centro de la reto ni havas Brocade RX8 kaj ĝi komencis sendi multajn nekonatajn-unuelsendajn pakaĵojn, ĉar la reto estas dividita en vlanojn - ĉi tio parte ne estas problemo, SED ekzistas specialaj vlanoj por blankaj adresoj, ktp. kaj ili estas etenditaj en ĉiuj direktoj de la reto. Do nun imagu alvenantan fluon al la adreso de kliento, kiu ne studas kiel landlima studento kaj tiu ĉi fluo flugas al radio-ligo al iu (aŭ ĉio) vilaĝo - la kanalo estas ŝtopiĝinta - la klientoj estas koleraj - malĝojo...

La celo estas igi cimon en funkcion. Mi pensis en la direkto de q-in-q kun plenrajta kliento vlan, sed ĉia aparataro kiel P3310, kiam dot1q estas ebligita, ĉesas trapasi DHCP, ili ankaŭ ne scias kiel elekti qinq kaj multajn kaptilojn de tiaspeca. Kio estas ip-sennombrita kaj kiel ĝi funkcias? Tre mallonge: enirejo adreso + itinero sur la interfaco. Por nia tasko, ni devas: tranĉi la formigilon, distribui adresojn al klientoj, aldoni itinerojn al klientoj per certaj interfacoj. Kiel fari ĉion ĉi? Shaper - lisg, dhcp - db2dhcp sur du sendependaj serviloj, dhcprelay funkcias per la alirserviloj, ucarp ankaŭ funkcias per la alirserviloj - por sekurkopio. Sed kiel aldoni itinerojn? Vi povas aldoni ĉion anticipe per granda skripto - sed tio ne estas vera. Do ni faros memskribitan lambastonon.

Post ĝisfunda serĉo en Interreto, mi trovis mirindan altnivelan bibliotekon por C++, kiu ebligas al vi bele flari trafikon. La algoritmo por la programo, kiu aldonas itinerojn, estas jena - ni aŭskultas arp-petojn sur la interfaco, se ni havas adreson sur la lo-interfaco sur la servilo, kiu estas petita, tiam ni aldonas itineron tra ĉi tiu interfaco kaj aldonas statikan arp. registri al ĉi tiu ip - ĝenerale, kelkajn kopi-pastojn, iom da adjektivo kaj vi estas finita

Fontoj de la "enkursigilo"

#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 instala skripto

#!/bin/bash

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

Komando konstrui la binaron

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

Kiel lanĉi ĝin?


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

Jes - ĝi rekonstruos la tabelojn surbaze de la signalo HUP. Kial vi ne uzis netlink? Estas nur maldiligento kaj Linukso estas skripto sur skripto - do ĉio estas en ordo. Nu, itineroj estas itineroj, kio sekvas? Poste, ni devas sendi la itinerojn, kiuj estas sur ĉi tiu servilo al la limo - ĉi tie, pro la sama malmoderna aparataro, ni prenis la vojon de malplej rezisto - ni asignis ĉi tiun taskon al BGP.

bgp agordogastiga nomo ******
Pasvorto *******
protokoldosiero /var/log/bgp.log
!
# AS-numero, adresoj kaj retoj estas fikciaj
enkursigilo bgp 12345
bgp router-id 1.2.3.4
redistribui konektita
redistribui statikan
najbaro 1.2.3.1 remote-as 12345
najbaro 1.2.3.1 next-hop-self
najbaro 1.2.3.1 itinero-mapo neniu en
najbaro 1.2.3.1 itinero-mapo eksporto
!
alir-listo eksportpermesilo 1.2.3.0/24
!
Itinermapo eksportpermeso 10
kongruas eksporto de ip-adreso
!
itinero-mapo eksporto nei 20

Ni daŭrigu. Por ke la servilo respondu al arp-petoj, vi devas ebligi la arp-prokurilon.


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

Ni pluiru - ukarpo. Ni mem skribas la lanĉajn skriptojn por ĉi tiu miraklo.

Ekzemplo pri rulado de unu demono


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"

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

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

Por ke dhcprelay funkciu sur interfaco, ĝi bezonas adreson. Tial, sur la interfacoj kiujn ni uzas ni aldonos maldekstrajn adresojn - ekzemple 10.255.255.1/32, 10.255.255.2/32, ktp. Mi ne diros al vi kiel agordi la relajson - ĉio estas simpla.

Kion do ni havas? Rezervo de enirejoj, aŭtomata agordo de itineroj, dhcp. Ĉi tiu estas la minimuma aro - lisg ankaŭ ĉirkaŭvolvas ĉion ĉirkaŭ ĝi kaj ni jam havas formilon. Kial ĉio estas tiel longa kaj komplika? Ĉu ne estas pli facile preni accel-pppd kaj uzi pppoe entute? Ne, ĝi ne estas pli simpla - homoj apenaŭ povas enmeti flikŝnuron en enkursigilon, por ne mencii pppoe. accel-ppp estas bonega afero - sed ĝi ne funkciis por ni - estas multaj eraroj en la kodo - ĝi diseriĝas, ĝi tranĉas malrekte, kaj la plej malĝoja estas ke se ĝi heliĝis - tiam homoj devas reŝargi ĉio – la telefonoj estas ruĝaj – ĝi tute ne funkciis. Kio estas la avantaĝo uzi ukarpon prefere ol keepalive? Jes, en ĉio - estas 100 enirejoj, konservitaj kaj unu eraro en la agordo - ĉio ne funkcias. 1 enirejo ne funkcias kun ukarpo. Koncerne sekurecon, ili diras, ke la maldekstraj registros adresojn por si kaj uzos ilin sur la akcio - por kontroli ĉi tiun momenton, ni starigas dhcp-snooping + source-guard + arp-inspektadon ĉe ĉiuj ŝaltiloj/olts/bazoj. Se la kliento ne havas dhpc sed statikan - alir-liston sur la haveno.

Kial ĉio ĉi estis farita? Por detrui nedeziratan trafikon. Nun ĉiu ŝaltilo havas sian propran vlan kaj nekonata-unicast ne plu estas timiga, ĉar ĝi bezonas iri nur al unu haveno kaj ne al ĉiuj... Nu, la kromefikoj estas normigita ekipaĵagordo, pli granda efikeco en asignado de adresspaco.

Kiel agordi lisg estas aparta temo. Ligiloj al bibliotekoj estas alfiksitaj. Eble la supre helpos iun atingi siajn celojn. Versio 6 ankoraŭ ne estas efektivigita en nia reto - sed estos problemo - estas planoj reverki lisg por versio 6, kaj estos necese korekti la programon kiu aldonas itinerojn.

Linukso ISG
DB2DHCP
Libtins

fonto: www.habr.com

Aldoni komenton