XDP not working as expected when using AF_XDP

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello everyone,

I'm having an issue with my XDP program where the filtering isn't working as expected when loaded through my user-space program.

I'm working on a project to optimize UDP traffic using AF_XDP. My goal is to filter the traffic using an XDP program and redirect it to my user-space application (using BPF_MAP_TYPE_XSKMAP) where I then can process the packets. However I'm running into the problem that my filter isn't working when loading it with my user-space application, which leads to receiving all traffic instead of only the UDP packets that I want. But when I tried loading it using xdp-loader it does work and filter out the correct traffic.

The setup I'm using is in virtualbox, OS: Debian GNU/Linux 12 (bookworm), kernel: 6.1.0-25-amd64, and using the virtio-net driver for my network interface. To test it I'm using a different VM to send ping requests and the UDP packets that I want to filter for in my XDP program.

I've tried many different things from the examples (like those from the XDP-tutorial, basic03-04 and advanced03 in particular) that I could find, but none of those options seem to work for my implementation. Changing the XDP_MODE isn't an option because the other modes made it fall back to the SKB mode.

I'm wondering if anyone has an idea on what I'm doing wrong or what I can do differently to make it work. Here are some relevant snippets, with some of it coming directly from the xdpsock.c example in the bpf-examples repo. For the creation of the UMEM, sockets and such I'm using libbpf and libxdp by recommendation of the documentation on kernel.org.

Has anyone encountered similar issues or have any ideas on how to fix the problem that I'm having?

The XDP program:
```
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <xdp/xdp_helpers.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/udp.h>
#include <bpf/bpf_endian.h>
#include <linux/in.h>

#define TARGET_PORT 42795

struct {
    __uint(type, BPF_MAP_TYPE_XSKMAP);
    __type(key, int);
    __type(value, int);
    __uint(max_entries, 1);
} xsks_map SEC(".maps");

struct {
    __uint(priority, 10);
    __uint(XDP_PASS, 1);
    __uint(XDP_DROP, 1);
} XDP_RUN_CONFIG(xdp_sock_prog);

SEC("xdp")
int xdp_sock_prog(struct xdp_md *ctx)
{
    void *data_end = (void *)(long)ctx->data_end;
    void *data = (void *)(long)ctx->data;
    bpf_printk("Processing packet, size: %d\n", data_end - data);

    struct ethhdr *eth = data;
    if ((void *)(eth + 1) > data_end) {
        bpf_printk("invalid size of packet");
        return XDP_PASS;
    }

    if (eth->h_proto != __bpf_htons(ETH_P_IP)) {
        bpf_printk("no IP packet");
        return XDP_PASS;
    }

    struct iphdr *ip = (void *)(eth + 1);
    if ((void *)(ip + 1) > data_end) {
        bpf_printk("invalid IP packet");
        return XDP_PASS;
    }
    bpf_printk("Protocol: %d\n", ip->protocol);

    if (ip->protocol != IPPROTO_UDP) {
        bpf_printk("No UDP packet");
        return XDP_PASS;
    }

    struct udphdr *udp = (void *)(ip + 1);
    if ((void *)(udp + 1) > data_end) {
        bpf_printk("invalid UDP packet");
        return XDP_PASS;
    }

    if (ip->protocol == IPPROTO_UDP && __bpf_ntohs(udp->dest) == TARGET_PORT) {
        int index = ctx->rx_queue_index;
        bpf_printk("found UDP packet with correct port!");

        if (bpf_map_lookup_elem(&xsks_map, &index)) {
            bpf_printk("Redirecting to map");
            long ret = bpf_redirect_map(&xsks_map, index, 0);
            bpf_printk("redirect return val: %lu", ret);
            return ret;
        }
    }

    bpf_printk("Passing to the kernel");
    return XDP_PASS;
}

char _license[] SEC("license") = "GPL";
```
The code that loads the XDP program:
```
#include <bpf/libbpf.h>
#include <xdp/xsk.h>
#include <xdp/libxdp.h>
#include <net/if.h>
#include <errno.h>
#include <unistd.h>

int attach_program(struct xdp_program **prog, struct xsk_socket_info *socket_info, const char *ifname) {
    struct bpf_object *bpf_obj;
    int err, map_fd;
    int queue_id = 0;

    *prog = xdp_program__open_file("bin/xdp_prog.o", "xdp", NULL);
    err = libxdp_get_error(*prog);
    if (err) {
        perror("Failed to open XDP program");
        return -1;
    }

    int ifindex = if_nametoindex(ifname);
    if (ifindex == 0) {
        perror("Interface index");
        return -1;
    }

    err = xdp_program__attach(*prog, ifindex, XDP_MODE_SKB, 0);
    if (err) {
        perror("Failed to attach XDP program");
        xdp_program__close(*prog);
        return err;
    }

    bpf_obj = xdp_program__bpf_obj(*prog);
    map_fd = bpf_object__find_map_fd_by_name(bpf_obj, "xsks_map");
    printf("found map fd, %d\n", map_fd);
    if (map_fd < 0) {
        perror("Failed to get map fd from bpf object");
        xdp_program__close(*prog);
        return map_fd;
    }
    socket_info->xdp_map_fd = map_fd;

    int xsk_fd = xsk_socket__fd(socket_info->xsk);
    int ret = bpf_map_update_elem(map_fd, &queue_id, &xsk_fd, BPF_ANY);
    if (ret < 0) {
        perror("Error updating BPF map element");
        return ret;
    }

    return 0;
}

void detach_program(struct xdp_program *prog, const char *ifname) {
    int ifindex = if_nametoindex(ifname);
    if (ifindex == 0) {
        perror("Interface index");
    }

    int err = xdp_program__detach(prog, ifindex, XDP_MODE_SKB, 0);
    if (err == -ENOENT) {
        perror("No XDP program attached");
    } else if (err) {
        perror("Failed to detach XDP program");
    }
    xdp_program__close(prog);
}
```
The code I'm using for receiving packets:
```
static void xsk_receive_packet(struct xsk_socket_info *socket_info) {
    uint32_t idx_rx, idx_fq, recvd = 0;
    int ret;

    recvd = xsk_ring_cons__peek(&socket_info->rx, 64, &idx_rx);
    if (!recvd) {
        if (xsk_ring_prod__needs_wakeup(&socket_info->umem->fq)) {
            recvfrom(xsk_socket__fd(socket_info->xsk), NULL, 0, MSG_DONTWAIT, NULL, NULL);
        }
        return;
    }

    ret = xsk_ring_prod__reserve(&socket_info->umem->fq, recvd, &idx_fq);
    while (ret != recvd) {
        if (ret < 0)
            return;
        if (xsk_ring_prod__needs_wakeup(&socket_info->umem->fq)) {
            recvfrom(xsk_socket__fd(socket_info->xsk), NULL, 0, MSG_DONTWAIT, NULL, NULL);
        }
        ret = xsk_ring_prod__reserve(&socket_info->umem->fq, recvd, &idx_fq);
    }

    for (size_t i = 0; i < recvd; i++) {
        const struct xdp_desc *desc = xsk_ring_cons__rx_desc(&socket_info->rx, idx_rx++);
        uint64_t addr = desc->addr;
        uint32_t len = desc->len;
        uint64_t orig = xsk_umem__extract_addr(addr);

        addr = xsk_umem__add_offset_to_addr(addr);
        char *pkt = xsk_umem__get_data(socket_info->umem->buffer, addr);
        print_hex(pkt, len);
        *xsk_ring_prod__fill_addr(&socket_info->umem->fq, idx_fq++) = orig;
    }
    xsk_ring_prod__submit(&socket_info->umem->fq, recvd);
    xsk_ring_cons__release(&socket_info->rx, recvd);
}

void xsk_receive_packets(struct xsk_socket_info *socket_info) {
    int ret;
    struct pollfd fds = {};
    fds.fd = xsk_socket__fd(socket_info->xsk);
    fds.events = POLLIN;
    unsigned int timeout_seconds = 100;
    time_t start = time(NULL);
    while(1) {
        time_t current = time(NULL);
        if (difftime(current, start) >= timeout_seconds) {
            printf("Timed out\n");
            break;
        }
        ret = poll(&fds, 1, 0);
        if (ret <= 0)
            continue;
        xsk_receive_packet(socket_info, and_send);
    }
}

```
Which are called in my main like this:
```
int main(void) {
    struct xsk_umem_info *umem_info;
    struct xsk_socket_info *socket_info;
    struct xdp_program *program = NULL;
    struct bpf_object *bpf_obj;
    int res;

    res = xsk_configure_umem(&umem_info, BUFFER_SIZE);
    if (res != 0) {
        perror("Configure UMEM");
        exit(EXIT_FAILURE);
    }

    res = xsk_create_socket(&socket_info, UP_INTERFACE, umem_info);
    if (res != 0) {
        perror("Configure XDP socket");
        exit(EXIT_FAILURE);
    }

    res = attach_program(&program, socket_info, UP_INTERFACE);
    if (res != 0) {
        perror("Attaching XDP program");
        exit(EXIT_FAILURE);
    }

    xsk_fill_ring(socket_info);
    xsk_receive_packets(socket_info, 1);

    detach_program(program, UP_INTERFACE);


    return 0;
}
```
This is the output I'm getting from the bpf_printk and my program
```
        upstream-4960    [000] ..s11 16329.727555: bpf_trace_printk: Processing packet, size: 98

        upstream-4960    [000] ..s11 16329.727655: bpf_trace_printk: Protocol: 1

        upstream-4960    [000] ..s11 16329.727656: bpf_trace_printk: No UDP packet         upstream-4960    [000] ..s11 16329.727997: bpf_trace_printk: Processing packet, size: 98

        upstream-4960    [000] ..s11 16329.727997: bpf_trace_printk: Protocol: 1

        upstream-4960    [000] ..s11 16329.727997: bpf_trace_printk: No UDP packet         upstream-4960    [000] ..s11 16329.785383: bpf_trace_printk: Processing packet, size: 980

        upstream-4960    [000] ..s11 16329.785498: bpf_trace_printk: Protocol: 17

        upstream-4960    [000] ..s11 16329.785499: bpf_trace_printk: found UDP packet with correct port!         upstream-4960    [000] ..s11 16329.785499: bpf_trace_printk: Redirecting to map         upstream-4960    [000] ..s11 16329.785500: bpf_trace_printk: redirect return val: 4         upstream-4960    [000] ..s11 16329.785505: bpf_trace_printk: Processing packet, size: 590

        upstream-4960    [000] ..s11 16329.785506: bpf_trace_printk: Protocol: 1

        upstream-4960    [000] ..s11 16329.785506: bpf_trace_printk: No UDP packet         upstream-4960    [000] ..s11 16329.891559: bpf_trace_printk: Processing packet, size: 60

        upstream-4960    [000] ..s11 16329.891658: bpf_trace_printk: no IP packet         upstream-4960    [000] ..s11 16329.891959: bpf_trace_printk: Processing packet, size: 42

        upstream-4960    [000] ..s11 16329.891959: bpf_trace_printk: no IP packet         upstream-4960    [000] ..s11 16330.286560: bpf_trace_printk: Processing packet, size: 980

        upstream-4960    [000] ..s11 16330.286656: bpf_trace_printk: Protocol: 17

        upstream-4960    [000] ..s11 16330.286657: bpf_trace_printk: found UDP packet with correct port!         upstream-4960    [000] ..s11 16330.286657: bpf_trace_printk: Redirecting to map         upstream-4960    [000] ..s11 16330.286658: bpf_trace_printk: redirect return val: 4         upstream-4960    [000] ..s11 16330.286822: bpf_trace_printk: Processing packet, size: 590

        upstream-4960    [000] ..s11 16330.286822: bpf_trace_printk: Protocol: 1

        upstream-4960    [000] ..s11 16330.286822: bpf_trace_printk: No UDP packet         upstream-4960    [000] ..s11 16330.728671: bpf_trace_printk: Processing packet, size: 98

        upstream-4960    [000] ..s11 16330.728758: bpf_trace_printk: Protocol: 1

        upstream-4960    [000] ..s11 16330.728758: bpf_trace_printk: No UDP packet         upstream-4960    [000] ..s11 16330.729036: bpf_trace_printk: Processing packet, size: 98

        upstream-4960    [000] ..s11 16330.729036: bpf_trace_printk: Protocol: 1

        upstream-4960    [000] ..s11 16330.729037: bpf_trace_printk: No UDP packet         upstream-4960    [000] ..s11 16330.787123: bpf_trace_printk: Processing packet, size: 980

        upstream-4960    [000] ..s11 16330.787234: bpf_trace_printk: Protocol: 17

        upstream-4960    [000] ..s11 16330.787235: bpf_trace_printk: found UDP packet with correct port!         upstream-4960    [000] ..s11 16330.787235: bpf_trace_printk: Redirecting to map         upstream-4960    [000] ..s11 16330.787236: bpf_trace_printk: redirect return val: 4         upstream-4960    [000] ..s11 16330.787633: bpf_trace_printk: Processing packet, size: 590

        upstream-4960    [000] ..s11 16330.787712: bpf_trace_printk: Protocol: 1

        upstream-4960    [000] ..s11 16330.787712: bpf_trace_printk: No UDP packet      ksoftirqd/0-14      [000] ..s11 16330.946334: bpf_trace_printk: Processing packet, size: 42

     ksoftirqd/0-14      [000] ..s11 16330.946598: bpf_trace_printk: no IP packet      ksoftirqd/0-14      [000] ..s11 16330.946606: bpf_trace_printk: Processing packet, size: 60

     ksoftirqd/0-14      [000] ..s11 16330.946606: bpf_trace_printk: no IP packet         upstream-4960    [000] ..s11 16331.289930: bpf_trace_printk: Processing packet, size: 980

        upstream-4960    [000] ..s11 16331.290022: bpf_trace_printk: Protocol: 17

        upstream-4960    [000] ..s11 16331.290023: bpf_trace_printk: found UDP packet with correct port!         upstream-4960    [000] ..s11 16331.290024: bpf_trace_printk: Redirecting to map         upstream-4960    [000] ..s11 16331.290024: bpf_trace_printk: redirect return val: 4
```
And the user-space program (with most of the bytes removed for brevity):
```
data [len: 98]: 0x080x000x270x2d0....
data [len: 98]: 0x080x000x270xfffffff40.....
data [len: 980]: 0x080x000x270x2d0xfffff....
data [len: 590]: 0x080x000x270xfff....
data [len: 980]: 0x080x000x270x2d0xff....
data [len: 98]: 0x080x000x270x2d0xffffffeb...
data [len: 98]: 0x080x000x270xfffffff40xf....
data [len: 980]: 0x080x000x270x2d0xffffffe....
```

I was thinking of posting it on StackOverflow but this seemed a more appropriate place to ask for advice.

Thanks in advance for any advice,
Thom




[Index of Archives]     [Linux Networking Development]     [Fedora Linux Users]     [Linux SCTP]     [DCCP]     [Gimp]     [Yosemite Campsites]

  Powered by Linux