diff options
-rw-r--r-- | include/linux/netdevice.h | 8 | ||||
-rw-r--r-- | include/net/protocol.h | 2 | ||||
-rw-r--r-- | include/net/tcp.h | 1 | ||||
-rw-r--r-- | net/core/dev.c | 36 | ||||
-rw-r--r-- | net/ipv4/af_inet.c | 36 | ||||
-rw-r--r-- | net/ipv4/tcp_ipv4.c | 18 | ||||
-rw-r--r-- | net/ipv6/ipv6_sockglue.c | 89 | ||||
-rw-r--r-- | net/ipv6/tcp_ipv6.c | 19 |
8 files changed, 174 insertions, 35 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0359a6430018..76cc099c8580 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -549,6 +549,7 @@ struct packet_type { | |||
549 | struct net_device *); | 549 | struct net_device *); |
550 | struct sk_buff *(*gso_segment)(struct sk_buff *skb, | 550 | struct sk_buff *(*gso_segment)(struct sk_buff *skb, |
551 | int features); | 551 | int features); |
552 | int (*gso_send_check)(struct sk_buff *skb); | ||
552 | void *af_packet_priv; | 553 | void *af_packet_priv; |
553 | struct list_head list; | 554 | struct list_head list; |
554 | }; | 555 | }; |
@@ -1001,13 +1002,14 @@ static inline int net_gso_ok(int features, int gso_type) | |||
1001 | 1002 | ||
1002 | static inline int skb_gso_ok(struct sk_buff *skb, int features) | 1003 | static inline int skb_gso_ok(struct sk_buff *skb, int features) |
1003 | { | 1004 | { |
1004 | return net_gso_ok(features, skb_is_gso(skb) ? | 1005 | return net_gso_ok(features, skb_shinfo(skb)->gso_type); |
1005 | skb_shinfo(skb)->gso_type : 0); | ||
1006 | } | 1006 | } |
1007 | 1007 | ||
1008 | static inline int netif_needs_gso(struct net_device *dev, struct sk_buff *skb) | 1008 | static inline int netif_needs_gso(struct net_device *dev, struct sk_buff *skb) |
1009 | { | 1009 | { |
1010 | return !skb_gso_ok(skb, dev->features); | 1010 | return skb_is_gso(skb) && |
1011 | (!skb_gso_ok(skb, dev->features) || | ||
1012 | unlikely(skb->ip_summed != CHECKSUM_HW)); | ||
1011 | } | 1013 | } |
1012 | 1014 | ||
1013 | #endif /* __KERNEL__ */ | 1015 | #endif /* __KERNEL__ */ |
diff --git a/include/net/protocol.h b/include/net/protocol.h index a225d6371cb1..c643bce64e55 100644 --- a/include/net/protocol.h +++ b/include/net/protocol.h | |||
@@ -36,6 +36,7 @@ | |||
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 | int (*gso_send_check)(struct sk_buff *skb); | ||
39 | struct sk_buff *(*gso_segment)(struct sk_buff *skb, | 40 | struct sk_buff *(*gso_segment)(struct sk_buff *skb, |
40 | int features); | 41 | int features); |
41 | int no_policy; | 42 | int no_policy; |
@@ -51,6 +52,7 @@ struct inet6_protocol | |||
51 | int type, int code, int offset, | 52 | int type, int code, int offset, |
52 | __u32 info); | 53 | __u32 info); |
53 | 54 | ||
55 | int (*gso_send_check)(struct sk_buff *skb); | ||
54 | struct sk_buff *(*gso_segment)(struct sk_buff *skb, | 56 | struct sk_buff *(*gso_segment)(struct sk_buff *skb, |
55 | int features); | 57 | int features); |
56 | 58 | ||
diff --git a/include/net/tcp.h b/include/net/tcp.h index 3cd803b0d7a5..0720bddff1e9 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h | |||
@@ -1086,6 +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 int tcp_v4_gso_send_check(struct sk_buff *skb); | ||
1089 | extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features); | 1090 | extern struct sk_buff *tcp_tso_segment(struct sk_buff *skb, int features); |
1090 | 1091 | ||
1091 | #ifdef CONFIG_PROC_FS | 1092 | #ifdef CONFIG_PROC_FS |
diff --git a/net/core/dev.c b/net/core/dev.c index 0096349ee38b..4d2b5167d7f5 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -1162,9 +1162,17 @@ int skb_checksum_help(struct sk_buff *skb, int inward) | |||
1162 | unsigned int csum; | 1162 | unsigned int csum; |
1163 | int ret = 0, offset = skb->h.raw - skb->data; | 1163 | int ret = 0, offset = skb->h.raw - skb->data; |
1164 | 1164 | ||
1165 | if (inward) { | 1165 | if (inward) |
1166 | skb->ip_summed = CHECKSUM_NONE; | 1166 | goto out_set_summed; |
1167 | goto out; | 1167 | |
1168 | if (unlikely(skb_shinfo(skb)->gso_size)) { | ||
1169 | static int warned; | ||
1170 | |||
1171 | WARN_ON(!warned); | ||
1172 | warned = 1; | ||
1173 | |||
1174 | /* Let GSO fix up the checksum. */ | ||
1175 | goto out_set_summed; | ||
1168 | } | 1176 | } |
1169 | 1177 | ||
1170 | if (skb_cloned(skb)) { | 1178 | if (skb_cloned(skb)) { |
@@ -1181,6 +1189,8 @@ int skb_checksum_help(struct sk_buff *skb, int inward) | |||
1181 | BUG_ON(skb->csum + 2 > offset); | 1189 | BUG_ON(skb->csum + 2 > offset); |
1182 | 1190 | ||
1183 | *(u16*)(skb->h.raw + skb->csum) = csum_fold(csum); | 1191 | *(u16*)(skb->h.raw + skb->csum) = csum_fold(csum); |
1192 | |||
1193 | out_set_summed: | ||
1184 | skb->ip_summed = CHECKSUM_NONE; | 1194 | skb->ip_summed = CHECKSUM_NONE; |
1185 | out: | 1195 | out: |
1186 | return ret; | 1196 | return ret; |
@@ -1201,17 +1211,35 @@ struct sk_buff *skb_gso_segment(struct sk_buff *skb, int features) | |||
1201 | struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); | 1211 | struct sk_buff *segs = ERR_PTR(-EPROTONOSUPPORT); |
1202 | struct packet_type *ptype; | 1212 | struct packet_type *ptype; |
1203 | int type = skb->protocol; | 1213 | int type = skb->protocol; |
1214 | int err; | ||
1204 | 1215 | ||
1205 | BUG_ON(skb_shinfo(skb)->frag_list); | 1216 | BUG_ON(skb_shinfo(skb)->frag_list); |
1206 | BUG_ON(skb->ip_summed != CHECKSUM_HW); | ||
1207 | 1217 | ||
1208 | skb->mac.raw = skb->data; | 1218 | skb->mac.raw = skb->data; |
1209 | skb->mac_len = skb->nh.raw - skb->data; | 1219 | skb->mac_len = skb->nh.raw - skb->data; |
1210 | __skb_pull(skb, skb->mac_len); | 1220 | __skb_pull(skb, skb->mac_len); |
1211 | 1221 | ||
1222 | if (unlikely(skb->ip_summed != CHECKSUM_HW)) { | ||
1223 | static int warned; | ||
1224 | |||
1225 | WARN_ON(!warned); | ||
1226 | warned = 1; | ||
1227 | |||
1228 | if (skb_header_cloned(skb) && | ||
1229 | (err = pskb_expand_head(skb, 0, 0, GFP_ATOMIC))) | ||
1230 | return ERR_PTR(err); | ||
1231 | } | ||
1232 | |||
1212 | rcu_read_lock(); | 1233 | rcu_read_lock(); |
1213 | list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & 15], list) { | 1234 | list_for_each_entry_rcu(ptype, &ptype_base[ntohs(type) & 15], list) { |
1214 | if (ptype->type == type && !ptype->dev && ptype->gso_segment) { | 1235 | if (ptype->type == type && !ptype->dev && ptype->gso_segment) { |
1236 | if (unlikely(skb->ip_summed != CHECKSUM_HW)) { | ||
1237 | err = ptype->gso_send_check(skb); | ||
1238 | segs = ERR_PTR(err); | ||
1239 | if (err || skb_gso_ok(skb, features)) | ||
1240 | break; | ||
1241 | __skb_push(skb, skb->data - skb->nh.raw); | ||
1242 | } | ||
1215 | segs = ptype->gso_segment(skb, features); | 1243 | segs = ptype->gso_segment(skb, features); |
1216 | break; | 1244 | break; |
1217 | } | 1245 | } |
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 318d4674faa1..c84a32070f8d 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c | |||
@@ -1097,6 +1097,40 @@ 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 int inet_gso_send_check(struct sk_buff *skb) | ||
1101 | { | ||
1102 | struct iphdr *iph; | ||
1103 | struct net_protocol *ops; | ||
1104 | int proto; | ||
1105 | int ihl; | ||
1106 | int err = -EINVAL; | ||
1107 | |||
1108 | if (unlikely(!pskb_may_pull(skb, sizeof(*iph)))) | ||
1109 | goto out; | ||
1110 | |||
1111 | iph = skb->nh.iph; | ||
1112 | ihl = iph->ihl * 4; | ||
1113 | if (ihl < sizeof(*iph)) | ||
1114 | goto out; | ||
1115 | |||
1116 | if (unlikely(!pskb_may_pull(skb, ihl))) | ||
1117 | goto out; | ||
1118 | |||
1119 | skb->h.raw = __skb_pull(skb, ihl); | ||
1120 | iph = skb->nh.iph; | ||
1121 | proto = iph->protocol & (MAX_INET_PROTOS - 1); | ||
1122 | err = -EPROTONOSUPPORT; | ||
1123 | |||
1124 | rcu_read_lock(); | ||
1125 | ops = rcu_dereference(inet_protos[proto]); | ||
1126 | if (likely(ops && ops->gso_send_check)) | ||
1127 | err = ops->gso_send_check(skb); | ||
1128 | rcu_read_unlock(); | ||
1129 | |||
1130 | out: | ||
1131 | return err; | ||
1132 | } | ||
1133 | |||
1100 | static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features) | 1134 | static struct sk_buff *inet_gso_segment(struct sk_buff *skb, int features) |
1101 | { | 1135 | { |
1102 | struct sk_buff *segs = ERR_PTR(-EINVAL); | 1136 | struct sk_buff *segs = ERR_PTR(-EINVAL); |
@@ -1162,6 +1196,7 @@ static struct net_protocol igmp_protocol = { | |||
1162 | static struct net_protocol tcp_protocol = { | 1196 | static struct net_protocol tcp_protocol = { |
1163 | .handler = tcp_v4_rcv, | 1197 | .handler = tcp_v4_rcv, |
1164 | .err_handler = tcp_v4_err, | 1198 | .err_handler = tcp_v4_err, |
1199 | .gso_send_check = tcp_v4_gso_send_check, | ||
1165 | .gso_segment = tcp_tso_segment, | 1200 | .gso_segment = tcp_tso_segment, |
1166 | .no_policy = 1, | 1201 | .no_policy = 1, |
1167 | }; | 1202 | }; |
@@ -1208,6 +1243,7 @@ static int ipv4_proc_init(void); | |||
1208 | static struct packet_type ip_packet_type = { | 1243 | static struct packet_type ip_packet_type = { |
1209 | .type = __constant_htons(ETH_P_IP), | 1244 | .type = __constant_htons(ETH_P_IP), |
1210 | .func = ip_rcv, | 1245 | .func = ip_rcv, |
1246 | .gso_send_check = inet_gso_send_check, | ||
1211 | .gso_segment = inet_gso_segment, | 1247 | .gso_segment = inet_gso_segment, |
1212 | }; | 1248 | }; |
1213 | 1249 | ||
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 5a886e6efbbe..a891133f00e4 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c | |||
@@ -496,6 +496,24 @@ void tcp_v4_send_check(struct sock *sk, int len, struct sk_buff *skb) | |||
496 | } | 496 | } |
497 | } | 497 | } |
498 | 498 | ||
499 | int tcp_v4_gso_send_check(struct sk_buff *skb) | ||
500 | { | ||
501 | struct iphdr *iph; | ||
502 | struct tcphdr *th; | ||
503 | |||
504 | if (!pskb_may_pull(skb, sizeof(*th))) | ||
505 | return -EINVAL; | ||
506 | |||
507 | iph = skb->nh.iph; | ||
508 | th = skb->h.th; | ||
509 | |||
510 | th->check = 0; | ||
511 | th->check = ~tcp_v4_check(th, skb->len, iph->saddr, iph->daddr, 0); | ||
512 | skb->csum = offsetof(struct tcphdr, check); | ||
513 | skb->ip_summed = CHECKSUM_HW; | ||
514 | return 0; | ||
515 | } | ||
516 | |||
499 | /* | 517 | /* |
500 | * This routine will send an RST to the other tcp. | 518 | * This routine will send an RST to the other tcp. |
501 | * | 519 | * |
diff --git a/net/ipv6/ipv6_sockglue.c b/net/ipv6/ipv6_sockglue.c index 0c17dec11c8d..43327264e69c 100644 --- a/net/ipv6/ipv6_sockglue.c +++ b/net/ipv6/ipv6_sockglue.c | |||
@@ -57,29 +57,11 @@ | |||
57 | 57 | ||
58 | DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics) __read_mostly; | 58 | DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics) __read_mostly; |
59 | 59 | ||
60 | static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) | 60 | static struct inet6_protocol *ipv6_gso_pull_exthdrs(struct sk_buff *skb, |
61 | int proto) | ||
61 | { | 62 | { |
62 | struct sk_buff *segs = ERR_PTR(-EINVAL); | 63 | struct inet6_protocol *ops = NULL; |
63 | struct ipv6hdr *ipv6h; | ||
64 | struct inet6_protocol *ops; | ||
65 | int proto; | ||
66 | 64 | ||
67 | if (unlikely(skb_shinfo(skb)->gso_type & | ||
68 | ~(SKB_GSO_UDP | | ||
69 | SKB_GSO_DODGY | | ||
70 | SKB_GSO_TCP_ECN | | ||
71 | SKB_GSO_TCPV6 | | ||
72 | 0))) | ||
73 | goto out; | ||
74 | |||
75 | if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) | ||
76 | goto out; | ||
77 | |||
78 | ipv6h = skb->nh.ipv6h; | ||
79 | proto = ipv6h->nexthdr; | ||
80 | __skb_pull(skb, sizeof(*ipv6h)); | ||
81 | |||
82 | rcu_read_lock(); | ||
83 | for (;;) { | 65 | for (;;) { |
84 | struct ipv6_opt_hdr *opth; | 66 | struct ipv6_opt_hdr *opth; |
85 | int len; | 67 | int len; |
@@ -88,30 +70,80 @@ static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) | |||
88 | ops = rcu_dereference(inet6_protos[proto]); | 70 | ops = rcu_dereference(inet6_protos[proto]); |
89 | 71 | ||
90 | if (unlikely(!ops)) | 72 | if (unlikely(!ops)) |
91 | goto unlock; | 73 | break; |
92 | 74 | ||
93 | if (!(ops->flags & INET6_PROTO_GSO_EXTHDR)) | 75 | if (!(ops->flags & INET6_PROTO_GSO_EXTHDR)) |
94 | break; | 76 | break; |
95 | } | 77 | } |
96 | 78 | ||
97 | if (unlikely(!pskb_may_pull(skb, 8))) | 79 | if (unlikely(!pskb_may_pull(skb, 8))) |
98 | goto unlock; | 80 | break; |
99 | 81 | ||
100 | opth = (void *)skb->data; | 82 | opth = (void *)skb->data; |
101 | len = opth->hdrlen * 8 + 8; | 83 | len = opth->hdrlen * 8 + 8; |
102 | 84 | ||
103 | if (unlikely(!pskb_may_pull(skb, len))) | 85 | if (unlikely(!pskb_may_pull(skb, len))) |
104 | goto unlock; | 86 | break; |
105 | 87 | ||
106 | proto = opth->nexthdr; | 88 | proto = opth->nexthdr; |
107 | __skb_pull(skb, len); | 89 | __skb_pull(skb, len); |
108 | } | 90 | } |
109 | 91 | ||
110 | skb->h.raw = skb->data; | 92 | return ops; |
111 | if (likely(ops->gso_segment)) | 93 | } |
112 | segs = ops->gso_segment(skb, features); | 94 | |
95 | static int ipv6_gso_send_check(struct sk_buff *skb) | ||
96 | { | ||
97 | struct ipv6hdr *ipv6h; | ||
98 | struct inet6_protocol *ops; | ||
99 | int err = -EINVAL; | ||
100 | |||
101 | if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) | ||
102 | goto out; | ||
113 | 103 | ||
114 | unlock: | 104 | ipv6h = skb->nh.ipv6h; |
105 | __skb_pull(skb, sizeof(*ipv6h)); | ||
106 | err = -EPROTONOSUPPORT; | ||
107 | |||
108 | rcu_read_lock(); | ||
109 | ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); | ||
110 | if (likely(ops && ops->gso_send_check)) { | ||
111 | skb->h.raw = skb->data; | ||
112 | err = ops->gso_send_check(skb); | ||
113 | } | ||
114 | rcu_read_unlock(); | ||
115 | |||
116 | out: | ||
117 | return err; | ||
118 | } | ||
119 | |||
120 | static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features) | ||
121 | { | ||
122 | struct sk_buff *segs = ERR_PTR(-EINVAL); | ||
123 | struct ipv6hdr *ipv6h; | ||
124 | struct inet6_protocol *ops; | ||
125 | |||
126 | if (unlikely(skb_shinfo(skb)->gso_type & | ||
127 | ~(SKB_GSO_UDP | | ||
128 | SKB_GSO_DODGY | | ||
129 | SKB_GSO_TCP_ECN | | ||
130 | SKB_GSO_TCPV6 | | ||
131 | 0))) | ||
132 | goto out; | ||
133 | |||
134 | if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h)))) | ||
135 | goto out; | ||
136 | |||
137 | ipv6h = skb->nh.ipv6h; | ||
138 | __skb_pull(skb, sizeof(*ipv6h)); | ||
139 | segs = ERR_PTR(-EPROTONOSUPPORT); | ||
140 | |||
141 | rcu_read_lock(); | ||
142 | ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr); | ||
143 | if (likely(ops && ops->gso_segment)) { | ||
144 | skb->h.raw = skb->data; | ||
145 | segs = ops->gso_segment(skb, features); | ||
146 | } | ||
115 | rcu_read_unlock(); | 147 | rcu_read_unlock(); |
116 | 148 | ||
117 | if (unlikely(IS_ERR(segs))) | 149 | if (unlikely(IS_ERR(segs))) |
@@ -130,6 +162,7 @@ out: | |||
130 | static struct packet_type ipv6_packet_type = { | 162 | static struct packet_type ipv6_packet_type = { |
131 | .type = __constant_htons(ETH_P_IPV6), | 163 | .type = __constant_htons(ETH_P_IPV6), |
132 | .func = ipv6_rcv, | 164 | .func = ipv6_rcv, |
165 | .gso_send_check = ipv6_gso_send_check, | ||
133 | .gso_segment = ipv6_gso_segment, | 166 | .gso_segment = ipv6_gso_segment, |
134 | }; | 167 | }; |
135 | 168 | ||
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 5bdcb9002cf7..923989d0520d 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c | |||
@@ -552,6 +552,24 @@ static void tcp_v6_send_check(struct sock *sk, int len, struct sk_buff *skb) | |||
552 | } | 552 | } |
553 | } | 553 | } |
554 | 554 | ||
555 | static int tcp_v6_gso_send_check(struct sk_buff *skb) | ||
556 | { | ||
557 | struct ipv6hdr *ipv6h; | ||
558 | struct tcphdr *th; | ||
559 | |||
560 | if (!pskb_may_pull(skb, sizeof(*th))) | ||
561 | return -EINVAL; | ||
562 | |||
563 | ipv6h = skb->nh.ipv6h; | ||
564 | th = skb->h.th; | ||
565 | |||
566 | th->check = 0; | ||
567 | th->check = ~csum_ipv6_magic(&ipv6h->saddr, &ipv6h->daddr, skb->len, | ||
568 | IPPROTO_TCP, 0); | ||
569 | skb->csum = offsetof(struct tcphdr, check); | ||
570 | skb->ip_summed = CHECKSUM_HW; | ||
571 | return 0; | ||
572 | } | ||
555 | 573 | ||
556 | static void tcp_v6_send_reset(struct sk_buff *skb) | 574 | static void tcp_v6_send_reset(struct sk_buff *skb) |
557 | { | 575 | { |
@@ -1603,6 +1621,7 @@ struct proto tcpv6_prot = { | |||
1603 | static struct inet6_protocol tcpv6_protocol = { | 1621 | static struct inet6_protocol tcpv6_protocol = { |
1604 | .handler = tcp_v6_rcv, | 1622 | .handler = tcp_v6_rcv, |
1605 | .err_handler = tcp_v6_err, | 1623 | .err_handler = tcp_v6_err, |
1624 | .gso_send_check = tcp_v6_gso_send_check, | ||
1606 | .gso_segment = tcp_tso_segment, | 1625 | .gso_segment = tcp_tso_segment, |
1607 | .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, | 1626 | .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, |
1608 | }; | 1627 | }; |