1 /*
2 * Copyright (c) 2011 Patrick McHardy <kaber@trash.net>
3 *
4 * Based on Rusty Russell's IPv4 DNAT 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_DEST = 0,
20 O_RANDOM,
21 O_PERSISTENT,
22 O_X_TO_DEST,
23 F_TO_DEST = 1 << O_TO_DEST,
24 F_RANDOM = 1 << O_RANDOM,
25 F_X_TO_DEST = 1 << O_X_TO_DEST,
26 };
27
DNAT_help(void)28 static void DNAT_help(void)
29 {
30 printf(
31 "DNAT target options:\n"
32 " --to-destination [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
33 " Address to map destination to.\n"
34 "[--random] [--persistent]\n");
35 }
36
DNAT_help_v2(void)37 static void DNAT_help_v2(void)
38 {
39 printf(
40 "DNAT target options:\n"
41 " --to-destination [<ipaddr>[-<ipaddr>]][:port[-port[/port]]]\n"
42 " Address to map destination to.\n"
43 "[--random] [--persistent]\n");
44 }
45
46 static const struct xt_option_entry DNAT_opts[] = {
47 {.name = "to-destination", .id = O_TO_DEST, .type = XTTYPE_STRING,
48 .flags = XTOPT_MAND | XTOPT_MULTI},
49 {.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
50 {.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
51 XTOPT_TABLEEND,
52 };
53
54 /* Ranges expected in network order. */
55 static void
parse_to(const char * orig_arg,int portok,struct nf_nat_range2 * range,int rev)56 parse_to(const char *orig_arg, int portok, struct nf_nat_range2 *range, int rev)
57 {
58 char *arg, *start, *end = NULL, *colon = NULL, *dash, *error;
59 const struct in6_addr *ip;
60
61 arg = strdup(orig_arg);
62 if (arg == NULL)
63 xtables_error(RESOURCE_PROBLEM, "strdup");
64
65 start = strchr(arg, '[');
66 if (start == NULL) {
67 start = arg;
68 /* Lets assume one colon is port information. Otherwise its an IPv6 address */
69 colon = strchr(arg, ':');
70 if (colon && strchr(colon+1, ':'))
71 colon = NULL;
72 }
73 else {
74 start++;
75 end = strchr(start, ']');
76 if (end == NULL)
77 xtables_error(PARAMETER_PROBLEM,
78 "Invalid address format");
79
80 *end = '\0';
81 colon = strchr(end + 1, ':');
82 }
83
84 if (colon) {
85 int port;
86
87 if (!portok)
88 xtables_error(PARAMETER_PROBLEM,
89 "Need TCP, UDP, SCTP or DCCP with port specification");
90
91 range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
92
93 port = atoi(colon+1);
94 if (port <= 0 || port > 65535)
95 xtables_error(PARAMETER_PROBLEM,
96 "Port `%s' not valid\n", colon+1);
97
98 error = strchr(colon+1, ':');
99 if (error)
100 xtables_error(PARAMETER_PROBLEM,
101 "Invalid port:port syntax - use dash\n");
102
103 dash = strchr(colon, '-');
104 if (!dash) {
105 range->min_proto.tcp.port
106 = range->max_proto.tcp.port
107 = htons(port);
108 } else {
109 int maxport;
110
111 maxport = atoi(dash + 1);
112 if (maxport <= 0 || maxport > 65535)
113 xtables_error(PARAMETER_PROBLEM,
114 "Port `%s' not valid\n", dash+1);
115 if (maxport < port)
116 /* People are stupid. */
117 xtables_error(PARAMETER_PROBLEM,
118 "Port range `%s' funky\n", colon+1);
119 range->min_proto.tcp.port = htons(port);
120 range->max_proto.tcp.port = htons(maxport);
121
122 if (rev >= 2) {
123 char *slash = strchr(dash, '/');
124 if (slash) {
125 int baseport;
126
127 baseport = atoi(slash + 1);
128 if (baseport <= 0 || baseport > 65535)
129 xtables_error(PARAMETER_PROBLEM,
130 "Port `%s' not valid\n", slash+1);
131 range->flags |= NF_NAT_RANGE_PROTO_OFFSET;
132 range->base_proto.tcp.port = htons(baseport);
133 }
134 }
135 }
136 /* Starts with colon or [] colon? No IP info...*/
137 if (colon == arg || colon == arg+2) {
138 free(arg);
139 return;
140 }
141 *colon = '\0';
142 }
143
144 range->flags |= NF_NAT_RANGE_MAP_IPS;
145 dash = strchr(start, '-');
146 if (colon && dash && dash > colon)
147 dash = NULL;
148
149 if (dash)
150 *dash = '\0';
151
152 ip = xtables_numeric_to_ip6addr(start);
153 if (!ip)
154 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
155 start);
156 range->min_addr.in6 = *ip;
157 if (dash) {
158 ip = xtables_numeric_to_ip6addr(dash + 1);
159 if (!ip)
160 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
161 dash+1);
162 range->max_addr.in6 = *ip;
163 } else
164 range->max_addr = range->min_addr;
165
166 free(arg);
167 return;
168 }
169
_DNAT_parse(struct xt_option_call * cb,struct nf_nat_range2 * range,int rev)170 static void _DNAT_parse(struct xt_option_call *cb,
171 struct nf_nat_range2 *range, int rev)
172 {
173 const struct ip6t_entry *entry = cb->xt_entry;
174 int portok;
175
176 if (entry->ipv6.proto == IPPROTO_TCP ||
177 entry->ipv6.proto == IPPROTO_UDP ||
178 entry->ipv6.proto == IPPROTO_SCTP ||
179 entry->ipv6.proto == IPPROTO_DCCP ||
180 entry->ipv6.proto == IPPROTO_ICMP)
181 portok = 1;
182 else
183 portok = 0;
184
185 xtables_option_parse(cb);
186 switch (cb->entry->id) {
187 case O_TO_DEST:
188 if (cb->xflags & F_X_TO_DEST) {
189 xtables_error(PARAMETER_PROBLEM,
190 "DNAT: Multiple --to-destination not supported");
191 }
192 parse_to(cb->arg, portok, range, rev);
193 cb->xflags |= F_X_TO_DEST;
194 break;
195 case O_PERSISTENT:
196 range->flags |= NF_NAT_RANGE_PERSISTENT;
197 break;
198 }
199 }
200
DNAT_parse(struct xt_option_call * cb)201 static void DNAT_parse(struct xt_option_call *cb)
202 {
203 struct nf_nat_range *range_v1 = (void *)cb->data;
204 struct nf_nat_range2 range = {};
205
206 memcpy(&range, range_v1, sizeof(*range_v1));
207 _DNAT_parse(cb, &range, 1);
208 memcpy(range_v1, &range, sizeof(*range_v1));
209 }
210
DNAT_parse_v2(struct xt_option_call * cb)211 static void DNAT_parse_v2(struct xt_option_call *cb)
212 {
213 _DNAT_parse(cb, (struct nf_nat_range2 *)cb->data, 2);
214 }
215
_DNAT_fcheck(struct xt_fcheck_call * cb,unsigned int * flags)216 static void _DNAT_fcheck(struct xt_fcheck_call *cb, unsigned int *flags)
217 {
218 static const unsigned int f = F_TO_DEST | F_RANDOM;
219
220 if ((cb->xflags & f) == f)
221 *flags |= NF_NAT_RANGE_PROTO_RANDOM;
222 }
223
DNAT_fcheck(struct xt_fcheck_call * cb)224 static void DNAT_fcheck(struct xt_fcheck_call *cb)
225 {
226 _DNAT_fcheck(cb, &((struct nf_nat_range *)cb->data)->flags);
227 }
228
DNAT_fcheck_v2(struct xt_fcheck_call * cb)229 static void DNAT_fcheck_v2(struct xt_fcheck_call *cb)
230 {
231 _DNAT_fcheck(cb, &((struct nf_nat_range2 *)cb->data)->flags);
232 }
233
print_range(const struct nf_nat_range2 * range,int rev)234 static void print_range(const struct nf_nat_range2 *range, int rev)
235 {
236 if (range->flags & NF_NAT_RANGE_MAP_IPS) {
237 if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
238 printf("[");
239 printf("%s", xtables_ip6addr_to_numeric(&range->min_addr.in6));
240 if (memcmp(&range->min_addr, &range->max_addr,
241 sizeof(range->min_addr)))
242 printf("-%s", xtables_ip6addr_to_numeric(&range->max_addr.in6));
243 if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
244 printf("]");
245 }
246 if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
247 printf(":");
248 printf("%hu", ntohs(range->min_proto.tcp.port));
249 if (range->max_proto.tcp.port != range->min_proto.tcp.port)
250 printf("-%hu", ntohs(range->max_proto.tcp.port));
251 if (rev >= 2 && (range->flags & NF_NAT_RANGE_PROTO_OFFSET))
252 printf("/%hu", ntohs(range->base_proto.tcp.port));
253 }
254 }
255
_DNAT_print(const struct nf_nat_range2 * range,int rev)256 static void _DNAT_print(const struct nf_nat_range2 *range, int rev)
257 {
258 printf(" to:");
259 print_range(range, rev);
260 if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
261 printf(" random");
262 if (range->flags & NF_NAT_RANGE_PERSISTENT)
263 printf(" persistent");
264 }
265
DNAT_print(const void * ip,const struct xt_entry_target * target,int numeric)266 static void DNAT_print(const void *ip, const struct xt_entry_target *target,
267 int numeric)
268 {
269 const struct nf_nat_range *range_v1 = (const void *)target->data;
270 struct nf_nat_range2 range = {};
271
272 memcpy(&range, range_v1, sizeof(*range_v1));
273 _DNAT_print(&range, 1);
274 }
275
DNAT_print_v2(const void * ip,const struct xt_entry_target * target,int numeric)276 static void DNAT_print_v2(const void *ip, const struct xt_entry_target *target,
277 int numeric)
278 {
279 _DNAT_print((const struct nf_nat_range2 *)target->data, 2);
280 }
281
_DNAT_save(const struct nf_nat_range2 * range,int rev)282 static void _DNAT_save(const struct nf_nat_range2 *range, int rev)
283 {
284 printf(" --to-destination ");
285 print_range(range, rev);
286 if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
287 printf(" --random");
288 if (range->flags & NF_NAT_RANGE_PERSISTENT)
289 printf(" --persistent");
290 }
291
DNAT_save(const void * ip,const struct xt_entry_target * target)292 static void DNAT_save(const void *ip, const struct xt_entry_target *target)
293 {
294 const struct nf_nat_range *range_v1 = (const void *)target->data;
295 struct nf_nat_range2 range = {};
296
297 memcpy(&range, range_v1, sizeof(*range_v1));
298 _DNAT_save(&range, 1);
299 }
300
DNAT_save_v2(const void * ip,const struct xt_entry_target * target)301 static void DNAT_save_v2(const void *ip, const struct xt_entry_target *target)
302 {
303 _DNAT_save((const struct nf_nat_range2 *)target->data, 2);
304 }
305
print_range_xlate(const struct nf_nat_range2 * range,struct xt_xlate * xl,int rev)306 static void print_range_xlate(const struct nf_nat_range2 *range,
307 struct xt_xlate *xl, int rev)
308 {
309 bool proto_specified = range->flags & NF_NAT_RANGE_PROTO_SPECIFIED;
310
311 if (range->flags & NF_NAT_RANGE_MAP_IPS) {
312 xt_xlate_add(xl, "%s%s%s",
313 proto_specified ? "[" : "",
314 xtables_ip6addr_to_numeric(&range->min_addr.in6),
315 proto_specified ? "]" : "");
316
317 if (memcmp(&range->min_addr, &range->max_addr,
318 sizeof(range->min_addr))) {
319 xt_xlate_add(xl, "-%s%s%s",
320 proto_specified ? "[" : "",
321 xtables_ip6addr_to_numeric(&range->max_addr.in6),
322 proto_specified ? "]" : "");
323 }
324 }
325 if (proto_specified) {
326 xt_xlate_add(xl, ":%hu", ntohs(range->min_proto.tcp.port));
327
328 if (range->max_proto.tcp.port != range->min_proto.tcp.port)
329 xt_xlate_add(xl, "-%hu",
330 ntohs(range->max_proto.tcp.port));
331 }
332 }
333
_DNAT_xlate(struct xt_xlate * xl,const struct nf_nat_range2 * range,int rev)334 static int _DNAT_xlate(struct xt_xlate *xl,
335 const struct nf_nat_range2 *range, int rev)
336 {
337 bool sep_need = false;
338 const char *sep = " ";
339
340 xt_xlate_add(xl, "dnat to ");
341 print_range_xlate(range, xl, rev);
342 if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) {
343 xt_xlate_add(xl, " random");
344 sep_need = true;
345 }
346 if (range->flags & NF_NAT_RANGE_PERSISTENT) {
347 if (sep_need)
348 sep = ",";
349 xt_xlate_add(xl, "%spersistent", sep);
350 }
351
352 return 1;
353 }
354
DNAT_xlate(struct xt_xlate * xl,const struct xt_xlate_tg_params * params)355 static int DNAT_xlate(struct xt_xlate *xl,
356 const struct xt_xlate_tg_params *params)
357 {
358 const struct nf_nat_range *range_v1 = (const void *)params->target->data;
359 struct nf_nat_range2 range = {};
360
361 memcpy(&range, range_v1, sizeof(*range_v1));
362 _DNAT_xlate(xl, &range, 1);
363
364 return 1;
365 }
366
DNAT_xlate_v2(struct xt_xlate * xl,const struct xt_xlate_tg_params * params)367 static int DNAT_xlate_v2(struct xt_xlate *xl,
368 const struct xt_xlate_tg_params *params)
369 {
370 _DNAT_xlate(xl, (const struct nf_nat_range2 *)params->target->data, 2);
371
372 return 1;
373 }
374
375 static struct xtables_target dnat_tg_reg[] = {
376 {
377 .name = "DNAT",
378 .version = XTABLES_VERSION,
379 .family = NFPROTO_IPV6,
380 .revision = 1,
381 .size = XT_ALIGN(sizeof(struct nf_nat_range)),
382 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_range)),
383 .help = DNAT_help,
384 .print = DNAT_print,
385 .save = DNAT_save,
386 .x6_parse = DNAT_parse,
387 .x6_fcheck = DNAT_fcheck,
388 .x6_options = DNAT_opts,
389 .xlate = DNAT_xlate,
390 },
391 {
392 .name = "DNAT",
393 .version = XTABLES_VERSION,
394 .family = NFPROTO_IPV6,
395 .revision = 2,
396 .size = XT_ALIGN(sizeof(struct nf_nat_range2)),
397 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_range2)),
398 .help = DNAT_help_v2,
399 .print = DNAT_print_v2,
400 .save = DNAT_save_v2,
401 .x6_parse = DNAT_parse_v2,
402 .x6_fcheck = DNAT_fcheck_v2,
403 .x6_options = DNAT_opts,
404 .xlate = DNAT_xlate_v2,
405 },
406 };
407
_init(void)408 void _init(void)
409 {
410 xtables_register_targets(dnat_tg_reg, ARRAY_SIZE(dnat_tg_reg));
411 }
412