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>
NF_IP_PRI_FILTER = 0,
NF_IP_PRI_NAT_SRC = 100,
NF_IP_PRI_SELINUX_LAST = 225,
NF_IP_PRI_FILTER = 0,
NF_IP_PRI_NAT_SRC = 100,
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,
};
const struct net_device *in,
const struct net_device *out,
int (*okfn)(struct sk_buff *))
const struct net_device *in,
const struct net_device *out,
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;
enum ip_conntrack_info ctinfo;
{
struct ip_conntrack *ct;
enum ip_conntrack_info ctinfo;
if (ret != NF_ACCEPT)
return ret;
}
if (ret != NF_ACCEPT)
return ret;
}
-
- /* 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,
.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. */
static struct nf_hook_ops ip_conntrack_out_ops = {
.hook = ip_refrag,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_POST_ROUTING,
/* Refragmenter; last chance. */
static struct nf_hook_ops ip_conntrack_out_ops = {
.hook = ip_refrag,
.owner = THIS_MODULE,
.pf = PF_INET,
.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 = {
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_LOCAL_IN,
- .priority = NF_IP_PRI_LAST-1,
+ .priority = NF_IP_PRI_CONNTRACK_CONFIRM,
printk("ip_conntrack: can't register local out hook.\n");
goto cleanup_inops;
}
printk("ip_conntrack: can't register local out hook.\n");
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);
if (ret < 0) {
printk("ip_conntrack: can't register post-routing hook.\n");
ret = nf_register_hook(&ip_conntrack_out_ops);
if (ret < 0) {
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);
if (ret < 0) {
}
ret = nf_register_hook(&ip_conntrack_local_in_ops);
if (ret < 0) {
nf_unregister_hook(&ip_conntrack_local_in_ops);
cleanup_inoutandlocalops:
nf_unregister_hook(&ip_conntrack_out_ops);
nf_unregister_hook(&ip_conntrack_local_in_ops);
cleanup_inoutandlocalops:
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:
nf_unregister_hook(&ip_conntrack_local_out_ops);
cleanup_inops:
cleanup_inandlocalops:
nf_unregister_hook(&ip_conntrack_local_out_ops);
cleanup_inops:
unsigned long statusbit;
enum ip_nat_manip_type mtype = HOOK2MANIP(hooknum);
unsigned long statusbit;
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)
statusbit = IPS_SRC_NAT;
else
if (mtype == IP_NAT_MANIP_SRC)
statusbit = IPS_SRC_NAT;
else
+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. */
/* Before packet filtering, change destination */
/* We must be after connection tracking and before packet filtering. */
/* Before packet filtering, change destination */
.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 */
static struct nf_hook_ops ip_nat_local_out_ops = {
.hook = ip_nat_local_fn,
/* Before packet filtering, change destination */
static struct nf_hook_ops ip_nat_local_out_ops = {
.hook = ip_nat_local_fn,
.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)
{
int ret = 0;
static int init_or_cleanup(int init)
{
int ret = 0;
printk("ip_nat_init: can't register out hook.\n");
goto cleanup_inops;
}
printk("ip_nat_init: can't register out hook.\n");
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);
if (ret < 0) {
printk("ip_nat_init: can't register local out hook.\n");
ret = nf_register_hook(&ip_nat_local_out_ops);
if (ret < 0) {
printk("ip_nat_init: can't register local out hook.\n");
+ goto cleanup_adjustout_ops;;
}
ret = nf_register_hook(&ip_nat_local_in_ops);
if (ret < 0) {
}
ret = nf_register_hook(&ip_nat_local_in_ops);
if (ret < 0) {
nf_unregister_hook(&ip_nat_local_in_ops);
cleanup_localoutops:
nf_unregister_hook(&ip_nat_local_out_ops);
nf_unregister_hook(&ip_nat_local_in_ops);
cleanup_localoutops:
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:
nf_unregister_hook(&ip_nat_out_ops);
cleanup_inops:
cleanup_outops:
nf_unregister_hook(&ip_nat_out_ops);
cleanup_inops: