All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/6]GSO_BY_FRAGS correctness improvements
@ 2018-02-27 13:04 Daniel Axtens
  2018-02-27 13:04 ` [PATCH 1/6] net: rename skb_gso_validate_mtu -> skb_gso_validate_network_len Daniel Axtens
                   ` (5 more replies)
  0 siblings, 6 replies; 12+ messages in thread
From: Daniel Axtens @ 2018-02-27 13:04 UTC (permalink / raw)
  To: netdev; +Cc: Daniel Axtens

As requested [1], I went through and had a look at users of gso_size to
see if there were things that need to be fixed to consider
GSO_BY_FRAGS, and I have tried to improve our helper functions to deal
with this case.

I found a few, and this fixes all but one of them. The one thing that
remains is qdisc_pkt_len_init which is discussed at [2] - it's caught
up in the GSO_DODGY mess. I don't have any expertise in GSO_DODGY, and
it looks like a good clean fix will involve unpicking the whole
validation mess, so I have left it for now.

Patch 1 renames skb_gso_validate_mtu to skb_gso_validate_network_len.
This is follow-up to my earlier patch 2b16f048729b ("net: create
skb_gso_validate_mac_len()"), and just makes everything a bit clearer.

Patches 2 and 3 replace the final users of skb_gso_network_seglen() -
which doesn't consider GSO_BY_FRAGS - with
skb_gso_validate_network_len(), which does. This allows me to make the
skb_gso_*_seglen functions private in patch 4 - now future users won't
accidentally do the wrong comparison.

Lastly, there are 3 eBPF opcodes that change the gso_size of an SKB
and don't consider GSO_BY_FRAGS. To address this, patch 5 adds a
couple of helpers for changing gso_size that throw a WARN if you use
them on a GSO_BY_FRAGS packet. To return a useful error to eBPF, patch
6 goes further and prevents any change of gso_size for SCTP GSO skbs.

Regards,
Daniel

[1] https://patchwork.ozlabs.org/comment/1852414/
[2] https://www.spinics.net/lists/netdev/msg482397.html

PS: This is all in the core networking stack. For a driver to be
affected by this it would need to support NETIF_F_GSO_SCTP /
NETIF_F_GSO_SOFTWARE and then either use gso_size or not be a purely
virtual device. (Many drivers look at gso_size, but do not support
SCTP segmentation, so the core network will segment an SCTP gso before
it hits them.) Based on that, the only driver that may be affected is
sunvnet, but I have no way of testing it, so I haven't looked at it.

Daniel Axtens (6):
  net: rename skb_gso_validate_mtu -> skb_gso_validate_network_len
  net: sched: tbf: handle GSO_BY_FRAGS case in enqueue
  net: xfrm: use skb_gso_validate_network_len() to check gso sizes
  net: make skb_gso_*_seglen functions private
  net: add and use helpers when adjusting gso_size
  net: filter: refuse to change gso_size of SCTP packets

 Documentation/networking/segmentation-offloads.txt | 11 ++++-
 include/linux/skbuff.h                             | 51 ++++++++--------------
 net/core/filter.c                                  | 24 ++++++++--
 net/core/skbuff.c                                  | 48 +++++++++++++++++---
 net/ipv4/ip_forward.c                              |  2 +-
 net/ipv4/ip_output.c                               |  2 +-
 net/ipv4/netfilter/nf_flow_table_ipv4.c            |  2 +-
 net/ipv4/xfrm4_output.c                            |  3 +-
 net/ipv6/ip6_output.c                              |  2 +-
 net/ipv6/netfilter/nf_flow_table_ipv6.c            |  2 +-
 net/ipv6/xfrm6_output.c                            |  2 +-
 net/mpls/af_mpls.c                                 |  2 +-
 net/sched/sch_tbf.c                                |  3 +-
 net/xfrm/xfrm_device.c                             |  2 +-
 14 files changed, 99 insertions(+), 57 deletions(-)

-- 
2.14.1

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH 1/6] net: rename skb_gso_validate_mtu -> skb_gso_validate_network_len
  2018-02-27 13:04 [PATCH 0/6]GSO_BY_FRAGS correctness improvements Daniel Axtens
@ 2018-02-27 13:04 ` Daniel Axtens
  2018-02-27 13:04 ` [PATCH 2/6] net: sched: tbf: handle GSO_BY_FRAGS case in enqueue Daniel Axtens
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 12+ messages in thread
From: Daniel Axtens @ 2018-02-27 13:04 UTC (permalink / raw)
  To: netdev; +Cc: Daniel Axtens

If you take a GSO skb, and split it into packets, will the network
length (L3 headers + L4 headers + payload) of those packets be small
enough to fit within a given MTU?

skb_gso_validate_mtu gives you the answer to that question. However,
we recently added to add a way to validate the MAC length of a split GSO
skb (L2+L3+L4+payload), and the names get confusing, so rename
skb_gso_validate_mtu to skb_gso_validate_network_len

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 include/linux/skbuff.h                  |  2 +-
 net/core/skbuff.c                       | 11 ++++++-----
 net/ipv4/ip_forward.c                   |  2 +-
 net/ipv4/ip_output.c                    |  2 +-
 net/ipv4/netfilter/nf_flow_table_ipv4.c |  2 +-
 net/ipv6/ip6_output.c                   |  2 +-
 net/ipv6/netfilter/nf_flow_table_ipv6.c |  2 +-
 net/mpls/af_mpls.c                      |  2 +-
 net/xfrm/xfrm_device.c                  |  2 +-
 9 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 9bc1750ca3d3..eeb76bb7730a 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -3289,7 +3289,7 @@ void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len);
 int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen);
 void skb_scrub_packet(struct sk_buff *skb, bool xnet);
 unsigned int skb_gso_transport_seglen(const struct sk_buff *skb);
-bool skb_gso_validate_mtu(const struct sk_buff *skb, unsigned int mtu);
+bool skb_gso_validate_network_len(const struct sk_buff *skb, unsigned int mtu);
 bool skb_gso_validate_mac_len(const struct sk_buff *skb, unsigned int len);
 struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features);
 struct sk_buff *skb_vlan_untag(struct sk_buff *skb);
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 96d36b81a3a5..0c0c1d6f28ef 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -4957,19 +4957,20 @@ static inline bool skb_gso_size_check(const struct sk_buff *skb,
 }
 
 /**
- * skb_gso_validate_mtu - Return in case such skb fits a given MTU
+ * skb_gso_validate_network_len - Will a split GSO skb fit into a given MTU?
  *
  * @skb: GSO skb
  * @mtu: MTU to validate against
  *
- * skb_gso_validate_mtu validates if a given skb will fit a wanted MTU
- * once split.
+ * skb_gso_validate_network_len validates if a given skb will fit a
+ * wanted MTU once split. It considers L3 headers, L4 headers, and the
+ * payload.
  */
-bool skb_gso_validate_mtu(const struct sk_buff *skb, unsigned int mtu)
+bool skb_gso_validate_network_len(const struct sk_buff *skb, unsigned int mtu)
 {
 	return skb_gso_size_check(skb, skb_gso_network_seglen(skb), mtu);
 }
-EXPORT_SYMBOL_GPL(skb_gso_validate_mtu);
+EXPORT_SYMBOL_GPL(skb_gso_validate_network_len);
 
 /**
  * skb_gso_validate_mac_len - Will a split GSO skb fit in a given length?
diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c
index 2dd21c3281a1..b54b948b0596 100644
--- a/net/ipv4/ip_forward.c
+++ b/net/ipv4/ip_forward.c
@@ -55,7 +55,7 @@ static bool ip_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
 	if (skb->ignore_df)
 		return false;
 
-	if (skb_is_gso(skb) && skb_gso_validate_mtu(skb, mtu))
+	if (skb_is_gso(skb) && skb_gso_validate_network_len(skb, mtu))
 		return false;
 
 	return true;
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index e8e675be60ec..66340ab750e6 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -248,7 +248,7 @@ static int ip_finish_output_gso(struct net *net, struct sock *sk,
 
 	/* common case: seglen is <= mtu
 	 */
-	if (skb_gso_validate_mtu(skb, mtu))
+	if (skb_gso_validate_network_len(skb, mtu))
 		return ip_finish_output2(net, sk, skb);
 
 	/* Slowpath -  GSO segment length exceeds the egress MTU.
diff --git a/net/ipv4/netfilter/nf_flow_table_ipv4.c b/net/ipv4/netfilter/nf_flow_table_ipv4.c
index 25d2975da156..2447077d163d 100644
--- a/net/ipv4/netfilter/nf_flow_table_ipv4.c
+++ b/net/ipv4/netfilter/nf_flow_table_ipv4.c
@@ -185,7 +185,7 @@ static bool __nf_flow_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
 	if ((ip_hdr(skb)->frag_off & htons(IP_DF)) == 0)
 		return false;
 
-	if (skb_is_gso(skb) && skb_gso_validate_mtu(skb, mtu))
+	if (skb_is_gso(skb) && skb_gso_validate_network_len(skb, mtu))
 		return false;
 
 	return true;
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 997c7f19ad62..a8a919520090 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -412,7 +412,7 @@ static bool ip6_pkt_too_big(const struct sk_buff *skb, unsigned int mtu)
 	if (skb->ignore_df)
 		return false;
 
-	if (skb_is_gso(skb) && skb_gso_validate_mtu(skb, mtu))
+	if (skb_is_gso(skb) && skb_gso_validate_network_len(skb, mtu))
 		return false;
 
 	return true;
diff --git a/net/ipv6/netfilter/nf_flow_table_ipv6.c b/net/ipv6/netfilter/nf_flow_table_ipv6.c
index d346705d6ee6..207cb35569b1 100644
--- a/net/ipv6/netfilter/nf_flow_table_ipv6.c
+++ b/net/ipv6/netfilter/nf_flow_table_ipv6.c
@@ -178,7 +178,7 @@ static bool __nf_flow_exceeds_mtu(const struct sk_buff *skb, unsigned int mtu)
 	if (skb->len <= mtu)
 		return false;
 
-	if (skb_is_gso(skb) && skb_gso_validate_mtu(skb, mtu))
+	if (skb_is_gso(skb) && skb_gso_validate_network_len(skb, mtu))
 		return false;
 
 	return true;
diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
index e545a3c9365f..7a4de6d618b1 100644
--- a/net/mpls/af_mpls.c
+++ b/net/mpls/af_mpls.c
@@ -122,7 +122,7 @@ bool mpls_pkt_too_big(const struct sk_buff *skb, unsigned int mtu)
 	if (skb->len <= mtu)
 		return false;
 
-	if (skb_is_gso(skb) && skb_gso_validate_mtu(skb, mtu))
+	if (skb_is_gso(skb) && skb_gso_validate_network_len(skb, mtu))
 		return false;
 
 	return true;
diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c
index 8e70291e586a..e87d6c4dd5b6 100644
--- a/net/xfrm/xfrm_device.c
+++ b/net/xfrm/xfrm_device.c
@@ -217,7 +217,7 @@ bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x)
 		if (skb->len <= mtu)
 			goto ok;
 
-		if (skb_is_gso(skb) && skb_gso_validate_mtu(skb, mtu))
+		if (skb_is_gso(skb) && skb_gso_validate_network_len(skb, mtu))
 			goto ok;
 	}
 
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 2/6] net: sched: tbf: handle GSO_BY_FRAGS case in enqueue
  2018-02-27 13:04 [PATCH 0/6]GSO_BY_FRAGS correctness improvements Daniel Axtens
  2018-02-27 13:04 ` [PATCH 1/6] net: rename skb_gso_validate_mtu -> skb_gso_validate_network_len Daniel Axtens
@ 2018-02-27 13:04 ` Daniel Axtens
  2018-02-27 13:04 ` [PATCH 3/6] net: xfrm: use skb_gso_validate_network_len() to check gso sizes Daniel Axtens
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 12+ messages in thread
From: Daniel Axtens @ 2018-02-27 13:04 UTC (permalink / raw)
  To: netdev; +Cc: Daniel Axtens

tbf_enqueue() checks the size of a packet before enqueuing it.
However, the GSO size check does not consider the GSO_BY_FRAGS
case, and so will drop GSO SCTP packets, causing a massive drop
in throughput.

Use skb_gso_validate_mac_len() instead, as it does consider that
case.

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 net/sched/sch_tbf.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c
index 229172d509cc..03225a8df973 100644
--- a/net/sched/sch_tbf.c
+++ b/net/sched/sch_tbf.c
@@ -188,7 +188,8 @@ static int tbf_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 	int ret;
 
 	if (qdisc_pkt_len(skb) > q->max_size) {
-		if (skb_is_gso(skb) && skb_gso_mac_seglen(skb) <= q->max_size)
+		if (skb_is_gso(skb) &&
+		    skb_gso_validate_mac_len(skb, q->max_size))
 			return tbf_segment(skb, sch, to_free);
 		return qdisc_drop(skb, sch, to_free);
 	}
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 3/6] net: xfrm: use skb_gso_validate_network_len() to check gso sizes
  2018-02-27 13:04 [PATCH 0/6]GSO_BY_FRAGS correctness improvements Daniel Axtens
  2018-02-27 13:04 ` [PATCH 1/6] net: rename skb_gso_validate_mtu -> skb_gso_validate_network_len Daniel Axtens
  2018-02-27 13:04 ` [PATCH 2/6] net: sched: tbf: handle GSO_BY_FRAGS case in enqueue Daniel Axtens
@ 2018-02-27 13:04 ` Daniel Axtens
  2018-02-27 13:04 ` [PATCH 4/6] net: make skb_gso_*_seglen functions private Daniel Axtens
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 12+ messages in thread
From: Daniel Axtens @ 2018-02-27 13:04 UTC (permalink / raw)
  To: netdev; +Cc: Daniel Axtens

Replace skb_gso_network_seglen() with
skb_gso_validate_network_len(), as it considers the GSO_BY_FRAGS
case.

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 net/ipv4/xfrm4_output.c | 3 ++-
 net/ipv6/xfrm6_output.c | 2 +-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/net/ipv4/xfrm4_output.c b/net/ipv4/xfrm4_output.c
index 94b8702603bc..be980c195fc5 100644
--- a/net/ipv4/xfrm4_output.c
+++ b/net/ipv4/xfrm4_output.c
@@ -30,7 +30,8 @@ static int xfrm4_tunnel_check_size(struct sk_buff *skb)
 
 	mtu = dst_mtu(skb_dst(skb));
 	if ((!skb_is_gso(skb) && skb->len > mtu) ||
-	    (skb_is_gso(skb) && skb_gso_network_seglen(skb) > ip_skb_dst_mtu(skb->sk, skb))) {
+	    (skb_is_gso(skb) &&
+	     !skb_gso_validate_network_len(skb, ip_skb_dst_mtu(skb->sk, skb)))) {
 		skb->protocol = htons(ETH_P_IP);
 
 		if (skb->sk)
diff --git a/net/ipv6/xfrm6_output.c b/net/ipv6/xfrm6_output.c
index 8ae87d4ec5ff..5959ce9620eb 100644
--- a/net/ipv6/xfrm6_output.c
+++ b/net/ipv6/xfrm6_output.c
@@ -82,7 +82,7 @@ static int xfrm6_tunnel_check_size(struct sk_buff *skb)
 
 	if ((!skb_is_gso(skb) && skb->len > mtu) ||
 	    (skb_is_gso(skb) &&
-	     skb_gso_network_seglen(skb) > ip6_skb_dst_mtu(skb))) {
+	     !skb_gso_validate_network_len(skb, ip6_skb_dst_mtu(skb)))) {
 		skb->dev = dst->dev;
 		skb->protocol = htons(ETH_P_IPV6);
 
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 4/6] net: make skb_gso_*_seglen functions private
  2018-02-27 13:04 [PATCH 0/6]GSO_BY_FRAGS correctness improvements Daniel Axtens
                   ` (2 preceding siblings ...)
  2018-02-27 13:04 ` [PATCH 3/6] net: xfrm: use skb_gso_validate_network_len() to check gso sizes Daniel Axtens
@ 2018-02-27 13:04 ` Daniel Axtens
  2018-02-28 15:53   ` David Miller
  2018-02-27 13:04 ` [PATCH 5/6] net: add and use helpers when adjusting gso_size Daniel Axtens
  2018-02-27 13:04 ` [PATCH 6/6] net: filter: refuse to change gso_size of SCTP packets Daniel Axtens
  5 siblings, 1 reply; 12+ messages in thread
From: Daniel Axtens @ 2018-02-27 13:04 UTC (permalink / raw)
  To: netdev; +Cc: Daniel Axtens

They're very hard to use properly as they do not consider the
GSO_BY_FRAGS case. Code should use skb_gso_validate_network_len
and skb_gso_validate_mac_len as they do consider this case.

Make the seglen functions static inline, which stops people
using them, and also means the validate functions don't have
to make any out of line calls.

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 include/linux/skbuff.h | 33 ---------------------------------
 net/core/skbuff.c      | 37 +++++++++++++++++++++++++++++++++++--
 2 files changed, 35 insertions(+), 35 deletions(-)

diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index eeb76bb7730a..d8340e6e8814 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -3288,7 +3288,6 @@ int skb_zerocopy(struct sk_buff *to, struct sk_buff *from,
 void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len);
 int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen);
 void skb_scrub_packet(struct sk_buff *skb, bool xnet);
-unsigned int skb_gso_transport_seglen(const struct sk_buff *skb);
 bool skb_gso_validate_network_len(const struct sk_buff *skb, unsigned int mtu);
 bool skb_gso_validate_mac_len(const struct sk_buff *skb, unsigned int len);
 struct sk_buff *skb_segment(struct sk_buff *skb, netdev_features_t features);
@@ -4107,38 +4106,6 @@ static inline bool skb_head_is_locked(const struct sk_buff *skb)
 	return !skb->head_frag || skb_cloned(skb);
 }
 
-/**
- * skb_gso_network_seglen - Return length of individual segments of a gso packet
- *
- * @skb: GSO skb
- *
- * skb_gso_network_seglen is used to determine the real size of the
- * individual segments, including Layer3 (IP, IPv6) and L4 headers (TCP/UDP).
- *
- * The MAC/L2 header is not accounted for.
- */
-static inline unsigned int skb_gso_network_seglen(const struct sk_buff *skb)
-{
-	unsigned int hdr_len = skb_transport_header(skb) -
-			       skb_network_header(skb);
-	return hdr_len + skb_gso_transport_seglen(skb);
-}
-
-/**
- * skb_gso_mac_seglen - Return length of individual segments of a gso packet
- *
- * @skb: GSO skb
- *
- * skb_gso_mac_seglen is used to determine the real size of the
- * individual segments, including MAC/L2, Layer3 (IP, IPv6) and L4
- * headers (TCP/UDP).
- */
-static inline unsigned int skb_gso_mac_seglen(const struct sk_buff *skb)
-{
-	unsigned int hdr_len = skb_transport_header(skb) - skb_mac_header(skb);
-	return hdr_len + skb_gso_transport_seglen(skb);
-}
-
 /* Local Checksum Offload.
  * Compute outer checksum based on the assumption that the
  * inner checksum will be offloaded later.
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 0c0c1d6f28ef..a664a3ae507e 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -4893,7 +4893,7 @@ EXPORT_SYMBOL_GPL(skb_scrub_packet);
  *
  * The MAC/L2 or network (IP, IPv6) headers are not accounted for.
  */
-unsigned int skb_gso_transport_seglen(const struct sk_buff *skb)
+static inline unsigned int skb_gso_transport_seglen(const struct sk_buff *skb)
 {
 	const struct skb_shared_info *shinfo = skb_shinfo(skb);
 	unsigned int thlen = 0;
@@ -4915,7 +4915,40 @@ unsigned int skb_gso_transport_seglen(const struct sk_buff *skb)
 	 */
 	return thlen + shinfo->gso_size;
 }
-EXPORT_SYMBOL_GPL(skb_gso_transport_seglen);
+
+/**
+ * skb_gso_network_seglen - Return length of individual segments of a gso packet
+ *
+ * @skb: GSO skb
+ *
+ * skb_gso_network_seglen is used to determine the real size of the
+ * individual segments, including Layer3 (IP, IPv6) and L4 headers (TCP/UDP).
+ *
+ * The MAC/L2 header is not accounted for.
+ */
+static inline unsigned int skb_gso_network_seglen(const struct sk_buff *skb)
+{
+	unsigned int hdr_len = skb_transport_header(skb) -
+			       skb_network_header(skb);
+
+	return hdr_len + skb_gso_transport_seglen(skb);
+}
+
+/**
+ * skb_gso_mac_seglen - Return length of individual segments of a gso packet
+ *
+ * @skb: GSO skb
+ *
+ * skb_gso_mac_seglen is used to determine the real size of the
+ * individual segments, including MAC/L2, Layer3 (IP, IPv6) and L4
+ * headers (TCP/UDP).
+ */
+static inline unsigned int skb_gso_mac_seglen(const struct sk_buff *skb)
+{
+	unsigned int hdr_len = skb_transport_header(skb) - skb_mac_header(skb);
+
+	return hdr_len + skb_gso_transport_seglen(skb);
+}
 
 /**
  * skb_gso_size_check - check the skb size, considering GSO_BY_FRAGS
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 5/6] net: add and use helpers when adjusting gso_size
  2018-02-27 13:04 [PATCH 0/6]GSO_BY_FRAGS correctness improvements Daniel Axtens
                   ` (3 preceding siblings ...)
  2018-02-27 13:04 ` [PATCH 4/6] net: make skb_gso_*_seglen functions private Daniel Axtens
@ 2018-02-27 13:04 ` Daniel Axtens
  2018-02-28 15:56   ` David Miller
  2018-02-27 13:04 ` [PATCH 6/6] net: filter: refuse to change gso_size of SCTP packets Daniel Axtens
  5 siblings, 1 reply; 12+ messages in thread
From: Daniel Axtens @ 2018-02-27 13:04 UTC (permalink / raw)
  To: netdev; +Cc: Daniel Axtens

An audit of users of gso_size reveals that an eBPF tc action on a
veth pair can be passed an SCTP GSO skb, which has gso_size of
GSO_BY_FRAGS.

If that action calls bpf_skb_change_proto(), bpf_skb_net_grow()
or bpf_skb_net_shrink(), the gso_size will be unconditionally
incremented or decremented to some nonsense value.

Add helpers that WARN if attempting to change a gso_size of a
GSO_BY_FRAGS skb (and leave the value unchanged).

Signed-off-by: Daniel Axtens <dja@axtens.net>
---
 Documentation/networking/segmentation-offloads.txt | 11 +++++++++--
 include/linux/skbuff.h                             | 16 ++++++++++++++++
 net/core/filter.c                                  |  8 ++++----
 3 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/Documentation/networking/segmentation-offloads.txt b/Documentation/networking/segmentation-offloads.txt
index d47480b61ac6..23a8dd91a9ec 100644
--- a/Documentation/networking/segmentation-offloads.txt
+++ b/Documentation/networking/segmentation-offloads.txt
@@ -153,8 +153,15 @@ To signal this, gso_size is set to the special value GSO_BY_FRAGS.
 
 Therefore, any code in the core networking stack must be aware of the
 possibility that gso_size will be GSO_BY_FRAGS and handle that case
-appropriately. (For size checks, the skb_gso_validate_*_len family of
-helpers do this automatically.)
+appropriately.
+
+There are a couple of helpers to make this easier:
+
+ - For size checks, the skb_gso_validate_*_len family of helpers correctly
+   considers GSO_BY_FRAGS.
+
+ - For manipulating packets, skb_increase_gso_size and skb_decrease_gso_size
+   will check for GSO_BY_FRAGS and WARN if asked to manipulate these skbs.
 
 This also affects drivers with the NETIF_F_FRAGLIST & NETIF_F_GSO_SCTP bits
 set. Note also that NETIF_F_GSO_SCTP is included in NETIF_F_GSO_SOFTWARE.
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index d8340e6e8814..157a91864e16 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -4047,6 +4047,22 @@ static inline void skb_gso_reset(struct sk_buff *skb)
 	skb_shinfo(skb)->gso_type = 0;
 }
 
+static inline void skb_increase_gso_size(struct sk_buff *skb, u16 increment)
+{
+	if (WARN_ON_ONCE(skb_shinfo(skb)->gso_size == GSO_BY_FRAGS))
+		return;
+
+	skb_shinfo(skb)->gso_size += increment;
+}
+
+static inline void skb_decrease_gso_size(struct sk_buff *skb, u16 decrement)
+{
+	if (WARN_ON_ONCE(skb_shinfo(skb)->gso_size == GSO_BY_FRAGS))
+		return;
+
+	skb_shinfo(skb)->gso_size -= decrement;
+}
+
 void __skb_warn_lro_forwarding(const struct sk_buff *skb);
 
 static inline bool skb_warn_if_lro(const struct sk_buff *skb)
diff --git a/net/core/filter.c b/net/core/filter.c
index 0c121adbdbaa..d9684d8a3ac7 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -2105,7 +2105,7 @@ static int bpf_skb_proto_4_to_6(struct sk_buff *skb)
 		}
 
 		/* Due to IPv6 header, MSS needs to be downgraded. */
-		skb_shinfo(skb)->gso_size -= len_diff;
+		skb_decrease_gso_size(skb, len_diff);
 		/* Header must be checked, and gso_segs recomputed. */
 		skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
 		skb_shinfo(skb)->gso_segs = 0;
@@ -2141,7 +2141,7 @@ static int bpf_skb_proto_6_to_4(struct sk_buff *skb)
 		}
 
 		/* Due to IPv4 header, MSS can be upgraded. */
-		skb_shinfo(skb)->gso_size += len_diff;
+		skb_increase_gso_size(skb, len_diff);
 		/* Header must be checked, and gso_segs recomputed. */
 		skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
 		skb_shinfo(skb)->gso_segs = 0;
@@ -2253,7 +2253,7 @@ static int bpf_skb_net_grow(struct sk_buff *skb, u32 len_diff)
 
 	if (skb_is_gso(skb)) {
 		/* Due to header grow, MSS needs to be downgraded. */
-		skb_shinfo(skb)->gso_size -= len_diff;
+		skb_decrease_gso_size(skb, len_diff);
 		/* Header must be checked, and gso_segs recomputed. */
 		skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
 		skb_shinfo(skb)->gso_segs = 0;
@@ -2277,7 +2277,7 @@ static int bpf_skb_net_shrink(struct sk_buff *skb, u32 len_diff)
 
 	if (skb_is_gso(skb)) {
 		/* Due to header shrink, MSS can be upgraded. */
-		skb_shinfo(skb)->gso_size += len_diff;
+		skb_increase_gso_size(skb, len_diff);
 		/* Header must be checked, and gso_segs recomputed. */
 		skb_shinfo(skb)->gso_type |= SKB_GSO_DODGY;
 		skb_shinfo(skb)->gso_segs = 0;
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 6/6] net: filter: refuse to change gso_size of SCTP packets
  2018-02-27 13:04 [PATCH 0/6]GSO_BY_FRAGS correctness improvements Daniel Axtens
                   ` (4 preceding siblings ...)
  2018-02-27 13:04 ` [PATCH 5/6] net: add and use helpers when adjusting gso_size Daniel Axtens
@ 2018-02-27 13:04 ` Daniel Axtens
  5 siblings, 0 replies; 12+ messages in thread
From: Daniel Axtens @ 2018-02-27 13:04 UTC (permalink / raw)
  To: netdev; +Cc: Daniel Axtens, Marcelo Ricardo Leitner

An eBPF tc action on a veth pair can be passed an SCTP GSO skb, which
has gso_size of GSO_BY_FRAGS.

If that action calls bpf_skb_change_proto(), bpf_skb_net_grow()
or bpf_skb_net_shrink(), the code will unconditionally attempt to
increment or decrement the gso_size to some nonsense value.

It's not clear how this could be done correctly, so simply refuse
to operate on SCTP GSO skbs.

Cc: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: Daniel Axtens <dja@axtens.net>

---

Marcelo - I am not an SCTP expert by any means, so if this code should
be doing something else, please let me know.
---
 net/core/filter.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/net/core/filter.c b/net/core/filter.c
index d9684d8a3ac7..f189c988962e 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -2096,6 +2096,10 @@ static int bpf_skb_proto_4_to_6(struct sk_buff *skb)
 		return ret;
 
 	if (skb_is_gso(skb)) {
+		/* SCTP uses GSO_BY_FRAGS, cannot adjust */
+		if (unlikely(skb_shinfo(skb)->gso_type & SKB_GSO_SCTP))
+			return -ENOTSUPP;
+
 		/* SKB_GSO_TCPV4 needs to be changed into
 		 * SKB_GSO_TCPV6.
 		 */
@@ -2132,6 +2136,10 @@ static int bpf_skb_proto_6_to_4(struct sk_buff *skb)
 		return ret;
 
 	if (skb_is_gso(skb)) {
+		/* SCTP uses GSO_BY_FRAGS, cannot adjust */
+		if (unlikely(skb_shinfo(skb)->gso_type & SKB_GSO_SCTP))
+			return -ENOTSUPP;
+
 		/* SKB_GSO_TCPV6 needs to be changed into
 		 * SKB_GSO_TCPV4.
 		 */
@@ -2252,6 +2260,10 @@ static int bpf_skb_net_grow(struct sk_buff *skb, u32 len_diff)
 		return ret;
 
 	if (skb_is_gso(skb)) {
+		/* SCTP uses GSO_BY_FRAGS, cannot adjust */
+		if (unlikely(skb_shinfo(skb)->gso_type & SKB_GSO_SCTP))
+			return -ENOTSUPP;
+
 		/* Due to header grow, MSS needs to be downgraded. */
 		skb_decrease_gso_size(skb, len_diff);
 		/* Header must be checked, and gso_segs recomputed. */
@@ -2276,6 +2288,10 @@ static int bpf_skb_net_shrink(struct sk_buff *skb, u32 len_diff)
 		return ret;
 
 	if (skb_is_gso(skb)) {
+		/* SCTP uses GSO_BY_FRAGS, cannot adjust */
+		if (unlikely(skb_shinfo(skb)->gso_type & SKB_GSO_SCTP))
+			return -ENOTSUPP;
+
 		/* Due to header shrink, MSS can be upgraded. */
 		skb_increase_gso_size(skb, len_diff);
 		/* Header must be checked, and gso_segs recomputed. */
-- 
2.14.1

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH 4/6] net: make skb_gso_*_seglen functions private
  2018-02-27 13:04 ` [PATCH 4/6] net: make skb_gso_*_seglen functions private Daniel Axtens
@ 2018-02-28 15:53   ` David Miller
  0 siblings, 0 replies; 12+ messages in thread
From: David Miller @ 2018-02-28 15:53 UTC (permalink / raw)
  To: dja; +Cc: netdev

From: Daniel Axtens <dja@axtens.net>
Date: Wed, 28 Feb 2018 00:04:05 +1100

> diff --git a/net/core/skbuff.c b/net/core/skbuff.c
> index 0c0c1d6f28ef..a664a3ae507e 100644
> --- a/net/core/skbuff.c
> +++ b/net/core/skbuff.c
> @@ -4893,7 +4893,7 @@ EXPORT_SYMBOL_GPL(skb_scrub_packet);
>   *
>   * The MAC/L2 or network (IP, IPv6) headers are not accounted for.
>   */
> -unsigned int skb_gso_transport_seglen(const struct sk_buff *skb)
> +static inline unsigned int skb_gso_transport_seglen(const struct sk_buff *skb)
 ...
> +static inline unsigned int skb_gso_network_seglen(const struct sk_buff *skb)
 ...
> +static inline unsigned int skb_gso_mac_seglen(const struct sk_buff *skb)

Please drop the inline keyword.  Functions in foo.c files should not be
marked inline, let the compiler decide whether it's beneficial to
inline or not.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 5/6] net: add and use helpers when adjusting gso_size
  2018-02-27 13:04 ` [PATCH 5/6] net: add and use helpers when adjusting gso_size Daniel Axtens
@ 2018-02-28 15:56   ` David Miller
  2018-02-28 16:21     ` Daniel Borkmann
  0 siblings, 1 reply; 12+ messages in thread
From: David Miller @ 2018-02-28 15:56 UTC (permalink / raw)
  To: dja; +Cc: netdev, daniel, alexei.starovoitov

From: Daniel Axtens <dja@axtens.net>
Date: Wed, 28 Feb 2018 00:04:06 +1100

> An audit of users of gso_size reveals that an eBPF tc action on a
> veth pair can be passed an SCTP GSO skb, which has gso_size of
> GSO_BY_FRAGS.
> 
> If that action calls bpf_skb_change_proto(), bpf_skb_net_grow()
> or bpf_skb_net_shrink(), the gso_size will be unconditionally
> incremented or decremented to some nonsense value.
> 
> Add helpers that WARN if attempting to change a gso_size of a
> GSO_BY_FRAGS skb (and leave the value unchanged).
> 
> Signed-off-by: Daniel Axtens <dja@axtens.net>

We can't really do this.

It means that a user loaded eBPF program can trigger logs full of
warnings merely by using this eBPF helper and generating GSO'd SCTP
traffic.

Daniel and Alexei, this is a serious problem.  The eBPF helpers
mentioned here cannot handle SCTP GSO packets properly, and in fact
corrupt them if they adjust the gso_size.

SCTP GSO packets use the GSO_BY_FRAGS scheme and cannot be treated
the same way we treat normal GSO packets.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 5/6] net: add and use helpers when adjusting gso_size
  2018-02-28 15:56   ` David Miller
@ 2018-02-28 16:21     ` Daniel Borkmann
  2018-03-01  0:17       ` Daniel Axtens
  0 siblings, 1 reply; 12+ messages in thread
From: Daniel Borkmann @ 2018-02-28 16:21 UTC (permalink / raw)
  To: David Miller, dja; +Cc: netdev, alexei.starovoitov

On 02/28/2018 04:56 PM, David Miller wrote:
> From: Daniel Axtens <dja@axtens.net>
> Date: Wed, 28 Feb 2018 00:04:06 +1100
> 
>> An audit of users of gso_size reveals that an eBPF tc action on a
>> veth pair can be passed an SCTP GSO skb, which has gso_size of
>> GSO_BY_FRAGS.
>>
>> If that action calls bpf_skb_change_proto(), bpf_skb_net_grow()
>> or bpf_skb_net_shrink(), the gso_size will be unconditionally
>> incremented or decremented to some nonsense value.
>>
>> Add helpers that WARN if attempting to change a gso_size of a
>> GSO_BY_FRAGS skb (and leave the value unchanged).
>>
>> Signed-off-by: Daniel Axtens <dja@axtens.net>
> 
> We can't really do this.
> 
> It means that a user loaded eBPF program can trigger logs full of
> warnings merely by using this eBPF helper and generating GSO'd SCTP
> traffic.
> 
> Daniel and Alexei, this is a serious problem.  The eBPF helpers
> mentioned here cannot handle SCTP GSO packets properly, and in fact
> corrupt them if they adjust the gso_size.
> 
> SCTP GSO packets use the GSO_BY_FRAGS scheme and cannot be treated
> the same way we treat normal GSO packets.

Thanks for Cc, I would have missed it. This patch in combination with
patch 6/6 seems okay since we reject it in these cases, which is fine
(although it could be a warn_once), but patch 6/6 is definitely buggy
in that we leave the skb in a half edited state when leaving the helper.
Daniel, want me to fix this up from bpf side and route 5/6 and actual
fix via bpf tree? Otherwise please respin with fixed 6/6.

Thanks,
Daniel

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 5/6] net: add and use helpers when adjusting gso_size
  2018-02-28 16:21     ` Daniel Borkmann
@ 2018-03-01  0:17       ` Daniel Axtens
  2018-03-01  0:47         ` Daniel Borkmann
  0 siblings, 1 reply; 12+ messages in thread
From: Daniel Axtens @ 2018-03-01  0:17 UTC (permalink / raw)
  To: Daniel Borkmann, David Miller; +Cc: netdev, alexei.starovoitov

Hi Daniel,

>> It means that a user loaded eBPF program can trigger logs full of
>> warnings merely by using this eBPF helper and generating GSO'd SCTP
>> traffic.
>> 
>> Daniel and Alexei, this is a serious problem.  The eBPF helpers
>> mentioned here cannot handle SCTP GSO packets properly, and in fact
>> corrupt them if they adjust the gso_size.
>> 
>> SCTP GSO packets use the GSO_BY_FRAGS scheme and cannot be treated
>> the same way we treat normal GSO packets.
>
> Thanks for Cc, I would have missed it. This patch in combination with
> patch 6/6 seems okay since we reject it in these cases, which is fine
> (although it could be a warn_once), but patch 6/6 is definitely buggy
> in that we leave the skb in a half edited state when leaving the helper.
> Daniel, want me to fix this up from bpf side and route 5/6 and actual
> fix via bpf tree? Otherwise please respin with fixed 6/6.

I'm happy for you to take these two via the bpf tree with whatever
fixes/changes are appropriate - I'm no eBPF expert so my fix was a 'best
guess' only. Let me know if you would like anything respun on my end.

Regards,
Daniel

PS. apologies for missing you on Cc - will make sure you're cced on any
future bpf-related work!
>
> Thanks,
> Daniel

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH 5/6] net: add and use helpers when adjusting gso_size
  2018-03-01  0:17       ` Daniel Axtens
@ 2018-03-01  0:47         ` Daniel Borkmann
  0 siblings, 0 replies; 12+ messages in thread
From: Daniel Borkmann @ 2018-03-01  0:47 UTC (permalink / raw)
  To: Daniel Axtens, David Miller; +Cc: netdev, alexei.starovoitov

On 03/01/2018 01:17 AM, Daniel Axtens wrote:
[...]
>>> It means that a user loaded eBPF program can trigger logs full of
>>> warnings merely by using this eBPF helper and generating GSO'd SCTP
>>> traffic.
>>>
>>> Daniel and Alexei, this is a serious problem.  The eBPF helpers
>>> mentioned here cannot handle SCTP GSO packets properly, and in fact
>>> corrupt them if they adjust the gso_size.
>>>
>>> SCTP GSO packets use the GSO_BY_FRAGS scheme and cannot be treated
>>> the same way we treat normal GSO packets.
>>
>> Thanks for Cc, I would have missed it. This patch in combination with
>> patch 6/6 seems okay since we reject it in these cases, which is fine
>> (although it could be a warn_once), but patch 6/6 is definitely buggy
>> in that we leave the skb in a half edited state when leaving the helper.
>> Daniel, want me to fix this up from bpf side and route 5/6 and actual
>> fix via bpf tree? Otherwise please respin with fixed 6/6.
> 
> I'm happy for you to take these two via the bpf tree with whatever
> fixes/changes are appropriate - I'm no eBPF expert so my fix was a 'best
> guess' only. Let me know if you would like anything respun on my end.

Ok, sounds good. In that case you would need to only resend the first
4 patches of your series targeted at -net (I presume?). For the others
no further action required from your side, I'll take care of them
tomorrow then.

Thanks,
Daniel

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2018-03-01  0:47 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-02-27 13:04 [PATCH 0/6]GSO_BY_FRAGS correctness improvements Daniel Axtens
2018-02-27 13:04 ` [PATCH 1/6] net: rename skb_gso_validate_mtu -> skb_gso_validate_network_len Daniel Axtens
2018-02-27 13:04 ` [PATCH 2/6] net: sched: tbf: handle GSO_BY_FRAGS case in enqueue Daniel Axtens
2018-02-27 13:04 ` [PATCH 3/6] net: xfrm: use skb_gso_validate_network_len() to check gso sizes Daniel Axtens
2018-02-27 13:04 ` [PATCH 4/6] net: make skb_gso_*_seglen functions private Daniel Axtens
2018-02-28 15:53   ` David Miller
2018-02-27 13:04 ` [PATCH 5/6] net: add and use helpers when adjusting gso_size Daniel Axtens
2018-02-28 15:56   ` David Miller
2018-02-28 16:21     ` Daniel Borkmann
2018-03-01  0:17       ` Daniel Axtens
2018-03-01  0:47         ` Daniel Borkmann
2018-02-27 13:04 ` [PATCH 6/6] net: filter: refuse to change gso_size of SCTP packets Daniel Axtens

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.