xdp-project / xdp-tutorial

XDP tutorial
2.44k stars 577 forks source link

Infinite loop in receiving packets #363

Closed incapdns closed 1 year ago

incapdns commented 1 year ago

Kernel:

6.3.5-1.el9.elrepo.x86_64

When I replace the if(false) to if(true) the system is returning me in the poll call even without new packets.

Example: image

Command line:

-p -d lo -z --filename /root/xdp-tutorial/af_xdp_kern.o -S

What is happening:

The call to syscall poll is returning true even though there are no new UDP packets received

That said, I'm in an eternal loop, that is, it's being returned immediately even without new packets.

What is expected:

As soon as I receive the UDP packet on port 80, I reply (tx) and the syscall poll call only returns when there are new packets

What has been modified:

  1. The af_xdp_user code was changed from if (false) to if(true)
  2. The af_xdp_kern.c code only redirects UDP packets whose destination port is 80
  3. The queue_index was set to zero

af_xdp_kern.c:

/* SPDX-License-Identifier: GPL-2.0 */

#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/in.h>

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

#define htons(x) ((__be16)___constant_swab16((x)))
#define htonl(x) ((__be32)___constant_swab32((x)))

struct {
    __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
    __type(key, __u32);
    __type(value, __u32);
    __uint(max_entries, 64);
} xdp_stats_map SEC(".maps");

SEC("xdp")
int xdp_sock_prog(struct xdp_md *ctx)
{
    int index = ctx->rx_queue_index;
    __u32 *pkt_count;

    pkt_count = bpf_map_lookup_elem(&xdp_stats_map, &index);
    if(pkt_count)
       (*pkt_count)++;

    void* data_end = (void *)(long)ctx->data_end;
    void* data = (void *)(long)ctx->data;
    struct ethhdr* eth = data;

    if(eth + 1 > (struct ethhdr*) data_end)
    return XDP_DROP;

    struct iphdr* iph = data + sizeof (struct ethhdr);

    if (iph + 1 > (struct iphdr *)data_end)
        return XDP_DROP;

    if(iph->protocol != IPPROTO_UDP)
    return XDP_PASS;

    struct udphdr* udph = data + sizeof(struct ethhdr) + (iph->ihl * 4);

    if (udph + 1 > (struct udphdr *)data_end)
        return XDP_DROP;

    bpf_printk("DEST: %d", htons(udph->dest));

    if(htons(udph->dest) != 80)
    return XDP_PASS;

    /* A set entry here means that the correspnding queue_id
     * has an active AF_XDP socket bound to it. */
    return bpf_redirect_map(&xsks_map, 0, XDP_PASS);
}

char _license[] SEC("license") = "GPL";
incapdns commented 1 year ago

It is important to highlight the following:

It It doesn't go into infinite loop if:

I comment the following part of code:

        //xsk_ring_prod__tx_desc(&xsk->tx, tx_idx)->addr = addr;
        //xsk_ring_prod__tx_desc(&xsk->tx, tx_idx)->len = len;
        xsk_ring_prod__submit(&xsk->tx, 1);
        xsk->outstanding_tx++;
        return true;

Or:

        xsk_ring_prod__tx_desc(&xsk->tx, tx_idx)->addr = addr;
        xsk_ring_prod__tx_desc(&xsk->tx, tx_idx)->len = len;
        //xsk_ring_prod__submit(&xsk->tx, 1);
        xsk->outstanding_tx++;
        return true;
incapdns commented 1 year ago

Sorry, I already managed to solve it.

It seems to be due to the change from c to c++, some things were missing