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.wichelm...@hetzner-cloud.de>
Reviewed-by: Willem de Bruijn <will...@google.com>
Acked-by: Jason Wang <jasow...@redhat.com>
---
 drivers/net/tun.c | 25 ++++++++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)

diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index cd463833a0ad..f75f912a0225 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -1535,7 +1535,8 @@ static bool tun_can_build_skb(struct tun_struct *tun, 
struct tun_file *tfile,
 
 static struct sk_buff *__tun_build_skb(struct tun_file *tfile,
                                       struct page_frag *alloc_frag, char *buf,
-                                      int buflen, int len, int pad)
+                                      int buflen, int len, int pad,
+                                      int metasize)
 {
        struct sk_buff *skb = build_skb(buf, buflen);
 
@@ -1544,6 +1545,8 @@ static struct sk_buff *__tun_build_skb(struct tun_file 
*tfile,
 
        skb_reserve(skb, pad);
        skb_put(skb, len);
+       if (metasize)
+               skb_metadata_set(skb, metasize);
        skb_set_owner_w(skb, tfile->socket.sk);
 
        get_page(alloc_frag->page);
@@ -1603,6 +1606,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct 
*tun,
        char *buf;
        size_t copied;
        int pad = TUN_RX_PAD;
+       int metasize = 0;
        int err = 0;
 
        rcu_read_lock();
@@ -1630,7 +1634,7 @@ static struct sk_buff *tun_build_skb(struct tun_struct 
*tun,
        if (hdr->gso_type || !xdp_prog) {
                *skb_xdp = 1;
                return __tun_build_skb(tfile, alloc_frag, buf, buflen, len,
-                                      pad);
+                                      pad, metasize);
        }
 
        *skb_xdp = 0;
@@ -1665,12 +1669,18 @@ static struct sk_buff *tun_build_skb(struct tun_struct 
*tun,
 
                pad = xdp.data - xdp.data_hard_start;
                len = xdp.data_end - xdp.data;
+
+               /* It is known that the xdp_buff was prepared with metadata
+                * support, so the metasize will never be negative.
+                */
+               metasize = xdp.data - xdp.data_meta;
        }
        bpf_net_ctx_clear(bpf_net_ctx);
        rcu_read_unlock();
        local_bh_enable();
 
-       return __tun_build_skb(tfile, alloc_frag, buf, buflen, len, pad);
+       return __tun_build_skb(tfile, alloc_frag, buf, buflen, len, pad,
+                              metasize);
 
 out:
        bpf_net_ctx_clear(bpf_net_ctx);
@@ -2353,6 +2363,7 @@ static int tun_xdp_one(struct tun_struct *tun,
        struct sk_buff_head *queue;
        u32 rxhash = 0, act;
        int buflen = hdr->buflen;
+       int metasize = 0;
        int ret = 0;
        bool skb_xdp = false;
        struct page *page;
@@ -2407,6 +2418,14 @@ static int tun_xdp_one(struct tun_struct *tun,
        skb_reserve(skb, xdp->data - xdp->data_hard_start);
        skb_put(skb, xdp->data_end - xdp->data);
 
+       /* The externally provided xdp_buff may have no metadata support, which
+        * is marked by xdp->data_meta being xdp->data + 1. This will lead to a
+        * metasize of -1 and is the reason why the condition checks for > 0.
+        */
+       metasize = xdp->data - xdp->data_meta;
+       if (metasize > 0)
+               skb_metadata_set(skb, metasize);
+
        if (tun_vnet_hdr_to_skb(tun->flags, skb, gso)) {
                atomic_long_inc(&tun->rx_frame_errors);
                kfree_skb(skb);
-- 
2.43.0


Reply via email to