1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2b3d54b3eSJan Engelhardt /*
3b3d54b3eSJan Engelhardt * (C) 2000-2001 Svenning Soerensen <svenning@post5.tele.dk>
4b3d54b3eSJan Engelhardt * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
5b3d54b3eSJan Engelhardt */
6b3d54b3eSJan Engelhardt
7b3d54b3eSJan Engelhardt #include <linux/ip.h>
8b3d54b3eSJan Engelhardt #include <linux/kernel.h>
9b3d54b3eSJan Engelhardt #include <linux/module.h>
10b3d54b3eSJan Engelhardt #include <linux/netdevice.h>
11b3d54b3eSJan Engelhardt #include <linux/ipv6.h>
12b3d54b3eSJan Engelhardt #include <linux/netfilter.h>
13b3d54b3eSJan Engelhardt #include <linux/netfilter_ipv4.h>
14b3d54b3eSJan Engelhardt #include <linux/netfilter_ipv6.h>
15b3d54b3eSJan Engelhardt #include <linux/netfilter/x_tables.h>
16b3d54b3eSJan Engelhardt #include <net/netfilter/nf_nat.h>
17b3d54b3eSJan Engelhardt
18b3d54b3eSJan Engelhardt static unsigned int
netmap_tg6(struct sk_buff * skb,const struct xt_action_param * par)19b3d54b3eSJan Engelhardt netmap_tg6(struct sk_buff *skb, const struct xt_action_param *par)
20b3d54b3eSJan Engelhardt {
212eb0f624SThierry Du Tre const struct nf_nat_range2 *range = par->targinfo;
222eb0f624SThierry Du Tre struct nf_nat_range2 newrange;
23b3d54b3eSJan Engelhardt struct nf_conn *ct;
24b3d54b3eSJan Engelhardt enum ip_conntrack_info ctinfo;
25b3d54b3eSJan Engelhardt union nf_inet_addr new_addr, netmask;
26b3d54b3eSJan Engelhardt unsigned int i;
27b3d54b3eSJan Engelhardt
28b3d54b3eSJan Engelhardt ct = nf_ct_get(skb, &ctinfo);
29b3d54b3eSJan Engelhardt for (i = 0; i < ARRAY_SIZE(range->min_addr.ip6); i++)
30b3d54b3eSJan Engelhardt netmask.ip6[i] = ~(range->min_addr.ip6[i] ^
31b3d54b3eSJan Engelhardt range->max_addr.ip6[i]);
32b3d54b3eSJan Engelhardt
33613dbd95SPablo Neira Ayuso if (xt_hooknum(par) == NF_INET_PRE_ROUTING ||
34613dbd95SPablo Neira Ayuso xt_hooknum(par) == NF_INET_LOCAL_OUT)
35b3d54b3eSJan Engelhardt new_addr.in6 = ipv6_hdr(skb)->daddr;
36b3d54b3eSJan Engelhardt else
37b3d54b3eSJan Engelhardt new_addr.in6 = ipv6_hdr(skb)->saddr;
38b3d54b3eSJan Engelhardt
39b3d54b3eSJan Engelhardt for (i = 0; i < ARRAY_SIZE(new_addr.ip6); i++) {
40b3d54b3eSJan Engelhardt new_addr.ip6[i] &= ~netmask.ip6[i];
41b3d54b3eSJan Engelhardt new_addr.ip6[i] |= range->min_addr.ip6[i] &
42b3d54b3eSJan Engelhardt netmask.ip6[i];
43b3d54b3eSJan Engelhardt }
44b3d54b3eSJan Engelhardt
45b3d54b3eSJan Engelhardt newrange.flags = range->flags | NF_NAT_RANGE_MAP_IPS;
46b3d54b3eSJan Engelhardt newrange.min_addr = new_addr;
47b3d54b3eSJan Engelhardt newrange.max_addr = new_addr;
48b3d54b3eSJan Engelhardt newrange.min_proto = range->min_proto;
49b3d54b3eSJan Engelhardt newrange.max_proto = range->max_proto;
50b3d54b3eSJan Engelhardt
51613dbd95SPablo Neira Ayuso return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(xt_hooknum(par)));
52b3d54b3eSJan Engelhardt }
53b3d54b3eSJan Engelhardt
netmap_tg6_checkentry(const struct xt_tgchk_param * par)54b3d54b3eSJan Engelhardt static int netmap_tg6_checkentry(const struct xt_tgchk_param *par)
55b3d54b3eSJan Engelhardt {
562eb0f624SThierry Du Tre const struct nf_nat_range2 *range = par->targinfo;
57b3d54b3eSJan Engelhardt
58b3d54b3eSJan Engelhardt if (!(range->flags & NF_NAT_RANGE_MAP_IPS))
59b3d54b3eSJan Engelhardt return -EINVAL;
60a357b3f8SFlorian Westphal return nf_ct_netns_get(par->net, par->family);
61a357b3f8SFlorian Westphal }
62a357b3f8SFlorian Westphal
netmap_tg_destroy(const struct xt_tgdtor_param * par)63a357b3f8SFlorian Westphal static void netmap_tg_destroy(const struct xt_tgdtor_param *par)
64a357b3f8SFlorian Westphal {
65a357b3f8SFlorian Westphal nf_ct_netns_put(par->net, par->family);
66b3d54b3eSJan Engelhardt }
67b3d54b3eSJan Engelhardt
68b3d54b3eSJan Engelhardt static unsigned int
netmap_tg4(struct sk_buff * skb,const struct xt_action_param * par)69b3d54b3eSJan Engelhardt netmap_tg4(struct sk_buff *skb, const struct xt_action_param *par)
70b3d54b3eSJan Engelhardt {
71b3d54b3eSJan Engelhardt struct nf_conn *ct;
72b3d54b3eSJan Engelhardt enum ip_conntrack_info ctinfo;
73b3d54b3eSJan Engelhardt __be32 new_ip, netmask;
74b3d54b3eSJan Engelhardt const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
752eb0f624SThierry Du Tre struct nf_nat_range2 newrange;
76b3d54b3eSJan Engelhardt
7744d6e2f2SVarsha Rao WARN_ON(xt_hooknum(par) != NF_INET_PRE_ROUTING &&
7844d6e2f2SVarsha Rao xt_hooknum(par) != NF_INET_POST_ROUTING &&
7944d6e2f2SVarsha Rao xt_hooknum(par) != NF_INET_LOCAL_OUT &&
8044d6e2f2SVarsha Rao xt_hooknum(par) != NF_INET_LOCAL_IN);
81b3d54b3eSJan Engelhardt ct = nf_ct_get(skb, &ctinfo);
82b3d54b3eSJan Engelhardt
83b3d54b3eSJan Engelhardt netmask = ~(mr->range[0].min_ip ^ mr->range[0].max_ip);
84b3d54b3eSJan Engelhardt
85613dbd95SPablo Neira Ayuso if (xt_hooknum(par) == NF_INET_PRE_ROUTING ||
86613dbd95SPablo Neira Ayuso xt_hooknum(par) == NF_INET_LOCAL_OUT)
87b3d54b3eSJan Engelhardt new_ip = ip_hdr(skb)->daddr & ~netmask;
88b3d54b3eSJan Engelhardt else
89b3d54b3eSJan Engelhardt new_ip = ip_hdr(skb)->saddr & ~netmask;
90b3d54b3eSJan Engelhardt new_ip |= mr->range[0].min_ip & netmask;
91b3d54b3eSJan Engelhardt
92b3d54b3eSJan Engelhardt memset(&newrange.min_addr, 0, sizeof(newrange.min_addr));
93b3d54b3eSJan Engelhardt memset(&newrange.max_addr, 0, sizeof(newrange.max_addr));
94b3d54b3eSJan Engelhardt newrange.flags = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS;
95b3d54b3eSJan Engelhardt newrange.min_addr.ip = new_ip;
96b3d54b3eSJan Engelhardt newrange.max_addr.ip = new_ip;
97b3d54b3eSJan Engelhardt newrange.min_proto = mr->range[0].min;
98b3d54b3eSJan Engelhardt newrange.max_proto = mr->range[0].max;
99b3d54b3eSJan Engelhardt
100b3d54b3eSJan Engelhardt /* Hand modified range to generic setup. */
101613dbd95SPablo Neira Ayuso return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(xt_hooknum(par)));
102b3d54b3eSJan Engelhardt }
103b3d54b3eSJan Engelhardt
netmap_tg4_check(const struct xt_tgchk_param * par)104b3d54b3eSJan Engelhardt static int netmap_tg4_check(const struct xt_tgchk_param *par)
105b3d54b3eSJan Engelhardt {
106b3d54b3eSJan Engelhardt const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
107b3d54b3eSJan Engelhardt
108b3d54b3eSJan Engelhardt if (!(mr->range[0].flags & NF_NAT_RANGE_MAP_IPS)) {
109b3d54b3eSJan Engelhardt pr_debug("bad MAP_IPS.\n");
110b3d54b3eSJan Engelhardt return -EINVAL;
111b3d54b3eSJan Engelhardt }
112b3d54b3eSJan Engelhardt if (mr->rangesize != 1) {
113b3d54b3eSJan Engelhardt pr_debug("bad rangesize %u.\n", mr->rangesize);
114b3d54b3eSJan Engelhardt return -EINVAL;
115b3d54b3eSJan Engelhardt }
116a357b3f8SFlorian Westphal return nf_ct_netns_get(par->net, par->family);
117b3d54b3eSJan Engelhardt }
118b3d54b3eSJan Engelhardt
119b3d54b3eSJan Engelhardt static struct xt_target netmap_tg_reg[] __read_mostly = {
120b3d54b3eSJan Engelhardt {
121b3d54b3eSJan Engelhardt .name = "NETMAP",
122b3d54b3eSJan Engelhardt .family = NFPROTO_IPV6,
123b3d54b3eSJan Engelhardt .revision = 0,
124b3d54b3eSJan Engelhardt .target = netmap_tg6,
125b3d54b3eSJan Engelhardt .targetsize = sizeof(struct nf_nat_range),
126b3d54b3eSJan Engelhardt .table = "nat",
127b3d54b3eSJan Engelhardt .hooks = (1 << NF_INET_PRE_ROUTING) |
128b3d54b3eSJan Engelhardt (1 << NF_INET_POST_ROUTING) |
129b3d54b3eSJan Engelhardt (1 << NF_INET_LOCAL_OUT) |
130b3d54b3eSJan Engelhardt (1 << NF_INET_LOCAL_IN),
131b3d54b3eSJan Engelhardt .checkentry = netmap_tg6_checkentry,
132a357b3f8SFlorian Westphal .destroy = netmap_tg_destroy,
133b3d54b3eSJan Engelhardt .me = THIS_MODULE,
134b3d54b3eSJan Engelhardt },
135b3d54b3eSJan Engelhardt {
136b3d54b3eSJan Engelhardt .name = "NETMAP",
137b3d54b3eSJan Engelhardt .family = NFPROTO_IPV4,
138b3d54b3eSJan Engelhardt .revision = 0,
139b3d54b3eSJan Engelhardt .target = netmap_tg4,
140b3d54b3eSJan Engelhardt .targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat),
141b3d54b3eSJan Engelhardt .table = "nat",
142b3d54b3eSJan Engelhardt .hooks = (1 << NF_INET_PRE_ROUTING) |
143b3d54b3eSJan Engelhardt (1 << NF_INET_POST_ROUTING) |
144b3d54b3eSJan Engelhardt (1 << NF_INET_LOCAL_OUT) |
145b3d54b3eSJan Engelhardt (1 << NF_INET_LOCAL_IN),
146b3d54b3eSJan Engelhardt .checkentry = netmap_tg4_check,
147a357b3f8SFlorian Westphal .destroy = netmap_tg_destroy,
148b3d54b3eSJan Engelhardt .me = THIS_MODULE,
149b3d54b3eSJan Engelhardt },
150b3d54b3eSJan Engelhardt };
151b3d54b3eSJan Engelhardt
netmap_tg_init(void)152b3d54b3eSJan Engelhardt static int __init netmap_tg_init(void)
153b3d54b3eSJan Engelhardt {
154b3d54b3eSJan Engelhardt return xt_register_targets(netmap_tg_reg, ARRAY_SIZE(netmap_tg_reg));
155b3d54b3eSJan Engelhardt }
156b3d54b3eSJan Engelhardt
netmap_tg_exit(void)157b3d54b3eSJan Engelhardt static void netmap_tg_exit(void)
158b3d54b3eSJan Engelhardt {
159b3d54b3eSJan Engelhardt xt_unregister_targets(netmap_tg_reg, ARRAY_SIZE(netmap_tg_reg));
160b3d54b3eSJan Engelhardt }
161b3d54b3eSJan Engelhardt
162b3d54b3eSJan Engelhardt module_init(netmap_tg_init);
163b3d54b3eSJan Engelhardt module_exit(netmap_tg_exit);
164b3d54b3eSJan Engelhardt
165b3d54b3eSJan Engelhardt MODULE_LICENSE("GPL");
166b3d54b3eSJan Engelhardt MODULE_DESCRIPTION("Xtables: 1:1 NAT mapping of subnets");
167b3d54b3eSJan Engelhardt MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
168b3d54b3eSJan Engelhardt MODULE_ALIAS("ip6t_NETMAP");
169b3d54b3eSJan Engelhardt MODULE_ALIAS("ipt_NETMAP");
170