Search code examples
linux-kernelebpfbpfwireguard

Determine if sk_buff is L2 or L3


I'm attaching an eBPF packet filter declared as:

int sock_peek_packet(struct __sk_buff *skb);

Using the python bcc library, as so:

interface = 'wg0' # or 'eth0'
b = bcc.BPF(src_file=src_path, debug=0)
fn = b.load_func('sock_peek_packet', bcc.BPF.SOCKET_FILTER)
bcc.BPF.attach_raw_socket(dev=interface, fn=fn)

When attaching to eth0, the contents of skb is an L2 ethernet frame (i.e. starts with a struct ethhdr, like how you find on examples all over the internet).

However, when attaching to wg0, the contents of skb is an L3 (IP) packet, (i.e, starts with a struct iphdr) with no proceeding ethernet header.

How can I determine at runtime which type of packet I'm getting? (in my final program I will be attaching to all interfaces on the host). skb->protocol and skb->pkt_type do not change between eth0 and wg0.

Note: currently testing on Linux-6.1.0-21-amd64 but have observed the same behavior on all other versions tested


Solution

  • You cannot choose. This comes down to the device type. The eth0 devices is L2 aware because it actually sends out packets over the wire. But a device like wg0 is virtual, L3 packets go in, and L3 packets come out.

    As far as I know, the way you can tell before hand is to see if the device has a MAC address when you inspect it via ip l. If a device has no MAC, it is an L3 device.