aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPatrick McHardy <kaber@trash.net>2006-08-05 03:58:33 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2006-09-22 17:53:54 -0400
commit4cf411de49c65140b3c259748629b561c0d3340f (patch)
tree71dbc9fbbb0b64e805033665e3653d991abb0f7e
parent84fa7933a33f806bbbaae6775e87459b1ec584c0 (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.h6
-rw-r--r--include/linux/netfilter_ipv4/ip_nat.h4
-rw-r--r--include/linux/netfilter_ipv4/ip_nat_core.h8
-rw-r--r--net/ipv4/netfilter/ip_nat_core.c52
-rw-r--r--net/ipv4/netfilter/ip_nat_helper.c59
-rw-r--r--net/ipv4/netfilter/ip_nat_proto_gre.c5
-rw-r--r--net/ipv4/netfilter/ip_nat_proto_icmp.c8
-rw-r--r--net/ipv4/netfilter/ip_nat_proto_tcp.c7
-rw-r--r--net/ipv4/netfilter/ip_nat_proto_udp.c15
-rw-r--r--net/ipv4/netfilter/ip_nat_standalone.c10
-rw-r--r--net/ipv4/netfilter/ipt_ECN.c19
-rw-r--r--net/ipv4/netfilter/ipt_REJECT.c1
-rw-r--r--net/ipv4/netfilter/ipt_TCPMSS.c39
-rw-r--r--net/netfilter/core.c22
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. */
283extern int skb_make_writable(struct sk_buff **pskb, unsigned int writable_len); 283extern int skb_make_writable(struct sk_buff **pskb, unsigned int writable_len);
284 284
285extern u_int16_t nf_csum_update(u_int32_t oldval, u_int32_t newval,
286 u_int32_t csum);
287extern 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
285struct nf_afinfo { 291struct 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,
72extern int ip_nat_used_tuple(const struct ip_conntrack_tuple *tuple, 72extern 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. */
76extern 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
14extern int ip_nat_icmp_reply_translation(struct sk_buff **pskb, 14extern 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.) */
107u_int16_t
108ip_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}
114EXPORT_SYMBOL(ip_nat_cheat_check);
115
116/* Is this tuple already taken? (not by us) */ 104/* Is this tuple already taken? (not by us) */
117int 105int
118ip_nat_used_tuple(const struct ip_conntrack_tuple *tuple, 106ip_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,
423EXPORT_SYMBOL_GPL(ip_nat_packet); 411EXPORT_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) */
426int ip_nat_icmp_reply_translation(struct sk_buff **pskb, 414int 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}
259EXPORT_SYMBOL(ip_nat_mangle_udp_packet); 279EXPORT_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
52set_ect_tcp(struct sk_buff **pskb, const struct ipt_ECN_info *einfo) 52set_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
30static u_int16_t
31cheat_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
38static inline unsigned int 30static inline unsigned int
39optlen(const u_int8_t *opt, unsigned int offset) 31optlen(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}
223EXPORT_SYMBOL(skb_make_writable); 223EXPORT_SYMBOL(skb_make_writable);
224 224
225u_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}
231EXPORT_SYMBOL(nf_csum_update);
232
233u_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}
246EXPORT_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