diff options
| author | Benjamin Poirier <bpoirier@suse.com> | 2016-02-29 15:03:33 -0800 |
|---|---|---|
| committer | Jiri Slaby <jslaby@suse.cz> | 2016-04-11 16:43:57 +0200 |
| commit | 6af792bb57d0ba6695b5390a19f8eedcf51b602c (patch) | |
| tree | 8f5381fb48a21fbb1fbe04424f00cf9506abc918 /include | |
| parent | e98ac0863a51df3929483164d775efd0d72a3564 (diff) | |
| download | linux-6af792bb57d0ba6695b5390a19f8eedcf51b602c.tar.gz linux-6af792bb57d0ba6695b5390a19f8eedcf51b602c.tar.bz2 linux-6af792bb57d0ba6695b5390a19f8eedcf51b602c.zip | |
mld, igmp: Fix reserved tailroom calculation
commit 1837b2e2bcd23137766555a63867e649c0b637f0 upstream.
The current reserved_tailroom calculation fails to take hlen and tlen into
account.
skb:
[__hlen__|__data____________|__tlen___|__extra__]
^ ^
head skb_end_offset
In this representation, hlen + data + tlen is the size passed to alloc_skb.
"extra" is the extra space made available in __alloc_skb because of
rounding up by kmalloc. We can reorder the representation like so:
[__hlen__|__data____________|__extra__|__tlen___]
^ ^
head skb_end_offset
The maximum space available for ip headers and payload without
fragmentation is min(mtu, data + extra). Therefore,
reserved_tailroom
= data + extra + tlen - min(mtu, data + extra)
= skb_end_offset - hlen - min(mtu, skb_end_offset - hlen - tlen)
= skb_tailroom - min(mtu, skb_tailroom - tlen) ; after skb_reserve(hlen)
Compare the second line to the current expression:
reserved_tailroom = skb_end_offset - min(mtu, skb_end_offset)
and we can see that hlen and tlen are not taken into account.
The min() in the third line can be expanded into:
if mtu < skb_tailroom - tlen:
reserved_tailroom = skb_tailroom - mtu
else:
reserved_tailroom = tlen
Depending on hlen, tlen, mtu and the number of multicast address records,
the current code may output skbs that have less tailroom than
dev->needed_tailroom or it may output more skbs than needed because not all
space available is used.
Fixes: 4c672e4b ("ipv6: mld: fix add_grhead skb_over_panic for devs with large MTUs")
Signed-off-by: Benjamin Poirier <bpoirier@suse.com>
Acked-by: Hannes Frederic Sowa <hannes@stressinduktion.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Diffstat (limited to 'include')
| -rw-r--r-- | include/linux/skbuff.h | 24 |
1 files changed, 24 insertions, 0 deletions
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index e492ab7aadbf..6242a9f80040 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1518,6 +1518,30 @@ static inline void skb_reserve(struct sk_buff *skb, int len) skb->tail += len; } +/** + * skb_tailroom_reserve - adjust reserved_tailroom + * @skb: buffer to alter + * @mtu: maximum amount of headlen permitted + * @needed_tailroom: minimum amount of reserved_tailroom + * + * Set reserved_tailroom so that headlen can be as large as possible but + * not larger than mtu and tailroom cannot be smaller than + * needed_tailroom. + * The required headroom should already have been reserved before using + * this function. + */ +static inline void skb_tailroom_reserve(struct sk_buff *skb, unsigned int mtu, + unsigned int needed_tailroom) +{ + SKB_LINEAR_ASSERT(skb); + if (mtu < skb_tailroom(skb) - needed_tailroom) + /* use at most mtu */ + skb->reserved_tailroom = skb_tailroom(skb) - mtu; + else + /* use up to all available space */ + skb->reserved_tailroom = needed_tailroom; +} + static inline void skb_reset_inner_headers(struct sk_buff *skb) { skb->inner_mac_header = skb->mac_header; |
