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