Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 2504

kernel-2.6.18-238.el5.src.rpm

From: Andy Gospodarek <gospo@redhat.com>
Date: Thu, 10 Sep 2009 16:26:48 -0400
Subject: [net] bridge: fix LRO crash with tun
Message-id: 20090910202647.GL32296@gospo.rdu.redhat.com
O-Subject: [RHEL5.5 PATCH] bridge: Fix LRO crash with tun
Bugzilla: 483646
RH-Acked-by: Jiri Pirko <jpirko@redhat.com>
RH-Acked-by: Neil Horman <nhorman@redhat.com>
RH-Acked-by: David Miller <davem@redhat.com>

This is a backport of the following two upstream patches:

    commit 4497b0763cb1afae463f5e144c28b5d806e28b60
    Author: Ben Hutchings <bhutchings@solarflare.com>
    Date:   Thu Jun 19 16:22:28 2008 -0700

        net: Discard and warn about LRO'd skbs received for forwarding

    commit 4906f9985e310fc01f956256b0d58ac28b0dcb19
    Author: Herbert Xu <herbert@gondor.apana.org.au>
    Date:   Mon Feb 9 15:07:18 2009 -0800

        bridge: Fix LRO crash with tun

I've been carrying them in my test kernels since someone reported to me
that bnx2x devices were crashing when in a bridge and using LRO.  This
patch kindly warns the user they should disable LRO if they expect
networking to function.

This will resolve RHBZ 483646.

diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 8c0b6d3..dfbb024 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -835,6 +835,20 @@ static inline unsigned char *skb_tail_pointer(const struct sk_buff *skb)
 	return skb->tail;
 }
 
+extern void __skb_warn_lro_forwarding(const struct sk_buff *skb);
+
+static inline bool skb_warn_if_lro(const struct sk_buff *skb)
+{
+	/* LRO sets gso_size but not gso_type, whereas if GSO is really
+	 * wanted then gso_type will be set. */
+	struct skb_shared_info *shinfo = skb_shinfo(skb);
+	if (shinfo->gso_size != 0 && unlikely(shinfo->gso_type == 0)) {
+		__skb_warn_lro_forwarding(skb);
+		return true;
+	}
+	return false;
+}
+
 static inline void skb_reset_tail_pointer(struct sk_buff *skb)
 {
 	skb->tail = skb->data;
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index 864fbbc..ef22968 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -72,6 +72,11 @@ static void __br_forward(const struct net_bridge_port *to, struct sk_buff *skb)
 {
 	struct net_device *indev;
 
+	if (skb_warn_if_lro(skb)) {
+		kfree_skb(skb);
+		return;
+	}
+
 	indev = skb->dev;
 	skb->dev = to->dev;
 	skb->ip_summed = CHECKSUM_NONE;
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 8113351..9752e02 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -2206,6 +2206,13 @@ void __init skb_init(void)
 		panic("cannot create skbuff cache");
 }
 
+void __skb_warn_lro_forwarding(const struct sk_buff *skb)
+{
+	if (net_ratelimit())
+		pr_info("%s: received packets cannot be forwarded"
+			   " while LRO is enabled\n", skb->dev->name);
+}
+
 EXPORT_SYMBOL(___pskb_trim);
 EXPORT_SYMBOL(__kfree_skb);
 EXPORT_SYMBOL(kfree_skb);
@@ -2241,3 +2248,4 @@ EXPORT_SYMBOL(skb_seq_read);
 EXPORT_SYMBOL(skb_abort_seq_read);
 EXPORT_SYMBOL(skb_find_text);
 EXPORT_SYMBOL(skb_append_datato_frags);
+EXPORT_SYMBOL(__skb_warn_lro_forwarding);
diff --git a/net/ipv4/ip_forward.c b/net/ipv4/ip_forward.c
index a22d11d..227581f 100644
--- a/net/ipv4/ip_forward.c
+++ b/net/ipv4/ip_forward.c
@@ -59,6 +59,9 @@ int ip_forward(struct sk_buff *skb)
 	struct rtable *rt;	/* Route we use */
 	struct ip_options * opt	= &(IPCB(skb)->opt);
 
+	if (skb_warn_if_lro(skb))
+		goto drop;
+
 	if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb))
 		goto drop;
 
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
index 94cf058..27e39ec 100644
--- a/net/ipv6/ip6_output.c
+++ b/net/ipv6/ip6_output.c
@@ -336,6 +336,9 @@ int ip6_forward(struct sk_buff *skb)
 	if (ipv6_devconf.forwarding == 0)
 		goto error;
 
+	if (skb_warn_if_lro(skb))
+		goto drop;
+
 	if (!xfrm6_policy_check(NULL, XFRM_POLICY_FWD, skb)) {
 		IP6_INC_STATS(ip6_dst_idev(dst), IPSTATS_MIB_INDISCARDS);
 		goto drop;