AF_XDP是一种高性能网络数据包处理技术,可用于加速数据包的传输和处理。以下是一个使用AF_XDP的简单例子:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <linux/if_link.h>
#include <net/if.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <linux/bpf.h>
#include <linux/if_xdp.h>
#define MAX_FRAME_SIZE 2048
static int xdp_sock;
int init_xdp_socket(char* if_name, int queue_id)
{
int err;
struct sockaddr_in addr;
struct xdp_umem_reg umem_reg = {};
struct xdp_desc desc = {};
struct ifreq ifr = {};
void* buffer;
xdp_sock = socket(AF_XDP, SOCK_RAW, 0);
if (xdp_sock < 0) {
printf("failed to create AF_XDP socket\n");
return -1;
}
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, if_name, IFNAMSIZ - 1);
if (setsockopt(xdp_sock, SOL_SOCKET, SO_ATTACH_BPF, &queue_id, sizeof(queue_id)) < 0) {
printf("failed to attach bpf program to socket\n");
return -1;
}
if (ioctl(xdp_sock, SIOCGIFFLAGS, &ifr) != 0) {
printf("failed to get interface flags\n");
return -1;
}
ifr.ifr_flags |= IFF_UP | IFF_RUNNING;
if (ioctl(xdp_sock, SIOCSIFFLAGS, &ifr) != 0) {
printf("failed to set interface flags\n");
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(xdp_sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
printf("failed to bind socket\n");
return -1;
}
// Allocate and register umem
int umem_fd = -1;
if ((umem_fd = open("/dev/xdp_umem", O_RDWR)) < 0) {
printf("failed to open xdp_umem device\n");
return -1;
}
buffer = mmap(NULL, MAX_FRAME_SIZE * XDP_UMEM_PGSIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
if (buffer == MAP_FAILED) {
printf("failed to allocate memory for umem\n");
return -1;
}
umem_reg.addr = (uint64_t)buffer;
umem_reg.len = MAX_FRAME_SIZE * XDP_UMEM_PGSIZE;
umem_reg.chunk_size = MAX_FRAME_SIZE;
umem_reg.headroom = 0;
if (ioctl(umem_fd, XDP_UMEM_REG, &umem_reg) < 0) {
printf("failed to register umem\n");
return -1;
}
memset(&desc, 0, sizeof(desc));
desc.addr = umem_reg.addr;
desc.len = MAX_FRAME_SIZE;
desc.options |= XDP_DESC_OPTIONS_SKB;
desc.skb = buffer;
if (setsockopt(xdp_sock, SOL_XDP, XDP_UMEM_REG, &desc, sizeof(desc)) < 0) {
printf("failed to set xdp umem reg\n");
return -1;
}
// Attach program to queue
struct bpf_prog_info info = {};
err = bpf_prog_query(BPF_PROG_TYPE_XDP, "xdp_program", 0, &info, sizeof(info));
if (err != 0) {
printf("failed to query xdp_program\n");
return -1;
}
struct xdp_link_info link_info = {};
link_info.umem_id = umem_reg.mem_umem_id;
link_info.reg_mem_chunk_size = MAX_FRAME_SIZE;
link_info.rx_queue_index = queue_id;
if (setsockopt(xdp_sock, SOL_XDP, XDP_ATTACH, &link_info, sizeof(link_info)) < 0) {
printf("failed to attach bpf program to queue\n");
return -1;
}
return xdp_sock;
}
void handle_packet(struct xdp_buff* xdp)
{
uint32_t* data = (uint32_t*)(xdp->data);
printf("Received packet with data: %u\n", *data);
}
int main(int argc, char** argv)
{
int queue_id = 0; // the queue id we want to bind to
char* if_name = "eth0"; // the interface name we want to use
int sock = init_xdp_socket(if_name, queue_id);
if (sock < 0) {
printf("failed to initialize xdp socket\n");
return -1;
}
while (1) {
struct xdp_buff xdp;
ssize_t len = recvfrom(sock, &xdp, sizeof(xdp), 0, NULL, NULL);
if (len < 0) {
printf("failed to receive packet\n");
continue;
}
handle_packet(&xdp);
}
return 0;
}
这个例子演示了如何使用AF_XDP收发数据包。它创建了一个套接字,并将其绑定到指定的队列和接口上。然后,它使用XDP_UMEM_REG来分配和注册用户内存(umem),并将其附加到套接字上。最后,它将BPF程序附加到队列上以处理传入的数据包。在循环中,它使用recvfrom从套接字接收数据包,并调用handle_packet函数来处理数据包。