#include <linux/module.h>
#include <linux/skbuff.h>
#include <linux/proc_fs.h>
+#include <net/ip.h>
#include <net/checksum.h>
#include <linux/spinlock.h>
: ((hooknum) == NF_IP_LOCAL_IN ? "LOCAL_IN" \
: "*ERROR*")))
-static inline int call_expect(struct ip_conntrack *master,
- struct sk_buff **pskb,
- unsigned int hooknum,
- struct ip_conntrack *ct,
- struct ip_nat_info *info)
-{
- return master->nat.info.helper->expect(pskb, hooknum, ct, info);
-}
-
static unsigned int
ip_nat_fn(unsigned int hooknum,
struct sk_buff **pskb,
/* If we had a hardware checksum before, it's now invalid */
if ((*pskb)->ip_summed == CHECKSUM_HW)
- if (skb_checksum_help(pskb, (out == NULL)))
+ if (skb_checksum_help(*pskb, (out == NULL)))
return NF_DROP;
ct = ip_conntrack_get(*pskb, &ctinfo);
hash table yet). We must not let this through, in
case we're doing NAT to the same network. */
if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) {
- struct icmphdr hdr;
+ struct icmphdr _hdr, *hp;
- if (skb_copy_bits(*pskb, (*pskb)->nh.iph->ihl*4,
- &hdr, sizeof(hdr)) == 0
- && hdr.type == ICMP_REDIRECT)
+ hp = skb_header_pointer(*pskb,
+ (*pskb)->nh.iph->ihl*4,
+ sizeof(_hdr), &_hdr);
+ if (hp != NULL &&
+ hp->type == ICMP_REDIRECT)
return NF_DROP;
}
return NF_ACCEPT;
case IP_CT_RELATED:
case IP_CT_RELATED+IP_CT_IS_REPLY:
if ((*pskb)->nh.iph->protocol == IPPROTO_ICMP) {
- if (!icmp_reply_translation(pskb, ct, hooknum,
+ if (!icmp_reply_translation(pskb, ct, maniptype,
CTINFO2DIR(ctinfo)))
return NF_DROP;
else
case IP_CT_NEW:
info = &ct->nat.info;
- WRITE_LOCK(&ip_nat_lock);
/* Seen it before? This can happen for loopback, retrans,
or local packets.. */
- if (!(info->initialized & (1 << maniptype))
-#ifndef CONFIG_IP_NF_NAT_LOCAL
- /* If this session has already been confirmed we must not
- * touch it again even if there is no mapping set up.
- * Can only happen on local->local traffic with
- * CONFIG_IP_NF_NAT_LOCAL disabled.
- */
- && !(ct->status & IPS_CONFIRMED)
-#endif
- ) {
+ if (!ip_nat_initialized(ct, maniptype)) {
unsigned int ret;
- if (ct->master
- && master_ct(ct)->nat.info.helper
- && master_ct(ct)->nat.info.helper->expect) {
- ret = call_expect(master_ct(ct), pskb,
- hooknum, ct, info);
- } else {
-#ifdef CONFIG_IP_NF_NAT_LOCAL
- /* LOCAL_IN hook doesn't have a chain! */
- if (hooknum == NF_IP_LOCAL_IN)
- ret = alloc_null_binding(ct, info,
- hooknum);
- else
-#endif
- ret = ip_nat_rule_find(pskb, hooknum, in, out,
- ct, info);
- }
+ /* LOCAL_IN hook doesn't have a chain! */
+ if (hooknum == NF_IP_LOCAL_IN)
+ ret = alloc_null_binding(ct, info, hooknum);
+ else
+ ret = ip_nat_rule_find(pskb, hooknum,
+ in, out, ct,
+ info);
if (ret != NF_ACCEPT) {
- WRITE_UNLOCK(&ip_nat_lock);
return ret;
}
} else
DEBUGP("Already setup manip %s for ct %p\n",
maniptype == IP_NAT_MANIP_SRC ? "SRC" : "DST",
ct);
- WRITE_UNLOCK(&ip_nat_lock);
break;
default:
}
IP_NF_ASSERT(info);
- return do_bindings(ct, ctinfo, info, hooknum, pskb);
+ return nat_packet(ct, ctinfo, hooknum, pskb);
+}
+
+static unsigned int
+ip_nat_in(unsigned int hooknum,
+ struct sk_buff **pskb,
+ const struct net_device *in,
+ const struct net_device *out,
+ int (*okfn)(struct sk_buff *))
+{
+ u_int32_t saddr, daddr;
+ unsigned int ret;
+
+ saddr = (*pskb)->nh.iph->saddr;
+ daddr = (*pskb)->nh.iph->daddr;
+
+ ret = ip_nat_fn(hooknum, pskb, in, out, okfn);
+ if (ret != NF_DROP && ret != NF_STOLEN
+ && ((*pskb)->nh.iph->saddr != saddr
+ || (*pskb)->nh.iph->daddr != daddr)) {
+ dst_release((*pskb)->dst);
+ (*pskb)->dst = NULL;
+ }
+ return ret;
}
static unsigned int
I'm starting to have nightmares about fragments. */
if ((*pskb)->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
- *pskb = ip_ct_gather_frags(*pskb);
+ *pskb = ip_ct_gather_frags(*pskb, IP_DEFRAG_NAT_OUT);
if (!*pskb)
return NF_STOLEN;
return ip_nat_fn(hooknum, pskb, in, out, okfn);
}
-#ifdef CONFIG_IP_NF_NAT_LOCAL
static unsigned int
ip_nat_local_fn(unsigned int hooknum,
struct sk_buff **pskb,
return ip_route_me_harder(pskb) == 0 ? ret : NF_DROP;
return ret;
}
-#endif
+
+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 */
static struct nf_hook_ops ip_nat_in_ops = {
- .hook = ip_nat_fn,
+ .hook = ip_nat_in,
.owner = THIS_MODULE,
.pf = PF_INET,
.hooknum = NF_IP_PRE_ROUTING,
.priority = NF_IP_PRI_NAT_SRC,
};
-#ifdef CONFIG_IP_NF_NAT_LOCAL
+/* 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,
.hooknum = NF_IP_LOCAL_IN,
.priority = NF_IP_PRI_NAT_SRC,
};
-#endif
-
-/* Protocol registration. */
-int ip_nat_protocol_register(struct ip_nat_protocol *proto)
-{
- int ret = 0;
- struct list_head *i;
-
- WRITE_LOCK(&ip_nat_lock);
- list_for_each(i, &protos) {
- if (((struct ip_nat_protocol *)i)->protonum
- == proto->protonum) {
- ret = -EBUSY;
- goto out;
- }
- }
-
- list_prepend(&protos, proto);
- out:
- WRITE_UNLOCK(&ip_nat_lock);
- return ret;
-}
-/* Noone stores the protocol anywhere; simply delete it. */
-void ip_nat_protocol_unregister(struct ip_nat_protocol *proto)
-{
- WRITE_LOCK(&ip_nat_lock);
- LIST_DELETE(&protos, proto);
- WRITE_UNLOCK(&ip_nat_lock);
+/* 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,
+};
- /* Someone could be still looking at the proto in a bh. */
- synchronize_net();
-}
static int init_or_cleanup(int init)
{
printk("ip_nat_init: can't register out hook.\n");
goto cleanup_inops;
}
-#ifdef CONFIG_IP_NF_NAT_LOCAL
+ 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");
- goto cleanup_outops;
+ goto cleanup_adjustout_ops;;
}
ret = nf_register_hook(&ip_nat_local_in_ops);
if (ret < 0) {
printk("ip_nat_init: can't register local in hook.\n");
goto cleanup_localoutops;
}
-#endif
return ret;
cleanup:
-#ifdef CONFIG_IP_NF_NAT_LOCAL
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:
-#endif
nf_unregister_hook(&ip_nat_out_ops);
cleanup_inops:
nf_unregister_hook(&ip_nat_in_ops);
EXPORT_SYMBOL(ip_nat_setup_info);
EXPORT_SYMBOL(ip_nat_protocol_register);
EXPORT_SYMBOL(ip_nat_protocol_unregister);
-EXPORT_SYMBOL(ip_nat_helper_register);
-EXPORT_SYMBOL(ip_nat_helper_unregister);
EXPORT_SYMBOL(ip_nat_cheat_check);
EXPORT_SYMBOL(ip_nat_mangle_tcp_packet);
EXPORT_SYMBOL(ip_nat_mangle_udp_packet);
EXPORT_SYMBOL(ip_nat_used_tuple);
+EXPORT_SYMBOL(ip_nat_follow_master);
MODULE_LICENSE("GPL");