xdp-project / xdp-tutorial

XDP tutorial
2.44k stars 577 forks source link

Still having problems with xsks_map #322

Closed tjcw closed 1 year ago

tjcw commented 1 year ago

@tohojo I'm still having problems. 1) What is the priority order for multiple eBPF XDP programs ? i.e. do higher numbers run first, or lower numbers ? 2) I need return bpf_redirect_map(&xsks_map, index, 0); to be run in my eBPF program if the program logic reaches it, but not run in the default program. I have coded

struct {
    __uint(priority, 10);
} XDP_RUN_CONFIG(xsk_my_prog);

in my program in an attempt to make the dispatcher not run the default program; if my program logic returns XDP_PASS then I want the packet fed to the linux kernel networking stack. But this does not seem to happen; have I misunderstood something ? 3) It looked to me (from observing behaviour and from looking at the libxdp source code) that having xsks_map in my eBPF program and also in the default eBPF program resulted in 2 items in memory both called xsks_map; the one in the default program gets pinned in /sys/bpf/maps and the one in my program isn't pinned anywhere. Is this the intent, or have I misunderstood something ?

tohojo commented 1 year ago

Lower numbers are run first.

Why are you loading the default XSK program at all? Stop doing that...

tjcw commented 1 year ago

I have changed my application to not load the default program, like

    xsk_cfg.libxdp_flags = XSK_LIBXDP_FLAGS__INHIBIT_PROG_LOAD;
//  xsk_cfg.libxdp_flags = 0;

and added this at the end of the eBPF code

__uint(xsk_prog_version, XSK_PROG_VERSION) SEC(XDP_METADATA_SECTION);

(though the same happens if I run without this line in my eBPF program) but now I get (after making sure no XDP program was loaded by running sudo ip link set dev enp25s0 xdpgeneric off )

umem_alloc_umem_frame umem=0x55ad4dba98f0 allocation_count=2048 free_count=0 frame=0x1800000
libbpf: elf: skipping unrecognized data section(7) .xdp_run_config
libbpf: elf: skipping unrecognized data section(8) xdp_metadata
libbpf: elf: skipping unrecognized data section(7) xdp_metadata
libbpf: elf: skipping unrecognized data section(7) xdp_metadata
libbpf: Kernel error message: Active program does not match expected
libxdp: Error attaching XDP program to ifindex 2: File exists
libxdp: XDP already loaded on device
ERROR:xsk_setup_xdp_prog returns -17
tjcw@r28b29-n10:~/workspace/xdp-tutorial/ebpf-filter$

After running the application, xdp-loader status shows

tjcw@r28b29-n10:~/workspace/xdp-tutorial/ebpf-filter$ sudo xdp-loader status
CURRENT XDP PROGRAM STATUS:

Interface        Prio  Program name      Mode     ID   Tag               Chain actions
--------------------------------------------------------------------------------------
lo                     <No XDP program loaded!>
enp25s0                xdp_dispatcher    skb      667  94d5f00c20184d17 
 =>              10     xsk_my_prog               674  083c5c5d445ff685  XDP_PASS
enp51s0f0              <No XDP program loaded!>
enp26s0                <No XDP program loaded!>
enp51s0f1              <No XDP program loaded!>
enp27s0                <No XDP program loaded!>
enp51s0f2              <No XDP program loaded!>
enp51s0f3              <No XDP program loaded!>
enp28s0                <No XDP program loaded!>
enp29s0                <No XDP program loaded!>
enp30s0                <No XDP program loaded!>
enp31s0                <No XDP program loaded!>
enp32s0                <No XDP program loaded!>
docker0                <No XDP program loaded!>
tun0                   <No XDP program loaded!>

tjcw@r28b29-n10:~/workspace/xdp-tutorial/ebpf-filter$

What am I doing wrong this time ? My test case is in https://github.com/tjcw/xdp-tutorial/tree/20221025-1542/ebpf-filter .

tohojo commented 1 year ago

Chris Ward @.***> writes:

I have changed my application to not load the default program, like


  xsk_cfg.libxdp_flags = XSK_LIBXDP_FLAGS__INHIBIT_PROG_LOAD;
//    xsk_cfg.libxdp_flags = 0;

You're calling xsk_setup_xdp_prog(). This does exactly the same that the flag is trying to prevent libxdp from doing: installing the default XDP program. Which, again, you shouldn't be using at all...

tjcw commented 1 year ago

Do you mean me to use bpftool to load the application XDP program and pin the xsks_map in the file system, and then revise the user-space program so that it opens this map and places the sockets (16 of them because my device supports 16 queues) into the map ?

tohojo commented 1 year ago

Chris Ward @.***> writes:

Do you mean me to use bpftool to load the application XDP program and pin the xsks_map in the file system, and then revise the user-space program so that it opens this map and places the sockets (16 of them because my device supports 16 queues) into the map ?

No. You don't need to pin anything. You are defining xsks_map in your BPF file; you just need to populate it.

Basically, between xdp_programopen_file() and xdp_programattach() you need to do:

obj = xdp_program__bpf_obj(xdp_prog); map = bpf_object__find_map_by_name(obj, "xsks_map");

/ for each key and socket fd: / bpf_map_update_elem(bpf_map__fd(map), key, socket_fd);

tjcw commented 1 year ago

I have revised my test case for that, and I am now getting EBADF (errno=9) from bpf_map_update_elem . My code is under https://github.com/tjcw/xdp-tutorial/tree/20221027-1148/ebpf-filter . I will investigate to see if I can find what is wrong.

To get my test case to compile it was necessary to copy the definitions of struct xsk_socket_info and struct xsk from xsk.c as these are declared static and I need access to the internals. Is this as expected ?

The full run log from my test case is

tjcw@r28b29-n10:~/workspace/xdp-tutorial/ebpf-filter$ sudo ./run.sh
+ export LD_LIBRARY_PATH=/usr/local/lib
+ LD_LIBRARY_PATH=/usr/local/lib
+ ip tuntap add mode tun tun0
ioctl(TUNSETIFF): Device or resource busy
+ ip link set dev tun0 addr 10.1.1.2/24
"10.1.1.2/24" is invalid lladdr.
+ ip link set dev tun0 up
+ ./af_xdp_user -S -d enp25s0 -Q 0 --filename ./af_xdp_kern.o
Opening program file ./af_xdp_kern.o
libbpf: elf: skipping unrecognized data section(8) .xdp_run_config
libbpf: elf: skipping unrecognized data section(9) xdp_metadata
xdp_prog=0x558c92624600
bpf_object=0x558c92621490
xsks_map=0x558c926279b0
xsk_socket__create returns 0
bpf_map_update_elem returns -9
ERROR: Cannot set up socket 0
ERROR: Can't setup AF_XDP sockets "Bad file descriptor"
tjcw@r28b29-n10:~/workspace/xdp-tutorial/ebpf-filter$ 
tjcw commented 1 year ago

The bpf_map_fd is set to -1 , so this must be the result of a previous error. I will investigate.

tjcw commented 1 year ago

I have revised my code (and my copy of libxsk) to work in terms of the file descriptor for the map. My initial pass at this results in errno 77 (EBADFD) when looking for the map file descriptor. I introduced a new function in libxdp my_fetch_xsks_map_fd(cfg.ifname, xdp_prog) which doesn't seem to be doing the right thing. I will debug this tomorrow. My test case is under https://github.com/tjcw/xdp-tutorial/tree/20221027-1659/ebpf-filter and my libxdp is under https://github.com/tjcw/xdp-tools/tree/20221027-1659 .

tjcw commented 1 year ago

Tracing through libxdp, it seems that after a call to xdp_program__open_file the prog->fd is set to -1 , and it is necessary to have something call xdp_program__load to set the file descriptor. In my test case xdp_program__load isn't being called, hence the problem; setting up sockets won't work until prog->fd is set to a real file descriptor. xdp_program__load is declared static, so applications using libxdp aren't suppsed to call this directly.

However I don't think I am supposed to call xdp_program__attach (which would call xdp_program__load inernally) until after the sockets have been set up.

What is the right order to do these in ?

tohojo commented 1 year ago

Ah yes, libxdp doesn't load the program until you attach it. This is because loading has to fudge the program type to be able to attach it to a dispatcher program.

In your case it should be fine to attach the program before populating the map, though; as long as you pass XDP_PASS as the 'flags' argument to the bpf_redirect_map() call. That way, if the map entry doesn't exist in the xsk_map yet, the packet will just be passed up to the stack, which is the same as the behaviour before you attach the program.

Longer term, we should probably expose a version of xdp_program__load() from libxdp, though; it should be possible to do this on kernels 5.10 and newer, at least...

tjcw commented 1 year ago

With this version ( https://github.com/tjcw/xdp-tutorial/tree/20221028-1612/ebpf-filter and https://github.com/tjcw/xdp-tools/tree/20221028-1612 ) where I defer the xsk_configure_socket_all call until after xdp_program__open_file , my_fetch_xsks_map_fd and xdp_program__attach, I get EBUSY (errno=16) from this code

int bpf_map__set_max_entries(struct bpf_map *map, __u32 max_entries)
{
    if (map->obj->loaded)
        return libbpf_err(-EBUSY);

    map->def.max_entries = max_entries;

    /* auto-adjust BPF ringbuf map max_entries to be a multiple of page size */
    if (map->def.type == BPF_MAP_TYPE_RINGBUF)
        map->def.max_entries = adjust_ringbuf_sz(map->def.max_entries);

    return 0;
}

so it looks like something needs to be done before the program is loaded. The run log is

tjcw@r28b29-n10:~/workspace/xdp-tutorial/ebpf-filter$ sudo ./run.sh
+ export LD_LIBRARY_PATH=/usr/local/lib
+ LD_LIBRARY_PATH=/usr/local/lib
+ ip tuntap add mode tun tun0
ioctl(TUNSETIFF): Device or resource busy
+ ip link set dev tun0 addr 10.1.1.2/24
"10.1.1.2/24" is invalid lladdr.
+ ip link set dev tun0 up
+ ./af_xdp_user -S -d enp25s0 -Q 0 --filename ./af_xdp_kern.o
main cfg.filename=./af_xdp_kern.o
main Opening program file ./af_xdp_kern.o
libbpf: elf: skipping unrecognized data section(8) .xdp_run_config
libbpf: elf: skipping unrecognized data section(9) xdp_metadata
libxdp: btf_get_datasec looking for .xdp_run_config found .maps
libxdp: btf_get_datasec looking for .xdp_run_config found .rodata
libxdp: btf_get_datasec looking for .xdp_run_config found .xdp_run_config
main xdp_prog=0x55df9b3955a0
libxdp: xdp_metadata_section=xdp_metadata
libxdp: btf_get_datasec looking for xdp_metadata found .rodata
libxdp: btf_get_datasec looking for xdp_metadata found license
libxdp: btf_get_datasec looking for xdp_metadata found xdp_metadata
libxdp: Verified XDP dispatcher version 1 <= 1
libxdp: btf_get_datasec looking for .xdp_run_config found .rodata
libxdp: btf_get_datasec looking for .xdp_run_config found license
libxdp: btf_get_datasec looking for .xdp_run_config found xdp_metadata
libxdp: DATASEC '.xdp_run_config' not found.
libxdp: Acquired lock from /sys/fs/bpf/xdp with fd 6
libxdp: Reading multiprog component programs from pinned directory
libxdp: btf_get_datasec looking for .xdp_run_config found .maps
libxdp: btf_get_datasec looking for .xdp_run_config found .rodata
libxdp: btf_get_datasec looking for .xdp_run_config found .xdp_run_config
libxdp: btf_get_datasec looking for .xdp_run_config found .maps
libxdp: btf_get_datasec looking for .xdp_run_config found .rodata
libxdp: btf_get_datasec looking for .xdp_run_config found .xdp_run_config
libxdp: btf_get_datasec looking for .xdp_run_config found .maps
libxdp: btf_get_datasec looking for .xdp_run_config found .rodata
libxdp: btf_get_datasec looking for .xdp_run_config found .xdp_run_config
libxdp: btf_get_datasec looking for .xdp_run_config found .maps
libxdp: btf_get_datasec looking for .xdp_run_config found .xdp_run_config
libxdp: Released lock fd 6
libxdp: Found multiprog with id 82 and 4 component progs
libxdp: Generating multi-prog dispatcher for 5 programs
libxdp: Looking for './xdp-dispatcher.o'
libxdp: Looking for '/usr/local/lib/bpf/xdp-dispatcher.o'
libxdp: Loading XDP program from '/usr/local/lib/bpf/xdp-dispatcher.o' section 'xdp_dispatcher'
libbpf: elf: skipping unrecognized data section(7) xdp_metadata
libxdp: btf_get_datasec looking for .xdp_run_config found .rodata
libxdp: btf_get_datasec looking for .xdp_run_config found license
libxdp: btf_get_datasec looking for .xdp_run_config found xdp_metadata
libxdp: DATASEC '.xdp_run_config' not found.
libxdp: xdp_metadata_section=xdp_metadata
libxdp: btf_get_datasec looking for xdp_metadata found .rodata
libxdp: btf_get_datasec looking for xdp_metadata found license
libxdp: btf_get_datasec looking for xdp_metadata found xdp_metadata
libxdp: Verified XDP dispatcher version 1 <= 1
libxdp: Loading multiprog dispatcher for 5 programs
libxdp: Loaded XDP program xdp_dispatcher, got fd 15
libxdp: Checking dispatcher compatibility
libxdp: Looking for './xdp-dispatcher.o'
libxdp: Looking for '/usr/local/lib/bpf/xdp-dispatcher.o'
libxdp: Loading XDP program from '/usr/local/lib/bpf/xdp-dispatcher.o' section 'xdp_pass'
libbpf: elf: skipping unrecognized data section(7) xdp_metadata
libxdp: btf_get_datasec looking for .xdp_run_config found .rodata
libxdp: btf_get_datasec looking for .xdp_run_config found license
libxdp: btf_get_datasec looking for .xdp_run_config found xdp_metadata
libxdp: DATASEC '.xdp_run_config' not found.
libxdp: Loaded XDP program xdp_pass, got fd 21
libxdp: Acquired lock from /sys/fs/bpf/xdp with fd 24
libxdp: Released lock fd 24
libxdp: Linking prog xsk_my_prog as multiprog entry 0
libxdp: btf_get_datasec looking for .xdp_run_config found .maps
libxdp: btf_get_datasec looking for .xdp_run_config found .rodata
libxdp: btf_get_datasec looking for .xdp_run_config found .xdp_run_config
libxdp: Attached prog 'xsk_my_prog' with priority 10 in dispatcher entry 'prog0' with fd 19
libxdp: Linking prog xsk_my_prog as multiprog entry 1
libxdp: btf_get_datasec looking for .xdp_run_config found .maps
libxdp: btf_get_datasec looking for .xdp_run_config found .rodata
libxdp: btf_get_datasec looking for .xdp_run_config found .xdp_run_config
libxdp: Attached prog 'xsk_my_prog' with priority 10 in dispatcher entry 'prog1' with fd 21
libxdp: Linking prog xsk_my_prog as multiprog entry 2
libxdp: btf_get_datasec looking for .xdp_run_config found .maps
libxdp: btf_get_datasec looking for .xdp_run_config found .rodata
libxdp: btf_get_datasec looking for .xdp_run_config found .xdp_run_config
libxdp: Attached prog 'xsk_my_prog' with priority 10 in dispatcher entry 'prog2' with fd 23
libxdp: Linking prog xsk_my_prog as multiprog entry 3
libxdp: Loaded XDP program xsk_my_prog, got fd 29
libxdp: btf_get_datasec looking for .xdp_run_config found .maps
libxdp: btf_get_datasec looking for .xdp_run_config found .rodata
libxdp: btf_get_datasec looking for .xdp_run_config found .xdp_run_config
libxdp: Attached prog 'xsk_my_prog' with priority 10 in dispatcher entry 'prog3' with fd 32
libxdp: Linking prog xsk_def_prog as multiprog entry 4
libxdp: btf_get_datasec looking for .xdp_run_config found .maps
libxdp: btf_get_datasec looking for .xdp_run_config found .xdp_run_config
libxdp: Attached prog 'xsk_def_prog' with priority 20 in dispatcher entry 'prog4' with fd 34
libxdp: Acquired lock from /sys/fs/bpf/xdp with fd 35
libxdp: Pinning multiprog fd 17 beneath /sys/fs/bpf/xdp/dispatch-2-95
libxdp: Pinned link for prog xsk_my_prog at /sys/fs/bpf/xdp/dispatch-2-95/prog0-link
libxdp: Pinned prog xsk_my_prog at /sys/fs/bpf/xdp/dispatch-2-95/prog0-prog
libxdp: Pinned link for prog xsk_my_prog at /sys/fs/bpf/xdp/dispatch-2-95/prog1-link
libxdp: Pinned prog xsk_my_prog at /sys/fs/bpf/xdp/dispatch-2-95/prog1-prog
libxdp: Pinned link for prog xsk_my_prog at /sys/fs/bpf/xdp/dispatch-2-95/prog2-link
libxdp: Pinned prog xsk_my_prog at /sys/fs/bpf/xdp/dispatch-2-95/prog2-prog
libxdp: Pinned link for prog xsk_my_prog at /sys/fs/bpf/xdp/dispatch-2-95/prog3-link
libxdp: Pinned prog xsk_my_prog at /sys/fs/bpf/xdp/dispatch-2-95/prog3-prog
libxdp: Pinned link for prog xsk_def_prog at /sys/fs/bpf/xdp/dispatch-2-95/prog4-link
libxdp: Pinned prog xsk_def_prog at /sys/fs/bpf/xdp/dispatch-2-95/prog4-prog
libxdp: Released lock fd 35
libxdp: Replacing XDP fd 5 with 17 on ifindex 2
libxdp: Loaded 5 programs on ifindex '2' in skb mode
libxdp: Acquired lock from /sys/fs/bpf/xdp with fd 35
libxdp: Unpinning multiprog fd 5 beneath /sys/fs/bpf/xdp/dispatch-2-82
libxdp: Unpinned link for prog xsk_my_prog from /sys/fs/bpf/xdp/dispatch-2-82/prog0-link
libxdp: Unpinned prog xsk_my_prog from /sys/fs/bpf/xdp/dispatch-2-82/prog0-prog
libxdp: Unpinned link for prog xsk_my_prog from /sys/fs/bpf/xdp/dispatch-2-82/prog1-link
libxdp: Unpinned prog xsk_my_prog from /sys/fs/bpf/xdp/dispatch-2-82/prog1-prog
libxdp: Unpinned link for prog xsk_my_prog from /sys/fs/bpf/xdp/dispatch-2-82/prog2-link
libxdp: Unpinned prog xsk_my_prog from /sys/fs/bpf/xdp/dispatch-2-82/prog2-prog
libxdp: Unpinned link for prog xsk_def_prog from /sys/fs/bpf/xdp/dispatch-2-82/prog3-link
libxdp: Unpinned prog xsk_def_prog from /sys/fs/bpf/xdp/dispatch-2-82/prog3-prog
libxdp: Removed pin directory /sys/fs/bpf/xdp/dispatch-2-82
libxdp: Released lock fd 35
libxdp: xsk_size_map max_queues=16
libxdp: xsk_size_map bpf_object__find_map_by_name(0x55df9b38e490, ...) ---> 0x55df9b399db0
libxdp: xsk_size_map bpf_map__set_max_entries(0x55df9b399db0, 16) --> -16
main bpf_object=0x55df9b38e490
libxdp: xsk_socket__create_shared ifname=enp25s0 queue_id=0
libxdp: libbpf_flags=0x00000001
xsk_socket__create returns 0
bpf_map_update_elem(-16,0x7ffd98ae0800,0x7ffd98ae0814,0)
bpf_map_update_elem returns -9
ERROR: Cannot set up socket 0
ERROR: Can't setup AF_XDP sockets "Bad file descriptor"
tjcw@r28b29-n10:~/workspace/xdp-tutorial/ebpf-filter$ 
tohojo commented 1 year ago

Yes, you have to resize the map before loading the program, like the libxdp XSK code is doing...

tjcw commented 1 year ago

I have made some new functions in libxdp to support loading the 'application' eBPF program instead of the default one; see https://github.com/tjcw/xdp-tools/blob/20221103-1502/lib/libxdp/xsk.c#L1307 https://github.com/tjcw/xdp-tools/blob/20221103-1502/lib/libxdp/xsk.c#L1522 https://github.com/tjcw/xdp-tutorial/blob/20221103-1502/ebpf-filter/af_xdp_user.c#L552 This time when I run, I can register 9 receive queues but then I get an error about too many file descriptors. I changed my main program to only attempt 4 queuees; this time it gets to the point where it is handling packets, but only packets for queue 0 are reflected into userspace, and xdp-loader reports

libxdp: Found multiprog with id 449 and 4 component progs
enp25s0                xdp_dispatcher    skb      449  94d5f00c20184d17 
 =>              10     xsk_my_prog               431  24c8d545c0b803d9  XDP_PASS
 =>              10     xsk_my_prog               439  24c8d545c0b803d9  XDP_PASS
 =>              10     xsk_my_prog               447  24c8d545c0b803d9  XDP_PASS
 =>              10     xsk_my_prog               455  24c8d545c0b803d9  XDP_PASS
enp51s0f0              <No XDP program loaded!>

i.e. I am getting the dispatcher and 4 copies of my program, where I want the dispatcher and 1 copy of my program with 4 entries in its xsks_map .@tohojo What do I have wrong now ?

tohojo commented 1 year ago

Looks like you're loading a separate program for each socket? You just need to have one program, and only populate the map with the socket FDs. And, again, you don't need to modify libxdp to do that...

tjcw commented 1 year ago

@tohojo I have to call xsk_socketcreate_shared ( or xsk_socketcreate_shared_named_prog in my revised libxdp ) for each socket, and that loads a copy of the program for each socket. I haven't been able to get xsk_cfg.libxdp_flags = XSK_LIBXDP_FLAGS__INHIBIT_PROG_LOAD to work; that gets into a chicken-and-egg problem as to what is loaded and configured when. Yes, I know I need to have one program and populate the map with the socket FDs; but I'm not yet sure how to achieve that.

tohojo commented 1 year ago

Well, the sequence is, more or less:

xdp_prog = xdp_program__open_file(); bpf_obj = xdp_programbpf_obj(); map = bpf_objectfind_map_by_name(); bpf_map__set_max_elements(); / and any other config before load /

xdp_program__attach(xdp_prog); / loads program /

bpf_map_update_elem(bpf_map__fd(map), queue_id, xsk_fd) / repeat for each socket /

tjcw commented 1 year ago

I have made changes along those lines, but now I find that bpf_map__fd(map) is evaluating to -1 , i.e. no file descriptor. I think this is because xsk_socketcreate_shared doesn't set up the file descriptor for xsks_map when XSK_LIBXDP_FLAGSINHIBIT_PROG_LOAD is set . To get the file descriptor, I would need to call xsk_lookup_bpf_map which is declared static in libxdp/xsk.c , or possibly __xsk_setup_xdp_prog . This seems to need some surgery in libxdp . My test case is here https://github.com/tjcw/xdp-tutorial/tree/20221104-1008/ebpf-filter ; it should build on top of the 'vanilla' xdp-tools, or alternatively on my libxdp from https://github.com/tjcw/xdp-tools/blob/20221103-1502/ .

tohojo commented 1 year ago

You're reading the map fd before you load the program; you'll have to move that call to bpf_map__fd() to after xdp_program__attach()

tjcw commented 1 year ago

Yes, it works now. Closing.