ip6table_nat.c (3737B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Copyright (c) 2011 Patrick McHardy <kaber@trash.net> 4 * 5 * Based on Rusty Russell's IPv4 NAT code. Development of IPv6 NAT 6 * funded by Astaro. 7 */ 8 9#include <linux/module.h> 10#include <linux/netfilter.h> 11#include <linux/netfilter_ipv6.h> 12#include <linux/netfilter_ipv6/ip6_tables.h> 13#include <linux/ipv6.h> 14#include <net/ipv6.h> 15 16#include <net/netfilter/nf_nat.h> 17 18struct ip6table_nat_pernet { 19 struct nf_hook_ops *nf_nat_ops; 20}; 21 22static unsigned int ip6table_nat_net_id __read_mostly; 23 24static const struct xt_table nf_nat_ipv6_table = { 25 .name = "nat", 26 .valid_hooks = (1 << NF_INET_PRE_ROUTING) | 27 (1 << NF_INET_POST_ROUTING) | 28 (1 << NF_INET_LOCAL_OUT) | 29 (1 << NF_INET_LOCAL_IN), 30 .me = THIS_MODULE, 31 .af = NFPROTO_IPV6, 32}; 33 34static const struct nf_hook_ops nf_nat_ipv6_ops[] = { 35 { 36 .hook = ip6t_do_table, 37 .pf = NFPROTO_IPV6, 38 .hooknum = NF_INET_PRE_ROUTING, 39 .priority = NF_IP6_PRI_NAT_DST, 40 }, 41 { 42 .hook = ip6t_do_table, 43 .pf = NFPROTO_IPV6, 44 .hooknum = NF_INET_POST_ROUTING, 45 .priority = NF_IP6_PRI_NAT_SRC, 46 }, 47 { 48 .hook = ip6t_do_table, 49 .pf = NFPROTO_IPV6, 50 .hooknum = NF_INET_LOCAL_OUT, 51 .priority = NF_IP6_PRI_NAT_DST, 52 }, 53 { 54 .hook = ip6t_do_table, 55 .pf = NFPROTO_IPV6, 56 .hooknum = NF_INET_LOCAL_IN, 57 .priority = NF_IP6_PRI_NAT_SRC, 58 }, 59}; 60 61static int ip6t_nat_register_lookups(struct net *net) 62{ 63 struct ip6table_nat_pernet *xt_nat_net; 64 struct nf_hook_ops *ops; 65 struct xt_table *table; 66 int i, ret; 67 68 table = xt_find_table(net, NFPROTO_IPV6, "nat"); 69 if (WARN_ON_ONCE(!table)) 70 return -ENOENT; 71 72 xt_nat_net = net_generic(net, ip6table_nat_net_id); 73 ops = kmemdup(nf_nat_ipv6_ops, sizeof(nf_nat_ipv6_ops), GFP_KERNEL); 74 if (!ops) 75 return -ENOMEM; 76 77 for (i = 0; i < ARRAY_SIZE(nf_nat_ipv6_ops); i++) { 78 ops[i].priv = table; 79 ret = nf_nat_ipv6_register_fn(net, &ops[i]); 80 if (ret) { 81 while (i) 82 nf_nat_ipv6_unregister_fn(net, &ops[--i]); 83 84 kfree(ops); 85 return ret; 86 } 87 } 88 89 xt_nat_net->nf_nat_ops = ops; 90 return 0; 91} 92 93static void ip6t_nat_unregister_lookups(struct net *net) 94{ 95 struct ip6table_nat_pernet *xt_nat_net = net_generic(net, ip6table_nat_net_id); 96 struct nf_hook_ops *ops = xt_nat_net->nf_nat_ops; 97 int i; 98 99 if (!ops) 100 return; 101 102 for (i = 0; i < ARRAY_SIZE(nf_nat_ipv6_ops); i++) 103 nf_nat_ipv6_unregister_fn(net, &ops[i]); 104 105 kfree(ops); 106} 107 108static int ip6table_nat_table_init(struct net *net) 109{ 110 struct ip6t_replace *repl; 111 int ret; 112 113 repl = ip6t_alloc_initial_table(&nf_nat_ipv6_table); 114 if (repl == NULL) 115 return -ENOMEM; 116 ret = ip6t_register_table(net, &nf_nat_ipv6_table, repl, 117 NULL); 118 if (ret < 0) { 119 kfree(repl); 120 return ret; 121 } 122 123 ret = ip6t_nat_register_lookups(net); 124 if (ret < 0) 125 ip6t_unregister_table_exit(net, "nat"); 126 127 kfree(repl); 128 return ret; 129} 130 131static void __net_exit ip6table_nat_net_pre_exit(struct net *net) 132{ 133 ip6t_nat_unregister_lookups(net); 134} 135 136static void __net_exit ip6table_nat_net_exit(struct net *net) 137{ 138 ip6t_unregister_table_exit(net, "nat"); 139} 140 141static struct pernet_operations ip6table_nat_net_ops = { 142 .pre_exit = ip6table_nat_net_pre_exit, 143 .exit = ip6table_nat_net_exit, 144 .id = &ip6table_nat_net_id, 145 .size = sizeof(struct ip6table_nat_pernet), 146}; 147 148static int __init ip6table_nat_init(void) 149{ 150 int ret = xt_register_template(&nf_nat_ipv6_table, 151 ip6table_nat_table_init); 152 153 if (ret < 0) 154 return ret; 155 156 ret = register_pernet_subsys(&ip6table_nat_net_ops); 157 if (ret) 158 xt_unregister_template(&nf_nat_ipv6_table); 159 160 return ret; 161} 162 163static void __exit ip6table_nat_exit(void) 164{ 165 unregister_pernet_subsys(&ip6table_nat_net_ops); 166 xt_unregister_template(&nf_nat_ipv6_table); 167} 168 169module_init(ip6table_nat_init); 170module_exit(ip6table_nat_exit); 171 172MODULE_LICENSE("GPL");