linux/net/ipv4/netfilter/iptable_nat.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/* (C) 1999-2001 Paul `Rusty' Russell
   3 * (C) 2002-2006 Netfilter Core Team <coreteam@netfilter.org>
   4 * (C) 2011 Patrick McHardy <kaber@trash.net>
   5 */
   6
   7#include <linux/module.h>
   8#include <linux/netfilter.h>
   9#include <linux/netfilter_ipv4.h>
  10#include <linux/netfilter_ipv4/ip_tables.h>
  11#include <linux/ip.h>
  12#include <net/ip.h>
  13
  14#include <net/netfilter/nf_nat.h>
  15
  16struct iptable_nat_pernet {
  17        struct nf_hook_ops *nf_nat_ops;
  18};
  19
  20static unsigned int iptable_nat_net_id __read_mostly;
  21
  22static const struct xt_table nf_nat_ipv4_table = {
  23        .name           = "nat",
  24        .valid_hooks    = (1 << NF_INET_PRE_ROUTING) |
  25                          (1 << NF_INET_POST_ROUTING) |
  26                          (1 << NF_INET_LOCAL_OUT) |
  27                          (1 << NF_INET_LOCAL_IN),
  28        .me             = THIS_MODULE,
  29        .af             = NFPROTO_IPV4,
  30};
  31
  32static unsigned int iptable_nat_do_chain(void *priv,
  33                                         struct sk_buff *skb,
  34                                         const struct nf_hook_state *state)
  35{
  36        return ipt_do_table(skb, state, priv);
  37}
  38
  39static const struct nf_hook_ops nf_nat_ipv4_ops[] = {
  40        {
  41                .hook           = iptable_nat_do_chain,
  42                .pf             = NFPROTO_IPV4,
  43                .hooknum        = NF_INET_PRE_ROUTING,
  44                .priority       = NF_IP_PRI_NAT_DST,
  45        },
  46        {
  47                .hook           = iptable_nat_do_chain,
  48                .pf             = NFPROTO_IPV4,
  49                .hooknum        = NF_INET_POST_ROUTING,
  50                .priority       = NF_IP_PRI_NAT_SRC,
  51        },
  52        {
  53                .hook           = iptable_nat_do_chain,
  54                .pf             = NFPROTO_IPV4,
  55                .hooknum        = NF_INET_LOCAL_OUT,
  56                .priority       = NF_IP_PRI_NAT_DST,
  57        },
  58        {
  59                .hook           = iptable_nat_do_chain,
  60                .pf             = NFPROTO_IPV4,
  61                .hooknum        = NF_INET_LOCAL_IN,
  62                .priority       = NF_IP_PRI_NAT_SRC,
  63        },
  64};
  65
  66static int ipt_nat_register_lookups(struct net *net)
  67{
  68        struct iptable_nat_pernet *xt_nat_net;
  69        struct nf_hook_ops *ops;
  70        struct xt_table *table;
  71        int i, ret;
  72
  73        xt_nat_net = net_generic(net, iptable_nat_net_id);
  74        table = xt_find_table(net, NFPROTO_IPV4, "nat");
  75        if (WARN_ON_ONCE(!table))
  76                return -ENOENT;
  77
  78        ops = kmemdup(nf_nat_ipv4_ops, sizeof(nf_nat_ipv4_ops), GFP_KERNEL);
  79        if (!ops)
  80                return -ENOMEM;
  81
  82        for (i = 0; i < ARRAY_SIZE(nf_nat_ipv4_ops); i++) {
  83                ops[i].priv = table;
  84                ret = nf_nat_ipv4_register_fn(net, &ops[i]);
  85                if (ret) {
  86                        while (i)
  87                                nf_nat_ipv4_unregister_fn(net, &ops[--i]);
  88
  89                        kfree(ops);
  90                        return ret;
  91                }
  92        }
  93
  94        xt_nat_net->nf_nat_ops = ops;
  95        return 0;
  96}
  97
  98static void ipt_nat_unregister_lookups(struct net *net)
  99{
 100        struct iptable_nat_pernet *xt_nat_net = net_generic(net, iptable_nat_net_id);
 101        struct nf_hook_ops *ops = xt_nat_net->nf_nat_ops;
 102        int i;
 103
 104        if (!ops)
 105                return;
 106
 107        for (i = 0; i < ARRAY_SIZE(nf_nat_ipv4_ops); i++)
 108                nf_nat_ipv4_unregister_fn(net, &ops[i]);
 109
 110        kfree(ops);
 111}
 112
 113static int iptable_nat_table_init(struct net *net)
 114{
 115        struct ipt_replace *repl;
 116        int ret;
 117
 118        repl = ipt_alloc_initial_table(&nf_nat_ipv4_table);
 119        if (repl == NULL)
 120                return -ENOMEM;
 121
 122        ret = ipt_register_table(net, &nf_nat_ipv4_table, repl, NULL);
 123        if (ret < 0) {
 124                kfree(repl);
 125                return ret;
 126        }
 127
 128        ret = ipt_nat_register_lookups(net);
 129        if (ret < 0)
 130                ipt_unregister_table_exit(net, "nat");
 131
 132        kfree(repl);
 133        return ret;
 134}
 135
 136static void __net_exit iptable_nat_net_pre_exit(struct net *net)
 137{
 138        ipt_nat_unregister_lookups(net);
 139}
 140
 141static void __net_exit iptable_nat_net_exit(struct net *net)
 142{
 143        ipt_unregister_table_exit(net, "nat");
 144}
 145
 146static struct pernet_operations iptable_nat_net_ops = {
 147        .pre_exit = iptable_nat_net_pre_exit,
 148        .exit   = iptable_nat_net_exit,
 149        .id     = &iptable_nat_net_id,
 150        .size   = sizeof(struct iptable_nat_pernet),
 151};
 152
 153static int __init iptable_nat_init(void)
 154{
 155        int ret = xt_register_template(&nf_nat_ipv4_table,
 156                                       iptable_nat_table_init);
 157
 158        if (ret < 0)
 159                return ret;
 160
 161        ret = register_pernet_subsys(&iptable_nat_net_ops);
 162        if (ret < 0) {
 163                xt_unregister_template(&nf_nat_ipv4_table);
 164                return ret;
 165        }
 166
 167        return ret;
 168}
 169
 170static void __exit iptable_nat_exit(void)
 171{
 172        unregister_pernet_subsys(&iptable_nat_net_ops);
 173        xt_unregister_template(&nf_nat_ipv4_table);
 174}
 175
 176module_init(iptable_nat_init);
 177module_exit(iptable_nat_exit);
 178
 179MODULE_LICENSE("GPL");
 180