Tudo com os feriados passados!
Decidimos dedicar nosso primeiro artigo após as férias ao Linux, ou seja, ao nosso maravilhoso curso de
Administrador Linux , que temos no grupo dos cursos mais dinâmicos, ou seja, com os materiais e práticas mais relevantes. Bem, e, consequentemente, oferecemos artigos interessantes e
uma lição aberta .
Postado por Matteo Croce
Título original: Depuração de rede com eBPF (RHEL 8 Beta)1. IntroduçãoO trabalho em rede é uma experiência emocionante, mas nem sempre os problemas são evitados. A solução de problemas pode ser complicada, pois está tentando reproduzir o comportamento errado que acontece “no campo”.
Felizmente, existem ferramentas que podem ajudar com isso: namespaces de rede, máquinas virtuais,
tc
e
netfilter
. Configurações simples de rede podem ser reproduzidas usando namespaces de rede e dispositivos veth, enquanto configurações mais complexas exigem a conexão de máquinas virtuais a uma ponte de software e o uso de ferramentas de rede padrão, como
iptables
ou
tc
, para simular comportamento incorreto. Se houver um problema com as respostas ICMP geradas quando o servidor SSH
iptables -A INPUT -p tcp --dport 22 -j REJECT --reject-with icmp-host-unreachable
, o
iptables -A INPUT -p tcp --dport 22 -j REJECT --reject-with icmp-host-unreachable
no namespace correto pode ajudar a resolver o problema.
Este artigo descreve como solucionar problemas complexos de rede com o
eBPF (BPF estendido) , uma versão avançada do Berkeley Packet Filter. O eBPF é uma tecnologia relativamente nova, o projeto está em um estágio inicial, portanto a documentação e o SDK ainda não estão prontos. Mas esperamos melhorias, especialmente porque o XDP (eXpress Data Path) é fornecido com o
Red Hat Enterprise Linux 8 Beta , que você pode baixar e executar agora.
O eBPF não resolverá todos os problemas, mas ainda é uma ferramenta poderosa de depuração de rede que merece atenção. Estou certo de que desempenhará um papel muito importante no futuro das redes.
O problemaEu estava depurando um problema de rede
Open vSwitch (OVS) que envolvia uma instalação muito complicada: alguns pacotes TCP foram espalhados e entregues na ordem errada, e a largura de banda das máquinas virtuais caiu de 6 Gb / s estáveis para 2-4 Gb / s flutuantes. A análise mostrou que o primeiro pacote TCP de cada conexão com o sinalizador PSH foi enviado na ordem errada: apenas o primeiro e o único por conexão.
Tentei reproduzir essa configuração com duas máquinas virtuais e, depois de muitos artigos de ajuda e consultas de pesquisa, descobri que nem o
iptables
nem o
nftables
podem manipular sinalizadores TCP, enquanto
tc
pode, mas apenas substituindo os sinalizadores e interrompendo novas conexões e TCP em geral
Pode ser possível resolver o problema com uma combinação de
iptables
,
conntrack
e
tc
, mas decidi que esse é um ótimo trabalho para o eBPF.
O que é o eBPF?eBPF é uma versão aprimorada do Berkeley Packet Filter. Ela traz muitas melhorias para o BPF. Em particular, ele permite que você escreva na memória, e não apenas leia, para que os pacotes possam não apenas ser filtrados, mas também editados.
Freqüentemente, o eBPF é simplesmente chamado BPF, e o próprio BPF é chamado cBPF (clássico (clássico) BPF), portanto a palavra "BPF" pode ser usada para significar as duas versões, dependendo do contexto: neste artigo eu sempre falo sobre a versão estendida.
“Under the hood” O eBPF possui uma máquina virtual muito simples que pode executar pequenos fragmentos de código de bytes e editar alguns buffers de memória. Existem limitações no eBPF que o protegem contra uso malicioso:
- Os ciclos são proibidos para que o programa sempre termine em um horário específico;
- Ele só pode acessar a memória através da pilha e do buffer temporário;
- Somente funções permitidas do kernel podem ser chamadas.
Um programa pode ser carregado no kernel de várias maneiras, usando
depuração e rastreamento . No nosso caso, o eBPF está interessado em trabalhar com subsistemas de rede. Existem duas maneiras de usar o programa eBPF:
- Conectado via XDP ao início do caminho RX de uma placa de rede física ou virtual;
- Conectado via
tc
ao qdisc na entrada ou na saída.
Para criar um programa eBPF para conexão, basta escrever o código C e convertê-lo em bytecode. A seguir, é apresentado um exemplo simples usando XDP:
SEC("prog") int xdp_main(struct xdp_md *ctx) { void *data_end = (void *)(uintptr_t)ctx->data_end; void *data = (void *)(uintptr_t)ctx->data; struct ethhdr *eth = data; struct iphdr *iph = (struct iphdr *)(eth + 1); struct icmphdr *icmph = (struct icmphdr *)(iph + 1); if (icmph + 1 > data_end) return XDP_PASS; if (eth->h_proto != ntohs(ETH_P_IP) || iph->protocol != IPPROTO_ICMP || icmph->type != ICMP_ECHOREPLY) return XDP_PASS; if (iph->ttl) { uint16_t *ttlproto = (uint16_t *)&iph->ttl; uint16_t old_ttlproto = *ttlproto; iph->ttl = bpf_get_prandom_u32() % iph->ttl + 1; csum_replace2(&iph->check, old_ttlproto, *ttlproto); } return XDP_PASS; } char _license[] SEC("license") = "GPL";
O snippet acima, sem
include
expressões, auxiliares e código opcional, é um programa XDP que altera o TTL das respostas de eco ICMP recebidas, ou seja, pongs, por um número aleatório. A função principal obtém a estrutura
xdp_md
, que contém dois ponteiros para o início e o fim do pacote.
Para compilar nosso código no bytecode do eBPF, é necessário um compilador com suporte apropriado. O Clang suporta e cria o bytecode do eBPF especificando bpf como o destino no momento da compilação:
$ clang -O2 -target bpf -c xdp_manglepong.c -o xdp_manglepong.o
O comando acima cria um arquivo que, à primeira vista, parece um arquivo de objeto normal, mas após uma inspeção mais detalhada, verifica-se que o tipo especificado de computador é Linux eBPF e não o tipo nativo de sistema operacional:
$ readelf -h xdp_manglepong.o ELF Header: Magic: 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: REL (Relocatable file) Machine: Linux BPF <--- HERE [...]
Após receber o invólucro de um arquivo de objeto comum, o programa eBPF está pronto para fazer o download e conectar-se ao dispositivo via XDP. Isso pode ser feito usando o
ip
do pacote
iproute2
com a seguinte sintaxe:
Este comando especifica a interface wlan0 de destino e, graças à opção -force, substitui qualquer código eBPF existente que já tenha sido carregado. Após carregar o bytecode do eBPF, o sistema se comporta da seguinte maneira:
$ ping -c10 192.168.85.1 PING 192.168.85.1 (192.168.85.1) 56(84) bytes of data. 64 bytes from 192.168.85.1: icmp_seq=1 ttl=41 time=0.929 ms 64 bytes from 192.168.85.1: icmp_seq=2 ttl=7 time=0.954 ms 64 bytes from 192.168.85.1: icmp_seq=3 ttl=17 time=0.944 ms 64 bytes from 192.168.85.1: icmp_seq=4 ttl=64 time=0.948 ms 64 bytes from 192.168.85.1: icmp_seq=5 ttl=9 time=0.803 ms 64 bytes from 192.168.85.1: icmp_seq=6 ttl=22 time=0.780 ms 64 bytes from 192.168.85.1: icmp_seq=7 ttl=32 time=0.847 ms 64 bytes from 192.168.85.1: icmp_seq=8 ttl=50 time=0.750 ms 64 bytes from 192.168.85.1: icmp_seq=9 ttl=24 time=0.744 ms 64 bytes from 192.168.85.1: icmp_seq=10 ttl=42 time=0.791 ms --- 192.168.85.1 ping statistics --- 10 packets transmitted, 10 received, 0% packet loss, time 125ms rtt min/avg/max/mdev = 0.744/0.849/0.954/0.082 ms
Cada pacote passa pelo eBPF, o que, em última análise, faz algumas alterações e decide se deve ser descartado ou ignorado.
Como o eBPF pode ajudarVoltando ao problema de rede original, lembramos que era necessário marcar vários sinalizadores TCP, um por conexão, e nem o
iptables
nem o
tc
poderiam fazer isso. Não é difícil escrever código para esse cenário: configure duas máquinas virtuais conectadas por uma ponte OVS e simplesmente conecte o eBPF a um dos dispositivos virtuais da VM.
Parece uma ótima solução, mas lembre-se de que o XDP suporta apenas o processamento de pacotes recebidos e a conexão do eBPF ao caminho
rx
da máquina virtual receptora não terá efeito no comutador.
Para resolver esse problema, o eBPF deve ser carregado usando
tc
e conectado ao caminho de saída da VM, porque
tc
pode carregar e conectar programas eBPF ao qdisk. Para marcar os pacotes que saem do host, o eBPF deve estar conectado ao qdisk de saída.
Ao carregar o programa eBPF, existem algumas diferenças entre a API
XDP
e a
tc
: por padrão, nomes de seções diferentes, o tipo de estrutura do argumento da função principal e diferentes valores de retorno. Mas isso não é um problema. Abaixo está um trecho de um programa que marca o TCP ao ingressar em uma ação tc:
#define RATIO 10 SEC("action") int bpf_main(struct __sk_buff *skb) { void *data = (void *)(uintptr_t)skb->data; void *data_end = (void *)(uintptr_t)skb->data_end; struct ethhdr *eth = data; struct iphdr *iph = (struct iphdr *)(eth + 1); struct tcphdr *tcphdr = (struct tcphdr *)(iph + 1); if ((void *)(tcphdr + 1) > data_end) return TC_ACT_OK; if (eth->h_proto != __constant_htons(ETH_P_IP) || iph->protocol != IPPROTO_TCP) return TC_ACT_OK; if (tcphdr->syn || tcphdr->fin || tcphdr->rst || tcphdr->psh) return TC_ACT_OK; if (bpf_get_prandom_u32() % RATIO == 0) tcphdr->psh = 1; return TC_ACT_OK; } char _license[] SEC("license") = "GPL";
A compilação no bytecode é feita conforme mostrado no exemplo XDP acima, usando o seguinte:
clang -O2 -target bpf -c tcp_psh.c -o tcp_psh.o
Mas o download é diferente:
Agora o eBPF está carregado no lugar certo e os pacotes que saem da VM estão marcados. Depois de verificar os pacotes recebidos na segunda VM, veremos o seguinte:

tcpdump
confirma que o novo código eBPF está funcionando e aproximadamente 1 em cada 10 pacotes TCP possui o sinalizador PSH definido. Foram necessárias apenas 20 linhas de código C para marcar seletivamente os pacotes TCP que saem da máquina virtual, reproduzir o erro que ocorre "em batalha" e tudo sem recompilar ou mesmo reiniciar! Isso simplificou bastante a verificação da
correção Open vSwitch , que era impossível de obter com outras ferramentas.
ConclusãoO eBPF é uma tecnologia relativamente nova e a comunidade tem uma opinião clara sobre sua implementação. Também é importante notar que os projetos baseados no eBPF, por exemplo,
bpfilter , estão se tornando mais populares e, como resultado, muitos fornecedores de equipamentos estão começando a implementar o suporte ao eBPF diretamente nas placas de rede.
O eBPF não resolverá todos os problemas; portanto, não abuse, mas continua sendo uma ferramenta muito poderosa para depuração de rede e merece atenção. Estou certo de que desempenhará um papel importante no futuro das redes.
O FIMEstamos aguardando seus comentários aqui e também convidamos você a visitar nossa
lição aberta , onde você também pode fazer perguntas.