Red IPeE tolerante a fallos utilizando herramientas improvisadas

Hola. Esto significa que hay una red de 5k clientes. Recientemente surgió un momento no muy agradable: en el centro de la red tenemos un Brocade RX8 y comenzó a enviar muchos paquetes de unidifusión desconocidos, ya que la red está dividida en VLAN; esto en parte no es un problema, PERO hay VLAN especiales para direcciones blancas, etc. y se extienden en todas las direcciones de la red. Ahora imaginemos un flujo entrante a la dirección de un cliente que no está estudiando como estudiante fronterizo y este flujo vuela hacia un enlace de radio a algún (o todos) los pueblos - el canal está obstruido - los clientes están enojados - tristeza...

El objetivo es convertir un error en una característica. Estaba pensando en la dirección de q-in-q con una vlan de cliente completa, pero todo tipo de hardware como P3310, cuando dot1q está habilitado, deja de dejar pasar DHCP, tampoco saben cómo seleccionar qinq y muchos trampas de ese tipo. ¿Qué es IP sin nombre y cómo funciona? Muy brevemente: dirección de puerta de enlace + ruta en la interfaz. Para nuestra tarea, necesitamos: cortar el modelador, distribuir direcciones a los clientes, agregar rutas a los clientes a través de ciertas interfaces. ¿Cómo hacer todo esto? Shaper - lisg, dhcp - db2dhcp en dos servidores independientes, dhcprelay se ejecuta en los servidores de acceso, ucarp también se ejecuta en los servidores de acceso - para respaldo. ¿Pero cómo agregar rutas? Puede agregar todo de antemano con un script grande, pero esto no es cierto. Entonces haremos una muleta autoescrita.

Después de una búsqueda exhaustiva en Internet, encontré una maravillosa biblioteca de alto nivel para C++, que le permite rastrear el tráfico de manera hermosa. El algoritmo para el programa que agrega rutas es el siguiente: escuchamos las solicitudes arp en la interfaz, si tenemos una dirección en la interfaz lo en el servidor que se solicita, luego agregamos una ruta a través de esta interfaz y agregamos un arp estático grabe en esta IP; en general, algunas copias y pegados, un pequeño adjetivo y listo

Fuentes del '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;
    }
}

script de instalación de libtins

#!/bin/bash

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

Comando para construir el binario

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

¿Cómo lanzarlo?


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

Sí, reconstruirá las tablas según la señal HUP. ¿Por qué no usaste netlink? Es simplemente pereza y Linux es un script sobre un script, así que todo está bien. Bueno, las rutas son rutas, ¿qué sigue? A continuación, debemos enviar las rutas que están en este servidor a la frontera; aquí, debido al mismo hardware obsoleto, tomamos el camino de menor resistencia; asignamos esta tarea a BGP.

configuración bgpnombre de host *******
contraseña *******
archivo de registro /var/log/bgp.log
!
# El número AS, las direcciones y las redes son ficticios.
enrutador bgp 12345
ID del enrutador BGP 1.2.3.4
redistribuir conectado
redistribuir estática
vecino 1.2.3.1 remoto-como 12345
vecino 1.2.3.1 siguiente salto-yo
vecino 1.2.3.1 mapa de ruta ninguno en
vecino 1.2.3.1 exportación de mapa de ruta
!
permiso de exportación de lista de acceso 1.2.3.0/24
!
permiso de exportación de mapa de ruta 10
coincidir con la exportación de la dirección IP
!
denegación de exportación de mapa de ruta 20

Continuemos. Para que el servidor responda a las solicitudes de arp, debe habilitar el proxy arp.


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

Sigamos adelante: ucarp. Nosotros mismos escribimos los guiones de lanzamiento de este milagro.

Ejemplo de ejecución de un demonio


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"

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

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

Para que dhcprelay funcione en una interfaz, necesita una dirección. Por lo tanto, en las interfaces que usemos agregaremos direcciones izquierdas, por ejemplo 10.255.255.1/32, 10.255.255.2/32, etc. No te diré cómo configurar el relé, todo es simple.

¿Entonces que tenemos? Backup de gateways, autoconfiguración de rutas, dhcp. Este es el conjunto mínimo: lisg también envuelve todo a su alrededor y ya tenemos un moldeador. ¿Por qué es todo tan largo y complicado? ¿No es más fácil tomar accel-pppd y usar pppoe por completo? No, no es más sencillo: la gente difícilmente puede colocar un cable de conexión en un enrutador, sin mencionar el pppoe. accel-ppp es algo genial, pero no funcionó para nosotros, hay muchos errores en el código, se desmorona, se corta torcidamente y lo más triste es que si se ilumina, entonces la gente necesita recargar Todo, los teléfonos están rojos, no funcionó en absoluto. ¿Cuál es la ventaja de utilizar ucarp en lugar de keepalived? Sí, en todo, hay 100 puertas de enlace, keepalived y un error en la configuración, no todo funciona. 1 puerta de enlace no funciona con ucarp. En cuanto a la seguridad, dicen que los de la izquierda registrarán direcciones por sí mismos y las usarán en el recurso compartido; para controlar este momento, configuramos dhcp-snooping + source-guard + inspección arp en todos los conmutadores/olts/bases. Si el cliente no tiene dhpc sino estático, lista de acceso en el puerto.

¿Por qué se hizo todo esto? Para destruir el tráfico no deseado. Ahora cada switch tiene su propia VLAN y la unidifusión desconocida ya no da miedo, ya que solo necesita ir a un puerto y no a todos... Bueno, los efectos secundarios son una configuración de equipo estandarizada y una mayor eficiencia en la asignación de espacio de direcciones.

Cómo configurar lisg es un tema aparte. Se adjuntan enlaces a bibliotecas. Quizás lo anterior ayude a alguien a lograr sus objetivos. La versión 6 aún no se está implementando en nuestra red, pero habrá un problema: hay planes para reescribir lisg para la versión 6 y será necesario corregir el programa que agrega rutas.

ISG de Linux
DB2DHCP
libinos

Fuente: habr.com

Añadir un comentario