2

I tried to implement the mapping I talked about in this post: AF_XDP: map `(SRC-IP, DST-IP, DST-Port)` to index to `BPF_MAP_TYPE_XSKMAP`

My Kernelprogram has this map:

struct bpf_map_def SEC("maps") xdp_packet_mapping = {
    .type = BPF_MAP_TYPE_HASH,
    .key_size = sizeof(struct pckt_raw_idntfy),
    .value_size = sizeof(int),
    .max_entries = sizeof(int)
};

with

struct pckt_raw_idntfy {
    int src_ip;
    int dst_ip;
    uint16_t dst_port;
};

In the AF-XDP program I create a pckt_raw_idntfy-struct with the corresponding values of the packet which is currently processed as a key:

const struct pckt_raw_idntfy raw = {
    .src_ip = iph->saddr,
    .dst_ip = iph->daddr,
    .dst_port = udh->dest
};

I then lookup the value in xdp_packet_mapping and redirect the packet into user-space in case the value exists:

void *ret = bpf_map_lookup_elem(&xdp_packet_mapping, &raw);
if(ret) {
    const int *idx = (int*)(ret);
    if (bpf_map_lookup_elem(&xsks_map, idx)) {
        return bpf_redirect_map(&xsks_map, *idx, 0);
    }
}

In user-space, I populate the map like this:

int find_map_fd(struct bpf_object *bpf_obj, const char *mapname) {
    struct bpf_map *map;
    int map_fd = -1;

    map = bpf_object__find_map_by_name(bpf_obj, mapname);
    if (!map) {
        fprintf(stderr, "ERR: cannot find map by name: %s\n", mapname);
        exit(1);
    } else {
        map_fd = bpf_map__fd(map);
    }
    return map_fd;
}

void populate_packet_mapping(struct bpf_object *bpf_obj) {
    const int xdp_packet_map_fd = find_map_fd(bpf_obj, "xdp_packet_mapping");

    for(uint16_t i = 0; i < ip_addrs_size; i++) {
        const struct pckt_idntfy *pck_triple = ip_addrs[i];
        struct pckt_raw_idntfy *raw;
        pckt_idntfy_to_raw(pck_triple, raw);

        int ret = bpf_map_update_elem(xdp_packet_map_fd, raw, &i, 0);
        if(ret == 0) {
            ;
        } else {
            fprintf(stderr, "Lookup elem for key %u failed!\n", i);
        }
    }
}

Compilation works fine but if I try to execute the program (which loads the AF-XDP kernel program right after program start), I get this error:

libbpf: load bpf program failed: Permission denied
libbpf: -- BEGIN DUMP LOG ---
libbpf: 
0: (b7) r6 = 2
1: (61) r2 = *(u32 *)(r1 +4)
2: (61) r1 = *(u32 *)(r1 +0)
3: (bf) r3 = r1
4: (07) r3 += 196
5: (2d) if r3 > r2 goto pc+49
 R1_w=pkt(id=0,off=0,r=196,imm=0) R2_w=pkt_end(id=0,off=0,imm=0) R3_w=pkt(id=0,off=196,r=196,imm=0) R6_w=inv2 R10=fp0
6: (71) r3 = *(u8 *)(r1 +12)
7: (71) r4 = *(u8 *)(r1 +13)
8: (67) r4 <<= 8
9: (4f) r4 |= r3
10: (55) if r4 != 0x8 goto pc+44
 R1_w=pkt(id=0,off=0,r=196,imm=0) R2_w=pkt_end(id=0,off=0,imm=0) R3_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff)) R4_w=inv8 R6_w=inv2 R10=fp0
11: (bf) r3 = r1
12: (07) r3 += 414
13: (2d) if r3 > r2 goto pc+41
 R1=pkt(id=0,off=0,r=414,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3=pkt(id=0,off=414,r=414,imm=0) R4=inv8 R6=inv2 R10=fp0
14: (71) r3 = *(u8 *)(r1 +23)
15: (55) if r3 != 0x11 goto pc+39
 R1=pkt(id=0,off=0,r=414,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=inv17 R4=inv8 R6=inv2 R10=fp0
16: (bf) r3 = r1
17: (07) r3 += 14
18: (71) r4 = *(u8 *)(r3 +0)
19: (67) r4 <<= 2
20: (57) r4 &= 60
21: (bf) r5 = r4
22: (27) r5 *= 20
23: (bf) r0 = r3
24: (0f) r0 += r5
last_idx 24 first_idx 13
regs=20 stack=0 before 23: (bf) r0 = r3
regs=20 stack=0 before 22: (27) r5 *= 20
regs=20 stack=0 before 21: (bf) r5 = r4
regs=10 stack=0 before 20: (57) r4 &= 60
regs=10 stack=0 before 19: (67) r4 <<= 2
regs=10 stack=0 before 18: (71) r4 = *(u8 *)(r3 +0)
25: (2d) if r0 > r2 goto pc+29
 R0=pkt(id=1,off=14,r=14,umax_value=1200,var_off=(0x0; 0x7f0)) R1=pkt(id=0,off=0,r=414,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3=pkt(id=0,off=14,r=414,imm=0) R4=inv(id=0,umax_value=60,var_off=(0x0; 0x3c)) R5=invP(id=0,umax_value=1200,var_off=(0x0; 0x7f0)) R6=inv2 R10=fp0
26: (0f) r3 += r4
last_idx 26 first_idx 25
regs=10 stack=0 before 25: (2d) if r0 > r2 goto pc+29
 R0_rw=pkt(id=1,off=14,r=0,umax_value=1200,var_off=(0x0; 0x7f0)) R1=pkt(id=0,off=0,r=414,imm=0) R2_r=pkt_end(id=0,off=0,imm=0) R3_rw=pkt(id=0,off=14,r=414,imm=0) R4_rw=invP(id=0,umax_value=60,var_off=(0x0; 0x3c)) R5_w=invP(id=0,umax_value=1200,var_off=(0x0; 0x7f0)) R6=inv2 R10=fp0
parent didn't have regs=10 stack=0 marks
last_idx 24 first_idx 13
regs=10 stack=0 before 24: (0f) r0 += r5
regs=10 stack=0 before 23: (bf) r0 = r3
regs=10 stack=0 before 22: (27) r5 *= 20
regs=10 stack=0 before 21: (bf) r5 = r4
regs=10 stack=0 before 20: (57) r4 &= 60
regs=10 stack=0 before 19: (67) r4 <<= 2
regs=10 stack=0 before 18: (71) r4 = *(u8 *)(r3 +0)
27: (bf) r4 = r3
28: (07) r4 += 64
29: (2d) if r4 > r2 goto pc+25
 R0=pkt(id=1,off=14,r=14,umax_value=1200,var_off=(0x0; 0x7f0)) R1=pkt(id=0,off=0,r=414,imm=0) R2=pkt_end(id=0,off=0,imm=0) R3_w=pkt(id=2,off=14,r=78,umax_value=60,var_off=(0x0; 0x3c)) R4_w=pkt(id=2,off=78,r=78,umax_value=60,var_off=(0x0; 0x3c)) R5=invP(id=0,umax_value=1200,var_off=(0x0; 0x7f0)) R6=inv2 R10=fp0
30: (61) r2 = *(u32 *)(r1 +26)
31: (63) *(u32 *)(r10 -16) = r2
32: (61) r1 = *(u32 *)(r1 +30)
33: (63) *(u32 *)(r10 -12) = r1
34: (69) r1 = *(u16 *)(r3 +2)
35: (6b) *(u16 *)(r10 -8) = r1
36: (bf) r2 = r10
37: (07) r2 += -16
38: (18) r1 = 0xffff915728bba000
40: (85) call bpf_map_lookup_elem#1
invalid indirect read from stack off -16+10 size 12
processed 40 insns (limit 1000000) max_states_per_insn 0 total_states 2 peak_states 2 mark_read 1

libbpf: -- END LOG --

Any ideas what I did wrong?

Edit: Thanks to Qeole, defining the struct with padding solved the problem:

struct pckt_raw_idntfy {
    int src_ip;
    int dst_ip;
    uint16_t dst_port;
    uint16_t pad;
};
binaryBigInt
  • 1,526
  • 2
  • 18
  • 44
  • 1
    Try padding or packing your key. See the [BPF and XDP Reference Guide](https://cilium.readthedocs.io/en/stable/bpf/) from Cilium (grep for `indirect`). – Qeole Mar 09 '20 at 13:30

2 Answers2

3

The BPF and XDP Reference Guide from Cilium has an excellent explanation for this problem.

In short, this is because the compiler automatically adds some padding to your key and moves it to the stack before calling the map update helper. But when this program is checked, the verifier realises there are some uninitialised bytes it is not aware of, and rejects the program.

You could fix it by packing your struct, although this also has some disadvantages. The recommended solution is to manually pad your key to have it fit on a multiple of four bytes. From OP's edit:

struct pckt_raw_idntfy {
    int src_ip;           /* 4 bytes */
    int dst_ip;           /* 4 bytes */
    uint16_t dst_port;    /* 2 bytes */
    uint16_t pad;         /* adding 2 bytes of padding here */
};

Please refer to the guide for more details (grep for indirect).

Qeole
  • 8,284
  • 1
  • 24
  • 52
1

Another solution, as suggested here, you can force the compiler to remove the padding with:

__builtin_memset(&raw, 0, sizeof(struct pckt_raw_idntfy));
void *ret = bpf_map_lookup_elem(&xdp_packet_mapping, &raw);

However, it depends on the compiler implementation. If are looking a more portable solution, I'd recommend you to follow the @Qeole's answer: manually pad.

nav
  • 410
  • 6
  • 14