{"schemaVersion":1,"id":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing","url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing","title":"eBPF y XDP: Procesamiento de Paquetes de Alto Rendimiento y Mitigación de DDoS a Line Rate","description":"Procesamiento de paquetes de alto rendimiento con eBPF y XDP: programas XDP en C, sockets AF_XDP, mitigación de DDoS a line rate y análisis del dataplane de Cilium.","language":"es","datePublished":"2026-02-17","dateModified":null,"contentFormat":"text/markdown","content":"Networking\n\n# eBPF y XDP: Procesamiento de Paquetes de Alto Rendimiento y Mitigación de DDoS a Line Rate\n\n2026-02-177 min lectura\n\n[Lucio Durán](<https://lucioduran.com/es>)\nEngineering Manager & AI Solutions Architect\n\nTambién disponible en: [English](<https://lucioduran.com/blog/bpf-xdp-high-performance-packet-processing>)\n\n## Por Qué XDP Es Diferente de Todo lo Demás\n\nEl stack de red de Linux procesa paquetes a través de una cadena larga: hardware NIC → driver NIC → asignación de sk\\_buff → netfilter/iptables → buffer de recepción del socket → aplicación. Cada paso copia datos, asigna memoria y quema CPU.\n\nXDP se hookea en el driver de NIC, antes de la asignación de sk\\_buff. Cuando llega un paquete, el driver llama a tu programa XDP con un puntero crudo a los datos del paquete. Tu programa retorna una de cinco acciones:\n\n```c\nXDP_PASS // Continuar procesamiento normal (mandar al stack del kernel)\nXDP_DROP // Dropear el paquete inmediatamente (sin asignación, sin copia)\nXDP_TX // Rebotar el paquete por la misma NIC\nXDP_REDIRECT // Mandar a otra NIC, CPU, o socket AF_XDP\nXDP_ABORTED // Ocurrió un error, dropear y logear\n```\n\n## Escribiendo Tu Primer Programa XDP\n\nUn programa XDP mínimo que dropea todo el tráfico UDP en puerto 53 (ataques de amplificación DNS):\n\n```c\n#include <linux/bpf.h>\n#include <linux/if_ether.h>\n#include <linux/ip.h>\n#include <linux/udp.h>\n#include <bpf/bpf_helpers.h>\n\nSEC(\"xdp\")\nint xdp_dns_filter(struct xdp_md *ctx)\n{\n void *data = (void *)(long)ctx->data;\n void *data_end = (void *)(long)ctx->data_end;\n\n struct ethhdr *eth = data;\n if ((void *)(eth + 1) > data_end)\n return XDP_PASS;\n\n if (eth->h_proto != __constant_htons(ETH_P_IP))\n return XDP_PASS;\n\n struct iphdr *ip = (void *)(eth + 1);\n if ((void *)(ip + 1) > data_end)\n return XDP_PASS;\n\n if (ip->protocol != IPPROTO_UDP)\n return XDP_PASS;\n\n struct udphdr *udp = (void *)ip + (ip->ihl * 4);\n if ((void *)(udp + 1) > data_end)\n return XDP_PASS;\n\n if (udp->source == __constant_htons(53))\n return XDP_DROP;\n\n return XDP_PASS;\n}\n\nchar _license[] SEC(\"license\") = \"GPL\";\n```\n\nCada chequeo de bounds es obligatorio. El verificador BPF va a rechazar tu programa si accedés a ` ip->protocol ` sin probar primero que ` (void *)(ip + 1) <= data_end `. Esto es molesto la primera vez y reconfortante todas las veces después, el verificador es la razón por la que los programas BPF no pueden crashear el kernel.\n\n```bash\n# Compilar a bytecode BPF\nclang -O2 -target bpf -c xdp_filter.c -o xdp_filter.o\n\n# Attachear a la interfaz (driver mode para performance)\nip link set dev eth0 xdpdrv obj xdp_filter.o sec xdp\n\n# Verificar que está cargado\nip link show eth0\n\n# Desattachear\nip link set dev eth0 xdp off\n```\n\n## Mitigación de DDoS Real: Rate Limiting con BPF Maps\n\nEl filtro DNS de arriba es muy crudo. La mitigación real necesita rate limiting:\n\n```c\nstruct rate_limit_entry {\n __u64 packet_count;\n __u64 last_reset_ns;\n};\n\nstruct {\n __uint(type, BPF_MAP_TYPE_LRU_HASH);\n __uint(max_entries, 1000000);\n __type(key, __u32);\n __type(value, struct rate_limit_entry);\n} rate_limits SEC(\".maps\");\n\nstruct {\n __uint(type, BPF_MAP_TYPE_ARRAY);\n __uint(max_entries, 1);\n __type(key, __u32);\n __type(value, __u64);\n} config SEC(\".maps\");\n\nSEC(\"xdp\")\nint xdp_rate_limiter(struct xdp_md *ctx)\n{\n void *data = (void *)(long)ctx->data;\n void *data_end = (void *)(long)ctx->data_end;\n\n struct ethhdr *eth = data;\n if ((void *)(eth + 1) > data_end)\n return XDP_PASS;\n\n if (eth->h_proto != __constant_htons(ETH_P_IP))\n return XDP_PASS;\n\n struct iphdr *ip = (void *)(eth + 1);\n if ((void *)(ip + 1) > data_end)\n return XDP_PASS;\n\n __u32 src_ip = ip->saddr;\n\n __u32 config_key = 0;\n __u64 *max_pps = bpf_map_lookup_elem(&config, &config_key);\n __u64 threshold = max_pps ? *max_pps : 10000;\n\n struct rate_limit_entry *entry = bpf_map_lookup_elem(&rate_limits, &src_ip);\n __u64 now = bpf_ktime_get_ns();\n\n if (entry) {\n if (now - entry->last_reset_ns > 1000000000ULL) {\n entry->packet_count = 1;\n entry->last_reset_ns = now;\n return XDP_PASS;\n }\n\n entry->packet_count++;\n if (entry->packet_count > threshold)\n return XDP_DROP;\n } else {\n struct rate_limit_entry new_entry = {\n .packet_count = 1,\n .last_reset_ns = now,\n };\n bpf_map_update_elem(&rate_limits, &src_ip, &new_entry, BPF_ANY);\n }\n\n return XDP_PASS;\n}\n```\n\nEl ` BPF_MAP_TYPE_LRU_HASH ` es clave aquí. Durante un DDoS, se va a ver millones de IPs de origen únicas (spoofed). Un hash map regular se llena y rechaza entradas nuevas, dejando pasar nuevas IPs de ataque sin trackear. El LRU map evicta las entradas más viejas, que son típicamente IPs de ataque que ya fueron rate-limited.\n\n## AF\\_XDP: Cuando Necesitás Procesamiento en Userspace\n\nA veces se necesita hacer más que dropear o pasar. AF\\_XDP te permite redirigir paquetes a una aplicación userspace a través de un ring buffer zero-copy, bypasseando todo el stack de red del kernel:\n\n```c\nstruct {\n __uint(type, BPF_MAP_TYPE_XSKMAP);\n __uint(max_entries, 64);\n __type(key, __u32);\n __type(value, __u32);\n} xsks_map SEC(\".maps\");\n\nSEC(\"xdp\")\nint xdp_af_xdp_redirect(struct xdp_md *ctx)\n{\n void *data = (void *)(long)ctx->data;\n void *data_end = (void *)(long)ctx->data_end;\n\n struct ethhdr *eth = data;\n if ((void *)(eth + 1) > data_end)\n return XDP_PASS;\n\n if (eth->h_proto != __constant_htons(ETH_P_IP))\n return XDP_PASS;\n\n struct iphdr *ip = (void *)(eth + 1);\n if ((void *)(ip + 1) > data_end)\n return XDP_PASS;\n\n if (ip->protocol == IPPROTO_TCP) {\n struct tcphdr *tcp = (void *)ip + (ip->ihl * 4);\n if ((void *)(tcp + 1) > data_end)\n return XDP_PASS;\n\n if (tcp->dest == __constant_htons(8080)) {\n return bpf_redirect_map(&xsks_map, ctx->rx_queue_index, XDP_PASS);\n }\n }\n\n return XDP_PASS;\n}\n```\n\nAF\\_XDP te da 8-12 millones de paquetes por segundo por core en userspace. Eso es como 10x más rápido que ` recvmsg() ` en un raw socket, porque no hay overhead de syscall, no hay asignación de sk\\_buff, y no hay copia de datos.\n\n## Dentro del Dataplane de Cilium\n\nCilium es el uso más sofisticado de BPF para networking en producción. Reemplaza kube-proxy completamente e implementa networking de Kubernetes en BPF.\n\nCuando un paquete sale del Pod A con destino a un servicio Kubernetes, el programa BPF de Cilium:\n\n1. Busca el destino en ` SERVICE_MAP ` para encontrar un backend pod\n2. Hace DNAT (reescribe la IP destino a la IP del backend pod)\n3. Crea una entrada de connection tracking en ` CT_MAP `\n4. Chequea ` POLICY_MAP ` para verificar que la network policy permite la conexión\n5. Forwardea el paquete directamente a la interfaz veth del backend pod\n\nTodo esto pasa en el kernel, sin context switches a userspace. Los benchmarks de Cilium muestran 2-3x mejor latencia y throughput comparado con kube-proxy basado en iptables.\n\n## Números de Producción\n\nDespués de deployar el rate limiter XDP en nuestros servidores edge (droplets [DigitalOcean](<https://digitalocean.com/>) Premium CPU con NICs Mellanox):\n\n| Métrica | Antes (iptables) | Después (XDP) |\n| --- | --- | --- |\n| Max absorción DDoS | ~5 Gbps | 40+ Gbps |\n| CPU durante flood de 10 Gbps | 95% | 8% |\n| Latencia tráfico legítimo durante ataque | 200-500ms | \\<5ms |\n| Tiempo de update de reglas | ~50ms (iptables flush) | \\<1ms (map update) |\n| Memoria por IP trackeada | ~512 bytes (conntrack) | 16 bytes (BPF map) |\n\nLa diferencia de memoria importa. Durante un DDoS volumétrico con millones de IPs de origen spoofed, el conntrack de Linux consumía gigabytes de RAM y eventualmente OOM-killeaba procesos. El BPF LRU map usa 16 MB fijos sin importar el tamaño del ataque.\n\n## Cuándo NO Usar XDP\n\nXDP es una herramienta quirúrgica, no una solución general:\n\n- **Filtrado a nivel aplicación** (reglas WAF, inspección HTTP): Utilizar un reverse proxy. XDP opera en paquetes crudos antes del reensamblado.\n- **Inspección profunda de paquetes stateful**: El límite de instrucciones BPF y la falta de asignación dinámica de memoria hacen impracticable el análisis stateful complejo.\n- **Firewalling simple**: Si iptables maneja tu carga, utilizar iptables. XDP agrega complejidad.\n\nXDP brilla para: mitigación de DDoS, load balancing, packet steering, telemetría/sampling, y cualquier cosa donde se necesita tomar decisiones por-paquete a millones de paquetes por segundo.\n\nLa curva de aprendizaje es empinada, se necesita entender C, el stack de red de Linux, las restricciones del verificador BPF y la arquitectura de drivers de NIC. Pero una vez que escribiste tu primer programa XDP que dropea 40 Gbps de tráfico de ataque usando el 3% de un core de CPU, nunca se va a mirar iptables de la misma forma.\n\n## Preguntas frecuentes\n\n### ¿Qué tan rápido puede XDP realmente procesar paquetes?\n\nEn un solo core con una NIC moderna (Intel E810, Mellanox ConnectX-6), XDP puede procesar 24-26 millones de paquetes por segundo en modo XDP\\_DROP. Son ~25 Gbps de paquetes de tamaño mínimo en un core. Con AF\\_XDP para procesamiento en userspace, esperá 8-12 Mpps por core.\n\n### ¿Necesito escribir C para usar XDP?\n\nEl programa XDP se escribe en C (compilado a bytecode BPF via clang), pero es posible usar frameworks de más alto nivel para el lado userspace. Cilium usa Go, Katran usa C++, y hay wrappers en Rust como Aya que te permiten escribir tanto el programa BPF como el loader en Rust.\n\n### ¿Qué pasa si mi programa XDP tiene un bug?\n\nEl verificador BPF atrapa la mayoría de los bugs al momento de carga, analiza estáticamente todos los code paths, chequea bounds en todos los accese es a memoria, asegura que no haya loops infinitos. Si un bug pasa el verificador (raro), lo peor que puede pasar es dropear o pasar paquetes incorrectamente, el kernel no puede crashear por un programa BPF.\n\n## Tips\n\n1. **LuchoTip:** Siempre utilizar ` __attribute__((always_inline)) ` para funciones helper en programas XDP. El verificador BPF cuenta instrucciones después del inlining, y una llamada a función no-inline agrega overhead que puede empujarte más allá del límite de 1 millón de instrucciones en kernels viejos.\n2. **LuchoTip:** Probar tus programas XDP con ` XDP_FLAGS_SKB_MODE ` primero, después pasá a ` XDP_FLAGS_DRV_MODE ` para producción. SKB mode corre en el stack de red del kernel (más lento pero siempre anda), driver mode corre en el driver de la NIC (rápido pero necesita soporte del driver).\n3. **LuchoTip:** Utilizar BPF maps de tipo ` BPF_MAP_TYPE_LRU_HASH ` en vez de ` BPF_MAP_TYPE_HASH ` para connection tracking. Los hash maps regulares fallan silenciosamente cuando están llenos. Los LRU maps evictan la entrada menos recientemente usada, que para un filtro de DDoS es la IP atacante que estuvo más tiempo callada. Exactamente lo que querés.\n\n- ebpf\n- xdp\n- networking\n- ddos\n- cilium\n- linux-kernel\n- procesamiento-paquetes\n\n[Volver al Blog](<https://lucioduran.com/es/blog>)","headings":[{"level":1,"text":"eBPF y XDP: Procesamiento de Paquetes de Alto Rendimiento y Mitigación de DDoS a Line Rate","url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#title"},{"level":2,"text":"Por Qué XDP Es Diferente de Todo lo Demás","url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#por-qu-xdp-es-diferente-de-todo-lo-dems"},{"level":2,"text":"Escribiendo Tu Primer Programa XDP","url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#escribiendo-tu-primer-programa-xdp"},{"level":2,"text":"Mitigación de DDoS Real: Rate Limiting con BPF Maps","url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#mitigacin-de-ddos-real-rate-limiting-con-bpf-maps"},{"level":2,"text":"AF_XDP: Cuando Necesitás Procesamiento en Userspace","url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#afxdp-cuando-necesits-procesamiento-en-userspace"},{"level":2,"text":"Dentro del Dataplane de Cilium","url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#dentro-del-dataplane-de-cilium"},{"level":2,"text":"Números de Producción","url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#nmeros-de-produccin"},{"level":2,"text":"Cuándo NO Usar XDP","url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#cundo-no-usar-xdp"},{"level":2,"text":"Preguntas frecuentes","url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#questions"},{"level":3,"text":"¿Qué tan rápido puede XDP realmente procesar paquetes?","url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#question-1"},{"level":3,"text":"¿Necesito escribir C para usar XDP?","url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#question-2"},{"level":3,"text":"¿Qué pasa si mi programa XDP tiene un bug?","url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#question-3"},{"level":2,"text":"Tips","url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#article-tips"}],"sections":[{"heading":"eBPF y XDP: Procesamiento de Paquetes de Alto Rendimiento y Mitigación de DDoS a Line Rate","level":1,"url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing","content":"Networking"},{"heading":"eBPF y XDP: Procesamiento de Paquetes de Alto Rendimiento y Mitigación de DDoS a Line Rate","level":1,"url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#title","content":"2026-02-177 min lectura\n\n[Lucio Durán](<https://lucioduran.com/es>)\nEngineering Manager & AI Solutions Architect\n\nTambién disponible en: [English](<https://lucioduran.com/blog/bpf-xdp-high-performance-packet-processing>)"},{"heading":"Por Qué XDP Es Diferente de Todo lo Demás","level":2,"url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#por-qu-xdp-es-diferente-de-todo-lo-dems","content":"El stack de red de Linux procesa paquetes a través de una cadena larga: hardware NIC → driver NIC → asignación de sk\\_buff → netfilter/iptables → buffer de recepción del socket → aplicación. Cada paso copia datos, asigna memoria y quema CPU.\n\nXDP se hookea en el driver de NIC, antes de la asignación de sk\\_buff. Cuando llega un paquete, el driver llama a tu programa XDP con un puntero crudo a los datos del paquete. Tu programa retorna una de cinco acciones:\n\n```c\nXDP_PASS // Continuar procesamiento normal (mandar al stack del kernel)\nXDP_DROP // Dropear el paquete inmediatamente (sin asignación, sin copia)\nXDP_TX // Rebotar el paquete por la misma NIC\nXDP_REDIRECT // Mandar a otra NIC, CPU, o socket AF_XDP\nXDP_ABORTED // Ocurrió un error, dropear y logear\n```"},{"heading":"Escribiendo Tu Primer Programa XDP","level":2,"url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#escribiendo-tu-primer-programa-xdp","content":"Un programa XDP mínimo que dropea todo el tráfico UDP en puerto 53 (ataques de amplificación DNS):\n\n```c\n#include <linux/bpf.h>\n#include <linux/if_ether.h>\n#include <linux/ip.h>\n#include <linux/udp.h>\n#include <bpf/bpf_helpers.h>\n\nSEC(\"xdp\")\nint xdp_dns_filter(struct xdp_md *ctx)\n{\n void *data = (void *)(long)ctx->data;\n void *data_end = (void *)(long)ctx->data_end;\n\n struct ethhdr *eth = data;\n if ((void *)(eth + 1) > data_end)\n return XDP_PASS;\n\n if (eth->h_proto != __constant_htons(ETH_P_IP))\n return XDP_PASS;\n\n struct iphdr *ip = (void *)(eth + 1);\n if ((void *)(ip + 1) > data_end)\n return XDP_PASS;\n\n if (ip->protocol != IPPROTO_UDP)\n return XDP_PASS;\n\n struct udphdr *udp = (void *)ip + (ip->ihl * 4);\n if ((void *)(udp + 1) > data_end)\n return XDP_PASS;\n\n if (udp->source == __constant_htons(53))\n return XDP_DROP;\n\n return XDP_PASS;\n}\n\nchar _license[] SEC(\"license\") = \"GPL\";\n```\n\nCada chequeo de bounds es obligatorio. El verificador BPF va a rechazar tu programa si accedés a ` ip->protocol ` sin probar primero que ` (void *)(ip + 1) <= data_end `. Esto es molesto la primera vez y reconfortante todas las veces después, el verificador es la razón por la que los programas BPF no pueden crashear el kernel.\n\n```bash\n# Compilar a bytecode BPF\nclang -O2 -target bpf -c xdp_filter.c -o xdp_filter.o\n\n# Attachear a la interfaz (driver mode para performance)\nip link set dev eth0 xdpdrv obj xdp_filter.o sec xdp\n\n# Verificar que está cargado\nip link show eth0\n\n# Desattachear\nip link set dev eth0 xdp off\n```"},{"heading":"Mitigación de DDoS Real: Rate Limiting con BPF Maps","level":2,"url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#mitigacin-de-ddos-real-rate-limiting-con-bpf-maps","content":"El filtro DNS de arriba es muy crudo. La mitigación real necesita rate limiting:\n\n```c\nstruct rate_limit_entry {\n __u64 packet_count;\n __u64 last_reset_ns;\n};\n\nstruct {\n __uint(type, BPF_MAP_TYPE_LRU_HASH);\n __uint(max_entries, 1000000);\n __type(key, __u32);\n __type(value, struct rate_limit_entry);\n} rate_limits SEC(\".maps\");\n\nstruct {\n __uint(type, BPF_MAP_TYPE_ARRAY);\n __uint(max_entries, 1);\n __type(key, __u32);\n __type(value, __u64);\n} config SEC(\".maps\");\n\nSEC(\"xdp\")\nint xdp_rate_limiter(struct xdp_md *ctx)\n{\n void *data = (void *)(long)ctx->data;\n void *data_end = (void *)(long)ctx->data_end;\n\n struct ethhdr *eth = data;\n if ((void *)(eth + 1) > data_end)\n return XDP_PASS;\n\n if (eth->h_proto != __constant_htons(ETH_P_IP))\n return XDP_PASS;\n\n struct iphdr *ip = (void *)(eth + 1);\n if ((void *)(ip + 1) > data_end)\n return XDP_PASS;\n\n __u32 src_ip = ip->saddr;\n\n __u32 config_key = 0;\n __u64 *max_pps = bpf_map_lookup_elem(&config, &config_key);\n __u64 threshold = max_pps ? *max_pps : 10000;\n\n struct rate_limit_entry *entry = bpf_map_lookup_elem(&rate_limits, &src_ip);\n __u64 now = bpf_ktime_get_ns();\n\n if (entry) {\n if (now - entry->last_reset_ns > 1000000000ULL) {\n entry->packet_count = 1;\n entry->last_reset_ns = now;\n return XDP_PASS;\n }\n\n entry->packet_count++;\n if (entry->packet_count > threshold)\n return XDP_DROP;\n } else {\n struct rate_limit_entry new_entry = {\n .packet_count = 1,\n .last_reset_ns = now,\n };\n bpf_map_update_elem(&rate_limits, &src_ip, &new_entry, BPF_ANY);\n }\n\n return XDP_PASS;\n}\n```\n\nEl ` BPF_MAP_TYPE_LRU_HASH ` es clave aquí. Durante un DDoS, se va a ver millones de IPs de origen únicas (spoofed). Un hash map regular se llena y rechaza entradas nuevas, dejando pasar nuevas IPs de ataque sin trackear. El LRU map evicta las entradas más viejas, que son típicamente IPs de ataque que ya fueron rate-limited."},{"heading":"AF_XDP: Cuando Necesitás Procesamiento en Userspace","level":2,"url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#afxdp-cuando-necesits-procesamiento-en-userspace","content":"A veces se necesita hacer más que dropear o pasar. AF\\_XDP te permite redirigir paquetes a una aplicación userspace a través de un ring buffer zero-copy, bypasseando todo el stack de red del kernel:\n\n```c\nstruct {\n __uint(type, BPF_MAP_TYPE_XSKMAP);\n __uint(max_entries, 64);\n __type(key, __u32);\n __type(value, __u32);\n} xsks_map SEC(\".maps\");\n\nSEC(\"xdp\")\nint xdp_af_xdp_redirect(struct xdp_md *ctx)\n{\n void *data = (void *)(long)ctx->data;\n void *data_end = (void *)(long)ctx->data_end;\n\n struct ethhdr *eth = data;\n if ((void *)(eth + 1) > data_end)\n return XDP_PASS;\n\n if (eth->h_proto != __constant_htons(ETH_P_IP))\n return XDP_PASS;\n\n struct iphdr *ip = (void *)(eth + 1);\n if ((void *)(ip + 1) > data_end)\n return XDP_PASS;\n\n if (ip->protocol == IPPROTO_TCP) {\n struct tcphdr *tcp = (void *)ip + (ip->ihl * 4);\n if ((void *)(tcp + 1) > data_end)\n return XDP_PASS;\n\n if (tcp->dest == __constant_htons(8080)) {\n return bpf_redirect_map(&xsks_map, ctx->rx_queue_index, XDP_PASS);\n }\n }\n\n return XDP_PASS;\n}\n```\n\nAF\\_XDP te da 8-12 millones de paquetes por segundo por core en userspace. Eso es como 10x más rápido que ` recvmsg() ` en un raw socket, porque no hay overhead de syscall, no hay asignación de sk\\_buff, y no hay copia de datos."},{"heading":"Dentro del Dataplane de Cilium","level":2,"url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#dentro-del-dataplane-de-cilium","content":"Cilium es el uso más sofisticado de BPF para networking en producción. Reemplaza kube-proxy completamente e implementa networking de Kubernetes en BPF.\n\nCuando un paquete sale del Pod A con destino a un servicio Kubernetes, el programa BPF de Cilium:\n\n1. Busca el destino en ` SERVICE_MAP ` para encontrar un backend pod\n2. Hace DNAT (reescribe la IP destino a la IP del backend pod)\n3. Crea una entrada de connection tracking en ` CT_MAP `\n4. Chequea ` POLICY_MAP ` para verificar que la network policy permite la conexión\n5. Forwardea el paquete directamente a la interfaz veth del backend pod\n\nTodo esto pasa en el kernel, sin context switches a userspace. Los benchmarks de Cilium muestran 2-3x mejor latencia y throughput comparado con kube-proxy basado en iptables."},{"heading":"Números de Producción","level":2,"url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#nmeros-de-produccin","content":"Después de deployar el rate limiter XDP en nuestros servidores edge (droplets [DigitalOcean](<https://digitalocean.com/>) Premium CPU con NICs Mellanox):\n\n| Métrica | Antes (iptables) | Después (XDP) |\n| --- | --- | --- |\n| Max absorción DDoS | ~5 Gbps | 40+ Gbps |\n| CPU durante flood de 10 Gbps | 95% | 8% |\n| Latencia tráfico legítimo durante ataque | 200-500ms | \\<5ms |\n| Tiempo de update de reglas | ~50ms (iptables flush) | \\<1ms (map update) |\n| Memoria por IP trackeada | ~512 bytes (conntrack) | 16 bytes (BPF map) |\n\nLa diferencia de memoria importa. Durante un DDoS volumétrico con millones de IPs de origen spoofed, el conntrack de Linux consumía gigabytes de RAM y eventualmente OOM-killeaba procesos. El BPF LRU map usa 16 MB fijos sin importar el tamaño del ataque."},{"heading":"Cuándo NO Usar XDP","level":2,"url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#cundo-no-usar-xdp","content":"XDP es una herramienta quirúrgica, no una solución general:\n\n- **Filtrado a nivel aplicación** (reglas WAF, inspección HTTP): Utilizar un reverse proxy. XDP opera en paquetes crudos antes del reensamblado.\n- **Inspección profunda de paquetes stateful**: El límite de instrucciones BPF y la falta de asignación dinámica de memoria hacen impracticable el análisis stateful complejo.\n- **Firewalling simple**: Si iptables maneja tu carga, utilizar iptables. XDP agrega complejidad.\n\nXDP brilla para: mitigación de DDoS, load balancing, packet steering, telemetría/sampling, y cualquier cosa donde se necesita tomar decisiones por-paquete a millones de paquetes por segundo.\n\nLa curva de aprendizaje es empinada, se necesita entender C, el stack de red de Linux, las restricciones del verificador BPF y la arquitectura de drivers de NIC. Pero una vez que escribiste tu primer programa XDP que dropea 40 Gbps de tráfico de ataque usando el 3% de un core de CPU, nunca se va a mirar iptables de la misma forma."},{"heading":"Preguntas frecuentes","level":2,"url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#questions","content":""},{"heading":"¿Qué tan rápido puede XDP realmente procesar paquetes?","level":3,"url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#question-1","content":"En un solo core con una NIC moderna (Intel E810, Mellanox ConnectX-6), XDP puede procesar 24-26 millones de paquetes por segundo en modo XDP\\_DROP. Son ~25 Gbps de paquetes de tamaño mínimo en un core. Con AF\\_XDP para procesamiento en userspace, esperá 8-12 Mpps por core."},{"heading":"¿Necesito escribir C para usar XDP?","level":3,"url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#question-2","content":"El programa XDP se escribe en C (compilado a bytecode BPF via clang), pero es posible usar frameworks de más alto nivel para el lado userspace. Cilium usa Go, Katran usa C++, y hay wrappers en Rust como Aya que te permiten escribir tanto el programa BPF como el loader en Rust."},{"heading":"¿Qué pasa si mi programa XDP tiene un bug?","level":3,"url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#question-3","content":"El verificador BPF atrapa la mayoría de los bugs al momento de carga, analiza estáticamente todos los code paths, chequea bounds en todos los accese es a memoria, asegura que no haya loops infinitos. Si un bug pasa el verificador (raro), lo peor que puede pasar es dropear o pasar paquetes incorrectamente, el kernel no puede crashear por un programa BPF."},{"heading":"Tips","level":2,"url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#article-tips","content":"1. **LuchoTip:** Siempre utilizar ` __attribute__((always_inline)) ` para funciones helper en programas XDP. El verificador BPF cuenta instrucciones después del inlining, y una llamada a función no-inline agrega overhead que puede empujarte más allá del límite de 1 millón de instrucciones en kernels viejos.\n2. **LuchoTip:** Probar tus programas XDP con ` XDP_FLAGS_SKB_MODE ` primero, después pasá a ` XDP_FLAGS_DRV_MODE ` para producción. SKB mode corre en el stack de red del kernel (más lento pero siempre anda), driver mode corre en el driver de la NIC (rápido pero necesita soporte del driver).\n3. **LuchoTip:** Utilizar BPF maps de tipo ` BPF_MAP_TYPE_LRU_HASH ` en vez de ` BPF_MAP_TYPE_HASH ` para connection tracking. Los hash maps regulares fallan silenciosamente cuando están llenos. Los LRU maps evictan la entrada menos recientemente usada, que para un filtro de DDoS es la IP atacante que estuvo más tiempo callada. Exactamente lo que querés.\n\n- ebpf\n- xdp\n- networking\n- ddos\n- cilium\n- linux-kernel\n- procesamiento-paquetes\n\n[Volver al Blog](<https://lucioduran.com/es/blog>)"}],"links":[{"text":"Lucio Durán","url":"https://lucioduran.com/es"},{"text":"English","url":"https://lucioduran.com/blog/bpf-xdp-high-performance-packet-processing"},{"text":"DigitalOcean","url":"https://digitalocean.com/"},{"text":"Volver al Blog","url":"https://lucioduran.com/es/blog"}],"structuredData":[{"@context":"https://schema.org","@graph":[{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com/"},{"@type":"WebSite","@id":"https://lucioduran.com/#website","url":"https://lucioduran.com/","name":"Lucio Durán","inLanguage":["en","es"],"publisher":{"@id":"https://lucioduran.com/#person"}},{"@type":"WebPage","@id":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#page","url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing","name":"eBPF y XDP: Procesamiento de Paquetes de Alto Rendimiento y Mitigación de DDoS a Line Rate | Lucio Durán","description":"Procesamiento de paquetes de alto rendimiento con eBPF y XDP: programas XDP en C, sockets AF_XDP, mitigación de DDoS a line rate y análisis del dataplane de Cilium.","inLanguage":"es","author":{"@id":"https://lucioduran.com/#person"},"publisher":{"@id":"https://lucioduran.com/#person"},"isPartOf":{"@id":"https://lucioduran.com/#website"},"isAccessibleForFree":true,"datePublished":"2026-02-17","mainEntity":{"@id":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#article"},"breadcrumb":{"@id":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#breadcrumb"}},{"@type":"BlogPosting","@id":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#article","mainEntityOfPage":{"@id":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#page"},"headline":"eBPF y XDP: Procesamiento de Paquetes de Alto Rendimiento y Mitigación de DDoS a Line Rate","description":"Procesamiento de paquetes de alto rendimiento con eBPF y XDP: programas XDP en C, sockets AF_XDP, mitigación de DDoS a line rate y análisis del dataplane de Cilium.","datePublished":"2026-02-17","author":{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com"},"publisher":{"@id":"https://lucioduran.com/#person"},"url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing","inLanguage":"es","isPartOf":{"@id":"https://lucioduran.com/es/blog#blog"},"articleSection":"Networking","keywords":"ebpf, xdp, networking, ddos, cilium, linux-kernel, procesamiento-paquetes","wordCount":1196,"timeRequired":"PT7M","workTranslation":[{"@type":"BlogPosting","@id":"https://lucioduran.com/blog/bpf-xdp-high-performance-packet-processing#article","inLanguage":"en","url":"https://lucioduran.com/blog/bpf-xdp-high-performance-packet-processing"}],"isAccessibleForFree":true},{"@type":"Blog","@id":"https://lucioduran.com/es/blog#blog","url":"https://lucioduran.com/es/blog","name":"Blog","inLanguage":"es","author":{"@type":"Person","@id":"https://lucioduran.com/#person","name":"Lucio Durán","url":"https://lucioduran.com/"},"publisher":{"@id":"https://lucioduran.com/#person"},"isPartOf":{"@id":"https://lucioduran.com/#website"}},{"@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Inicio","item":"https://lucioduran.com/es"},{"@type":"ListItem","position":2,"name":"Blog","item":"https://lucioduran.com/es/blog"},{"@type":"ListItem","position":3,"name":"eBPF y XDP: Procesamiento de Paquetes de Alto Rendimiento y Mitigación de DDoS a Line Rate","item":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing"}],"@id":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#breadcrumb"},{"@type":"FAQPage","mainEntity":[{"@type":"Question","name":"¿Qué tan rápido puede XDP realmente procesar paquetes?","acceptedAnswer":{"@type":"Answer","text":"En un solo core con una NIC moderna (Intel E810, Mellanox ConnectX-6), XDP puede procesar 24-26 millones de paquetes por segundo en modo XDP_DROP. Son ~25 Gbps de paquetes de tamaño mínimo en un core. Con AF_XDP para procesamiento en userspace, esperá 8-12 Mpps por core."}},{"@type":"Question","name":"¿Necesito escribir C para usar XDP?","acceptedAnswer":{"@type":"Answer","text":"El programa XDP se escribe en C (compilado a bytecode BPF via clang), pero es posible usar frameworks de más alto nivel para el lado userspace. Cilium usa Go, Katran usa C++, y hay wrappers en Rust como Aya que te permiten escribir tanto el programa BPF como el loader en Rust."}},{"@type":"Question","name":"¿Qué pasa si mi programa XDP tiene un bug?","acceptedAnswer":{"@type":"Answer","text":"El verificador BPF atrapa la mayoría de los bugs al momento de carga, analiza estáticamente todos los code paths, chequea bounds en todos los accese es a memoria, asegura que no haya loops infinitos. Si un bug pasa el verificador (raro), lo peor que puede pasar es dropear o pasar paquetes incorrectamente, el kernel no puede crashear por un programa BPF."}}],"@id":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#questions","url":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#questions","inLanguage":"es","isPartOf":{"@id":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing#page"},"author":{"@id":"https://lucioduran.com/#person"}}]}],"contentHash":"2ec668212bc8fde6a617b64478af9156aee89cb0a9ba25a2e9f60cb444e9692c","provenance":{"method":"Deterministic serialization of the server-rendered main element","source":"https://lucioduran.com/es/blog/bpf-xdp-high-performance-packet-processing","hashAlgorithm":"sha256","hashScope":"JSON document fields before contentHash and provenance","sourceHTMLBytes":63951,"contentBytes":10856}}
