Skip to content
13 changes: 6 additions & 7 deletions include/linux/if_vlan.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,16 +355,17 @@ static inline int __vlan_insert_inner_tag(struct sk_buff *skb,
__be16 vlan_proto, u16 vlan_tci,
unsigned int mac_len)
{
const u8 meta_len = mac_len > ETH_HLEN ? skb_metadata_len(skb) : 0;
struct vlan_ethhdr *veth;

if (skb_cow_head(skb, VLAN_HLEN) < 0)
if (skb_cow_head(skb, meta_len + VLAN_HLEN) < 0)
return -ENOMEM;

skb_push(skb, VLAN_HLEN);

/* Move the mac header sans proto to the beginning of the new header. */
if (likely(mac_len > ETH_TLEN))
memmove(skb->data, skb->data + VLAN_HLEN, mac_len - ETH_TLEN);
skb_postpush_data_move(skb, VLAN_HLEN, mac_len - ETH_TLEN);
if (skb_mac_header_was_set(skb))
skb->mac_header -= VLAN_HLEN;

Expand Down Expand Up @@ -731,18 +732,16 @@ static inline void vlan_set_encap_proto(struct sk_buff *skb,
*
* Expects the skb to contain a VLAN tag in the payload, and to have skb->data
* pointing at the MAC header.
*
* Returns: a new pointer to skb->data, or NULL on failure to pull.
*/
static inline void *vlan_remove_tag(struct sk_buff *skb, u16 *vlan_tci)
static inline void vlan_remove_tag(struct sk_buff *skb, u16 *vlan_tci)
{
struct vlan_hdr *vhdr = (struct vlan_hdr *)(skb->data + ETH_HLEN);

*vlan_tci = ntohs(vhdr->h_vlan_TCI);

memmove(skb->data + VLAN_HLEN, skb->data, 2 * ETH_ALEN);
vlan_set_encap_proto(skb, vhdr);
return __skb_pull(skb, VLAN_HLEN);
__skb_pull(skb, VLAN_HLEN);
skb_postpull_data_move(skb, VLAN_HLEN, 2 * ETH_ALEN);
}

/**
Expand Down
74 changes: 74 additions & 0 deletions include/linux/skbuff.h
Original file line number Diff line number Diff line change
Expand Up @@ -4514,6 +4514,80 @@ static inline void skb_metadata_clear(struct sk_buff *skb)
skb_metadata_set(skb, 0);
}

/**
* skb_data_move - Move packet data and metadata after skb_push() or skb_pull().
* @skb: packet to operate on
* @len: number of bytes pushed or pulled from &sk_buff->data
* @n: number of bytes to memmove() from pre-push/pull &sk_buff->data
*
* Moves both packet data (@n bytes) and metadata. Assumes metadata is located
* immediately before &sk_buff->data prior to the push/pull, and that sufficient
* headroom exists to hold it after an skb_push(). Otherwise, metadata is
* cleared and a one-time warning is issued.
*
* Use skb_postpull_data_move() or skb_postpush_data_move() instead of calling
* this helper directly.
*/
static inline void skb_data_move(struct sk_buff *skb, const int len,
const unsigned int n)
{
const u8 meta_len = skb_metadata_len(skb);
u8 *meta, *meta_end;

if (!len || (!n && !meta_len))
return;

if (!meta_len)
goto no_metadata;

meta_end = skb_metadata_end(skb);
meta = meta_end - meta_len;

if (WARN_ON_ONCE(meta_end + len != skb->data ||
meta + len < skb->head)) {
skb_metadata_clear(skb);
goto no_metadata;
}

memmove(meta + len, meta, meta_len + n);
return;

no_metadata:
memmove(skb->data, skb->data - len, n);
}

/**
* skb_postpull_data_move - Move packet data and metadata after skb_pull().
* @skb: packet to operate on
* @len: number of bytes pulled from &sk_buff->data
* @n: number of bytes to memmove() from pre-pull &sk_buff->data
*
* See skb_data_move() for details.
*/
static inline void skb_postpull_data_move(struct sk_buff *skb,
const unsigned int len,
const unsigned int n)
{
DEBUG_NET_WARN_ON_ONCE(len > INT_MAX);
skb_data_move(skb, len, n);
}

/**
* skb_postpush_data_move - Move packet data and metadata after skb_push().
* @skb: packet to operate on
* @len: number of bytes pushed onto &sk_buff->data
* @n: number of bytes to memmove() from pre-push &sk_buff->data
*
* See skb_data_move() for details.
*/
static inline void skb_postpush_data_move(struct sk_buff *skb,
const unsigned int len,
const unsigned int n)
{
DEBUG_NET_WARN_ON_ONCE(len > INT_MAX);
skb_data_move(skb, -len, n);
}

struct sk_buff *skb_clone_sk(struct sk_buff *skb);

#ifdef CONFIG_NETWORK_PHY_TIMESTAMPING
Expand Down
16 changes: 10 additions & 6 deletions net/core/filter.c
Original file line number Diff line number Diff line change
Expand Up @@ -3251,11 +3251,11 @@ static void bpf_skb_change_protocol(struct sk_buff *skb, u16 proto)

static int bpf_skb_generic_push(struct sk_buff *skb, u32 off, u32 len)
{
/* Caller already did skb_cow() with len as headroom,
/* Caller already did skb_cow() with meta_len+len as headroom,
* so no need to do it here.
*/
skb_push(skb, len);
memmove(skb->data, skb->data + len, off);
skb_postpush_data_move(skb, len, off);
memset(skb->data + off, 0, len);

/* No skb_postpush_rcsum(skb, skb->data + off, len)
Expand All @@ -3279,7 +3279,7 @@ static int bpf_skb_generic_pop(struct sk_buff *skb, u32 off, u32 len)
old_data = skb->data;
__skb_pull(skb, len);
skb_postpull_rcsum(skb, old_data + off, len);
memmove(skb->data, old_data, off);
skb_postpull_data_move(skb, len, off);

return 0;
}
Expand Down Expand Up @@ -3324,10 +3324,11 @@ static int bpf_skb_net_hdr_pop(struct sk_buff *skb, u32 off, u32 len)
static int bpf_skb_proto_4_to_6(struct sk_buff *skb)
{
const u32 len_diff = sizeof(struct ipv6hdr) - sizeof(struct iphdr);
const u8 meta_len = skb_metadata_len(skb);
u32 off = skb_mac_header_len(skb);
int ret;

ret = skb_cow(skb, len_diff);
ret = skb_cow(skb, meta_len + len_diff);
if (unlikely(ret < 0))
return ret;

Expand Down Expand Up @@ -3487,6 +3488,7 @@ static int bpf_skb_net_grow(struct sk_buff *skb, u32 off, u32 len_diff,
u8 inner_mac_len = flags >> BPF_ADJ_ROOM_ENCAP_L2_SHIFT;
bool encap = flags & BPF_F_ADJ_ROOM_ENCAP_L3_MASK;
u16 mac_len = 0, inner_net = 0, inner_trans = 0;
const u8 meta_len = skb_metadata_len(skb);
unsigned int gso_type = SKB_GSO_DODGY;
int ret;

Expand All @@ -3497,7 +3499,7 @@ static int bpf_skb_net_grow(struct sk_buff *skb, u32 off, u32 len_diff,
return -ENOTSUPP;
}

ret = skb_cow_head(skb, len_diff);
ret = skb_cow_head(skb, meta_len + len_diff);
if (unlikely(ret < 0))
return ret;

Expand Down Expand Up @@ -3871,6 +3873,7 @@ static const struct bpf_func_proto sk_skb_change_tail_proto = {
static inline int __bpf_skb_change_head(struct sk_buff *skb, u32 head_room,
u64 flags)
{
const u8 meta_len = skb_metadata_len(skb);
u32 max_len = BPF_SKB_MAX_LEN;
u32 new_len = skb->len + head_room;
int ret;
Expand All @@ -3879,7 +3882,7 @@ static inline int __bpf_skb_change_head(struct sk_buff *skb, u32 head_room,
new_len < skb->len))
return -EINVAL;

ret = skb_cow(skb, head_room);
ret = skb_cow(skb, meta_len + head_room);
if (likely(!ret)) {
/* Idea for this helper is that we currently only
* allow to expand on mac header. This means that
Expand All @@ -3891,6 +3894,7 @@ static inline int __bpf_skb_change_head(struct sk_buff *skb, u32 head_room,
* for redirection into L2 device.
*/
__skb_push(skb, head_room);
skb_postpush_data_move(skb, head_room, 0);
memset(skb->data, 0, head_room);
skb_reset_mac_header(skb);
skb_reset_mac_len(skb);
Expand Down
2 changes: 0 additions & 2 deletions net/core/skbuff.c
Original file line number Diff line number Diff line change
Expand Up @@ -2288,8 +2288,6 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail,
skb->nohdr = 0;
atomic_set(&skb_shinfo(skb)->dataref, 1);

skb_metadata_clear(skb);

/* It is not generally safe to change skb->truesize.
* For the moment, we really care of rx path, or
* when skb is orphaned (not attached to a socket).
Expand Down
20 changes: 10 additions & 10 deletions tools/testing/selftests/bpf/prog_tests/xdp_context_test_run.c
Original file line number Diff line number Diff line change
Expand Up @@ -462,25 +462,25 @@ void test_xdp_context_tuntap(void)
skel->progs.ing_cls_dynptr_offset_oob,
skel->progs.ing_cls,
skel->maps.test_result);
if (test__start_subtest("clone_data_meta_empty_on_data_write"))
if (test__start_subtest("clone_data_meta_kept_on_data_write"))
test_tuntap_mirred(skel->progs.ing_xdp,
skel->progs.clone_data_meta_empty_on_data_write,
skel->progs.clone_data_meta_kept_on_data_write,
&skel->bss->test_pass);
if (test__start_subtest("clone_data_meta_empty_on_meta_write"))
if (test__start_subtest("clone_data_meta_kept_on_meta_write"))
test_tuntap_mirred(skel->progs.ing_xdp,
skel->progs.clone_data_meta_empty_on_meta_write,
skel->progs.clone_data_meta_kept_on_meta_write,
&skel->bss->test_pass);
if (test__start_subtest("clone_dynptr_empty_on_data_slice_write"))
if (test__start_subtest("clone_dynptr_kept_on_data_slice_write"))
test_tuntap_mirred(skel->progs.ing_xdp,
skel->progs.clone_dynptr_empty_on_data_slice_write,
skel->progs.clone_dynptr_kept_on_data_slice_write,
&skel->bss->test_pass);
if (test__start_subtest("clone_dynptr_empty_on_meta_slice_write"))
if (test__start_subtest("clone_dynptr_kept_on_meta_slice_write"))
test_tuntap_mirred(skel->progs.ing_xdp,
skel->progs.clone_dynptr_empty_on_meta_slice_write,
skel->progs.clone_dynptr_kept_on_meta_slice_write,
&skel->bss->test_pass);
if (test__start_subtest("clone_dynptr_rdonly_before_data_dynptr_write"))
if (test__start_subtest("clone_dynptr_rdonly_before_data_dynptr_write_then_rw"))
test_tuntap_mirred(skel->progs.ing_xdp,
skel->progs.clone_dynptr_rdonly_before_data_dynptr_write,
skel->progs.clone_dynptr_rdonly_before_data_dynptr_write_then_rw,
&skel->bss->test_pass);
if (test__start_subtest("clone_dynptr_rdonly_before_meta_dynptr_write"))
test_tuntap_mirred(skel->progs.ing_xdp,
Expand Down
Loading
Loading