En el artículo anterior, utilizamos un mapa eBPF con XDP para intercambiar datos con el espacio de usuario.
En el artículo anterior a ese, escribimos un programa que descartaba todos los paquetes utilizando un programa XDP.
Esta vez, vamos a escribir un programa que devuelva un ICMP Echo Reply en respuesta a un ICMP Echo Request.
Cómo devolver ICMP Echo Reply
Para devolver un ICMP Echo Reply a un ICMP Echo Request, se necesitan los siguientes pasos.
- Recibir el paquete ICMP Echo Request
- Cambiar la bandera a ICMP Echo Reply
- Intercambiar las direcciones IP de origen y destino
- Intercambiar las direcciones MAC de origen y destino en el encabezado Ethernet
- Enviar el paquete
Solo DESCARTAR ICMP Echo Request
Primero, queremos poder procesar ICMP Echo Requests, así que escribimos un programa que solo DESCARTE los ICMP Echo Requests.
- xdp_echo_request_drop.c
#include <linux/bpf.h> #include <bpf/bpf_helpers.h> #include <linux/if_ether.h> #include <linux/ip.h> #include <linux/icmp.h> #include <arpa/inet.h> SEC("xdp") int xdp_drop_icmp(struct xdp_md *ctx) { void *data_end = (void *)(unsigned long)ctx->data_end; void *data = (void *)(unsigned long)ctx->data; // Obtener el puntero al encabezado Ethernet struct ethhdr *eth = data; if ((void *)eth + sizeof(*eth) > data_end) return XDP_PASS; // Confirmar que el marco Ethernet contiene un paquete IP if (eth->h_proto != htons(ETH_P_IP)) return XDP_PASS; // Obtener el puntero al encabezado IP struct iphdr *ip = data + sizeof(*eth); if ((void *)ip + sizeof(*ip) > data_end) return XDP_PASS; // Confirmar que el protocolo es ICMP if (ip->protocol != IPPROTO_ICMP) return XDP_PASS; // Obtener el puntero al encabezado ICMP struct icmphdr *icmp = (void *)ip + sizeof(*ip); if ((void *)icmp + sizeof(*icmp) > data_end) return XDP_PASS; // Pasar paquetes que no sean ICMP Echo Request if (icmp->type != ICMP_ECHO) return XDP_PASS; // DESCARTAR el paquete ICMP Echo Request return XDP_DROP; } char _license[] SEC("license") = "MIT";
Es casi el mismo programa que se creó en Realización Rápida de una Introducción a XDP.
// Pasar paquetes que no sean ICMP Echo Request
if (icmp->type != ICMP_ECHO)
return XDP_PASS;
En esta parte, permitimos que pasen los paquetes que no son ICMP Echo Request.
Convertir ICMP Echo a Reply
A continuación, vamos a escribir un programa para devolver un ICMP Echo Reply en respuesta a un ICMP Echo Request.
- Cambiar la bandera a ICMP Echo Reply
// Cambiar la bandera a ICMP Echo Reply
icmp->type = ICMP_ECHOREPLY;
Cambiamos el tipo de ICMP_ECHO (8) a ICMP_ECHOREPLY (0).
- Intercambiar las direcciones IP de origen y destino
// Intercambiar las direcciones IP de origen y destino
__u32 tmp = ip->saddr;
ip->saddr = ip->daddr;
ip->daddr = tmp;
- Intercambiar las direcciones MAC de origen y destino en el encabezado Ethernet
// Intercambiar las direcciones MAC de origen y destino
unsigned char tmp_mac[ETH_ALEN];
__builtin_memcpy(tmp_mac, eth->h_dest, ETH_ALEN);
__builtin_memcpy(eth->h_dest, eth->h_source, ETH_ALEN);
__builtin_memcpy(eth->h_source, tmp_mac, ETH_ALEN);
- Enviar el paquete con XDP_TX
// Enviar el paquete
return XDP_TX;
Esto completa hasta cierto punto el programa para devolver un ICMP Echo Reply.
SEC("xdp")
int xdp_echo_reply(struct xdp_md *ctx)
{
void *data_end = (void *)(unsigned long)ctx->data_end;
void *data = (void *)(unsigned long)ctx->data;
// Obtener el puntero al encabezado Ethernet
struct ethhdr *eth = data;
if ((void *)eth + sizeof(*eth) > data_end)
return XDP_PASS;
// Confirmar que el marco Ethernet contiene un paquete IP
if (eth->h_proto != htons(ETH_P_IP))
return XDP_PASS;
// Obtener el puntero al encabezado IP
struct iphdr *ip = data + sizeof(*eth);
if ((void *)ip + sizeof(*ip) > data_end)
return XDP_PASS;
// Confirmar que el protocolo es ICMP
if (ip->protocol != IPPROTO_ICMP)
return XDP_PASS;
// Obtener el puntero al encabezado ICMP
struct icmphdr *icmp = (void *)ip + sizeof(*ip);
if ((void *)icmp + sizeof(*icmp) > data_end)
return XDP_PASS;
// Pasar paquetes que no sean ICMP Echo Request
if (icmp->type != ICMP_ECHO)
return XDP_PASS;
// Cambiar la bandera a ICMP Echo Reply
icmp->type = ICMP_ECHOREPLY;
// Intercambiar las direcciones IP de origen y destino
__u32 tmp = ip->saddr;
ip->saddr = ip->daddr;
ip->daddr = tmp;
// Intercambiar las direcciones MAC de origen y destino
unsigned char tmp_mac[ETH_ALEN];
__builtin_memcpy(tmp_mac, eth->h_dest, ETH_ALEN);
__builtin_memcpy(eth->h_dest, eth->h_source, ETH_ALEN);
__builtin_memcpy(eth->h_source, tmp_mac, ETH_ALEN);
// DESCARTAR el paquete ICMP Echo Request
// return XDP_DROP;
return XDP_TX;
}
Cargar el programa en el NIC
Compilamos el programa y lo cargamos en el NIC.
$ clang -O2 -target bpf -c xdp_echo_reply.c -o xdp_echo_reply.o
$ sudo ip link set dev eth0 xdp obj xdp_echo_reply.o
Verificación de funcionamiento
Enviar un ping desde el cliente
$ ping 192.168.XXX.XXX
Ejecutar tcpdump en el servidor para verificar que no se ha recibido un ICMP Request. Dado que XDP está procesando los paquetes directamente, no se puede verificar en tcpdump y se puede entender que el kernel de Linux no está respondiendo con un Echo Reply.
$ sudo tcpdump -i eth0 icmp
Confirmar que cuando se envía un ping desde el cliente, se devuelve un ICMP Echo Reply.
$ sudo tcpdump -i eth0 icmp -vvv XX:XX:XX.160507 IP (tos 0x0, ttl 64, id 53521, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.XXX.1 > 192.168.XXX.2: ICMP echo request, id 58371, seq 684, length 64 XX:XX:XX.161394 IP (tos 0x0, ttl 64, id 53521, offset 0, flags [DF], proto ICMP (1), length 84) 192.168.XXX.2 > 192.168.XXX.1: ICMP echo reply, id 58371, seq 684, length 64 (checksum icmp incorrect 767b (->7e7b)!)
Se ha confirmado que se ha devuelto un ICMP Echo Reply.
Sin embargo, se observa la advertencia checksum icmp incorrect
, lo que indica que la verificación de la suma de verificación en el encabezado ICMP no se ha calculado correctamente.
Espero poder continuar escribiendo sobre cómo calcular la suma de verificación de ICMP y devolverla correctamente, cuando me sienta menos perezoso.
Resumen
- Hemos implementado un programa XDP que devuelve un ICMP Echo Reply a un ICMP Echo Request utilizando XDP.
- En términos de procedimiento, hemos detectado el ICMP Echo Request, generado paquetes de respuesta, intercambiado las direcciones MAC e IP de origen y destino, y enviado directamente la respuesta utilizando XDP_TX sin pasar por el kernel.
- Finalmente, se generó un error de
checksum icmp incorrect
, lo que indicó que es necesario recalcular la suma de verificación de ICMP.
Próximos Pasos (Actualizado: 06/03/2025)
Guía rápida: Introducción a Super XDP Parte 3 (ICMP Echo Reply - Parte 2)