Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: tun: Enable transfer of XDP metadata to skb

When the XDP metadata area was used, it is expected that the same
metadata can also be accessed from TC, as can be read in the description
of the bpf_xdp_adjust_meta helper function. In the tun driver, this was
not yet implemented.

To make this work, the skb that is being built on XDP_PASS should know
of the current size of the metadata area. This is ensured by adding
calls to skb_metadata_set. For the tun_xdp_one code path, an additional
check is necessary to handle the case where the externally initialized
xdp_buff has no metadata support (xdp->data_meta == xdp->data + 1).

More information about this feature can be found in the commit message
of commit de8f3a83b0a0 ("bpf: add meta pointer for direct access").

Signed-off-by: Marcus Wichelmann <marcus.wichelmann@hetzner-cloud.de>
Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
Reviewed-by: Willem de Bruijn <willemb@google.com>
Acked-by: Jason Wang <jasowang@redhat.com>
Link: https://patch.msgid.link/20250305213438.3863922-3-marcus.wichelmann@hetzner-cloud.de

authored by

Marcus Wichelmann and committed by
Martin KaFai Lau
0ca23a4d c2315ebb

+22 -3
+22 -3
drivers/net/tun.c
··· 1535 1535 1536 1536 static struct sk_buff *__tun_build_skb(struct tun_file *tfile, 1537 1537 struct page_frag *alloc_frag, char *buf, 1538 - int buflen, int len, int pad) 1538 + int buflen, int len, int pad, 1539 + int metasize) 1539 1540 { 1540 1541 struct sk_buff *skb = build_skb(buf, buflen); 1541 1542 ··· 1545 1544 1546 1545 skb_reserve(skb, pad); 1547 1546 skb_put(skb, len); 1547 + if (metasize) 1548 + skb_metadata_set(skb, metasize); 1548 1549 skb_set_owner_w(skb, tfile->socket.sk); 1549 1550 1550 1551 get_page(alloc_frag->page); ··· 1606 1603 char *buf; 1607 1604 size_t copied; 1608 1605 int pad = TUN_RX_PAD; 1606 + int metasize = 0; 1609 1607 int err = 0; 1610 1608 1611 1609 rcu_read_lock(); ··· 1634 1630 if (hdr->gso_type || !xdp_prog) { 1635 1631 *skb_xdp = 1; 1636 1632 return __tun_build_skb(tfile, alloc_frag, buf, buflen, len, 1637 - pad); 1633 + pad, metasize); 1638 1634 } 1639 1635 1640 1636 *skb_xdp = 0; ··· 1669 1665 1670 1666 pad = xdp.data - xdp.data_hard_start; 1671 1667 len = xdp.data_end - xdp.data; 1668 + 1669 + /* It is known that the xdp_buff was prepared with metadata 1670 + * support, so the metasize will never be negative. 1671 + */ 1672 + metasize = xdp.data - xdp.data_meta; 1672 1673 } 1673 1674 bpf_net_ctx_clear(bpf_net_ctx); 1674 1675 rcu_read_unlock(); 1675 1676 local_bh_enable(); 1676 1677 1677 - return __tun_build_skb(tfile, alloc_frag, buf, buflen, len, pad); 1678 + return __tun_build_skb(tfile, alloc_frag, buf, buflen, len, pad, 1679 + metasize); 1678 1680 1679 1681 out: 1680 1682 bpf_net_ctx_clear(bpf_net_ctx); ··· 2363 2353 struct sk_buff_head *queue; 2364 2354 u32 rxhash = 0, act; 2365 2355 int buflen = hdr->buflen; 2356 + int metasize = 0; 2366 2357 int ret = 0; 2367 2358 bool skb_xdp = false; 2368 2359 struct page *page; ··· 2417 2406 2418 2407 skb_reserve(skb, xdp->data - xdp->data_hard_start); 2419 2408 skb_put(skb, xdp->data_end - xdp->data); 2409 + 2410 + /* The externally provided xdp_buff may have no metadata support, which 2411 + * is marked by xdp->data_meta being xdp->data + 1. This will lead to a 2412 + * metasize of -1 and is the reason why the condition checks for > 0. 2413 + */ 2414 + metasize = xdp->data - xdp->data_meta; 2415 + if (metasize > 0) 2416 + skb_metadata_set(skb, metasize); 2420 2417 2421 2418 if (tun_vnet_hdr_to_skb(tun->flags, skb, gso)) { 2422 2419 atomic_long_inc(&tun->rx_frame_errors);