Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[NETFILTER]: Fix NAT sequence number adjustment

The NAT changes in 2.6.11 changed the position where helpers
are called and perform packet mangling. Before 2.6.11, a NAT
helper was called before the packet was NATed and had its
sequence number adjusted. Since 2.6.11, the helpers get packets
with already adjusted sequence numbers.

This breaks sequence number adjustment, adjust_tcp_sequence()
needs the original sequence number to determine whether
a packet was a retransmission and to store it for further
corrections. It can't be reconstructed without more information
than available, so this patch restores the old order by
calling helpers from a new conntrack hook two priorities
below ip_conntrack_confirm() and adjusting the sequence number
from a new NAT hook one priority below ip_conntrack_confirm().

Tracked down by Phil Oester <kernel@linuxace.com>

Signed-off-by: Patrick McHardy <kaber@trash.net>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Patrick McHardy and committed by
David S. Miller
e281e3ac 8e293ada

+101 -16
+3
include/linux/netfilter_ipv4.h
··· 62 62 NF_IP_PRI_FILTER = 0, 63 63 NF_IP_PRI_NAT_SRC = 100, 64 64 NF_IP_PRI_SELINUX_LAST = 225, 65 + NF_IP_PRI_CONNTRACK_HELPER = INT_MAX - 2, 66 + NF_IP_PRI_NAT_SEQ_ADJUST = INT_MAX - 1, 67 + NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX, 65 68 NF_IP_PRI_LAST = INT_MAX, 66 69 }; 67 70
+45 -6
net/ipv4/netfilter/ip_conntrack_standalone.c
··· 401 401 const struct net_device *out, 402 402 int (*okfn)(struct sk_buff *)) 403 403 { 404 + /* We've seen it coming out the other side: confirm it */ 405 + return ip_conntrack_confirm(pskb); 406 + } 407 + 408 + static unsigned int ip_conntrack_help(unsigned int hooknum, 409 + struct sk_buff **pskb, 410 + const struct net_device *in, 411 + const struct net_device *out, 412 + int (*okfn)(struct sk_buff *)) 413 + { 404 414 struct ip_conntrack *ct; 405 415 enum ip_conntrack_info ctinfo; 406 416 ··· 422 412 if (ret != NF_ACCEPT) 423 413 return ret; 424 414 } 425 - 426 - /* We've seen it coming out the other side: confirm it */ 427 - return ip_conntrack_confirm(pskb); 415 + return NF_ACCEPT; 428 416 } 429 417 430 418 static unsigned int ip_conntrack_defrag(unsigned int hooknum, ··· 524 516 .priority = NF_IP_PRI_CONNTRACK, 525 517 }; 526 518 519 + /* helpers */ 520 + static struct nf_hook_ops ip_conntrack_helper_out_ops = { 521 + .hook = ip_conntrack_help, 522 + .owner = THIS_MODULE, 523 + .pf = PF_INET, 524 + .hooknum = NF_IP_POST_ROUTING, 525 + .priority = NF_IP_PRI_CONNTRACK_HELPER, 526 + }; 527 + 528 + static struct nf_hook_ops ip_conntrack_helper_in_ops = { 529 + .hook = ip_conntrack_help, 530 + .owner = THIS_MODULE, 531 + .pf = PF_INET, 532 + .hooknum = NF_IP_LOCAL_IN, 533 + .priority = NF_IP_PRI_CONNTRACK_HELPER, 534 + }; 535 + 527 536 /* Refragmenter; last chance. */ 528 537 static struct nf_hook_ops ip_conntrack_out_ops = { 529 538 .hook = ip_refrag, 530 539 .owner = THIS_MODULE, 531 540 .pf = PF_INET, 532 541 .hooknum = NF_IP_POST_ROUTING, 533 - .priority = NF_IP_PRI_LAST, 542 + .priority = NF_IP_PRI_CONNTRACK_CONFIRM, 534 543 }; 535 544 536 545 static struct nf_hook_ops ip_conntrack_local_in_ops = { ··· 555 530 .owner = THIS_MODULE, 556 531 .pf = PF_INET, 557 532 .hooknum = NF_IP_LOCAL_IN, 558 - .priority = NF_IP_PRI_LAST-1, 533 + .priority = NF_IP_PRI_CONNTRACK_CONFIRM, 559 534 }; 560 535 561 536 /* Sysctl support */ ··· 856 831 printk("ip_conntrack: can't register local out hook.\n"); 857 832 goto cleanup_inops; 858 833 } 834 + ret = nf_register_hook(&ip_conntrack_helper_in_ops); 835 + if (ret < 0) { 836 + printk("ip_conntrack: can't register local in helper hook.\n"); 837 + goto cleanup_inandlocalops; 838 + } 839 + ret = nf_register_hook(&ip_conntrack_helper_out_ops); 840 + if (ret < 0) { 841 + printk("ip_conntrack: can't register postrouting helper hook.\n"); 842 + goto cleanup_helperinops; 843 + } 859 844 ret = nf_register_hook(&ip_conntrack_out_ops); 860 845 if (ret < 0) { 861 846 printk("ip_conntrack: can't register post-routing hook.\n"); 862 - goto cleanup_inandlocalops; 847 + goto cleanup_helperoutops; 863 848 } 864 849 ret = nf_register_hook(&ip_conntrack_local_in_ops); 865 850 if (ret < 0) { ··· 895 860 nf_unregister_hook(&ip_conntrack_local_in_ops); 896 861 cleanup_inoutandlocalops: 897 862 nf_unregister_hook(&ip_conntrack_out_ops); 863 + cleanup_helperoutops: 864 + nf_unregister_hook(&ip_conntrack_helper_out_ops); 865 + cleanup_helperinops: 866 + nf_unregister_hook(&ip_conntrack_helper_in_ops); 898 867 cleanup_inandlocalops: 899 868 nf_unregister_hook(&ip_conntrack_local_out_ops); 900 869 cleanup_inops:
-9
net/ipv4/netfilter/ip_nat_core.c
··· 356 356 unsigned long statusbit; 357 357 enum ip_nat_manip_type mtype = HOOK2MANIP(hooknum); 358 358 359 - if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status) 360 - && (hooknum == NF_IP_POST_ROUTING || hooknum == NF_IP_LOCAL_IN)) { 361 - DEBUGP("ip_nat_core: adjusting sequence number\n"); 362 - /* future: put this in a l4-proto specific function, 363 - * and call this function here. */ 364 - if (!ip_nat_seq_adjust(pskb, ct, ctinfo)) 365 - return NF_DROP; 366 - } 367 - 368 359 if (mtype == IP_NAT_MANIP_SRC) 369 360 statusbit = IPS_SRC_NAT; 370 361 else
+53 -1
net/ipv4/netfilter/ip_nat_standalone.c
··· 230 230 return ret; 231 231 } 232 232 233 + static unsigned int 234 + ip_nat_adjust(unsigned int hooknum, 235 + struct sk_buff **pskb, 236 + const struct net_device *in, 237 + const struct net_device *out, 238 + int (*okfn)(struct sk_buff *)) 239 + { 240 + struct ip_conntrack *ct; 241 + enum ip_conntrack_info ctinfo; 242 + 243 + ct = ip_conntrack_get(*pskb, &ctinfo); 244 + if (ct && test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)) { 245 + DEBUGP("ip_nat_standalone: adjusting sequence number\n"); 246 + if (!ip_nat_seq_adjust(pskb, ct, ctinfo)) 247 + return NF_DROP; 248 + } 249 + return NF_ACCEPT; 250 + } 251 + 233 252 /* We must be after connection tracking and before packet filtering. */ 234 253 235 254 /* Before packet filtering, change destination */ ··· 269 250 .priority = NF_IP_PRI_NAT_SRC, 270 251 }; 271 252 253 + /* After conntrack, adjust sequence number */ 254 + static struct nf_hook_ops ip_nat_adjust_out_ops = { 255 + .hook = ip_nat_adjust, 256 + .owner = THIS_MODULE, 257 + .pf = PF_INET, 258 + .hooknum = NF_IP_POST_ROUTING, 259 + .priority = NF_IP_PRI_NAT_SEQ_ADJUST, 260 + }; 261 + 272 262 /* Before packet filtering, change destination */ 273 263 static struct nf_hook_ops ip_nat_local_out_ops = { 274 264 .hook = ip_nat_local_fn, ··· 295 267 .hooknum = NF_IP_LOCAL_IN, 296 268 .priority = NF_IP_PRI_NAT_SRC, 297 269 }; 270 + 271 + /* After conntrack, adjust sequence number */ 272 + static struct nf_hook_ops ip_nat_adjust_in_ops = { 273 + .hook = ip_nat_adjust, 274 + .owner = THIS_MODULE, 275 + .pf = PF_INET, 276 + .hooknum = NF_IP_LOCAL_IN, 277 + .priority = NF_IP_PRI_NAT_SEQ_ADJUST, 278 + }; 279 + 298 280 299 281 static int init_or_cleanup(int init) 300 282 { ··· 334 296 printk("ip_nat_init: can't register out hook.\n"); 335 297 goto cleanup_inops; 336 298 } 299 + ret = nf_register_hook(&ip_nat_adjust_in_ops); 300 + if (ret < 0) { 301 + printk("ip_nat_init: can't register adjust in hook.\n"); 302 + goto cleanup_outops; 303 + } 304 + ret = nf_register_hook(&ip_nat_adjust_out_ops); 305 + if (ret < 0) { 306 + printk("ip_nat_init: can't register adjust out hook.\n"); 307 + goto cleanup_adjustin_ops; 308 + } 337 309 ret = nf_register_hook(&ip_nat_local_out_ops); 338 310 if (ret < 0) { 339 311 printk("ip_nat_init: can't register local out hook.\n"); 340 - goto cleanup_outops; 312 + goto cleanup_adjustout_ops;; 341 313 } 342 314 ret = nf_register_hook(&ip_nat_local_in_ops); 343 315 if (ret < 0) { ··· 360 312 nf_unregister_hook(&ip_nat_local_in_ops); 361 313 cleanup_localoutops: 362 314 nf_unregister_hook(&ip_nat_local_out_ops); 315 + cleanup_adjustout_ops: 316 + nf_unregister_hook(&ip_nat_adjust_out_ops); 317 + cleanup_adjustin_ops: 318 + nf_unregister_hook(&ip_nat_adjust_in_ops); 363 319 cleanup_outops: 364 320 nf_unregister_hook(&ip_nat_out_ops); 365 321 cleanup_inops: