Search code examples
cebpftracepoint

What is the correct way for reading kernel tracepoint arguments?


I am trying to read the arguments of the following tracepoint program:


// Declared at /sys/kernel/debug/tracing/events/net/netif_receive_skb/format
struct netif_receive_skb_context {
    unsigned short  type;
    unsigned char   flags;
    unsigned char   preempt_count;
    int             pid;
    const void *skbaddr;
    unsigned int len;
    int data_loc_name;
};


// Userspace path: /sys/kernel/debug/tracing/events/net/netif_receive_skb
SEC("tracepoint/net/netif_receive_skb")
int netif_receive_skb(struct netif_receive_skb_context *ctx) {
    // Attempt to read socket buffer from kernel structure.
    struct __sk_buff skb;
    bpf_probe_read(&skb, sizeof(skb), ctx->skbaddr);
    
    void *data_end = (void *)(long)skb.data_end;
    void *data     = (void *)(long)skb.data;
    struct ethhdr *eth = data;
    // Retrieve L2 header.
    if ((void*)eth + sizeof(*eth) > data_end) {
        return 0;
    }

    
    // Retrieve IP header.
    struct iphdr *ip = data + sizeof(*eth);
    if ((void*)ip + sizeof(*ip) > data_end) {
        return 0;
    }

    unsigned char proto;
    bpf_probe_read(&proto, sizeof(proto), &ip->protocol);
    bpf_printk("Got here in netif_rx_entry with proto: %d\n", proto);

    return 0;
}

However I am consistently seeing IP protocol equals zero which makes no sense (I am sending a packet with TCP, proto=6).. How should I approach reading the memory and fields after assigning to skb?


Solution

  • Firstly, one way to check whether your context structure is correct is to look for the corresponding structure in the vmlinux.h file. Here is a link to more about vmlinux.h and how to generate one from your local machine.

    Secondly, it seems like your skb fields are empty. bpf_probe_read() reads struct sk_buff and not struct __sk_buff.

    Thirdly, running your code, I am unsure which packets you are trying to catch. It seems like there is no ethernet header when packets are captured from this function.

    To get the ip protocol of your packets, your function would look like

    SEC("tracepoint/net/netif_receive_skb")
    int netif_receive_skb(struct trace_event_raw_net_dev_template *ctx) {
        // Attempt to read socket buffer from kernel structure.
        struct sk_buff skb;
        bpf_probe_read(&skb, sizeof(skb), ctx->skbaddr);
        // Retrieve IP header.
        struct iphdr iph;
        bpf_probe_read(&iph, sizeof(struct iphdr), skb.data);
        bpf_printk("Got here in netif_receive_skb with proto: %d\n", iph.protocol);
        return 0;
    }