1 /*
2 * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
3 *
4 * Based on Rusty Russell's IPv4 SNAT target. Development of IPv6 NAT
5 * funded by Astaro.
6 */
7
8 #include <stdio.h>
9 #include <netdb.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include <xtables.h>
13 #include <iptables.h>
14 #include <limits.h> /* INT_MAX in ip_tables.h */
15 #include <linux/netfilter_ipv6/ip6_tables.h>
16 #include <linux/netfilter/nf_nat.h>
17
18 enum {
19 O_TO_SRC = 0,
20 O_RANDOM,
21 O_RANDOM_FULLY,
22 O_PERSISTENT,
23 O_X_TO_SRC,
24 F_TO_SRC = 1 << O_TO_SRC,
25 F_RANDOM = 1 << O_RANDOM,
26 F_RANDOM_FULLY = 1 << O_RANDOM_FULLY,
27 F_X_TO_SRC = 1 << O_X_TO_SRC,
28 };
29
SNAT_help(void)30 static void SNAT_help(void)
31 {
32 printf(
33 "SNAT target options:\n"
34 " --to-source [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
35 " Address to map source to.\n"
36 "[--random] [--random-fully] [--persistent]\n");
37 }
38
39 static const struct xt_option_entry SNAT_opts[] = {
40 {.name = "to-source", .id = O_TO_SRC, .type = XTTYPE_STRING,
41 .flags = XTOPT_MAND | XTOPT_MULTI},
42 {.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
43 {.name = "random-fully", .id = O_RANDOM_FULLY, .type = XTTYPE_NONE},
44 {.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
45 XTOPT_TABLEEND,
46 };
47
48 /* Ranges expected in network order. */
49 static void
parse_to(const char * orig_arg,int portok,struct nf_nat_range * range)50 parse_to(const char *orig_arg, int portok, struct nf_nat_range *range)
51 {
52 char *arg, *start, *end = NULL, *colon = NULL, *dash, *error;
53 const struct in6_addr *ip;
54
55 arg = strdup(orig_arg);
56 if (arg == NULL)
57 xtables_error(RESOURCE_PROBLEM, "strdup");
58
59 start = strchr(arg, '[');
60 if (start == NULL) {
61 start = arg;
62 /* Lets assume one colon is port information. Otherwise its an IPv6 address */
63 colon = strchr(arg, ':');
64 if (colon && strchr(colon+1, ':'))
65 colon = NULL;
66 }
67 else {
68 start++;
69 end = strchr(start, ']');
70 if (end == NULL)
71 xtables_error(PARAMETER_PROBLEM,
72 "Invalid address format");
73
74 *end = '\0';
75 colon = strchr(end + 1, ':');
76 }
77
78 if (colon) {
79 int port;
80
81 if (!portok)
82 xtables_error(PARAMETER_PROBLEM,
83 "Need TCP, UDP, SCTP or DCCP with port specification");
84
85 range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
86
87 port = atoi(colon+1);
88 if (port <= 0 || port > 65535)
89 xtables_error(PARAMETER_PROBLEM,
90 "Port `%s' not valid\n", colon+1);
91
92 error = strchr(colon+1, ':');
93 if (error)
94 xtables_error(PARAMETER_PROBLEM,
95 "Invalid port:port syntax - use dash\n");
96
97 dash = strchr(colon, '-');
98 if (!dash) {
99 range->min_proto.tcp.port
100 = range->max_proto.tcp.port
101 = htons(port);
102 } else {
103 int maxport;
104
105 maxport = atoi(dash + 1);
106 if (maxport <= 0 || maxport > 65535)
107 xtables_error(PARAMETER_PROBLEM,
108 "Port `%s' not valid\n", dash+1);
109 if (maxport < port)
110 /* People are stupid. */
111 xtables_error(PARAMETER_PROBLEM,
112 "Port range `%s' funky\n", colon+1);
113 range->min_proto.tcp.port = htons(port);
114 range->max_proto.tcp.port = htons(maxport);
115 }
116 /* Starts with colon or [] colon? No IP info...*/
117 if (colon == arg || colon == arg+2) {
118 free(arg);
119 return;
120 }
121 *colon = '\0';
122 }
123
124 range->flags |= NF_NAT_RANGE_MAP_IPS;
125 dash = strchr(start, '-');
126 if (colon && dash && dash > colon)
127 dash = NULL;
128
129 if (dash)
130 *dash = '\0';
131
132 ip = xtables_numeric_to_ip6addr(start);
133 if (!ip)
134 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
135 start);
136 range->min_addr.in6 = *ip;
137 if (dash) {
138 ip = xtables_numeric_to_ip6addr(dash + 1);
139 if (!ip)
140 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
141 dash+1);
142 range->max_addr.in6 = *ip;
143 } else
144 range->max_addr = range->min_addr;
145
146 free(arg);
147 return;
148 }
149
SNAT_parse(struct xt_option_call * cb)150 static void SNAT_parse(struct xt_option_call *cb)
151 {
152 const struct ip6t_entry *entry = cb->xt_entry;
153 struct nf_nat_range *range = cb->data;
154 int portok;
155
156 if (entry->ipv6.proto == IPPROTO_TCP ||
157 entry->ipv6.proto == IPPROTO_UDP ||
158 entry->ipv6.proto == IPPROTO_SCTP ||
159 entry->ipv6.proto == IPPROTO_DCCP ||
160 entry->ipv6.proto == IPPROTO_ICMP)
161 portok = 1;
162 else
163 portok = 0;
164
165 xtables_option_parse(cb);
166 switch (cb->entry->id) {
167 case O_TO_SRC:
168 if (cb->xflags & F_X_TO_SRC) {
169 xtables_error(PARAMETER_PROBLEM,
170 "SNAT: Multiple --to-source not supported");
171 }
172 parse_to(cb->arg, portok, range);
173 cb->xflags |= F_X_TO_SRC;
174 break;
175 case O_PERSISTENT:
176 range->flags |= NF_NAT_RANGE_PERSISTENT;
177 break;
178 }
179 }
180
SNAT_fcheck(struct xt_fcheck_call * cb)181 static void SNAT_fcheck(struct xt_fcheck_call *cb)
182 {
183 static const unsigned int f = F_TO_SRC | F_RANDOM;
184 static const unsigned int r = F_TO_SRC | F_RANDOM_FULLY;
185 struct nf_nat_range *range = cb->data;
186
187 if ((cb->xflags & f) == f)
188 range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
189 if ((cb->xflags & r) == r)
190 range->flags |= NF_NAT_RANGE_PROTO_RANDOM_FULLY;
191 }
192
print_range(const struct nf_nat_range * range)193 static void print_range(const struct nf_nat_range *range)
194 {
195 if (range->flags & NF_NAT_RANGE_MAP_IPS) {
196 if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
197 printf("[");
198 printf("%s", xtables_ip6addr_to_numeric(&range->min_addr.in6));
199 if (memcmp(&range->min_addr, &range->max_addr,
200 sizeof(range->min_addr)))
201 printf("-%s", xtables_ip6addr_to_numeric(&range->max_addr.in6));
202 if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
203 printf("]");
204 }
205 if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
206 printf(":");
207 printf("%hu", ntohs(range->min_proto.tcp.port));
208 if (range->max_proto.tcp.port != range->min_proto.tcp.port)
209 printf("-%hu", ntohs(range->max_proto.tcp.port));
210 }
211 }
212
SNAT_print(const void * ip,const struct xt_entry_target * target,int numeric)213 static void SNAT_print(const void *ip, const struct xt_entry_target *target,
214 int numeric)
215 {
216 const struct nf_nat_range *range = (const void *)target->data;
217
218 printf(" to:");
219 print_range(range);
220 if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
221 printf(" random");
222 if (range->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY)
223 printf(" random-fully");
224 if (range->flags & NF_NAT_RANGE_PERSISTENT)
225 printf(" persistent");
226 }
227
SNAT_save(const void * ip,const struct xt_entry_target * target)228 static void SNAT_save(const void *ip, const struct xt_entry_target *target)
229 {
230 const struct nf_nat_range *range = (const void *)target->data;
231
232 printf(" --to-source ");
233 print_range(range);
234 if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
235 printf(" --random");
236 if (range->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY)
237 printf(" --random-fully");
238 if (range->flags & NF_NAT_RANGE_PERSISTENT)
239 printf(" --persistent");
240 }
241
print_range_xlate(const struct nf_nat_range * range,struct xt_xlate * xl)242 static void print_range_xlate(const struct nf_nat_range *range,
243 struct xt_xlate *xl)
244 {
245 bool proto_specified = range->flags & NF_NAT_RANGE_PROTO_SPECIFIED;
246
247 if (range->flags & NF_NAT_RANGE_MAP_IPS) {
248 xt_xlate_add(xl, "%s%s%s",
249 proto_specified ? "[" : "",
250 xtables_ip6addr_to_numeric(&range->min_addr.in6),
251 proto_specified ? "]" : "");
252
253 if (memcmp(&range->min_addr, &range->max_addr,
254 sizeof(range->min_addr))) {
255 xt_xlate_add(xl, "-%s%s%s",
256 proto_specified ? "[" : "",
257 xtables_ip6addr_to_numeric(&range->max_addr.in6),
258 proto_specified ? "]" : "");
259 }
260 }
261 if (proto_specified) {
262 xt_xlate_add(xl, ":%hu", ntohs(range->min_proto.tcp.port));
263
264 if (range->max_proto.tcp.port != range->min_proto.tcp.port)
265 xt_xlate_add(xl, "-%hu",
266 ntohs(range->max_proto.tcp.port));
267 }
268 }
269
SNAT_xlate(struct xt_xlate * xl,const struct xt_xlate_tg_params * params)270 static int SNAT_xlate(struct xt_xlate *xl,
271 const struct xt_xlate_tg_params *params)
272 {
273 const struct nf_nat_range *range = (const void *)params->target->data;
274 bool sep_need = false;
275 const char *sep = " ";
276
277 xt_xlate_add(xl, "snat to ");
278 print_range_xlate(range, xl);
279 if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) {
280 xt_xlate_add(xl, " random");
281 sep_need = true;
282 }
283 if (range->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) {
284 if (sep_need)
285 sep = ",";
286 xt_xlate_add(xl, "%sfully-random", sep);
287 sep_need = true;
288 }
289 if (range->flags & NF_NAT_RANGE_PERSISTENT) {
290 if (sep_need)
291 sep = ",";
292 xt_xlate_add(xl, "%spersistent", sep);
293 }
294
295 return 1;
296 }
297
298 static struct xtables_target snat_tg_reg = {
299 .name = "SNAT",
300 .version = XTABLES_VERSION,
301 .family = NFPROTO_IPV6,
302 .revision = 1,
303 .size = XT_ALIGN(sizeof(struct nf_nat_range)),
304 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_range)),
305 .help = SNAT_help,
306 .x6_parse = SNAT_parse,
307 .x6_fcheck = SNAT_fcheck,
308 .print = SNAT_print,
309 .save = SNAT_save,
310 .x6_options = SNAT_opts,
311 .xlate = SNAT_xlate,
312 };
313
_init(void)314 void _init(void)
315 {
316 xtables_register_target(&snat_tg_reg);
317 }
318