xref: /openbmc/linux/net/netfilter/xt_NETMAP.c (revision 44d6e2f2)
1b3d54b3eSJan Engelhardt /*
2b3d54b3eSJan Engelhardt  * (C) 2000-2001 Svenning Soerensen <svenning@post5.tele.dk>
3b3d54b3eSJan Engelhardt  * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
4b3d54b3eSJan Engelhardt  *
5b3d54b3eSJan Engelhardt  * This program is free software; you can redistribute it and/or modify
6b3d54b3eSJan Engelhardt  * it under the terms of the GNU General Public License version 2 as
7b3d54b3eSJan Engelhardt  * published by the Free Software Foundation.
8b3d54b3eSJan Engelhardt  */
9b3d54b3eSJan Engelhardt 
10b3d54b3eSJan Engelhardt #include <linux/ip.h>
11b3d54b3eSJan Engelhardt #include <linux/kernel.h>
12b3d54b3eSJan Engelhardt #include <linux/module.h>
13b3d54b3eSJan Engelhardt #include <linux/netdevice.h>
14b3d54b3eSJan Engelhardt #include <linux/ipv6.h>
15b3d54b3eSJan Engelhardt #include <linux/netfilter.h>
16b3d54b3eSJan Engelhardt #include <linux/netfilter_ipv4.h>
17b3d54b3eSJan Engelhardt #include <linux/netfilter_ipv6.h>
18b3d54b3eSJan Engelhardt #include <linux/netfilter/x_tables.h>
19b3d54b3eSJan Engelhardt #include <net/netfilter/nf_nat.h>
20b3d54b3eSJan Engelhardt 
21b3d54b3eSJan Engelhardt static unsigned int
22b3d54b3eSJan Engelhardt netmap_tg6(struct sk_buff *skb, const struct xt_action_param *par)
23b3d54b3eSJan Engelhardt {
24b3d54b3eSJan Engelhardt 	const struct nf_nat_range *range = par->targinfo;
25b3d54b3eSJan Engelhardt 	struct nf_nat_range newrange;
26b3d54b3eSJan Engelhardt 	struct nf_conn *ct;
27b3d54b3eSJan Engelhardt 	enum ip_conntrack_info ctinfo;
28b3d54b3eSJan Engelhardt 	union nf_inet_addr new_addr, netmask;
29b3d54b3eSJan Engelhardt 	unsigned int i;
30b3d54b3eSJan Engelhardt 
31b3d54b3eSJan Engelhardt 	ct = nf_ct_get(skb, &ctinfo);
32b3d54b3eSJan Engelhardt 	for (i = 0; i < ARRAY_SIZE(range->min_addr.ip6); i++)
33b3d54b3eSJan Engelhardt 		netmask.ip6[i] = ~(range->min_addr.ip6[i] ^
34b3d54b3eSJan Engelhardt 				   range->max_addr.ip6[i]);
35b3d54b3eSJan Engelhardt 
36613dbd95SPablo Neira Ayuso 	if (xt_hooknum(par) == NF_INET_PRE_ROUTING ||
37613dbd95SPablo Neira Ayuso 	    xt_hooknum(par) == NF_INET_LOCAL_OUT)
38b3d54b3eSJan Engelhardt 		new_addr.in6 = ipv6_hdr(skb)->daddr;
39b3d54b3eSJan Engelhardt 	else
40b3d54b3eSJan Engelhardt 		new_addr.in6 = ipv6_hdr(skb)->saddr;
41b3d54b3eSJan Engelhardt 
42b3d54b3eSJan Engelhardt 	for (i = 0; i < ARRAY_SIZE(new_addr.ip6); i++) {
43b3d54b3eSJan Engelhardt 		new_addr.ip6[i] &= ~netmask.ip6[i];
44b3d54b3eSJan Engelhardt 		new_addr.ip6[i] |= range->min_addr.ip6[i] &
45b3d54b3eSJan Engelhardt 				   netmask.ip6[i];
46b3d54b3eSJan Engelhardt 	}
47b3d54b3eSJan Engelhardt 
48b3d54b3eSJan Engelhardt 	newrange.flags	= range->flags | NF_NAT_RANGE_MAP_IPS;
49b3d54b3eSJan Engelhardt 	newrange.min_addr	= new_addr;
50b3d54b3eSJan Engelhardt 	newrange.max_addr	= new_addr;
51b3d54b3eSJan Engelhardt 	newrange.min_proto	= range->min_proto;
52b3d54b3eSJan Engelhardt 	newrange.max_proto	= range->max_proto;
53b3d54b3eSJan Engelhardt 
54613dbd95SPablo Neira Ayuso 	return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(xt_hooknum(par)));
55b3d54b3eSJan Engelhardt }
56b3d54b3eSJan Engelhardt 
57b3d54b3eSJan Engelhardt static int netmap_tg6_checkentry(const struct xt_tgchk_param *par)
58b3d54b3eSJan Engelhardt {
59b3d54b3eSJan Engelhardt 	const struct nf_nat_range *range = par->targinfo;
60b3d54b3eSJan Engelhardt 
61b3d54b3eSJan Engelhardt 	if (!(range->flags & NF_NAT_RANGE_MAP_IPS))
62b3d54b3eSJan Engelhardt 		return -EINVAL;
63a357b3f8SFlorian Westphal 	return nf_ct_netns_get(par->net, par->family);
64a357b3f8SFlorian Westphal }
65a357b3f8SFlorian Westphal 
66a357b3f8SFlorian Westphal static void netmap_tg_destroy(const struct xt_tgdtor_param *par)
67a357b3f8SFlorian Westphal {
68a357b3f8SFlorian Westphal 	nf_ct_netns_put(par->net, par->family);
69b3d54b3eSJan Engelhardt }
70b3d54b3eSJan Engelhardt 
71b3d54b3eSJan Engelhardt static unsigned int
72b3d54b3eSJan Engelhardt netmap_tg4(struct sk_buff *skb, const struct xt_action_param *par)
73b3d54b3eSJan Engelhardt {
74b3d54b3eSJan Engelhardt 	struct nf_conn *ct;
75b3d54b3eSJan Engelhardt 	enum ip_conntrack_info ctinfo;
76b3d54b3eSJan Engelhardt 	__be32 new_ip, netmask;
77b3d54b3eSJan Engelhardt 	const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
78b3d54b3eSJan Engelhardt 	struct nf_nat_range newrange;
79b3d54b3eSJan Engelhardt 
8044d6e2f2SVarsha Rao 	WARN_ON(xt_hooknum(par) != NF_INET_PRE_ROUTING &&
8144d6e2f2SVarsha Rao 		xt_hooknum(par) != NF_INET_POST_ROUTING &&
8244d6e2f2SVarsha Rao 		xt_hooknum(par) != NF_INET_LOCAL_OUT &&
8344d6e2f2SVarsha Rao 		xt_hooknum(par) != NF_INET_LOCAL_IN);
84b3d54b3eSJan Engelhardt 	ct = nf_ct_get(skb, &ctinfo);
85b3d54b3eSJan Engelhardt 
86b3d54b3eSJan Engelhardt 	netmask = ~(mr->range[0].min_ip ^ mr->range[0].max_ip);
87b3d54b3eSJan Engelhardt 
88613dbd95SPablo Neira Ayuso 	if (xt_hooknum(par) == NF_INET_PRE_ROUTING ||
89613dbd95SPablo Neira Ayuso 	    xt_hooknum(par) == NF_INET_LOCAL_OUT)
90b3d54b3eSJan Engelhardt 		new_ip = ip_hdr(skb)->daddr & ~netmask;
91b3d54b3eSJan Engelhardt 	else
92b3d54b3eSJan Engelhardt 		new_ip = ip_hdr(skb)->saddr & ~netmask;
93b3d54b3eSJan Engelhardt 	new_ip |= mr->range[0].min_ip & netmask;
94b3d54b3eSJan Engelhardt 
95b3d54b3eSJan Engelhardt 	memset(&newrange.min_addr, 0, sizeof(newrange.min_addr));
96b3d54b3eSJan Engelhardt 	memset(&newrange.max_addr, 0, sizeof(newrange.max_addr));
97b3d54b3eSJan Engelhardt 	newrange.flags	     = mr->range[0].flags | NF_NAT_RANGE_MAP_IPS;
98b3d54b3eSJan Engelhardt 	newrange.min_addr.ip = new_ip;
99b3d54b3eSJan Engelhardt 	newrange.max_addr.ip = new_ip;
100b3d54b3eSJan Engelhardt 	newrange.min_proto   = mr->range[0].min;
101b3d54b3eSJan Engelhardt 	newrange.max_proto   = mr->range[0].max;
102b3d54b3eSJan Engelhardt 
103b3d54b3eSJan Engelhardt 	/* Hand modified range to generic setup. */
104613dbd95SPablo Neira Ayuso 	return nf_nat_setup_info(ct, &newrange, HOOK2MANIP(xt_hooknum(par)));
105b3d54b3eSJan Engelhardt }
106b3d54b3eSJan Engelhardt 
107b3d54b3eSJan Engelhardt static int netmap_tg4_check(const struct xt_tgchk_param *par)
108b3d54b3eSJan Engelhardt {
109b3d54b3eSJan Engelhardt 	const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo;
110b3d54b3eSJan Engelhardt 
111b3d54b3eSJan Engelhardt 	if (!(mr->range[0].flags & NF_NAT_RANGE_MAP_IPS)) {
112b3d54b3eSJan Engelhardt 		pr_debug("bad MAP_IPS.\n");
113b3d54b3eSJan Engelhardt 		return -EINVAL;
114b3d54b3eSJan Engelhardt 	}
115b3d54b3eSJan Engelhardt 	if (mr->rangesize != 1) {
116b3d54b3eSJan Engelhardt 		pr_debug("bad rangesize %u.\n", mr->rangesize);
117b3d54b3eSJan Engelhardt 		return -EINVAL;
118b3d54b3eSJan Engelhardt 	}
119a357b3f8SFlorian Westphal 	return nf_ct_netns_get(par->net, par->family);
120b3d54b3eSJan Engelhardt }
121b3d54b3eSJan Engelhardt 
122b3d54b3eSJan Engelhardt static struct xt_target netmap_tg_reg[] __read_mostly = {
123b3d54b3eSJan Engelhardt 	{
124b3d54b3eSJan Engelhardt 		.name       = "NETMAP",
125b3d54b3eSJan Engelhardt 		.family     = NFPROTO_IPV6,
126b3d54b3eSJan Engelhardt 		.revision   = 0,
127b3d54b3eSJan Engelhardt 		.target     = netmap_tg6,
128b3d54b3eSJan Engelhardt 		.targetsize = sizeof(struct nf_nat_range),
129b3d54b3eSJan Engelhardt 		.table      = "nat",
130b3d54b3eSJan Engelhardt 		.hooks      = (1 << NF_INET_PRE_ROUTING) |
131b3d54b3eSJan Engelhardt 		              (1 << NF_INET_POST_ROUTING) |
132b3d54b3eSJan Engelhardt 		              (1 << NF_INET_LOCAL_OUT) |
133b3d54b3eSJan Engelhardt 		              (1 << NF_INET_LOCAL_IN),
134b3d54b3eSJan Engelhardt 		.checkentry = netmap_tg6_checkentry,
135a357b3f8SFlorian Westphal 		.destroy    = netmap_tg_destroy,
136b3d54b3eSJan Engelhardt 		.me         = THIS_MODULE,
137b3d54b3eSJan Engelhardt 	},
138b3d54b3eSJan Engelhardt 	{
139b3d54b3eSJan Engelhardt 		.name       = "NETMAP",
140b3d54b3eSJan Engelhardt 		.family     = NFPROTO_IPV4,
141b3d54b3eSJan Engelhardt 		.revision   = 0,
142b3d54b3eSJan Engelhardt 		.target     = netmap_tg4,
143b3d54b3eSJan Engelhardt 		.targetsize = sizeof(struct nf_nat_ipv4_multi_range_compat),
144b3d54b3eSJan Engelhardt 		.table      = "nat",
145b3d54b3eSJan Engelhardt 		.hooks      = (1 << NF_INET_PRE_ROUTING) |
146b3d54b3eSJan Engelhardt 		              (1 << NF_INET_POST_ROUTING) |
147b3d54b3eSJan Engelhardt 		              (1 << NF_INET_LOCAL_OUT) |
148b3d54b3eSJan Engelhardt 		              (1 << NF_INET_LOCAL_IN),
149b3d54b3eSJan Engelhardt 		.checkentry = netmap_tg4_check,
150a357b3f8SFlorian Westphal 		.destroy    = netmap_tg_destroy,
151b3d54b3eSJan Engelhardt 		.me         = THIS_MODULE,
152b3d54b3eSJan Engelhardt 	},
153b3d54b3eSJan Engelhardt };
154b3d54b3eSJan Engelhardt 
155b3d54b3eSJan Engelhardt static int __init netmap_tg_init(void)
156b3d54b3eSJan Engelhardt {
157b3d54b3eSJan Engelhardt 	return xt_register_targets(netmap_tg_reg, ARRAY_SIZE(netmap_tg_reg));
158b3d54b3eSJan Engelhardt }
159b3d54b3eSJan Engelhardt 
160b3d54b3eSJan Engelhardt static void netmap_tg_exit(void)
161b3d54b3eSJan Engelhardt {
162b3d54b3eSJan Engelhardt 	xt_unregister_targets(netmap_tg_reg, ARRAY_SIZE(netmap_tg_reg));
163b3d54b3eSJan Engelhardt }
164b3d54b3eSJan Engelhardt 
165b3d54b3eSJan Engelhardt module_init(netmap_tg_init);
166b3d54b3eSJan Engelhardt module_exit(netmap_tg_exit);
167b3d54b3eSJan Engelhardt 
168b3d54b3eSJan Engelhardt MODULE_LICENSE("GPL");
169b3d54b3eSJan Engelhardt MODULE_DESCRIPTION("Xtables: 1:1 NAT mapping of subnets");
170b3d54b3eSJan Engelhardt MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
171b3d54b3eSJan Engelhardt MODULE_ALIAS("ip6t_NETMAP");
172b3d54b3eSJan Engelhardt MODULE_ALIAS("ipt_NETMAP");
173