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 problema
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 tcao 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.