159 lines
5.8 KiB
C
159 lines
5.8 KiB
C
// Exemplo AF_XDP com RX, TX, multi-queue e recycling, sem libbpf/xsk.h
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <net/if.h>
|
|
#include <sys/socket.h>
|
|
#include <linux/if_xdp.h>
|
|
#include <linux/if_ether.h>
|
|
#include <sys/mman.h>
|
|
#include <poll.h>
|
|
#include <stdint.h>
|
|
#include <sys/types.h>
|
|
|
|
#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 <iface> [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;
|
|
}
|