/* (C) 1999-2001 Paul `Rusty' Russell * (C) 2002-2004 Netfilter Core Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ /* Everything about the rules for NAT. */ #include #include #include #include #include #include #include #include #include #include #include #define ASSERT_READ_LOCK(x) #define ASSERT_WRITE_LOCK(x) #include #include #include #include #include #if 0 #define DEBUGP printk #else #define DEBUGP(format, args...) #endif #define NAT_VALID_HOOKS ((1<range[0], hooknum); } /* Before 2.6.11 we did implicit source NAT if required. Warn about change. */ static void warn_if_extra_mangle(u32 dstip, u32 srcip) { static int warned = 0; struct flowi fl = { .nl_u = { .ip4_u = { .daddr = dstip } } }; struct rtable *rt; if (ip_route_output_key(&rt, &fl) != 0) return; if (rt->rt_src != srcip && !warned) { printk("NAT: no longer support implicit source local NAT\n"); printk("NAT: packet src %u.%u.%u.%u -> dst %u.%u.%u.%u\n", NIPQUAD(srcip), NIPQUAD(dstip)); warned = 1; } ip_rt_put(rt); } static unsigned int ipt_dnat_target(struct sk_buff **pskb, const struct net_device *in, const struct net_device *out, unsigned int hooknum, const void *targinfo, void *userinfo) { struct ip_conntrack *ct; enum ip_conntrack_info ctinfo; const struct ip_nat_multi_range_compat *mr = targinfo; IP_NF_ASSERT(hooknum == NF_IP_PRE_ROUTING || hooknum == NF_IP_LOCAL_OUT); ct = ip_conntrack_get(*pskb, &ctinfo); /* Connection must be valid and new. */ IP_NF_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); if (hooknum == NF_IP_LOCAL_OUT && mr->range[0].flags & IP_NAT_RANGE_MAP_IPS) warn_if_extra_mangle((*pskb)->nh.iph->daddr, mr->range[0].min_ip); return ip_nat_setup_info(ct, &mr->range[0], hooknum); } static int ipt_snat_checkentry(const char *tablename, const struct ipt_entry *e, void *targinfo, unsigned int targinfosize, unsigned int hook_mask) { struct ip_nat_multi_range_compat *mr = targinfo; /* Must be a valid range */ if (mr->rangesize != 1) { printk("SNAT: multiple ranges no longer supported\n"); return 0; } if (targinfosize != IPT_ALIGN(sizeof(struct ip_nat_multi_range_compat))) { DEBUGP("SNAT: Target size %u wrong for %u ranges\n", targinfosize, mr->rangesize); return 0; } /* Only allow these for NAT. */ if (strcmp(tablename, "nat") != 0) { DEBUGP("SNAT: wrong table %s\n", tablename); return 0; } if (hook_mask & ~(1 << NF_IP_POST_ROUTING)) { DEBUGP("SNAT: hook mask 0x%x bad\n", hook_mask); return 0; } return 1; } static int ipt_dnat_checkentry(const char *tablename, const struct ipt_entry *e, void *targinfo, unsigned int targinfosize, unsigned int hook_mask) { struct ip_nat_multi_range_compat *mr = targinfo; /* Must be a valid range */ if (mr->rangesize != 1) { printk("DNAT: multiple ranges no longer supported\n"); return 0; } if (targinfosize != IPT_ALIGN(sizeof(struct ip_nat_multi_range_compat))) { DEBUGP("DNAT: Target size %u wrong for %u ranges\n", targinfosize, mr->rangesize); return 0; } /* Only allow these for NAT. */ if (strcmp(tablename, "nat") != 0) { DEBUGP("DNAT: wrong table %s\n", tablename); return 0; } if (hook_mask & ~((1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_OUT))) { DEBUGP("DNAT: hook mask 0x%x bad\n", hook_mask); return 0; } return 1; } inline unsigned int alloc_null_binding(struct ip_conntrack *conntrack, struct ip_nat_info *info, unsigned int hooknum) { /* Force range to this IP; let proto decide mapping for per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED). Use reply in case it's already been mangled (eg local packet). */ u_int32_t ip = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC ? conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.dst.ip : conntrack->tuplehash[IP_CT_DIR_REPLY].tuple.src.ip); struct ip_nat_range range = { IP_NAT_RANGE_MAP_IPS, ip, ip, { 0 }, { 0 } }; DEBUGP("Allocating NULL binding for %p (%u.%u.%u.%u)\n", conntrack, NIPQUAD(ip)); return ip_nat_setup_info(conntrack, &range, hooknum); } int ip_nat_rule_find(struct sk_buff **pskb, unsigned int hooknum, const struct net_device *in, const struct net_device *out, struct ip_conntrack *ct, struct ip_nat_info *info) { int ret; ret = ipt_do_table(pskb, hooknum, in, out, &nat_table, NULL); if (ret == NF_ACCEPT) { if (!ip_nat_initialized(ct, HOOK2MANIP(hooknum))) /* NUL mapping */ ret = alloc_null_binding(ct, info, hooknum); } return ret; } static struct ipt_target ipt_snat_reg = { .name = "SNAT", .target = ipt_snat_target, .checkentry = ipt_snat_checkentry, }; static struct ipt_target ipt_dnat_reg = { .name = "DNAT", .target = ipt_dnat_target, .checkentry = ipt_dnat_checkentry, }; int __init ip_nat_rule_init(void) { int ret; ret = ipt_register_table(&nat_table, &nat_initial_table.repl); if (ret != 0) return ret; ret = ipt_register_target(&ipt_snat_reg); if (ret != 0) goto unregister_table; ret = ipt_register_target(&ipt_dnat_reg); if (ret != 0) goto unregister_snat; return ret; unregister_snat: ipt_unregister_target(&ipt_snat_reg); unregister_table: ipt_unregister_table(&nat_table); return ret; } void ip_nat_rule_cleanup(void) { ipt_unregister_target(&ipt_dnat_reg); ipt_unregister_target(&ipt_snat_reg); ipt_unregister_table(&nat_table); }