diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 03cd7551a7a100b2ba0079f40b26e957e531b5cf..84b0f0d16fcbd0166b92c7088a7d7c135ea82ed7 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -315,6 +315,7 @@ struct net_device
 #define NETIF_F_GSO_SHIFT	16
 #define NETIF_F_TSO		(SKB_GSO_TCPV4 << NETIF_F_GSO_SHIFT)
 #define NETIF_F_UFO		(SKB_GSO_UDPV4 << NETIF_F_GSO_SHIFT)
+#define NETIF_F_GSO_ROBUST	(SKB_GSO_DODGY << NETIF_F_GSO_SHIFT)
 
 #define NETIF_F_GEN_CSUM	(NETIF_F_NO_CSUM | NETIF_F_HW_CSUM)
 #define NETIF_F_ALL_CSUM	(NETIF_F_IP_CSUM | NETIF_F_GEN_CSUM)
@@ -543,7 +544,8 @@ struct packet_type {
 					 struct net_device *,
 					 struct packet_type *,
 					 struct net_device *);
-	struct sk_buff		*(*gso_segment)(struct sk_buff *skb, int sg);
+	struct sk_buff		*(*gso_segment)(struct sk_buff *skb,
+						int features);
 	void			*af_packet_priv;
 	struct list_head	list;
 };
@@ -968,7 +970,7 @@ extern int		netdev_max_backlog;
 extern int		weight_p;
 extern int		netdev_set_master(struct net_device *dev, struct net_device *master);
 extern int skb_checksum_help(struct sk_buff *skb, int inward);
-extern struct sk_buff *skb_gso_segment(struct sk_buff *skb, int sg);
+extern struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features);
 #ifdef CONFIG_BUG
 extern void netdev_rx_csum_fault(struct net_device *dev);
 #else
@@ -988,11 +990,16 @@ extern void dev_seq_stop(struct seq_file *seq, void *v);
 
 extern void linkwatch_run_queue(void);
 
+static inline int skb_gso_ok(struct sk_buff *skb, int features)
+{
+	int feature = skb_shinfo(skb)->gso_size ?
+		      skb_shinfo(skb)->gso_type << NETIF_F_GSO_SHIFT : 0;
+	return (features & feature) != feature;
+}
+
 static inline int netif_needs_gso(struct net_device *dev, struct sk_buff *skb)
 {
-	int feature = skb_shinfo(skb)->gso_type << NETIF_F_GSO_SHIFT;
-	return skb_shinfo(skb)->gso_size &&
-	       (dev->features & feature) != feature;
+	return skb_gso_ok(skb, dev->features);
 }
 
 #endif /* __KERNEL__ */
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h
index 16eef03ce0eb2dba578c52cc136e1b80b068fa99..5fb72da7da03656cad40587408751f94954a11d5 100644
--- a/include/linux/skbuff.h
+++ b/include/linux/skbuff.h
@@ -172,6 +172,9 @@ enum {
 enum {
 	SKB_GSO_TCPV4 = 1 << 0,
 	SKB_GSO_UDPV4 = 1 << 1,
+
+	/* This indicates the skb is from an untrusted source. */
+	SKB_GSO_DODGY = 1 << 2,
 };
 
 /** 
@@ -1299,7 +1302,7 @@ extern void	       skb_split(struct sk_buff *skb,
 				 struct sk_buff *skb1, const u32 len);
 
 extern void	       skb_release_data(struct sk_buff *skb);
-extern struct sk_buff *skb_segment(struct sk_buff *skb, int sg);
+extern struct sk_buff *skb_segment(struct sk_buff *skb, int features);
 
 static inline void *skb_header_pointer(const struct sk_buff *skb, int offset,
 				       int len, void *buffer)
diff --git a/include/net/protocol.h b/include/net/protocol.h
index 3b6dc15c68a58fcd0c19996a4f058a1f6879588b..40b6b9c9973f5dd2756fd3dc4c6439d7676784e6 100644
--- a/include/net/protocol.h
+++ b/include/net/protocol.h
@@ -36,7 +36,8 @@
 struct net_protocol {
 	int			(*handler)(struct sk_buff *skb);
 	void			(*err_handler)(struct sk_buff *skb, u32 info);
-	struct sk_buff	       *(*gso_segment)(struct sk_buff *skb, int sg);
+	struct sk_buff	       *(*gso_segment)(struct sk_buff *skb,
+					       int features);
 	int			no_policy;
 };
 
diff --git a/include/net/tcp.h b/include/net/tcp.h
index ca3d38dfc00bb3911c697dea4a936f36b1edd067..624921e763329d952cf5b68805cc75ca20399d5c 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -1086,7 +1086,7 @@ extern struct request_sock_ops tcp_request_sock_ops;
 
 extern int tcp_v4_destroy_sock(struct sock *sk);
 
-extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg);
+extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features);
 
 #ifdef CONFIG_PROC_FS
 extern int  tcp4_proc_init(void);
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 2afdc7c0736c48b6baa39c5e9fa37987a7cdb779..f8dbcee80ebad7cdffb110fd87a8796db93643ab 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -184,6 +184,6 @@ void br_dev_setup(struct net_device *dev)
 	dev->set_mac_address = br_set_mac_address;
 	dev->priv_flags = IFF_EBRIDGE;
 
- 	dev->features = NETIF_F_SG | NETIF_F_FRAGLIST
- 		| NETIF_F_HIGHDMA | NETIF_F_TSO | NETIF_F_NO_CSUM;
+ 	dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA |
+ 			NETIF_F_TSO | NETIF_F_NO_CSUM | NETIF_F_GSO_ROBUST;
 }
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index 07956ecf545e313775967da4e48570bf33243732..f55ef682ef846e979b7e1b24569399a7c2fbd7da 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -392,7 +392,8 @@ void br_features_recompute(struct net_bridge *br)
 		features &= feature;
 	}
 
-	br->dev->features = features | checksum | NETIF_F_LLTX;
+	br->dev->features = features | checksum | NETIF_F_LLTX |
+			    NETIF_F_GSO_ROBUST;
 }
 
 /* called with RTNL */
diff --git a/net/core/dev.c b/net/core/dev.c
index f1c52cbd6ef7bfa8ae13ff04913b585b0c9be286..4f2014994a847ce7066a7c8deee9d12befb9e354 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1190,11 +1190,14 @@ out:
 /**
  *	skb_gso_segment - Perform segmentation on skb.
  *	@skb: buffer to segment
- *	@sg: whether scatter-gather is supported on the target.
+ *	@features: features for the output path (see dev->features)
  *
  *	This function segments the given skb and returns a list of segments.
+ *
+ *	It may return NULL if the skb requires no segmentation.  This is
+ *	only possible when GSO is used for verifying header integrity.
  */
-struct sk_buff *skb_gso_segment(struct sk_buff *skb, int sg)
+struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features)
 {
 	struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT);
 	struct packet_type *ptype;
@@ -1210,12 +1213,14 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, int sg)
 	rcu_read_lock();
 	list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & 15], list) {
 		if (ptype->type == type && !ptype->dev && ptype->gso_segment) {
-			segs = ptype->gso_segment(skb, sg);
+			segs = ptype->gso_segment(skb, features);
 			break;
 		}
 	}
 	rcu_read_unlock();
 
+	__skb_push(skb, skb->data - skb->mac.raw);
+
 	return segs;
 }
 
@@ -1291,9 +1296,15 @@ static int dev_gso_segment(struct sk_buff *skb)
 {
 	struct net_device *dev = skb->dev;
 	struct sk_buff *segs;
+	int features = dev->features & ~(illegal_highdma(dev, skb) ?
+					 NETIF_F_SG : 0);
+
+	segs = skb_gso_segment(skb, features);
+
+	/* Verifying header integrity only. */
+	if (!segs)
+		return 0;
 
-	segs = skb_gso_segment(skb, dev->features & NETIF_F_SG &&
-				    !illegal_highdma(dev, skb));
 	if (unlikely(IS_ERR(segs)))
 		return PTR_ERR(segs);
 
@@ -1310,13 +1321,17 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
 		if (netdev_nit)
 			dev_queue_xmit_nit(skb, dev);
 
-		if (!netif_needs_gso(dev, skb))
-			return dev->hard_start_xmit(skb, dev);
+		if (netif_needs_gso(dev, skb)) {
+			if (unlikely(dev_gso_segment(skb)))
+				goto out_kfree_skb;
+			if (skb->next)
+				goto gso;
+		}
 
-		if (unlikely(dev_gso_segment(skb)))
-			goto out_kfree_skb;
+		return dev->hard_start_xmit(skb, dev);
 	}
 
+gso:
 	do {
 		struct sk_buff *nskb = skb->next;
 		int rc;
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index 6edbb90cbcec303605be4064b2f1da0d56e18eb3..dfef9eece83e1cb30e5fa88450d7545b2b8c3a8d 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -1848,13 +1848,13 @@ EXPORT_SYMBOL_GPL(skb_pull_rcsum);
 /**
  *	skb_segment - Perform protocol segmentation on skb.
  *	@skb: buffer to segment
- *	@sg: whether scatter-gather can be used for generated segments
+ *	@features: features for the output path (see dev->features)
  *
  *	This function performs segmentation on the given skb.  It returns
  *	the segment at the given position.  It returns NULL if there are
  *	no more segments to generate, or when an error is encountered.
  */
-struct sk_buff *skb_segment(struct sk_buff *skb, int sg)
+struct sk_buff *skb_segment(struct sk_buff *skb, int features)
 {
 	struct sk_buff *segs = NULL;
 	struct sk_buff *tail = NULL;
@@ -1863,6 +1863,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int sg)
 	unsigned int offset = doffset;
 	unsigned int headroom;
 	unsigned int len;
+	int sg = features & NETIF_F_SG;
 	int nfrags = skb_shinfo(skb)->nr_frags;
 	int err = -ENOMEM;
 	int i = 0;
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 461216b47948681791162829e8c4ca2b78e54d87..8d157157bf8e68c65ae3523b62c13ba0486437ab 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1097,7 +1097,7 @@ int inet_sk_rebuild_header(struct sock *sk)
 
 EXPORT_SYMBOL(inet_sk_rebuild_header);
 
-static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int sg)
+static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features)
 {
 	struct sk_buff *segs = ERR_PTR(-EINVAL);
 	struct iphdr *iph;
@@ -1126,10 +1126,10 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int sg)
 	rcu_read_lock();
 	ops = rcu_dereference(inet_protos[proto]);
 	if (ops && ops->gso_segment)
-		segs = ops->gso_segment(skb, sg);
+		segs = ops->gso_segment(skb, features);
 	rcu_read_unlock();
 
-	if (IS_ERR(segs))
+	if (!segs || unlikely(IS_ERR(segs)))
 		goto out;
 
 	skb = segs;
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index c04176be7ed12826de51bcf036d2e64cd7f0b17e..0336422c88a03becbf006d26c064b25504b62bf9 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -2145,7 +2145,7 @@ int compat_tcp_getsockopt(struct sock *sk, int level, int optname,
 EXPORT_SYMBOL(compat_tcp_getsockopt);
 #endif
 
-struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg)
+struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features)
 {
 	struct sk_buff *segs = ERR_PTR(-EINVAL);
 	struct tcphdr *th;
@@ -2166,10 +2166,14 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg)
 	if (!pskb_may_pull(skb, thlen))
 		goto out;
 
+	segs = NULL;
+	if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST))
+		goto out;
+
 	oldlen = (u16)~skb->len;
 	__skb_pull(skb, thlen);
 
-	segs = skb_segment(skb, sg);
+	segs = skb_segment(skb, features);
 	if (IS_ERR(segs))
 		goto out;