diff options
author | Patrick McHardy <kaber@trash.net> | 2006-08-05 03:58:33 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-09-22 17:53:54 -0400 |
commit | 4cf411de49c65140b3c259748629b561c0d3340f (patch) | |
tree | 71dbc9fbbb0b64e805033665e3653d991abb0f7e | |
parent | 84fa7933a33f806bbbaae6775e87459b1ec584c0 (diff) |
[NETFILTER]: Get rid of HW checksum invalidation
Update hardware checksums incrementally to avoid breaking GSO.
Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | include/linux/netfilter.h | 6 | ||||
-rw-r--r-- | include/linux/netfilter_ipv4/ip_nat.h | 4 | ||||
-rw-r--r-- | include/linux/netfilter_ipv4/ip_nat_core.h | 8 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_nat_core.c | 52 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_nat_helper.c | 59 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_nat_proto_gre.c | 5 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_nat_proto_icmp.c | 8 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_nat_proto_tcp.c | 7 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_nat_proto_udp.c | 15 | ||||
-rw-r--r-- | net/ipv4/netfilter/ip_nat_standalone.c | 10 | ||||
-rw-r--r-- | net/ipv4/netfilter/ipt_ECN.c | 19 | ||||
-rw-r--r-- | net/ipv4/netfilter/ipt_REJECT.c | 1 | ||||
-rw-r--r-- | net/ipv4/netfilter/ipt_TCPMSS.c | 39 | ||||
-rw-r--r-- | net/netfilter/core.c | 22 |
14 files changed, 138 insertions, 117 deletions
diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 10168e26a846..b7e67d1d4382 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h | |||
@@ -282,6 +282,12 @@ extern void nf_invalidate_cache(int pf); | |||
282 | Returns true or false. */ | 282 | Returns true or false. */ |
283 | extern int skb_make_writable(struct sk_buff **pskb, unsigned int writable_len); | 283 | extern int skb_make_writable(struct sk_buff **pskb, unsigned int writable_len); |
284 | 284 | ||
285 | extern u_int16_t nf_csum_update(u_int32_t oldval, u_int32_t newval, | ||
286 | u_int32_t csum); | ||
287 | extern u_int16_t nf_proto_csum_update(struct sk_buff *skb, | ||
288 | u_int32_t oldval, u_int32_t newval, | ||
289 | u_int16_t csum, int pseudohdr); | ||
290 | |||
285 | struct nf_afinfo { | 291 | struct nf_afinfo { |
286 | unsigned short family; | 292 | unsigned short family; |
287 | unsigned int (*checksum)(struct sk_buff *skb, unsigned int hook, | 293 | unsigned int (*checksum)(struct sk_buff *skb, unsigned int hook, |
diff --git a/include/linux/netfilter_ipv4/ip_nat.h b/include/linux/netfilter_ipv4/ip_nat.h index e9f5ed1d9f68..98f8407e4cb5 100644 --- a/include/linux/netfilter_ipv4/ip_nat.h +++ b/include/linux/netfilter_ipv4/ip_nat.h | |||
@@ -72,10 +72,6 @@ extern unsigned int ip_nat_setup_info(struct ip_conntrack *conntrack, | |||
72 | extern int ip_nat_used_tuple(const struct ip_conntrack_tuple *tuple, | 72 | extern int ip_nat_used_tuple(const struct ip_conntrack_tuple *tuple, |
73 | const struct ip_conntrack *ignored_conntrack); | 73 | const struct ip_conntrack *ignored_conntrack); |
74 | 74 | ||
75 | /* Calculate relative checksum. */ | ||
76 | extern u_int16_t ip_nat_cheat_check(u_int32_t oldvalinv, | ||
77 | u_int32_t newval, | ||
78 | u_int16_t oldcheck); | ||
79 | #else /* !__KERNEL__: iptables wants this to compile. */ | 75 | #else /* !__KERNEL__: iptables wants this to compile. */ |
80 | #define ip_nat_multi_range ip_nat_multi_range_compat | 76 | #define ip_nat_multi_range ip_nat_multi_range_compat |
81 | #endif /*__KERNEL__*/ | 77 | #endif /*__KERNEL__*/ |
diff --git a/include/linux/netfilter_ipv4/ip_nat_core.h b/include/linux/netfilter_ipv4/ip_nat_core.h index 30db23f06b03..60566f9fd7b3 100644 --- a/include/linux/netfilter_ipv4/ip_nat_core.h +++ b/include/linux/netfilter_ipv4/ip_nat_core.h | |||
@@ -11,8 +11,8 @@ extern unsigned int ip_nat_packet(struct ip_conntrack *ct, | |||
11 | unsigned int hooknum, | 11 | unsigned int hooknum, |
12 | struct sk_buff **pskb); | 12 | struct sk_buff **pskb); |
13 | 13 | ||
14 | extern int ip_nat_icmp_reply_translation(struct sk_buff **pskb, | 14 | extern int ip_nat_icmp_reply_translation(struct ip_conntrack *ct, |
15 | struct ip_conntrack *ct, | 15 | enum ip_conntrack_info ctinfo, |
16 | enum ip_nat_manip_type manip, | 16 | unsigned int hooknum, |
17 | enum ip_conntrack_dir dir); | 17 | struct sk_buff **pskb); |
18 | #endif /* _IP_NAT_CORE_H */ | 18 | #endif /* _IP_NAT_CORE_H */ |
diff --git a/net/ipv4/netfilter/ip_nat_core.c b/net/ipv4/netfilter/ip_nat_core.c index 1741d555ad0d..4c540d03d48e 100644 --- a/net/ipv4/netfilter/ip_nat_core.c +++ b/net/ipv4/netfilter/ip_nat_core.c | |||
@@ -101,18 +101,6 @@ static void ip_nat_cleanup_conntrack(struct ip_conntrack *conn) | |||
101 | write_unlock_bh(&ip_nat_lock); | 101 | write_unlock_bh(&ip_nat_lock); |
102 | } | 102 | } |
103 | 103 | ||
104 | /* We do checksum mangling, so if they were wrong before they're still | ||
105 | * wrong. Also works for incomplete packets (eg. ICMP dest | ||
106 | * unreachables.) */ | ||
107 | u_int16_t | ||
108 | ip_nat_cheat_check(u_int32_t oldvalinv, u_int32_t newval, u_int16_t oldcheck) | ||
109 | { | ||
110 | u_int32_t diffs[] = { oldvalinv, newval }; | ||
111 | return csum_fold(csum_partial((char *)diffs, sizeof(diffs), | ||
112 | oldcheck^0xFFFF)); | ||
113 | } | ||
114 | EXPORT_SYMBOL(ip_nat_cheat_check); | ||
115 | |||
116 | /* Is this tuple already taken? (not by us) */ | 104 | /* Is this tuple already taken? (not by us) */ |
117 | int | 105 | int |
118 | ip_nat_used_tuple(const struct ip_conntrack_tuple *tuple, | 106 | ip_nat_used_tuple(const struct ip_conntrack_tuple *tuple, |
@@ -378,12 +366,12 @@ manip_pkt(u_int16_t proto, | |||
378 | iph = (void *)(*pskb)->data + iphdroff; | 366 | iph = (void *)(*pskb)->data + iphdroff; |
379 | 367 | ||
380 | if (maniptype == IP_NAT_MANIP_SRC) { | 368 | if (maniptype == IP_NAT_MANIP_SRC) { |
381 | iph->check = ip_nat_cheat_check(~iph->saddr, target->src.ip, | 369 | iph->check = nf_csum_update(~iph->saddr, target->src.ip, |
382 | iph->check); | 370 | iph->check); |
383 | iph->saddr = target->src.ip; | 371 | iph->saddr = target->src.ip; |
384 | } else { | 372 | } else { |
385 | iph->check = ip_nat_cheat_check(~iph->daddr, target->dst.ip, | 373 | iph->check = nf_csum_update(~iph->daddr, target->dst.ip, |
386 | iph->check); | 374 | iph->check); |
387 | iph->daddr = target->dst.ip; | 375 | iph->daddr = target->dst.ip; |
388 | } | 376 | } |
389 | return 1; | 377 | return 1; |
@@ -423,10 +411,10 @@ unsigned int ip_nat_packet(struct ip_conntrack *ct, | |||
423 | EXPORT_SYMBOL_GPL(ip_nat_packet); | 411 | EXPORT_SYMBOL_GPL(ip_nat_packet); |
424 | 412 | ||
425 | /* Dir is direction ICMP is coming from (opposite to packet it contains) */ | 413 | /* Dir is direction ICMP is coming from (opposite to packet it contains) */ |
426 | int ip_nat_icmp_reply_translation(struct sk_buff **pskb, | 414 | int ip_nat_icmp_reply_translation(struct ip_conntrack *ct, |
427 | struct ip_conntrack *ct, | 415 | enum ip_conntrack_info ctinfo, |
428 | enum ip_nat_manip_type manip, | 416 | unsigned int hooknum, |
429 | enum ip_conntrack_dir dir) | 417 | struct sk_buff **pskb) |
430 | { | 418 | { |
431 | struct { | 419 | struct { |
432 | struct icmphdr icmp; | 420 | struct icmphdr icmp; |
@@ -434,7 +422,9 @@ int ip_nat_icmp_reply_translation(struct sk_buff **pskb, | |||
434 | } *inside; | 422 | } *inside; |
435 | struct ip_conntrack_tuple inner, target; | 423 | struct ip_conntrack_tuple inner, target; |
436 | int hdrlen = (*pskb)->nh.iph->ihl * 4; | 424 | int hdrlen = (*pskb)->nh.iph->ihl * 4; |
425 | enum ip_conntrack_dir dir = CTINFO2DIR(ctinfo); | ||
437 | unsigned long statusbit; | 426 | unsigned long statusbit; |
427 | enum ip_nat_manip_type manip = HOOK2MANIP(hooknum); | ||
438 | 428 | ||
439 | if (!skb_make_writable(pskb, hdrlen + sizeof(*inside))) | 429 | if (!skb_make_writable(pskb, hdrlen + sizeof(*inside))) |
440 | return 0; | 430 | return 0; |
@@ -443,12 +433,8 @@ int ip_nat_icmp_reply_translation(struct sk_buff **pskb, | |||
443 | 433 | ||
444 | /* We're actually going to mangle it beyond trivial checksum | 434 | /* We're actually going to mangle it beyond trivial checksum |
445 | adjustment, so make sure the current checksum is correct. */ | 435 | adjustment, so make sure the current checksum is correct. */ |
446 | if ((*pskb)->ip_summed != CHECKSUM_UNNECESSARY) { | 436 | if (nf_ip_checksum(*pskb, hooknum, hdrlen, 0)) |
447 | hdrlen = (*pskb)->nh.iph->ihl * 4; | 437 | return 0; |
448 | if ((u16)csum_fold(skb_checksum(*pskb, hdrlen, | ||
449 | (*pskb)->len - hdrlen, 0))) | ||
450 | return 0; | ||
451 | } | ||
452 | 438 | ||
453 | /* Must be RELATED */ | 439 | /* Must be RELATED */ |
454 | IP_NF_ASSERT((*pskb)->nfctinfo == IP_CT_RELATED || | 440 | IP_NF_ASSERT((*pskb)->nfctinfo == IP_CT_RELATED || |
@@ -487,12 +473,14 @@ int ip_nat_icmp_reply_translation(struct sk_buff **pskb, | |||
487 | !manip)) | 473 | !manip)) |
488 | return 0; | 474 | return 0; |
489 | 475 | ||
490 | /* Reloading "inside" here since manip_pkt inner. */ | 476 | if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) { |
491 | inside = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4; | 477 | /* Reloading "inside" here since manip_pkt inner. */ |
492 | inside->icmp.checksum = 0; | 478 | inside = (void *)(*pskb)->data + (*pskb)->nh.iph->ihl*4; |
493 | inside->icmp.checksum = csum_fold(skb_checksum(*pskb, hdrlen, | 479 | inside->icmp.checksum = 0; |
494 | (*pskb)->len - hdrlen, | 480 | inside->icmp.checksum = csum_fold(skb_checksum(*pskb, hdrlen, |
495 | 0)); | 481 | (*pskb)->len - hdrlen, |
482 | 0)); | ||
483 | } | ||
496 | 484 | ||
497 | /* Change outer to look the reply to an incoming packet | 485 | /* Change outer to look the reply to an incoming packet |
498 | * (proto 0 means don't invert per-proto part). */ | 486 | * (proto 0 means don't invert per-proto part). */ |
diff --git a/net/ipv4/netfilter/ip_nat_helper.c b/net/ipv4/netfilter/ip_nat_helper.c index cbcaa45370ae..021c3daae3ed 100644 --- a/net/ipv4/netfilter/ip_nat_helper.c +++ b/net/ipv4/netfilter/ip_nat_helper.c | |||
@@ -165,7 +165,7 @@ ip_nat_mangle_tcp_packet(struct sk_buff **pskb, | |||
165 | { | 165 | { |
166 | struct iphdr *iph; | 166 | struct iphdr *iph; |
167 | struct tcphdr *tcph; | 167 | struct tcphdr *tcph; |
168 | int datalen; | 168 | int oldlen, datalen; |
169 | 169 | ||
170 | if (!skb_make_writable(pskb, (*pskb)->len)) | 170 | if (!skb_make_writable(pskb, (*pskb)->len)) |
171 | return 0; | 171 | return 0; |
@@ -180,13 +180,22 @@ ip_nat_mangle_tcp_packet(struct sk_buff **pskb, | |||
180 | iph = (*pskb)->nh.iph; | 180 | iph = (*pskb)->nh.iph; |
181 | tcph = (void *)iph + iph->ihl*4; | 181 | tcph = (void *)iph + iph->ihl*4; |
182 | 182 | ||
183 | oldlen = (*pskb)->len - iph->ihl*4; | ||
183 | mangle_contents(*pskb, iph->ihl*4 + tcph->doff*4, | 184 | mangle_contents(*pskb, iph->ihl*4 + tcph->doff*4, |
184 | match_offset, match_len, rep_buffer, rep_len); | 185 | match_offset, match_len, rep_buffer, rep_len); |
185 | 186 | ||
186 | datalen = (*pskb)->len - iph->ihl*4; | 187 | datalen = (*pskb)->len - iph->ihl*4; |
187 | tcph->check = 0; | 188 | if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) { |
188 | tcph->check = tcp_v4_check(tcph, datalen, iph->saddr, iph->daddr, | 189 | tcph->check = 0; |
189 | csum_partial((char *)tcph, datalen, 0)); | 190 | tcph->check = tcp_v4_check(tcph, datalen, |
191 | iph->saddr, iph->daddr, | ||
192 | csum_partial((char *)tcph, | ||
193 | datalen, 0)); | ||
194 | } else | ||
195 | tcph->check = nf_proto_csum_update(*pskb, | ||
196 | htons(oldlen) ^ 0xFFFF, | ||
197 | htons(datalen), | ||
198 | tcph->check, 1); | ||
190 | 199 | ||
191 | if (rep_len != match_len) { | 200 | if (rep_len != match_len) { |
192 | set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); | 201 | set_bit(IPS_SEQ_ADJUST_BIT, &ct->status); |
@@ -221,6 +230,7 @@ ip_nat_mangle_udp_packet(struct sk_buff **pskb, | |||
221 | { | 230 | { |
222 | struct iphdr *iph; | 231 | struct iphdr *iph; |
223 | struct udphdr *udph; | 232 | struct udphdr *udph; |
233 | int datalen, oldlen; | ||
224 | 234 | ||
225 | /* UDP helpers might accidentally mangle the wrong packet */ | 235 | /* UDP helpers might accidentally mangle the wrong packet */ |
226 | iph = (*pskb)->nh.iph; | 236 | iph = (*pskb)->nh.iph; |
@@ -238,22 +248,32 @@ ip_nat_mangle_udp_packet(struct sk_buff **pskb, | |||
238 | 248 | ||
239 | iph = (*pskb)->nh.iph; | 249 | iph = (*pskb)->nh.iph; |
240 | udph = (void *)iph + iph->ihl*4; | 250 | udph = (void *)iph + iph->ihl*4; |
251 | |||
252 | oldlen = (*pskb)->len - iph->ihl*4; | ||
241 | mangle_contents(*pskb, iph->ihl*4 + sizeof(*udph), | 253 | mangle_contents(*pskb, iph->ihl*4 + sizeof(*udph), |
242 | match_offset, match_len, rep_buffer, rep_len); | 254 | match_offset, match_len, rep_buffer, rep_len); |
243 | 255 | ||
244 | /* update the length of the UDP packet */ | 256 | /* update the length of the UDP packet */ |
245 | udph->len = htons((*pskb)->len - iph->ihl*4); | 257 | datalen = (*pskb)->len - iph->ihl*4; |
258 | udph->len = htons(datalen); | ||
246 | 259 | ||
247 | /* fix udp checksum if udp checksum was previously calculated */ | 260 | /* fix udp checksum if udp checksum was previously calculated */ |
248 | if (udph->check) { | 261 | if (!udph->check && (*pskb)->ip_summed != CHECKSUM_PARTIAL) |
249 | int datalen = (*pskb)->len - iph->ihl * 4; | 262 | return 1; |
263 | |||
264 | if ((*pskb)->ip_summed != CHECKSUM_PARTIAL) { | ||
250 | udph->check = 0; | 265 | udph->check = 0; |
251 | udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, | 266 | udph->check = csum_tcpudp_magic(iph->saddr, iph->daddr, |
252 | datalen, IPPROTO_UDP, | 267 | datalen, IPPROTO_UDP, |
253 | csum_partial((char *)udph, | 268 | csum_partial((char *)udph, |
254 | datalen, 0)); | 269 | datalen, 0)); |
255 | } | 270 | if (!udph->check) |
256 | 271 | udph->check = -1; | |
272 | } else | ||
273 | udph->check = nf_proto_csum_update(*pskb, | ||
274 | htons(oldlen) ^ 0xFFFF, | ||
275 | htons(datalen), | ||
276 | udph->check, 1); | ||
257 | return 1; | 277 | return 1; |
258 | } | 278 | } |
259 | EXPORT_SYMBOL(ip_nat_mangle_udp_packet); | 279 | EXPORT_SYMBOL(ip_nat_mangle_udp_packet); |
@@ -293,11 +313,14 @@ sack_adjust(struct sk_buff *skb, | |||
293 | ntohl(sack->start_seq), new_start_seq, | 313 | ntohl(sack->start_seq), new_start_seq, |
294 | ntohl(sack->end_seq), new_end_seq); | 314 | ntohl(sack->end_seq), new_end_seq); |
295 | 315 | ||
296 | tcph->check = | 316 | tcph->check = nf_proto_csum_update(skb, |
297 | ip_nat_cheat_check(~sack->start_seq, new_start_seq, | 317 | ~sack->start_seq, |
298 | ip_nat_cheat_check(~sack->end_seq, | 318 | new_start_seq, |
299 | new_end_seq, | 319 | tcph->check, 0); |
300 | tcph->check)); | 320 | tcph->check = nf_proto_csum_update(skb, |
321 | ~sack->end_seq, | ||
322 | new_end_seq, | ||
323 | tcph->check, 0); | ||
301 | sack->start_seq = new_start_seq; | 324 | sack->start_seq = new_start_seq; |
302 | sack->end_seq = new_end_seq; | 325 | sack->end_seq = new_end_seq; |
303 | sackoff += sizeof(*sack); | 326 | sackoff += sizeof(*sack); |
@@ -381,10 +404,10 @@ ip_nat_seq_adjust(struct sk_buff **pskb, | |||
381 | newack = ntohl(tcph->ack_seq) - other_way->offset_before; | 404 | newack = ntohl(tcph->ack_seq) - other_way->offset_before; |
382 | newack = htonl(newack); | 405 | newack = htonl(newack); |
383 | 406 | ||
384 | tcph->check = ip_nat_cheat_check(~tcph->seq, newseq, | 407 | tcph->check = nf_proto_csum_update(*pskb, ~tcph->seq, newseq, |
385 | ip_nat_cheat_check(~tcph->ack_seq, | 408 | tcph->check, 0); |
386 | newack, | 409 | tcph->check = nf_proto_csum_update(*pskb, ~tcph->ack_seq, newack, |
387 | tcph->check)); | 410 | tcph->check, 0); |
388 | 411 | ||
389 | DEBUGP("Adjusting sequence number from %u->%u, ack from %u->%u\n", | 412 | DEBUGP("Adjusting sequence number from %u->%u, ack from %u->%u\n", |
390 | ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq), | 413 | ntohl(tcph->seq), ntohl(newseq), ntohl(tcph->ack_seq), |
diff --git a/net/ipv4/netfilter/ip_nat_proto_gre.c b/net/ipv4/netfilter/ip_nat_proto_gre.c index 38acfdf540eb..70a65372225a 100644 --- a/net/ipv4/netfilter/ip_nat_proto_gre.c +++ b/net/ipv4/netfilter/ip_nat_proto_gre.c | |||
@@ -130,9 +130,10 @@ gre_manip_pkt(struct sk_buff **pskb, | |||
130 | if (greh->csum) { | 130 | if (greh->csum) { |
131 | /* FIXME: Never tested this code... */ | 131 | /* FIXME: Never tested this code... */ |
132 | *(gre_csum(greh)) = | 132 | *(gre_csum(greh)) = |
133 | ip_nat_cheat_check(~*(gre_key(greh)), | 133 | nf_proto_csum_update(*pskb, |
134 | ~*(gre_key(greh)), | ||
134 | tuple->dst.u.gre.key, | 135 | tuple->dst.u.gre.key, |
135 | *(gre_csum(greh))); | 136 | *(gre_csum(greh)), 0); |
136 | } | 137 | } |
137 | *(gre_key(greh)) = tuple->dst.u.gre.key; | 138 | *(gre_key(greh)) = tuple->dst.u.gre.key; |
138 | break; | 139 | break; |
diff --git a/net/ipv4/netfilter/ip_nat_proto_icmp.c b/net/ipv4/netfilter/ip_nat_proto_icmp.c index 31a3f4ccb99c..ec50cc295317 100644 --- a/net/ipv4/netfilter/ip_nat_proto_icmp.c +++ b/net/ipv4/netfilter/ip_nat_proto_icmp.c | |||
@@ -66,10 +66,10 @@ icmp_manip_pkt(struct sk_buff **pskb, | |||
66 | return 0; | 66 | return 0; |
67 | 67 | ||
68 | hdr = (struct icmphdr *)((*pskb)->data + hdroff); | 68 | hdr = (struct icmphdr *)((*pskb)->data + hdroff); |
69 | 69 | hdr->checksum = nf_proto_csum_update(*pskb, | |
70 | hdr->checksum = ip_nat_cheat_check(hdr->un.echo.id ^ 0xFFFF, | 70 | hdr->un.echo.id ^ 0xFFFF, |
71 | tuple->src.u.icmp.id, | 71 | tuple->src.u.icmp.id, |
72 | hdr->checksum); | 72 | hdr->checksum, 0); |
73 | hdr->un.echo.id = tuple->src.u.icmp.id; | 73 | hdr->un.echo.id = tuple->src.u.icmp.id; |
74 | return 1; | 74 | return 1; |
75 | } | 75 | } |
diff --git a/net/ipv4/netfilter/ip_nat_proto_tcp.c b/net/ipv4/netfilter/ip_nat_proto_tcp.c index a3d14079eba6..72a6307bd2db 100644 --- a/net/ipv4/netfilter/ip_nat_proto_tcp.c +++ b/net/ipv4/netfilter/ip_nat_proto_tcp.c | |||
@@ -129,10 +129,9 @@ tcp_manip_pkt(struct sk_buff **pskb, | |||
129 | if (hdrsize < sizeof(*hdr)) | 129 | if (hdrsize < sizeof(*hdr)) |
130 | return 1; | 130 | return 1; |
131 | 131 | ||
132 | hdr->check = ip_nat_cheat_check(~oldip, newip, | 132 | hdr->check = nf_proto_csum_update(*pskb, ~oldip, newip, hdr->check, 1); |
133 | ip_nat_cheat_check(oldport ^ 0xFFFF, | 133 | hdr->check = nf_proto_csum_update(*pskb, oldport ^ 0xFFFF, newport, |
134 | newport, | 134 | hdr->check, 0); |
135 | hdr->check)); | ||
136 | return 1; | 135 | return 1; |
137 | } | 136 | } |
138 | 137 | ||
diff --git a/net/ipv4/netfilter/ip_nat_proto_udp.c b/net/ipv4/netfilter/ip_nat_proto_udp.c index ec6053fdc867..5da196ae758c 100644 --- a/net/ipv4/netfilter/ip_nat_proto_udp.c +++ b/net/ipv4/netfilter/ip_nat_proto_udp.c | |||
@@ -113,11 +113,16 @@ udp_manip_pkt(struct sk_buff **pskb, | |||
113 | newport = tuple->dst.u.udp.port; | 113 | newport = tuple->dst.u.udp.port; |
114 | portptr = &hdr->dest; | 114 | portptr = &hdr->dest; |
115 | } | 115 | } |
116 | if (hdr->check) /* 0 is a special case meaning no checksum */ | 116 | |
117 | hdr->check = ip_nat_cheat_check(~oldip, newip, | 117 | if (hdr->check || (*pskb)->ip_summed == CHECKSUM_PARTIAL) { |
118 | ip_nat_cheat_check(*portptr ^ 0xFFFF, | 118 | hdr->check = nf_proto_csum_update(*pskb, ~oldip, newip, |
119 | newport, | 119 | hdr->check, 1); |
120 | hdr->check)); | 120 | hdr->check = nf_proto_csum_update(*pskb, |
121 | *portptr ^ 0xFFFF, newport, | ||
122 | hdr->check, 0); | ||
123 | if (!hdr->check) | ||
124 | hdr->check = -1; | ||
125 | } | ||
121 | *portptr = newport; | 126 | *portptr = newport; |
122 | return 1; | 127 | return 1; |
123 | } | 128 | } |
diff --git a/net/ipv4/netfilter/ip_nat_standalone.c b/net/ipv4/netfilter/ip_nat_standalone.c index f4f00c816d87..f3b778355432 100644 --- a/net/ipv4/netfilter/ip_nat_standalone.c +++ b/net/ipv4/netfilter/ip_nat_standalone.c | |||
@@ -110,12 +110,6 @@ ip_nat_fn(unsigned int hooknum, | |||
110 | IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off | 110 | IP_NF_ASSERT(!((*pskb)->nh.iph->frag_off |
111 | & htons(IP_MF|IP_OFFSET))); | 111 | & htons(IP_MF|IP_OFFSET))); |
112 | 112 | ||
113 | /* If we had a hardware checksum before, it's now invalid */ | ||
114 | if ((*pskb)->ip_summed == CHECKSUM_PARTIAL || | ||
115 | (*pskb)->ip_summed == CHECKSUM_COMPLETE) | ||
116 | if (skb_checksum_help(*pskb)) | ||
117 | return NF_DROP; | ||
118 | |||
119 | ct = ip_conntrack_get(*pskb, &ctinfo); | 113 | ct = ip_conntrack_get(*pskb, &ctinfo); |
120 | /* Can't track? It's not due to stress, or conntrack would | 114 | /* Can't track? It's not due to stress, or conntrack would |
121 | have dropped it. Hence it's the user's responsibilty to | 115 | have dropped it. Hence it's the user's responsibilty to |
@@ -146,8 +140,8 @@ ip_nat_fn(unsigned int hooknum, | |||
146 | case IP_CT_RELATED: | 140 | case IP_CT_RELATED: |
147 | case IP_CT_RELATED+IP_CT_IS_REPLY: | 141 | case IP_CT_RELATED+IP_CT_IS_REPLY: |
148 | if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) { | 142 | if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) { |
149 | if (!ip_nat_icmp_reply_translation(pskb, ct, maniptype, | 143 | if (!ip_nat_icmp_reply_translation(ct, ctinfo, |
150 | CTINFO2DIR(ctinfo))) | 144 | hooknum, pskb)) |
151 | return NF_DROP; | 145 | return NF_DROP; |
152 | else | 146 | else |
153 | return NF_ACCEPT; | 147 | return NF_ACCEPT; |
diff --git a/net/ipv4/netfilter/ipt_ECN.c b/net/ipv4/netfilter/ipt_ECN.c index 4ec43f98fe49..35916c74fe4e 100644 --- a/net/ipv4/netfilter/ipt_ECN.c +++ b/net/ipv4/netfilter/ipt_ECN.c | |||
@@ -52,7 +52,7 @@ static inline int | |||
52 | set_ect_tcp(struct sk_buff **pskb, const struct ipt_ECN_info *einfo) | 52 | set_ect_tcp(struct sk_buff **pskb, const struct ipt_ECN_info *einfo) |
53 | { | 53 | { |
54 | struct tcphdr _tcph, *tcph; | 54 | struct tcphdr _tcph, *tcph; |
55 | u_int16_t diffs[2]; | 55 | u_int16_t oldval; |
56 | 56 | ||
57 | /* Not enought header? */ | 57 | /* Not enought header? */ |
58 | tcph = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4, | 58 | tcph = skb_header_pointer(*pskb, (*pskb)->nh.iph->ihl*4, |
@@ -70,23 +70,16 @@ set_ect_tcp(struct sk_buff **pskb, const struct ipt_ECN_info *einfo) | |||
70 | return 0; | 70 | return 0; |
71 | tcph = (void *)(*pskb)->nh.iph + (*pskb)->nh.iph->ihl*4; | 71 | tcph = (void *)(*pskb)->nh.iph + (*pskb)->nh.iph->ihl*4; |
72 | 72 | ||
73 | if (((*pskb)->ip_summed == CHECKSUM_PARTIAL || | 73 | oldval = ((u_int16_t *)tcph)[6]; |
74 | (*pskb)->ip_summed == CHECKSUM_COMPLETE) && | ||
75 | skb_checksum_help(*pskb)) | ||
76 | return 0; | ||
77 | |||
78 | diffs[0] = ((u_int16_t *)tcph)[6]; | ||
79 | if (einfo->operation & IPT_ECN_OP_SET_ECE) | 74 | if (einfo->operation & IPT_ECN_OP_SET_ECE) |
80 | tcph->ece = einfo->proto.tcp.ece; | 75 | tcph->ece = einfo->proto.tcp.ece; |
81 | if (einfo->operation & IPT_ECN_OP_SET_CWR) | 76 | if (einfo->operation & IPT_ECN_OP_SET_CWR) |
82 | tcph->cwr = einfo->proto.tcp.cwr; | 77 | tcph->cwr = einfo->proto.tcp.cwr; |
83 | diffs[1] = ((u_int16_t *)tcph)[6]; | ||
84 | diffs[0] = diffs[0] ^ 0xFFFF; | ||
85 | 78 | ||
86 | if ((*pskb)->ip_summed != CHECKSUM_UNNECESSARY) | 79 | tcph->check = nf_proto_csum_update((*pskb), |
87 | tcph->check = csum_fold(csum_partial((char *)diffs, | 80 | oldval ^ 0xFFFF, |
88 | sizeof(diffs), | 81 | ((u_int16_t *)tcph)[6], |
89 | tcph->check^0xFFFF)); | 82 | tcph->check, 0); |
90 | return 1; | 83 | return 1; |
91 | } | 84 | } |
92 | 85 | ||
diff --git a/net/ipv4/netfilter/ipt_REJECT.c b/net/ipv4/netfilter/ipt_REJECT.c index 7f905bf2bde5..95c6662b663c 100644 --- a/net/ipv4/netfilter/ipt_REJECT.c +++ b/net/ipv4/netfilter/ipt_REJECT.c | |||
@@ -185,6 +185,7 @@ static void send_reset(struct sk_buff *oldskb, int hook) | |||
185 | tcph->urg_ptr = 0; | 185 | tcph->urg_ptr = 0; |
186 | 186 | ||
187 | /* Adjust TCP checksum */ | 187 | /* Adjust TCP checksum */ |
188 | nskb->ip_summed = CHECKSUM_NONE; | ||
188 | tcph->check = 0; | 189 | tcph->check = 0; |
189 | tcph->check = tcp_v4_check(tcph, sizeof(struct tcphdr), | 190 | tcph->check = tcp_v4_check(tcph, sizeof(struct tcphdr), |
190 | nskb->nh.iph->saddr, | 191 | nskb->nh.iph->saddr, |
diff --git a/net/ipv4/netfilter/ipt_TCPMSS.c b/net/ipv4/netfilter/ipt_TCPMSS.c index c998dc0fcd15..0fce85e05507 100644 --- a/net/ipv4/netfilter/ipt_TCPMSS.c +++ b/net/ipv4/netfilter/ipt_TCPMSS.c | |||
@@ -27,14 +27,6 @@ MODULE_DESCRIPTION("iptables TCP MSS modification module"); | |||
27 | #define DEBUGP(format, args...) | 27 | #define DEBUGP(format, args...) |
28 | #endif | 28 | #endif |
29 | 29 | ||
30 | static u_int16_t | ||
31 | cheat_check(u_int32_t oldvalinv, u_int32_t newval, u_int16_t oldcheck) | ||
32 | { | ||
33 | u_int32_t diffs[] = { oldvalinv, newval }; | ||
34 | return csum_fold(csum_partial((char *)diffs, sizeof(diffs), | ||
35 | oldcheck^0xFFFF)); | ||
36 | } | ||
37 | |||
38 | static inline unsigned int | 30 | static inline unsigned int |
39 | optlen(const u_int8_t *opt, unsigned int offset) | 31 | optlen(const u_int8_t *opt, unsigned int offset) |
40 | { | 32 | { |
@@ -62,11 +54,6 @@ ipt_tcpmss_target(struct sk_buff **pskb, | |||
62 | if (!skb_make_writable(pskb, (*pskb)->len)) | 54 | if (!skb_make_writable(pskb, (*pskb)->len)) |
63 | return NF_DROP; | 55 | return NF_DROP; |
64 | 56 | ||
65 | if (((*pskb)->ip_summed == CHECKSUM_PARTIAL || | ||
66 | (*pskb)->ip_summed == CHECKSUM_COMPLETE) && | ||
67 | skb_checksum_help(*pskb)) | ||
68 | return NF_DROP; | ||
69 | |||
70 | iph = (*pskb)->nh.iph; | 57 | iph = (*pskb)->nh.iph; |
71 | tcplen = (*pskb)->len - iph->ihl*4; | 58 | tcplen = (*pskb)->len - iph->ihl*4; |
72 | 59 | ||
@@ -120,9 +107,10 @@ ipt_tcpmss_target(struct sk_buff **pskb, | |||
120 | opt[i+2] = (newmss & 0xff00) >> 8; | 107 | opt[i+2] = (newmss & 0xff00) >> 8; |
121 | opt[i+3] = (newmss & 0x00ff); | 108 | opt[i+3] = (newmss & 0x00ff); |
122 | 109 | ||
123 | tcph->check = cheat_check(htons(oldmss)^0xFFFF, | 110 | tcph->check = nf_proto_csum_update(*pskb, |
124 | htons(newmss), | 111 | htons(oldmss)^0xFFFF, |
125 | tcph->check); | 112 | htons(newmss), |
113 | tcph->check, 0); | ||
126 | 114 | ||
127 | DEBUGP(KERN_INFO "ipt_tcpmss_target: %u.%u.%u.%u:%hu" | 115 | DEBUGP(KERN_INFO "ipt_tcpmss_target: %u.%u.%u.%u:%hu" |
128 | "->%u.%u.%u.%u:%hu changed TCP MSS option" | 116 | "->%u.%u.%u.%u:%hu changed TCP MSS option" |
@@ -162,8 +150,10 @@ ipt_tcpmss_target(struct sk_buff **pskb, | |||
162 | opt = (u_int8_t *)tcph + sizeof(struct tcphdr); | 150 | opt = (u_int8_t *)tcph + sizeof(struct tcphdr); |
163 | memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr)); | 151 | memmove(opt + TCPOLEN_MSS, opt, tcplen - sizeof(struct tcphdr)); |
164 | 152 | ||
165 | tcph->check = cheat_check(htons(tcplen) ^ 0xFFFF, | 153 | tcph->check = nf_proto_csum_update(*pskb, |
166 | htons(tcplen + TCPOLEN_MSS), tcph->check); | 154 | htons(tcplen) ^ 0xFFFF, |
155 | htons(tcplen + TCPOLEN_MSS), | ||
156 | tcph->check, 1); | ||
167 | tcplen += TCPOLEN_MSS; | 157 | tcplen += TCPOLEN_MSS; |
168 | 158 | ||
169 | opt[0] = TCPOPT_MSS; | 159 | opt[0] = TCPOPT_MSS; |
@@ -171,16 +161,19 @@ ipt_tcpmss_target(struct sk_buff **pskb, | |||
171 | opt[2] = (newmss & 0xff00) >> 8; | 161 | opt[2] = (newmss & 0xff00) >> 8; |
172 | opt[3] = (newmss & 0x00ff); | 162 | opt[3] = (newmss & 0x00ff); |
173 | 163 | ||
174 | tcph->check = cheat_check(~0, *((u_int32_t *)opt), tcph->check); | 164 | tcph->check = nf_proto_csum_update(*pskb, ~0, *((u_int32_t *)opt), |
165 | tcph->check, 0); | ||
175 | 166 | ||
176 | oldval = ((u_int16_t *)tcph)[6]; | 167 | oldval = ((u_int16_t *)tcph)[6]; |
177 | tcph->doff += TCPOLEN_MSS/4; | 168 | tcph->doff += TCPOLEN_MSS/4; |
178 | tcph->check = cheat_check(oldval ^ 0xFFFF, | 169 | tcph->check = nf_proto_csum_update(*pskb, |
179 | ((u_int16_t *)tcph)[6], tcph->check); | 170 | oldval ^ 0xFFFF, |
171 | ((u_int16_t *)tcph)[6], | ||
172 | tcph->check, 0); | ||
180 | 173 | ||
181 | newtotlen = htons(ntohs(iph->tot_len) + TCPOLEN_MSS); | 174 | newtotlen = htons(ntohs(iph->tot_len) + TCPOLEN_MSS); |
182 | iph->check = cheat_check(iph->tot_len ^ 0xFFFF, | 175 | iph->check = nf_csum_update(iph->tot_len ^ 0xFFFF, |
183 | newtotlen, iph->check); | 176 | newtotlen, iph->check); |
184 | iph->tot_len = newtotlen; | 177 | iph->tot_len = newtotlen; |
185 | 178 | ||
186 | DEBUGP(KERN_INFO "ipt_tcpmss_target: %u.%u.%u.%u:%hu" | 179 | DEBUGP(KERN_INFO "ipt_tcpmss_target: %u.%u.%u.%u:%hu" |
diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 5d29d5e23624..27f639f3ac2a 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c | |||
@@ -222,6 +222,28 @@ copy_skb: | |||
222 | } | 222 | } |
223 | EXPORT_SYMBOL(skb_make_writable); | 223 | EXPORT_SYMBOL(skb_make_writable); |
224 | 224 | ||
225 | u_int16_t nf_csum_update(u_int32_t oldval, u_int32_t newval, u_int32_t csum) | ||
226 | { | ||
227 | u_int32_t diff[] = { oldval, newval }; | ||
228 | |||
229 | return csum_fold(csum_partial((char *)diff, sizeof(diff), ~csum)); | ||
230 | } | ||
231 | EXPORT_SYMBOL(nf_csum_update); | ||
232 | |||
233 | u_int16_t nf_proto_csum_update(struct sk_buff *skb, | ||
234 | u_int32_t oldval, u_int32_t newval, | ||
235 | u_int16_t csum, int pseudohdr) | ||
236 | { | ||
237 | if (skb->ip_summed != CHECKSUM_PARTIAL) { | ||
238 | csum = nf_csum_update(oldval, newval, csum); | ||
239 | if (skb->ip_summed == CHECKSUM_COMPLETE && pseudohdr) | ||
240 | skb->csum = nf_csum_update(oldval, newval, skb->csum); | ||
241 | } else if (pseudohdr) | ||
242 | csum = ~nf_csum_update(oldval, newval, ~csum); | ||
243 | |||
244 | return csum; | ||
245 | } | ||
246 | EXPORT_SYMBOL(nf_proto_csum_update); | ||
225 | 247 | ||
226 | /* This does not belong here, but locally generated errors need it if connection | 248 | /* This does not belong here, but locally generated errors need it if connection |
227 | tracking in use: without this, connection may not be in hash table, and hence | 249 | tracking in use: without this, connection may not be in hash table, and hence |