// Exemplo AF_XDP com RX, TX, multi-queue e recycling, sem libbpf/xsk.h #include #include #include #include #include #include #include #include #include #include #include #include #include #define NUM_FRAMES 4096 #define FRAME_SIZE 2048 #define UMEM_SIZE (NUM_FRAMES * FRAME_SIZE) #define RX_RING_SIZE 2048 #define TX_RING_SIZE 2048 #define FILL_RING_SIZE 2048 #define COMP_RING_SIZE 2048 #define BATCH_SIZE 64 static inline void die(const char *msg) { perror(msg); exit(1); } static size_t roundup_page(size_t sz) { size_t pg = sysconf(_SC_PAGESIZE); return ((sz + pg - 1) / pg) * pg; } // Helpers para acessar os pointers dos rings #define RING_PROD_PTR(ring) ((volatile __u32 *)(ring)) #define RING_CONS_PTR(ring) ((volatile __u32 *)(ring) + 1) #define RING_DESC(ring) ((void *)((volatile __u32 *)(ring) + 2)) int main(int argc, char **argv) { if (argc < 2) { printf("Uso: %s [queue_id]\n", argv[0]); return 1; } const char *ifname = argv[1]; int queue_id = 0; if (argc > 2) queue_id = atoi(argv[2]); int ifindex = if_nametoindex(ifname); if (!ifindex) die("if_nametoindex"); // 1. Crie socket AF_XDP int xdp_sock = socket(AF_XDP, SOCK_RAW, 0); if (xdp_sock < 0) die("socket(AF_XDP)"); // 2. Alocar UMEM void *umem_area = NULL; if (posix_memalign(&umem_area, getpagesize(), UMEM_SIZE)) die("posix_memalign"); // 3. Registrar UMEM no kernel struct xdp_umem_reg mr = { .addr = (__u64)(uintptr_t)umem_area, .len = UMEM_SIZE, .chunk_size = FRAME_SIZE, .headroom = 0, .flags = 0, }; if (setsockopt(xdp_sock, SOL_XDP, XDP_UMEM_REG, &mr, sizeof(mr)) < 0) die("setsockopt(XDP_UMEM_REG)"); // 4. Configurar tamanho dos rings int sz = FILL_RING_SIZE; if (setsockopt(xdp_sock, SOL_XDP, XDP_UMEM_FILL_RING, &sz, sizeof(sz)) < 0) die("setsockopt(XDP_UMEM_FILL_RING)"); sz = COMP_RING_SIZE; if (setsockopt(xdp_sock, SOL_XDP, XDP_UMEM_COMPLETION_RING, &sz, sizeof(sz)) < 0) die("setsockopt(XDP_UMEM_COMPLETION_RING)"); sz = RX_RING_SIZE; if (setsockopt(xdp_sock, SOL_XDP, XDP_RX_RING, &sz, sizeof(sz)) < 0) die("setsockopt(XDP_RX_RING)"); sz = TX_RING_SIZE; if (setsockopt(xdp_sock, SOL_XDP, XDP_TX_RING, &sz, sizeof(sz)) < 0) die("setsockopt(XDP_TX_RING)"); // 5. mmap dos rings (tamanho exato, sem roundup_page) size_t fill_ring_sz = FILL_RING_SIZE * sizeof(__u64) + 8; size_t comp_ring_sz = COMP_RING_SIZE * sizeof(__u64) + 8; size_t rx_ring_sz = RX_RING_SIZE * sizeof(struct xdp_desc) + 8; size_t tx_ring_sz = TX_RING_SIZE * sizeof(struct xdp_desc) + 8; void *fill_ring = mmap(NULL, fill_ring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, xdp_sock, XDP_UMEM_PGOFF_FILL_RING); if (fill_ring == MAP_FAILED) die("mmap(FILL_RING)"); void *comp_ring = mmap(NULL, comp_ring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, xdp_sock, XDP_UMEM_PGOFF_COMPLETION_RING); if (comp_ring == MAP_FAILED) die("mmap(COMP_RING)"); void *rx_ring = mmap(NULL, rx_ring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, xdp_sock, XDP_PGOFF_RX_RING); if (rx_ring == MAP_FAILED) die("mmap(RX_RING)"); void *tx_ring = mmap(NULL, tx_ring_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE, xdp_sock, XDP_PGOFF_TX_RING); if (tx_ring == MAP_FAILED) die("mmap(TX_RING)"); printf("fill_ring: %p, comp_ring: %p, rx_ring: %p, tx_ring: %p\n", fill_ring, comp_ring, rx_ring, tx_ring); // Cheque ponteiros dos rings printf("fill_prod_ptr: %p, fill_desc: %p\n", RING_PROD_PTR(fill_ring), RING_DESC(fill_ring)); printf("rx_prod_ptr: %p, rx_desc: %p\n", RING_PROD_PTR(rx_ring), RING_DESC(rx_ring)); printf("tx_prod_ptr: %p, tx_desc: %p\n", RING_PROD_PTR(tx_ring), RING_DESC(tx_ring)); printf("comp_prod_ptr: %p, comp_desc: %p\n", RING_PROD_PTR(comp_ring), RING_DESC(comp_ring)); // 6. Preencher fill ring com endereços dos frames __u64 *fill = (__u64 *)RING_DESC(fill_ring); for (int i = 0; i < NUM_FRAMES; i++) { fill[i] = i * FRAME_SIZE; } // Atualizar producer pointer do fill ring *RING_PROD_PTR(fill_ring) = NUM_FRAMES; // 7. Bind socket AF_XDP à interface/fila struct sockaddr_xdp sxdp = { .sxdp_family = AF_XDP, .sxdp_ifindex = ifindex, .sxdp_queue_id = queue_id, .sxdp_flags = 0, }; if (bind(xdp_sock, (struct sockaddr *)&sxdp, sizeof(sxdp)) < 0) die("bind(AF_XDP)"); printf("Socket AF_XDP ligado na interface %s (fila %d)\n", ifname, queue_id); // 8. Poll para pacotes struct pollfd fds = { .fd = xdp_sock, .events = POLLIN }; struct xdp_desc *rx = (struct xdp_desc *)RING_DESC(rx_ring); while (1) { int ret = poll(&fds, 1, 1000); if (ret < 0) die("poll"); if (ret == 0) continue; __u32 rx_cons = *RING_CONS_PTR(rx_ring); __u32 rx_prod = *RING_PROD_PTR(rx_ring); if (!rx || rx_prod == rx_cons) continue; while (rx_cons != rx_prod) { struct xdp_desc *desc = &rx[rx_cons % RX_RING_SIZE]; if (!desc) break; void *pkt = (char *)umem_area + desc->addr; printf("Pacote RX: %u bytes (addr=%llu)\n", desc->len, (unsigned long long)desc->addr); rx_cons++; } *RING_CONS_PTR(rx_ring) = rx_cons; } // Cleanup (não alcançado neste exemplo) close(xdp_sock); free(umem_area); return 0; }