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;