marxlaml
marxlaml

Reputation: 351

sk_buff filled with 0 in consume_skb tracepoint

This is sort of a follow up to a (deleted) previous question. I have discovered that the sk_buff I am trying to read is filled with 0s. Note that the following code prints 0 twice, once for the successful read, and another time for the supposed value of data_len. Am I going wrong about reading the struct, or is it just not filled on entry to the tracepoint?

SEC("tracepoint/skb/consume_skb")
int handle_skb(struct sk_buff *skb)
{
    unsigned int data_len = 1;
    long ret;

    ret = bpf_probe_read_kernel(&data_len, sizeof(data_len), &(skb->data_len));
    if (ret < 0) {
        bpf_printk("Error on probe read.n \n");
    }
    bpf_printk("ret: %d \n", ret);

    bpf_printk("len: %u \n", data_len);

    return 0;
}

(Although this code perfectly shows my problem, I have tried many different configurations without any change to the result)

The loader I am using: gist

Any help would be appreciated, thanks!

EDIT

I have tested something similar with kfree_skb, here the same problem persists. However when printing protocol available in the tracepoint I get 1 which also cannot be right.

I am now thinking this has to do with something in the setup instead of the code.

Upvotes: 1

Views: 405

Answers (2)

duerrfk
duerrfk

Reputation: 36

I think, you are using the wrong argument format for the consume_skb tracepoint. Instead of using a single sk_buff argument, try the format as reported here:

$ cat /sys/kernel/debug/tracing/events/skb/consume_skb/format
name: consume_skb
ID: 1425
format:
        field:unsigned short common_type;               offset:0;       size:2; signed:0;
        field:unsigned char common_flags;               offset:2;       size:1; signed:0;
        field:unsigned char common_preempt_count;       offset:3;       size:1; signed:0;
        field:int common_pid;                           offset:4;       size:4; signed:1;
        field:void * skbaddr;                           offset:8;       size:8; signed:0;

print fmt: "skbaddr=%p", REC->skbaddr

The following code shows reasonable, non-zero output for me:

struct __attribute__((__packed__)) consume_skb_args {
        unsigned short common_type;
        unsigned char common_flags;
        unsigned char common_preempt_count;
        int common_pid;
        void *skbaddr;
};

SEC("tracepoint/skb/consume_skb")
int tp_consume_skb(struct consume_skb_args *ctx)
{
        char devname[IFNAMSIZ];

        struct sk_buff *skb = (struct sk_buff *) ctx->skbaddr;
        struct net_device *dev;
        unsigned int data_len;

        bpf_probe_read(&dev, sizeof(dev), &(skb->dev));
        bpf_probe_read(devname, IFNAMSIZ, &(dev->name));
        bpf_probe_read(&data_len, sizeof(data_len), &(skb->data_len));

        bpf_printk("skbaddr=%p dev=%s datalen=%d\n", skb, devname, data_len);

        return 0;
}

Ouput:

<idle>-0       [004] ..s. 20482442.519133: 0: skbaddr=00000000d60bcc39 dev=enp2s0 datalen=1232
<idle>-0       [004] ..s. 20482442.519183: 0: skbaddr=000000004be4ad2d dev=enp2s0 datalen=64
<idle>-0       [004] ..s. 20482442.534944: 0: skbaddr=000000005dbe2a3e dev=enp2s0 datalen=64
<idle>-0       [004] .Ns. 20482442.535075: 0: skbaddr=0000000068722cfa dev=enp2s0 datalen=496

Upvotes: 2

marxlaml
marxlaml

Reputation: 351

I am not sure why, but I have tried multiple ways to access the data in skb to no avail.

A "workaround" is to use a kprobe, as then at least the transport header becomes readable as such:

SEC("kprobe/consume_skb")
int BPF_KPROBE(consume_skb, struct sk_buff *skb)
{
    char *head;
    __u16 mh, eth_proto;
    __u8 m0, m1, m2, m3, m4, m5;

    bpf_probe_read(&head, sizeof(skb->head), &(skb->head));
    bpf_probe_read(&mh, sizeof(__u16), &(skb->mac_header));

    char *eth_head = head + mh;

    bpf_probe_read(&eth_proto, sizeof(u16), &eth_head[12]);
    bpf_probe_read(&m0, sizeof(__u8), &eth_head[6]);
    bpf_probe_read(&m1, sizeof(__u8), &eth_head[7]);
    bpf_probe_read(&m2, sizeof(__u8), &eth_head[8]);
    bpf_probe_read(&m3, sizeof(__u8), &eth_head[9]);
    bpf_probe_read(&m4, sizeof(__u8), &eth_head[10]);
    bpf_probe_read(&m5, sizeof(__u8), &eth_head[11]);

    //eth_proto contains the ethernet protocol field
    //m0 to m5 contain the source MAC-address

    return 0;
}

Upvotes: -1

Related Questions