linux/net/ipv4/netfilter/nf_nat_rule.c
<<
>>
Prefs
   1/* (C) 1999-2001 Paul `Rusty' Russell
   2 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 */
   8
   9/* Everything about the rules for NAT. */
  10#include <linux/types.h>
  11#include <linux/ip.h>
  12#include <linux/netfilter.h>
  13#include <linux/netfilter_ipv4.h>
  14#include <linux/module.h>
  15#include <linux/kmod.h>
  16#include <linux/skbuff.h>
  17#include <linux/proc_fs.h>
  18#include <net/checksum.h>
  19#include <net/route.h>
  20#include <linux/bitops.h>
  21
  22#include <linux/netfilter_ipv4/ip_tables.h>
  23#include <net/netfilter/nf_nat.h>
  24#include <net/netfilter/nf_nat_core.h>
  25#include <net/netfilter/nf_nat_rule.h>
  26
  27#define NAT_VALID_HOOKS ((1<<NF_IP_PRE_ROUTING) | (1<<NF_IP_POST_ROUTING) | (1<<NF_IP_LOCAL_OUT))
  28
  29static struct
  30{
  31        struct ipt_replace repl;
  32        struct ipt_standard entries[3];
  33        struct ipt_error term;
  34} nat_initial_table __initdata = {
  35        .repl = {
  36                .name = "nat",
  37                .valid_hooks = NAT_VALID_HOOKS,
  38                .num_entries = 4,
  39                .size = sizeof(struct ipt_standard) * 3 + sizeof(struct ipt_error),
  40                .hook_entry = {
  41                        [NF_IP_PRE_ROUTING] = 0,
  42                        [NF_IP_POST_ROUTING] = sizeof(struct ipt_standard),
  43                        [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 2
  44                },
  45                .underflow = {
  46                        [NF_IP_PRE_ROUTING] = 0,
  47                        [NF_IP_POST_ROUTING] = sizeof(struct ipt_standard),
  48                        [NF_IP_LOCAL_OUT] = sizeof(struct ipt_standard) * 2
  49                },
  50        },
  51        .entries = {
  52                IPT_STANDARD_INIT(NF_ACCEPT),   /* PRE_ROUTING */
  53                IPT_STANDARD_INIT(NF_ACCEPT),   /* POST_ROUTING */
  54                IPT_STANDARD_INIT(NF_ACCEPT),   /* LOCAL_OUT */
  55        },
  56        .term = IPT_ERROR_INIT,                 /* ERROR */
  57};
  58
  59static struct xt_table nat_table = {
  60        .name           = "nat",
  61        .valid_hooks    = NAT_VALID_HOOKS,
  62        .lock           = RW_LOCK_UNLOCKED,
  63        .me             = THIS_MODULE,
  64        .af             = AF_INET,
  65};
  66
  67/* Source NAT */
  68static unsigned int ipt_snat_target(struct sk_buff *skb,
  69                                    const struct net_device *in,
  70                                    const struct net_device *out,
  71                                    unsigned int hooknum,
  72                                    const struct xt_target *target,
  73                                    const void *targinfo)
  74{
  75        struct nf_conn *ct;
  76        enum ip_conntrack_info ctinfo;
  77        const struct nf_nat_multi_range_compat *mr = targinfo;
  78
  79        NF_CT_ASSERT(hooknum == NF_IP_POST_ROUTING);
  80
  81        ct = nf_ct_get(skb, &ctinfo);
  82
  83        /* Connection must be valid and new. */
  84        NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED ||
  85                            ctinfo == IP_CT_RELATED + IP_CT_IS_REPLY));
  86        NF_CT_ASSERT(out);
  87
  88        return nf_nat_setup_info(ct, &mr->range[0], hooknum);
  89}
  90
  91/* Before 2.6.11 we did implicit source NAT if required. Warn about change. */
  92static void warn_if_extra_mangle(__be32 dstip, __be32 srcip)
  93{
  94        static int warned = 0;
  95        struct flowi fl = { .nl_u = { .ip4_u = { .daddr = dstip } } };
  96        struct rtable *rt;
  97
  98        if (ip_route_output_key(&rt, &fl) != 0)
  99                return;
 100
 101        if (rt->rt_src != srcip && !warned) {
 102                printk("NAT: no longer support implicit source local NAT\n");
 103                printk("NAT: packet src %u.%u.%u.%u -> dst %u.%u.%u.%u\n",
 104                       NIPQUAD(srcip), NIPQUAD(dstip));
 105                warned = 1;
 106        }
 107        ip_rt_put(rt);
 108}
 109
 110static unsigned int ipt_dnat_target(struct sk_buff *skb,
 111                                    const struct net_device *in,
 112                                    const struct net_device *out,
 113                                    unsigned int hooknum,
 114                                    const struct xt_target *target,
 115                                    const void *targinfo)
 116{
 117        struct nf_conn *ct;
 118        enum ip_conntrack_info ctinfo;
 119        const struct nf_nat_multi_range_compat *mr = targinfo;
 120
 121        NF_CT_ASSERT(hooknum == NF_IP_PRE_ROUTING ||
 122                     hooknum == NF_IP_LOCAL_OUT);
 123
 124        ct = nf_ct_get(skb, &ctinfo);
 125
 126        /* Connection must be valid and new. */
 127        NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED));
 128
 129        if (hooknum == NF_IP_LOCAL_OUT &&
 130            mr->range[0].flags & IP_NAT_RANGE_MAP_IPS)
 131                warn_if_extra_mangle(ip_hdr(skb)->daddr,
 132                                     mr->range[0].min_ip);
 133
 134        return nf_nat_setup_info(ct, &mr->range[0], hooknum);
 135}
 136
 137static bool ipt_snat_checkentry(const char *tablename,
 138                                const void *entry,
 139                                const struct xt_target *target,
 140                                void *targinfo,
 141                                unsigned int hook_mask)
 142{
 143        struct nf_nat_multi_range_compat *mr = targinfo;
 144
 145        /* Must be a valid range */
 146        if (mr->rangesize != 1) {
 147                printk("SNAT: multiple ranges no longer supported\n");
 148                return false;
 149        }
 150        return true;
 151}
 152
 153static bool ipt_dnat_checkentry(const char *tablename,
 154                                const void *entry,
 155                                const struct xt_target *target,
 156                                void *targinfo,
 157                                unsigned int hook_mask)
 158{
 159        struct nf_nat_multi_range_compat *mr = targinfo;
 160
 161        /* Must be a valid range */
 162        if (mr->rangesize != 1) {
 163                printk("DNAT: multiple ranges no longer supported\n");
 164                return false;
 165        }
 166        return true;
 167}
 168
 169unsigned int
 170alloc_null_binding(struct nf_conn *ct, unsigned int hooknum)
 171{
 172        /* Force range to this IP; let proto decide mapping for
 173           per-proto parts (hence not IP_NAT_RANGE_PROTO_SPECIFIED).
 174           Use reply in case it's already been mangled (eg local packet).
 175        */
 176        __be32 ip
 177                = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC
 178                   ? ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip
 179                   : ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip);
 180        struct nf_nat_range range
 181                = { IP_NAT_RANGE_MAP_IPS, ip, ip, { 0 }, { 0 } };
 182
 183        pr_debug("Allocating NULL binding for %p (%u.%u.%u.%u)\n",
 184                 ct, NIPQUAD(ip));
 185        return nf_nat_setup_info(ct, &range, hooknum);
 186}
 187
 188unsigned int
 189alloc_null_binding_confirmed(struct nf_conn *ct, unsigned int hooknum)
 190{
 191        __be32 ip
 192                = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC
 193                   ? ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip
 194                   : ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip);
 195        __be16 all
 196                = (HOOK2MANIP(hooknum) == IP_NAT_MANIP_SRC
 197                   ? ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u.all
 198                   : ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u.all);
 199        struct nf_nat_range range
 200                = { IP_NAT_RANGE_MAP_IPS, ip, ip, { all }, { all } };
 201
 202        pr_debug("Allocating NULL binding for confirmed %p (%u.%u.%u.%u)\n",
 203                 ct, NIPQUAD(ip));
 204        return nf_nat_setup_info(ct, &range, hooknum);
 205}
 206
 207int nf_nat_rule_find(struct sk_buff *skb,
 208                     unsigned int hooknum,
 209                     const struct net_device *in,
 210                     const struct net_device *out,
 211                     struct nf_conn *ct)
 212{
 213        int ret;
 214
 215        ret = ipt_do_table(skb, hooknum, in, out, &nat_table);
 216
 217        if (ret == NF_ACCEPT) {
 218                if (!nf_nat_initialized(ct, HOOK2MANIP(hooknum)))
 219                        /* NUL mapping */
 220                        ret = alloc_null_binding(ct, hooknum);
 221        }
 222        return ret;
 223}
 224
 225static struct xt_target ipt_snat_reg __read_mostly = {
 226        .name           = "SNAT",
 227        .target         = ipt_snat_target,
 228        .targetsize     = sizeof(struct nf_nat_multi_range_compat),
 229        .table          = "nat",
 230        .hooks          = 1 << NF_IP_POST_ROUTING,
 231        .checkentry     = ipt_snat_checkentry,
 232        .family         = AF_INET,
 233};
 234
 235static struct xt_target ipt_dnat_reg __read_mostly = {
 236        .name           = "DNAT",
 237        .target         = ipt_dnat_target,
 238        .targetsize     = sizeof(struct nf_nat_multi_range_compat),
 239        .table          = "nat",
 240        .hooks          = (1 << NF_IP_PRE_ROUTING) | (1 << NF_IP_LOCAL_OUT),
 241        .checkentry     = ipt_dnat_checkentry,
 242        .family         = AF_INET,
 243};
 244
 245int __init nf_nat_rule_init(void)
 246{
 247        int ret;
 248
 249        ret = ipt_register_table(&nat_table, &nat_initial_table.repl);
 250        if (ret != 0)
 251                return ret;
 252        ret = xt_register_target(&ipt_snat_reg);
 253        if (ret != 0)
 254                goto unregister_table;
 255
 256        ret = xt_register_target(&ipt_dnat_reg);
 257        if (ret != 0)
 258                goto unregister_snat;
 259
 260        return ret;
 261
 262 unregister_snat:
 263        xt_unregister_target(&ipt_snat_reg);
 264 unregister_table:
 265        ipt_unregister_table(&nat_table);
 266
 267        return ret;
 268}
 269
 270void nf_nat_rule_cleanup(void)
 271{
 272        xt_unregister_target(&ipt_dnat_reg);
 273        xt_unregister_target(&ipt_snat_reg);
 274        ipt_unregister_table(&nat_table);
 275}
 276