diff mbox series

[RFC,v1] net: xdp: allow for layer 3 packets in generic skb handler

Message ID 20200427011002.320081-1-Jason@zx2c4.com
State RFC
Delegated to: BPF Maintainers
Headers show
Series [RFC,v1] net: xdp: allow for layer 3 packets in generic skb handler | expand

Commit Message

Jason A. Donenfeld April 27, 2020, 1:10 a.m. UTC
A user reported a few days ago that packets from wireguard were possibly
ignored by XDP [1]. We haven't heard back from the original reporter to
receive more info, so this here is mostly speculative. Successfully nerd
sniped, Toke and I started poking around. Toke noticed that the generic
skb xdp handler path seems to assume that packets will always have an
ethernet header, which really isn't always the case for layer 3 packets,
which are produced by multiple drivers. This patch is untested, but I
wanted to gauge interest in this approach: if the mac_len is 0, then we
assume that it's a layer 3 packet, and figure out skb->protocol from
looking at the IP header. This patch also adds some stricter testing
around mac_len before we assume that it's an ethhdr.

[1] https://lore.kernel.org/wireguard/M5WzVK5--3-2@tuta.io/

Cc: Toke Høiland-Jørgensen <toke@redhat.com>
Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
---
 net/core/dev.c | 31 +++++++++++++++++++++++--------
 1 file changed, 23 insertions(+), 8 deletions(-)

Comments

Toke Høiland-Jørgensen April 27, 2020, 7:20 a.m. UTC | #1
"Jason A. Donenfeld" <Jason@zx2c4.com> writes:

> A user reported a few days ago that packets from wireguard were possibly
> ignored by XDP [1]. We haven't heard back from the original reporter to
> receive more info, so this here is mostly speculative. Successfully nerd
> sniped, Toke and I started poking around. Toke noticed that the generic
> skb xdp handler path seems to assume that packets will always have an
> ethernet header, which really isn't always the case for layer 3 packets,
> which are produced by multiple drivers. This patch is untested, but I
> wanted to gauge interest in this approach: if the mac_len is 0, then we
> assume that it's a layer 3 packet, and figure out skb->protocol from
> looking at the IP header. This patch also adds some stricter testing
> around mac_len before we assume that it's an ethhdr.

While your patch will fix the header pointer mangling for the skb, it
unfortunately won't fix generic XDP for Wireguard: The assumption that
there's an Ethernet header present is made for compatibility with native
XDP, so you might say it's deliberate. I.e., the eBPF programs running
in the XDP hook expect to see an Ethernet header as part of the packet
data (and parses the packet like in [0]).

So, to make XDP generic work for Wireguard (or other IP-header-only
devices) we'd need to either (1) introduce a new XDP sub-type that
assumes L4 packets, or (2) make Wireguard add a fake Ethernet header to
the head of the packet and set the skb mac_header accordingly.

We've discussed (1) before in other contexts (specifically, adding a
802.11 sub-type), but IIRC we decided that there wasn't enough interest.
I wonder if the same wouldn't be the case for an IP sub-type, since
users would have to re-write their XDP programs to fit that hook type,
and it would only be usable for generic XDP on certain tunnel interface
types. Not sure about the feasibility of (2).

-Toke

[0] https://github.com/xdp-project/xdp-tutorial/blob/master/packet01-parsing/xdp_prog_kern.c
Jason A. Donenfeld April 27, 2020, 10:05 a.m. UTC | #2
(2) is infeasible, but there's another option: we can insert a pseudo
ethernet header during the xdp hook for the case of mac_len==0. I'll
play with that and send an RFC.
Toke Høiland-Jørgensen April 27, 2020, 10:30 a.m. UTC | #3
"Jason A. Donenfeld" <Jason@zx2c4.com> writes:

> (2) is infeasible, but there's another option: we can insert a pseudo
> ethernet header during the xdp hook for the case of mac_len==0. I'll
> play with that and send an RFC.

Oh, right, that might work too! :)

-Toke
diff mbox series

Patch

diff --git a/net/core/dev.c b/net/core/dev.c
index 522288177bbd..1c4b0af09be2 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -4551,9 +4551,11 @@  static u32 netif_receive_generic_xdp(struct sk_buff *skb,
 	xdp->data_hard_start = skb->data - skb_headroom(skb);
 	orig_data_end = xdp->data_end;
 	orig_data = xdp->data;
-	eth = (struct ethhdr *)xdp->data;
-	orig_bcast = is_multicast_ether_addr_64bits(eth->h_dest);
-	orig_eth_type = eth->h_proto;
+	if (mac_len == sizeof(struct ethhdr)) {
+		eth = (struct ethhdr *)xdp->data;
+		orig_bcast = is_multicast_ether_addr_64bits(eth->h_dest);
+		orig_eth_type = eth->h_proto;
+	}
 
 	rxqueue = netif_get_rxqueue(skb);
 	xdp->rxq = &rxqueue->xdp_rxq;
@@ -4583,11 +4585,24 @@  static u32 netif_receive_generic_xdp(struct sk_buff *skb,
 	}
 
 	/* check if XDP changed eth hdr such SKB needs update */
-	eth = (struct ethhdr *)xdp->data;
-	if ((orig_eth_type != eth->h_proto) ||
-	    (orig_bcast != is_multicast_ether_addr_64bits(eth->h_dest))) {
-		__skb_push(skb, ETH_HLEN);
-		skb->protocol = eth_type_trans(skb, skb->dev);
+	if (mac_len == 0) {
+		switch (ip_hdr(skb)->version) {
+		case 4:
+			skb->protocol = htons(ETH_P_IP);
+			break;
+		case 6:
+			skb->protocol = htons(ETH_P_IPV6);
+			break;
+		default:
+			goto do_drop;
+		}
+	} else if (mac_len == sizeof(struct ethhdr)) {
+		eth = (struct ethhdr *)xdp->data;
+		if ((orig_eth_type != eth->h_proto) ||
+		    (orig_bcast != is_multicast_ether_addr_64bits(eth->h_dest))) {
+			__skb_push(skb, ETH_HLEN);
+			skb->protocol = eth_type_trans(skb, skb->dev);
+		}
 	}
 
 	switch (act) {