Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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_TLEN ? 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 @@ -4561,6 +4561,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_headroom(skb))) {
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 @@ -3253,11 +3253,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 @@ -3281,7 +3281,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 @@ -3326,10 +3326,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 @@ -3489,6 +3490,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 @@ -3499,7 +3501,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 @@ -3873,6 +3875,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 @@ -3881,7 +3884,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 @@ -3893,6 +3896,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 @@ -2289,8 +2289,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
Loading
Loading