Commit e281e3ac authored by Patrick McHardy's avatar Patrick McHardy Committed by David S. Miller

[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: default avatarPatrick McHardy <kaber@trash.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 8e293ada
...@@ -62,6 +62,9 @@ enum nf_ip_hook_priorities { ...@@ -62,6 +62,9 @@ enum nf_ip_hook_priorities {
NF_IP_PRI_FILTER = 0, NF_IP_PRI_FILTER = 0,
NF_IP_PRI_NAT_SRC = 100, NF_IP_PRI_NAT_SRC = 100,
NF_IP_PRI_SELINUX_LAST = 225, NF_IP_PRI_SELINUX_LAST = 225,
NF_IP_PRI_CONNTRACK_HELPER = INT_MAX - 2,
NF_IP_PRI_NAT_SEQ_ADJUST = INT_MAX - 1,
NF_IP_PRI_CONNTRACK_CONFIRM = INT_MAX,
NF_IP_PRI_LAST = INT_MAX, NF_IP_PRI_LAST = INT_MAX,
}; };
......
...@@ -400,6 +400,16 @@ static unsigned int ip_confirm(unsigned int hooknum, ...@@ -400,6 +400,16 @@ static unsigned int ip_confirm(unsigned int hooknum,
const struct net_device *in, const struct net_device *in,
const struct net_device *out, const struct net_device *out,
int (*okfn)(struct sk_buff *)) int (*okfn)(struct sk_buff *))
{
/* We've seen it coming out the other side: confirm it */
return ip_conntrack_confirm(pskb);
}
static unsigned int ip_conntrack_help(unsigned int hooknum,
struct sk_buff **pskb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{ {
struct ip_conntrack *ct; struct ip_conntrack *ct;
enum ip_conntrack_info ctinfo; enum ip_conntrack_info ctinfo;
...@@ -412,9 +422,7 @@ static unsigned int ip_confirm(unsigned int hooknum, ...@@ -412,9 +422,7 @@ static unsigned int ip_confirm(unsigned int hooknum,
if (ret != NF_ACCEPT) if (ret != NF_ACCEPT)
return ret; return ret;
} }
return NF_ACCEPT;
/* We've seen it coming out the other side: confirm it */
return ip_conntrack_confirm(pskb);
} }
static unsigned int ip_conntrack_defrag(unsigned int hooknum, static unsigned int ip_conntrack_defrag(unsigned int hooknum,
...@@ -516,13 +524,30 @@ static struct nf_hook_ops ip_conntrack_local_out_ops = { ...@@ -516,13 +524,30 @@ static struct nf_hook_ops ip_conntrack_local_out_ops = {
.priority = NF_IP_PRI_CONNTRACK, .priority = NF_IP_PRI_CONNTRACK,
}; };
/* helpers */
static struct nf_hook_ops ip_conntrack_helper_out_ops = {
.hook = ip_conntrack_help,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING,
.priority = NF_IP_PRI_CONNTRACK_HELPER,
};
static struct nf_hook_ops ip_conntrack_helper_in_ops = {
.hook = ip_conntrack_help,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
.priority = NF_IP_PRI_CONNTRACK_HELPER,
};
/* Refragmenter; last chance. */ /* Refragmenter; last chance. */
static struct nf_hook_ops ip_conntrack_out_ops = { static struct nf_hook_ops ip_conntrack_out_ops = {
.hook = ip_refrag, .hook = ip_refrag,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pf = PF_INET, .pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING, .hooknum = NF_IP_POST_ROUTING,
.priority = NF_IP_PRI_LAST, .priority = NF_IP_PRI_CONNTRACK_CONFIRM,
}; };
static struct nf_hook_ops ip_conntrack_local_in_ops = { static struct nf_hook_ops ip_conntrack_local_in_ops = {
...@@ -530,7 +555,7 @@ static struct nf_hook_ops ip_conntrack_local_in_ops = { ...@@ -530,7 +555,7 @@ static struct nf_hook_ops ip_conntrack_local_in_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pf = PF_INET, .pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN, .hooknum = NF_IP_LOCAL_IN,
.priority = NF_IP_PRI_LAST-1, .priority = NF_IP_PRI_CONNTRACK_CONFIRM,
}; };
/* Sysctl support */ /* Sysctl support */
...@@ -831,10 +856,20 @@ static int init_or_cleanup(int init) ...@@ -831,10 +856,20 @@ static int init_or_cleanup(int init)
printk("ip_conntrack: can't register local out hook.\n"); printk("ip_conntrack: can't register local out hook.\n");
goto cleanup_inops; goto cleanup_inops;
} }
ret = nf_register_hook(&ip_conntrack_helper_in_ops);
if (ret < 0) {
printk("ip_conntrack: can't register local in helper hook.\n");
goto cleanup_inandlocalops;
}
ret = nf_register_hook(&ip_conntrack_helper_out_ops);
if (ret < 0) {
printk("ip_conntrack: can't register postrouting helper hook.\n");
goto cleanup_helperinops;
}
ret = nf_register_hook(&ip_conntrack_out_ops); ret = nf_register_hook(&ip_conntrack_out_ops);
if (ret < 0) { if (ret < 0) {
printk("ip_conntrack: can't register post-routing hook.\n"); printk("ip_conntrack: can't register post-routing hook.\n");
goto cleanup_inandlocalops; goto cleanup_helperoutops;
} }
ret = nf_register_hook(&ip_conntrack_local_in_ops); ret = nf_register_hook(&ip_conntrack_local_in_ops);
if (ret < 0) { if (ret < 0) {
...@@ -860,6 +895,10 @@ static int init_or_cleanup(int init) ...@@ -860,6 +895,10 @@ static int init_or_cleanup(int init)
nf_unregister_hook(&ip_conntrack_local_in_ops); nf_unregister_hook(&ip_conntrack_local_in_ops);
cleanup_inoutandlocalops: cleanup_inoutandlocalops:
nf_unregister_hook(&ip_conntrack_out_ops); nf_unregister_hook(&ip_conntrack_out_ops);
cleanup_helperoutops:
nf_unregister_hook(&ip_conntrack_helper_out_ops);
cleanup_helperinops:
nf_unregister_hook(&ip_conntrack_helper_in_ops);
cleanup_inandlocalops: cleanup_inandlocalops:
nf_unregister_hook(&ip_conntrack_local_out_ops); nf_unregister_hook(&ip_conntrack_local_out_ops);
cleanup_inops: cleanup_inops:
......
...@@ -356,15 +356,6 @@ unsigned int nat_packet(struct ip_conntrack *ct, ...@@ -356,15 +356,6 @@ unsigned int nat_packet(struct ip_conntrack *ct,
unsigned long statusbit; unsigned long statusbit;
enum ip_nat_manip_type mtype = HOOK2MANIP(hooknum); enum ip_nat_manip_type mtype = HOOK2MANIP(hooknum);
if (test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)
&& (hooknum == NF_IP_POST_ROUTING || hooknum == NF_IP_LOCAL_IN)) {
DEBUGP("ip_nat_core: adjusting sequence number\n");
/* future: put this in a l4-proto specific function,
* and call this function here. */
if (!ip_nat_seq_adjust(pskb, ct, ctinfo))
return NF_DROP;
}
if (mtype == IP_NAT_MANIP_SRC) if (mtype == IP_NAT_MANIP_SRC)
statusbit = IPS_SRC_NAT; statusbit = IPS_SRC_NAT;
else else
......
...@@ -230,6 +230,25 @@ ip_nat_local_fn(unsigned int hooknum, ...@@ -230,6 +230,25 @@ ip_nat_local_fn(unsigned int hooknum,
return ret; return ret;
} }
static unsigned int
ip_nat_adjust(unsigned int hooknum,
struct sk_buff **pskb,
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
{
struct ip_conntrack *ct;
enum ip_conntrack_info ctinfo;
ct = ip_conntrack_get(*pskb, &ctinfo);
if (ct && test_bit(IPS_SEQ_ADJUST_BIT, &ct->status)) {
DEBUGP("ip_nat_standalone: adjusting sequence number\n");
if (!ip_nat_seq_adjust(pskb, ct, ctinfo))
return NF_DROP;
}
return NF_ACCEPT;
}
/* We must be after connection tracking and before packet filtering. */ /* We must be after connection tracking and before packet filtering. */
/* Before packet filtering, change destination */ /* Before packet filtering, change destination */
...@@ -250,6 +269,15 @@ static struct nf_hook_ops ip_nat_out_ops = { ...@@ -250,6 +269,15 @@ static struct nf_hook_ops ip_nat_out_ops = {
.priority = NF_IP_PRI_NAT_SRC, .priority = NF_IP_PRI_NAT_SRC,
}; };
/* After conntrack, adjust sequence number */
static struct nf_hook_ops ip_nat_adjust_out_ops = {
.hook = ip_nat_adjust,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING,
.priority = NF_IP_PRI_NAT_SEQ_ADJUST,
};
/* Before packet filtering, change destination */ /* Before packet filtering, change destination */
static struct nf_hook_ops ip_nat_local_out_ops = { static struct nf_hook_ops ip_nat_local_out_ops = {
.hook = ip_nat_local_fn, .hook = ip_nat_local_fn,
...@@ -268,6 +296,16 @@ static struct nf_hook_ops ip_nat_local_in_ops = { ...@@ -268,6 +296,16 @@ static struct nf_hook_ops ip_nat_local_in_ops = {
.priority = NF_IP_PRI_NAT_SRC, .priority = NF_IP_PRI_NAT_SRC,
}; };
/* After conntrack, adjust sequence number */
static struct nf_hook_ops ip_nat_adjust_in_ops = {
.hook = ip_nat_adjust,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
.priority = NF_IP_PRI_NAT_SEQ_ADJUST,
};
static int init_or_cleanup(int init) static int init_or_cleanup(int init)
{ {
int ret = 0; int ret = 0;
...@@ -296,10 +334,20 @@ static int init_or_cleanup(int init) ...@@ -296,10 +334,20 @@ static int init_or_cleanup(int init)
printk("ip_nat_init: can't register out hook.\n"); printk("ip_nat_init: can't register out hook.\n");
goto cleanup_inops; goto cleanup_inops;
} }
ret = nf_register_hook(&ip_nat_adjust_in_ops);
if (ret < 0) {
printk("ip_nat_init: can't register adjust in hook.\n");
goto cleanup_outops;
}
ret = nf_register_hook(&ip_nat_adjust_out_ops);
if (ret < 0) {
printk("ip_nat_init: can't register adjust out hook.\n");
goto cleanup_adjustin_ops;
}
ret = nf_register_hook(&ip_nat_local_out_ops); ret = nf_register_hook(&ip_nat_local_out_ops);
if (ret < 0) { if (ret < 0) {
printk("ip_nat_init: can't register local out hook.\n"); printk("ip_nat_init: can't register local out hook.\n");
goto cleanup_outops; goto cleanup_adjustout_ops;;
} }
ret = nf_register_hook(&ip_nat_local_in_ops); ret = nf_register_hook(&ip_nat_local_in_ops);
if (ret < 0) { if (ret < 0) {
...@@ -312,6 +360,10 @@ static int init_or_cleanup(int init) ...@@ -312,6 +360,10 @@ static int init_or_cleanup(int init)
nf_unregister_hook(&ip_nat_local_in_ops); nf_unregister_hook(&ip_nat_local_in_ops);
cleanup_localoutops: cleanup_localoutops:
nf_unregister_hook(&ip_nat_local_out_ops); nf_unregister_hook(&ip_nat_local_out_ops);
cleanup_adjustout_ops:
nf_unregister_hook(&ip_nat_adjust_out_ops);
cleanup_adjustin_ops:
nf_unregister_hook(&ip_nat_adjust_in_ops);
cleanup_outops: cleanup_outops:
nf_unregister_hook(&ip_nat_out_ops); nf_unregister_hook(&ip_nat_out_ops);
cleanup_inops: cleanup_inops:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment