Reputation: 7090
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
Upvotes: 1
Views: 68
Reputation: 7908
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.
Upvotes: 3
Reputation: 13063
I don't believe there's a field of __sk_buff
(or some helper) that can tell you at runtime in which case you are (L2 or L3). Depending on your situation, you may be able have to parse the packet to check its first header (see below).
I would however recommend to detect the device type in userspace and use an eBPF global variable to tell the eBPF program what headers to expect. You can achieve that with the method pointed out by Dylan.
Detecting at Runtime Without Help from Userspace
If you know which MAC and IP addresses to expect, you can probably do this without risking an L2 header being confused for an L3 header (or vice-versa). The following for example checks if the first header is an IPv4 header with the expected destination IP address:
void *data = (void *)(long)skb->data;
unsigned char dst_mac[ETH_ALEN];
mac_offset = data + offsetof(struct ethhdr, h_dest);
if (memcpy(mac_offset, expected_dst_mac, ETH_ALEN)) {
// The destination MAC address doesn't match what we expect,
// so this likely a packet on the L3 device.
}
Note you will need to add the appropriate bounds check.
This is somewhat fragile. You might receive packets with incorrect destination MAC addresses, in which case they will be treated as L3. You will need to consider that possibility and its security implications.
Upvotes: 2