xref: /openbmc/linux/net/netfilter/xt_NETMAP.c (revision d2912cb1)
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