diff options
-rw-r--r-- | include/linux/netdevice.h | 17 | ||||
-rw-r--r-- | include/linux/skbuff.h | 5 | ||||
-rw-r--r-- | include/net/protocol.h | 3 | ||||
-rw-r--r-- | include/net/tcp.h | 2 | ||||
-rw-r--r-- | net/bridge/br_device.c | 4 | ||||
-rw-r--r-- | net/bridge/br_if.c | 3 | ||||
-rw-r--r-- | net/core/dev.c | 33 | ||||
-rw-r--r-- | net/core/skbuff.c | 5 | ||||
-rw-r--r-- | net/ipv4/af_inet.c | 6 | ||||
-rw-r--r-- | net/ipv4/tcp.c | 8 |
10 files changed, 59 insertions, 27 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 03cd7551a7a1..84b0f0d16fcb 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -315,6 +315,7 @@ struct net_device | |||
315 | #define NETIF_F_GSO_SHIFT 16 | 315 | #define NETIF_F_GSO_SHIFT 16 |
316 | #define NETIF_F_TSO (SKB_GSO_TCPV4 << NETIF_F_GSO_SHIFT) | 316 | #define NETIF_F_TSO (SKB_GSO_TCPV4 << NETIF_F_GSO_SHIFT) |
317 | #define NETIF_F_UFO (SKB_GSO_UDPV4 << NETIF_F_GSO_SHIFT) | 317 | #define NETIF_F_UFO (SKB_GSO_UDPV4 << NETIF_F_GSO_SHIFT) |
318 | #define NETIF_F_GSO_ROBUST (SKB_GSO_DODGY << NETIF_F_GSO_SHIFT) | ||
318 | 319 | ||
319 | #define NETIF_F_GEN_CSUM (NETIF_F_NO_CSUM | NETIF_F_HW_CSUM) | 320 | #define NETIF_F_GEN_CSUM (NETIF_F_NO_CSUM | NETIF_F_HW_CSUM) |
320 | #define NETIF_F_ALL_CSUM (NETIF_F_IP_CSUM | NETIF_F_GEN_CSUM) | 321 | #define NETIF_F_ALL_CSUM (NETIF_F_IP_CSUM | NETIF_F_GEN_CSUM) |
@@ -543,7 +544,8 @@ struct packet_type { | |||
543 | struct net_device *, | 544 | struct net_device *, |
544 | struct packet_type *, | 545 | struct packet_type *, |
545 | struct net_device *); | 546 | struct net_device *); |
546 | struct sk_buff *(*gso_segment)(struct sk_buff *skb, int sg); | 547 | struct sk_buff *(*gso_segment)(struct sk_buff *skb, |
548 | int features); | ||
547 | void *af_packet_priv; | 549 | void *af_packet_priv; |
548 | struct list_head list; | 550 | struct list_head list; |
549 | }; | 551 | }; |
@@ -968,7 +970,7 @@ extern int netdev_max_backlog; | |||
968 | extern int weight_p; | 970 | extern int weight_p; |
969 | extern int netdev_set_master(struct net_device *dev, struct net_device *master); | 971 | extern int netdev_set_master(struct net_device *dev, struct net_device *master); |
970 | extern int skb_checksum_help(struct sk_buff *skb, int inward); | 972 | extern int skb_checksum_help(struct sk_buff *skb, int inward); |
971 | extern struct sk_buff *skb_gso_segment(struct sk_buff *skb, int sg); | 973 | extern struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features); |
972 | #ifdef CONFIG_BUG | 974 | #ifdef CONFIG_BUG |
973 | extern void netdev_rx_csum_fault(struct net_device *dev); | 975 | extern void netdev_rx_csum_fault(struct net_device *dev); |
974 | #else | 976 | #else |
@@ -988,11 +990,16 @@ extern void dev_seq_stop(struct seq_file *seq, void *v); | |||
988 | 990 | ||
989 | extern void linkwatch_run_queue(void); | 991 | extern void linkwatch_run_queue(void); |
990 | 992 | ||
993 | static inline int skb_gso_ok(struct sk_buff *skb, int features) | ||
994 | { | ||
995 | int feature = skb_shinfo(skb)->gso_size ? | ||
996 | skb_shinfo(skb)->gso_type << NETIF_F_GSO_SHIFT : 0; | ||
997 | return (features & feature) != feature; | ||
998 | } | ||
999 | |||
991 | static inline int netif_needs_gso(struct net_device *dev, struct sk_buff *skb) | 1000 | static inline int netif_needs_gso(struct net_device *dev, struct sk_buff *skb) |
992 | { | 1001 | { |
993 | int feature = skb_shinfo(skb)->gso_type << NETIF_F_GSO_SHIFT; | 1002 | return skb_gso_ok(skb, dev->features); |
994 | return skb_shinfo(skb)->gso_size && | ||
995 | (dev->features & feature) != feature; | ||
996 | } | 1003 | } |
997 | 1004 | ||
998 | #endif /* __KERNEL__ */ | 1005 | #endif /* __KERNEL__ */ |
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 16eef03ce0eb..5fb72da7da03 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h | |||
@@ -172,6 +172,9 @@ enum { | |||
172 | enum { | 172 | enum { |
173 | SKB_GSO_TCPV4 = 1 << 0, | 173 | SKB_GSO_TCPV4 = 1 << 0, |
174 | SKB_GSO_UDPV4 = 1 << 1, | 174 | SKB_GSO_UDPV4 = 1 << 1, |
175 | |||
176 | /* This indicates the skb is from an untrusted source. */ | ||
177 | SKB_GSO_DODGY = 1 << 2, | ||
175 | }; | 178 | }; |
176 | 179 | ||
177 | /** | 180 | /** |
@@ -1299,7 +1302,7 @@ extern void skb_split(struct sk_buff *skb, | |||
1299 | struct sk_buff *skb1, const u32 len); | 1302 | struct sk_buff *skb1, const u32 len); |
1300 | 1303 | ||
1301 | extern void skb_release_data(struct sk_buff *skb); | 1304 | extern void skb_release_data(struct sk_buff *skb); |
1302 | extern struct sk_buff *skb_segment(struct sk_buff *skb, int sg); | 1305 | extern struct sk_buff *skb_segment(struct sk_buff *skb, int features); |
1303 | 1306 | ||
1304 | static inline void *skb_header_pointer(const struct sk_buff *skb, int offset, | 1307 | static inline void *skb_header_pointer(const struct sk_buff *skb, int offset, |
1305 | int len, void *buffer) | 1308 | int len, void *buffer) |
diff --git a/include/net/protocol.h b/include/net/protocol.h index 3b6dc15c68a5..40b6b9c9973f 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h | |||
@@ -36,7 +36,8 @@ | |||
36 | struct net_protocol { | 36 | struct net_protocol { |
37 | int (*handler)(struct sk_buff *skb); | 37 | int (*handler)(struct sk_buff *skb); |
38 | void (*err_handler)(struct sk_buff *skb, u32 info); | 38 | void (*err_handler)(struct sk_buff *skb, u32 info); |
39 | struct sk_buff *(*gso_segment)(struct sk_buff *skb, int sg); | 39 | struct sk_buff *(*gso_segment)(struct sk_buff *skb, |
40 | int features); | ||
40 | int no_policy; | 41 | int no_policy; |
41 | }; | 42 | }; |
42 | 43 | ||
diff --git a/include/net/tcp.h b/include/net/tcp.h index ca3d38dfc00b..624921e76332 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h | |||
@@ -1086,7 +1086,7 @@ extern struct request_sock_ops tcp_request_sock_ops; | |||
1086 | 1086 | ||
1087 | extern int tcp_v4_destroy_sock(struct sock *sk); | 1087 | extern int tcp_v4_destroy_sock(struct sock *sk); |
1088 | 1088 | ||
1089 | extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg); | 1089 | extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features); |
1090 | 1090 | ||
1091 | #ifdef CONFIG_PROC_FS | 1091 | #ifdef CONFIG_PROC_FS |
1092 | extern int tcp4_proc_init(void); | 1092 | extern int tcp4_proc_init(void); |
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 2afdc7c0736c..f8dbcee80eba 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) | |||
184 | dev->set_mac_address = br_set_mac_address; | 184 | dev->set_mac_address = br_set_mac_address; |
185 | dev->priv_flags = IFF_EBRIDGE; | 185 | dev->priv_flags = IFF_EBRIDGE; |
186 | 186 | ||
187 | dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | 187 | dev->features = NETIF_F_SG | NETIF_F_FRAGLIST | NETIF_F_HIGHDMA | |
188 | | NETIF_F_HIGHDMA | NETIF_F_TSO | NETIF_F_NO_CSUM; | 188 | NETIF_F_TSO | NETIF_F_NO_CSUM | NETIF_F_GSO_ROBUST; |
189 | } | 189 | } |
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 07956ecf545e..f55ef682ef84 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) | |||
392 | features &= feature; | 392 | features &= feature; |
393 | } | 393 | } |
394 | 394 | ||
395 | br->dev->features = features | checksum | NETIF_F_LLTX; | 395 | br->dev->features = features | checksum | NETIF_F_LLTX | |
396 | NETIF_F_GSO_ROBUST; | ||
396 | } | 397 | } |
397 | 398 | ||
398 | /* called with RTNL */ | 399 | /* called with RTNL */ |
diff --git a/net/core/dev.c b/net/core/dev.c index f1c52cbd6ef7..4f2014994a84 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -1190,11 +1190,14 @@ out: | |||
1190 | /** | 1190 | /** |
1191 | * skb_gso_segment - Perform segmentation on skb. | 1191 | * skb_gso_segment - Perform segmentation on skb. |
1192 | * @skb: buffer to segment | 1192 | * @skb: buffer to segment |
1193 | * @sg: whether scatter-gather is supported on the target. | 1193 | * @features: features for the output path (see dev->features) |
1194 | * | 1194 | * |
1195 | * This function segments the given skb and returns a list of segments. | 1195 | * This function segments the given skb and returns a list of segments. |
1196 | * | ||
1197 | * It may return NULL if the skb requires no segmentation. This is | ||
1198 | * only possible when GSO is used for verifying header integrity. | ||
1196 | */ | 1199 | */ |
1197 | struct sk_buff *skb_gso_segment(struct sk_buff *skb, int sg) | 1200 | struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features) |
1198 | { | 1201 | { |
1199 | struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); | 1202 | struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); |
1200 | struct packet_type *ptype; | 1203 | struct packet_type *ptype; |
@@ -1210,12 +1213,14 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, int sg) | |||
1210 | rcu_read_lock(); | 1213 | rcu_read_lock(); |
1211 | list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & 15], list) { | 1214 | list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & 15], list) { |
1212 | if (ptype->type == type && !ptype->dev && ptype->gso_segment) { | 1215 | if (ptype->type == type && !ptype->dev && ptype->gso_segment) { |
1213 | segs = ptype->gso_segment(skb, sg); | 1216 | segs = ptype->gso_segment(skb, features); |
1214 | break; | 1217 | break; |
1215 | } | 1218 | } |
1216 | } | 1219 | } |
1217 | rcu_read_unlock(); | 1220 | rcu_read_unlock(); |
1218 | 1221 | ||
1222 | __skb_push(skb, skb->data - skb->mac.raw); | ||
1223 | |||
1219 | return segs; | 1224 | return segs; |
1220 | } | 1225 | } |
1221 | 1226 | ||
@@ -1291,9 +1296,15 @@ static int dev_gso_segment(struct sk_buff *skb) | |||
1291 | { | 1296 | { |
1292 | struct net_device *dev = skb->dev; | 1297 | struct net_device *dev = skb->dev; |
1293 | struct sk_buff *segs; | 1298 | struct sk_buff *segs; |
1299 | int features = dev->features & ~(illegal_highdma(dev, skb) ? | ||
1300 | NETIF_F_SG : 0); | ||
1301 | |||
1302 | segs = skb_gso_segment(skb, features); | ||
1303 | |||
1304 | /* Verifying header integrity only. */ | ||
1305 | if (!segs) | ||
1306 | return 0; | ||
1294 | 1307 | ||
1295 | segs = skb_gso_segment(skb, dev->features & NETIF_F_SG && | ||
1296 | !illegal_highdma(dev, skb)); | ||
1297 | if (unlikely(IS_ERR(segs))) | 1308 | if (unlikely(IS_ERR(segs))) |
1298 | return PTR_ERR(segs); | 1309 | return PTR_ERR(segs); |
1299 | 1310 | ||
@@ -1310,13 +1321,17 @@ int dev_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) | |||
1310 | if (netdev_nit) | 1321 | if (netdev_nit) |
1311 | dev_queue_xmit_nit(skb, dev); | 1322 | dev_queue_xmit_nit(skb, dev); |
1312 | 1323 | ||
1313 | if (!netif_needs_gso(dev, skb)) | 1324 | if (netif_needs_gso(dev, skb)) { |
1314 | return dev->hard_start_xmit(skb, dev); | 1325 | if (unlikely(dev_gso_segment(skb))) |
1326 | goto out_kfree_skb; | ||
1327 | if (skb->next) | ||
1328 | goto gso; | ||
1329 | } | ||
1315 | 1330 | ||
1316 | if (unlikely(dev_gso_segment(skb))) | 1331 | return dev->hard_start_xmit(skb, dev); |
1317 | goto out_kfree_skb; | ||
1318 | } | 1332 | } |
1319 | 1333 | ||
1334 | gso: | ||
1320 | do { | 1335 | do { |
1321 | struct sk_buff *nskb = skb->next; | 1336 | struct sk_buff *nskb = skb->next; |
1322 | int rc; | 1337 | int rc; |
diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 6edbb90cbcec..dfef9eece83e 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c | |||
@@ -1848,13 +1848,13 @@ EXPORT_SYMBOL_GPL(skb_pull_rcsum); | |||
1848 | /** | 1848 | /** |
1849 | * skb_segment - Perform protocol segmentation on skb. | 1849 | * skb_segment - Perform protocol segmentation on skb. |
1850 | * @skb: buffer to segment | 1850 | * @skb: buffer to segment |
1851 | * @sg: whether scatter-gather can be used for generated segments | 1851 | * @features: features for the output path (see dev->features) |
1852 | * | 1852 | * |
1853 | * This function performs segmentation on the given skb. It returns | 1853 | * This function performs segmentation on the given skb. It returns |
1854 | * the segment at the given position. It returns NULL if there are | 1854 | * the segment at the given position. It returns NULL if there are |
1855 | * no more segments to generate, or when an error is encountered. | 1855 | * no more segments to generate, or when an error is encountered. |
1856 | */ | 1856 | */ |
1857 | struct sk_buff *skb_segment(struct sk_buff *skb, int sg) | 1857 | struct sk_buff *skb_segment(struct sk_buff *skb, int features) |
1858 | { | 1858 | { |
1859 | struct sk_buff *segs = NULL; | 1859 | struct sk_buff *segs = NULL; |
1860 | struct sk_buff *tail = NULL; | 1860 | struct sk_buff *tail = NULL; |
@@ -1863,6 +1863,7 @@ struct sk_buff *skb_segment(struct sk_buff *skb, int sg) | |||
1863 | unsigned int offset = doffset; | 1863 | unsigned int offset = doffset; |
1864 | unsigned int headroom; | 1864 | unsigned int headroom; |
1865 | unsigned int len; | 1865 | unsigned int len; |
1866 | int sg = features & NETIF_F_SG; | ||
1866 | int nfrags = skb_shinfo(skb)->nr_frags; | 1867 | int nfrags = skb_shinfo(skb)->nr_frags; |
1867 | int err = -ENOMEM; | 1868 | int err = -ENOMEM; |
1868 | int i = 0; | 1869 | int i = 0; |
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 461216b47948..8d157157bf8e 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) | |||
1097 | 1097 | ||
1098 | EXPORT_SYMBOL(inet_sk_rebuild_header); | 1098 | EXPORT_SYMBOL(inet_sk_rebuild_header); |
1099 | 1099 | ||
1100 | static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int sg) | 1100 | static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features) |
1101 | { | 1101 | { |
1102 | struct sk_buff *segs = ERR_PTR(-EINVAL); | 1102 | struct sk_buff *segs = ERR_PTR(-EINVAL); |
1103 | struct iphdr *iph; | 1103 | struct iphdr *iph; |
@@ -1126,10 +1126,10 @@ static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int sg) | |||
1126 | rcu_read_lock(); | 1126 | rcu_read_lock(); |
1127 | ops = rcu_dereference(inet_protos[proto]); | 1127 | ops = rcu_dereference(inet_protos[proto]); |
1128 | if (ops && ops->gso_segment) | 1128 | if (ops && ops->gso_segment) |
1129 | segs = ops->gso_segment(skb, sg); | 1129 | segs = ops->gso_segment(skb, features); |
1130 | rcu_read_unlock(); | 1130 | rcu_read_unlock(); |
1131 | 1131 | ||
1132 | if (IS_ERR(segs)) | 1132 | if (!segs || unlikely(IS_ERR(segs))) |
1133 | goto out; | 1133 | goto out; |
1134 | 1134 | ||
1135 | skb = segs; | 1135 | skb = segs; |
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index c04176be7ed1..0336422c88a0 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, | |||
2145 | EXPORT_SYMBOL(compat_tcp_getsockopt); | 2145 | EXPORT_SYMBOL(compat_tcp_getsockopt); |
2146 | #endif | 2146 | #endif |
2147 | 2147 | ||
2148 | struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg) | 2148 | struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features) |
2149 | { | 2149 | { |
2150 | struct sk_buff *segs = ERR_PTR(-EINVAL); | 2150 | struct sk_buff *segs = ERR_PTR(-EINVAL); |
2151 | struct tcphdr *th; | 2151 | struct tcphdr *th; |
@@ -2166,10 +2166,14 @@ struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int sg) | |||
2166 | if (!pskb_may_pull(skb, thlen)) | 2166 | if (!pskb_may_pull(skb, thlen)) |
2167 | goto out; | 2167 | goto out; |
2168 | 2168 | ||
2169 | segs = NULL; | ||
2170 | if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) | ||
2171 | goto out; | ||
2172 | |||
2169 | oldlen = (u16)~skb->len; | 2173 | oldlen = (u16)~skb->len; |
2170 | __skb_pull(skb, thlen); | 2174 | __skb_pull(skb, thlen); |
2171 | 2175 | ||
2172 | segs = skb_segment(skb, sg); | 2176 | segs = skb_segment(skb, features); |
2173 | if (IS_ERR(segs)) | 2177 | if (IS_ERR(segs)) |
2174 | goto out; | 2178 | goto out; |
2175 | 2179 | ||