Olá. Portanto, há uma rede de 5k clientes. Recentemente, surgiu um momento não muito agradável - no centro da rede e temos o Brocade RX8 e ele começou a enviar muitos pacotes unicast desconhecidos, já que a rede é dividida em vlans - isso não é um problema, mas em parte não há problema, mas existem vlans especiais para endereços brancos etc. e eles são estendidos em todas as direções da rede. Então agora imagine um fluxo de entrada para o endereço de um cliente que não estuda como pensionista e esse fluxo voa em direção ao link de rádio para alguma (e assim por diante) vila - o canal está entupido - os clientes são maus - tristes ...
A tarefa é transformar o bug em um recurso. Pensei na direção do q-in-q com uma cliente-vlan completa, mas todos os tipos de ferro como o P3310 quando ligo o dot1q param de passar pelo DHCP, eles ainda não sabem o qinq seletivo e muitas muletas subaquáticas como essa. O que é sem IP e como funciona? Se for muito breve, o endereço do gateway + a rota na interface. Para nossa tarefa, precisamos: cortar shapers, distribuir endereços para clientes, adicionar rotas aos clientes por meio de interfaces específicas. Como fazer tudo isso? Shaper - lisg, dhcp - db2dhcp em dois servidores independentes, o dhcprelay está sendo executado em servidores de acesso, o ucarp também trabalha em servidores de acesso - para backup. Mas como adicionar rotas? Você pode adicionar tudo em um script grande antecipadamente - mas isso não é verdade. Então, cercaremos uma muleta feita por nós mesmos.
Tendo vasculhado completamente a Internet, encontrei uma maravilhosa biblioteca de alto nível para c ++ que permite que você cheire belamente o tráfego. O algoritmo do programa que adiciona rotas é o seguinte - ouvimos as solicitações da interface arp, se tivermos um endereço de servidor na interface lo que solicitamos, adicione a rota por essa interface e adicione uma entrada estática arp a este IP - em geral, alguns copiar-colar, um pouco de mordaça e pronto
Fontes do 'microônibus'#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 instalação Libtins O comando para construir o binário g++ main.cpp -o arp-rt -O3 -std=c++11 -lpthread -ltins
Como executá-lo? start-stop-daemon --start --exec /opt/ipoe/arp-routes/arp-rt -b -m -p /opt/ipoe/arp-routes/daemons/eth0.800.pid -- eth0.800
Sim - substitui as tabelas no sinal HUP. Por que não usou o netlink? Preguiça é apenas sim, e Linux é um script em um script - para que tudo esteja bem. Bem rotas rotas, o que vem a seguir? Em seguida, precisamos enviar as rotas que estão neste servidor para a fronteira - aqui, devido ao mesmo pedaço desatualizado de ferro, seguimos o caminho com menos resistência - colocamos essa tarefa no BGP.
Configuração Bgphostname *******
senha *******
arquivo de log /var/log/bgp.log
!
# número de endereço, endereço e rede inventados
roteador bgp 12345
ID do roteador bgp 1.2.3.4
redistribuir conectado
redistribuir estático
vizinho 1.2.3.1 remoto como 12345
vizinho 1.2.3.1 self-hop-self
mapa de rotas vizinho 1.2.3.1 nenhum em
exportação de mapa de rotas vizinho 1.2.3.1
!
licença de exportação da lista de acesso 1.2.3.0/24
!
licença de exportação de mapas de rotas 10
corresponder à exportação de endereço IP
!
exportação de mapa de rota negar 20
Nós continuamos. Para que o servidor responda às solicitações arp, é necessário ativar o proxy arp.
echo 1 > /proc/sys/net/ipv4/conf/eth0.800/proxy_arp
Vá em frente - ucarp. Scripts para lançar este milagre, escrevemos a nós mesmos
Exemplo de como iniciar um único 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"
Para o dhcprelay funcionar em uma interface, ele precisa de um endereço. Portanto, nas interfaces que usamos, adicionaremos endereços à esquerda - por exemplo, 10.255.255.1/32, 10.255.255.2/32, etc. Não vou lhe dizer como configurar relés - tudo é simples lá.
Então, o que temos. Gateways de backup, rotas de autoajuste, dhcp. Este é o conjunto mínimo - até o lisg está ferrado e já temos um shaper. Por que tudo é tão longo e legal? Não é mais fácil usar o accel-pppd e geralmente usar o pppoe? Não, não é mais fácil - as pessoas dificilmente podem enfiar um patchcord em um roteador, para não mencionar pppoe. O accel-ppp é uma coisa legal - mas não funcionou para nós - um monte de erros no código - ele entra, corta torto e o triste é que, se estiver iluminado, as pessoas precisam reiniciar tudo - os telefones são vermelhos - em geral, não se encaixavam. Qual é a vantagem de usar ucarp em vez de keepalived? Sim, em tudo - existem 100 gateways, keepalived e um erro na configuração - tudo não funciona. 1 gateway não funciona com o ucarp. Em relação à segurança, eles dizem que os endereços serão canhotos e serão usados na bola - para controlar esse momento, configuramos a inspeção dhcp-snooping + source-guard + arp em todos os switches / alt / bases. Se o cliente não tiver dhpc, mas estática - acces-list na porta.
Por que tudo isso foi feito? Para destruir o tráfego que não gostamos. Agora, cada switch possui sua própria vlan e o unicast desconhecido não tem mais medo, pois precisa acessar apenas uma porta e nem todas ... Bem, os efeitos colaterais são uma configuração de hardware padronizada e uma alta eficiência de alocação de espaço de endereço.
Como configurar o lisg é um tópico separado. Links para bibliotecas estão anexados. Talvez alguém ajude o exposto na implementação de suas tarefas. Ainda não estamos introduzindo a versão 6 em nossa rede - mas haverá um problema - há planos de reescrever o lisg para a versão 6 e, bem, será necessário corrigir o programa, que adiciona rotas.
Linux ISGDB2DHCPLibtinsUPD
Tudo acabou sendo um pouco mais complicado ... Eu tive que escrever um demônio mais ou menos normal. E fazer sem proxy arp. Agora, meu daemon responde ao arp, também adiciona / remove rotas nas sub-redes para os clientes, e também tive que aprender a trabalhar com o netlink. E resultou em um recurso - quando o Linux reconhece o arp em algum endereço e depois de encontrar a interface - ele pega o primeiro endereço que ele encontra do último - ao qual alguns clientes não respondem - é resolvido usando arptables.
Em geral, já existe uma opção normal para o link - ali, a propósito, são ouvidas alterações nas rotas e endereços e adicionando uma remoção de rota via netlink (com o último, a dor de cabeça era terrível)
github