1 /*
2 * Copyright (c) 2011 Patrick McHardy <[email protected]>
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> /* get_kernel_version */
14 #include <limits.h> /* INT_MAX in ip_tables.h */
15 #include <arpa/inet.h>
16 #include <linux/netfilter_ipv4/ip_tables.h>
17 #include <linux/netfilter_ipv6/ip6_tables.h>
18 #include <linux/netfilter/nf_nat.h>
19
20 #define TO_IPV4_MRC(ptr) ((const struct nf_nat_ipv4_multi_range_compat *)(ptr))
21 #define RANGE2_INIT_FROM_IPV4_MRC(ptr) { \
22 .flags = TO_IPV4_MRC(ptr)->range[0].flags, \
23 .min_addr.ip = TO_IPV4_MRC(ptr)->range[0].min_ip, \
24 .max_addr.ip = TO_IPV4_MRC(ptr)->range[0].max_ip, \
25 .min_proto = TO_IPV4_MRC(ptr)->range[0].min, \
26 .max_proto = TO_IPV4_MRC(ptr)->range[0].max, \
27 };
28 #define TO_NF_NAT_RANGE(ptr) ((const struct nf_nat_range *)(ptr))
29 #define RANGE2_INIT_FROM_RANGE(ptr) { \
30 .flags = TO_NF_NAT_RANGE(ptr)->flags, \
31 .min_addr = TO_NF_NAT_RANGE(ptr)->min_addr, \
32 .max_addr = TO_NF_NAT_RANGE(ptr)->max_addr, \
33 .min_proto = TO_NF_NAT_RANGE(ptr)->min_proto, \
34 .max_proto = TO_NF_NAT_RANGE(ptr)->max_proto, \
35 };
36
37 enum {
38 O_TO_DEST = 0,
39 O_TO_SRC,
40 O_TO_PORTS,
41 O_RANDOM,
42 O_RANDOM_FULLY,
43 O_PERSISTENT,
44 };
45
SNAT_help(void)46 static void SNAT_help(void)
47 {
48 printf(
49 "SNAT target options:\n"
50 " --to-source [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
51 " Address to map source to.\n"
52 "[--random] [--random-fully] [--persistent]\n");
53 }
54
MASQUERADE_help(void)55 static void MASQUERADE_help(void)
56 {
57 printf(
58 "MASQUERADE target options:\n"
59 " --to-ports <port>[-<port>]\n"
60 " Port (range) to map to.\n"
61 " --random\n"
62 " Randomize source port.\n"
63 " --random-fully\n"
64 " Fully randomize source port.\n");
65 }
66
DNAT_help(void)67 static void DNAT_help(void)
68 {
69 printf(
70 "DNAT target options:\n"
71 " --to-destination [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
72 " Address to map destination to.\n"
73 "[--random] [--persistent]\n");
74 }
75
DNAT_help_v2(void)76 static void DNAT_help_v2(void)
77 {
78 printf(
79 "DNAT target options:\n"
80 " --to-destination [<ipaddr>[-<ipaddr>]][:port[-port[/port]]]\n"
81 " Address to map destination to.\n"
82 "[--random] [--persistent]\n");
83 }
84
REDIRECT_help(void)85 static void REDIRECT_help(void)
86 {
87 printf(
88 "REDIRECT target options:\n"
89 " --to-ports <port>[-<port>]\n"
90 " Port (range) to map to.\n"
91 " [--random]\n");
92 }
93
94 static const struct xt_option_entry SNAT_opts[] = {
95 {.name = "to-source", .id = O_TO_SRC, .type = XTTYPE_STRING,
96 .flags = XTOPT_MAND},
97 {.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
98 {.name = "random-fully", .id = O_RANDOM_FULLY, .type = XTTYPE_NONE},
99 {.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
100 XTOPT_TABLEEND,
101 };
102
103 static const struct xt_option_entry MASQUERADE_opts[] = {
104 {.name = "to-ports", .id = O_TO_PORTS, .type = XTTYPE_STRING},
105 {.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
106 {.name = "random-fully", .id = O_RANDOM_FULLY, .type = XTTYPE_NONE},
107 XTOPT_TABLEEND,
108 };
109
110 static const struct xt_option_entry DNAT_opts[] = {
111 {.name = "to-destination", .id = O_TO_DEST, .type = XTTYPE_STRING,
112 .flags = XTOPT_MAND},
113 {.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
114 {.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
115 XTOPT_TABLEEND,
116 };
117
118 static const struct xt_option_entry REDIRECT_opts[] = {
119 {.name = "to-ports", .id = O_TO_PORTS, .type = XTTYPE_STRING},
120 {.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
121 XTOPT_TABLEEND,
122 };
123
124 /* Parses ports */
125 static void
parse_ports(const char * arg,bool portok,struct nf_nat_range2 * range)126 parse_ports(const char *arg, bool portok, struct nf_nat_range2 *range)
127 {
128 unsigned int port, maxport, baseport;
129 char *end = NULL;
130
131 if (!portok)
132 xtables_error(PARAMETER_PROBLEM,
133 "Need TCP, UDP, SCTP or DCCP with port specification");
134
135 range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
136
137 if (!xtables_strtoui(arg, &end, &port, 0, UINT16_MAX)) {
138 port = xtables_service_to_port(arg, NULL);
139 if (port == (unsigned)-1)
140 xtables_error(PARAMETER_PROBLEM,
141 "Port `%s' not valid", arg);
142 end = "";
143 }
144
145 switch (*end) {
146 case '\0':
147 range->min_proto.tcp.port
148 = range->max_proto.tcp.port
149 = htons(port);
150 return;
151 case '-':
152 arg = end + 1;
153 break;
154 case ':':
155 xtables_error(PARAMETER_PROBLEM,
156 "Invalid port:port syntax - use dash");
157 default:
158 xtables_error(PARAMETER_PROBLEM,
159 "Garbage after port value: `%s'", end);
160 }
161
162 /* it is a range, don't allow service names here */
163 if (!xtables_strtoui(arg, &end, &maxport, 0, UINT16_MAX))
164 xtables_error(PARAMETER_PROBLEM, "Port `%s' not valid", arg);
165
166 if (maxport < port)
167 /* People are stupid. */
168 xtables_error(PARAMETER_PROBLEM,
169 "Port range `%s' funky", arg);
170
171 range->min_proto.tcp.port = htons(port);
172 range->max_proto.tcp.port = htons(maxport);
173
174 switch (*end) {
175 case '\0':
176 return;
177 case '/':
178 arg = end + 1;
179 break;
180 default:
181 xtables_error(PARAMETER_PROBLEM,
182 "Garbage after port range: `%s'", end);
183 }
184
185 if (!xtables_strtoui(arg, &end, &baseport, 1, UINT16_MAX)) {
186 baseport = xtables_service_to_port(arg, NULL);
187 if (baseport == (unsigned)-1)
188 xtables_error(PARAMETER_PROBLEM,
189 "Port `%s' not valid", arg);
190 }
191
192 range->flags |= NF_NAT_RANGE_PROTO_OFFSET;
193 range->base_proto.tcp.port = htons(baseport);
194 }
195
196 /* Ranges expected in network order. */
197 static void
parse_to(const char * orig_arg,bool portok,struct nf_nat_range2 * range,int family)198 parse_to(const char *orig_arg, bool portok,
199 struct nf_nat_range2 *range, int family)
200 {
201 char *arg, *start, *end, *colon, *dash;
202
203 arg = xtables_strdup(orig_arg);
204 start = strchr(arg, '[');
205 if (!start) {
206 start = arg;
207 /* Lets assume one colon is port information.
208 * Otherwise its an IPv6 address */
209 colon = strchr(arg, ':');
210 if (colon && strchr(colon + 1, ':'))
211 colon = NULL;
212 } else {
213 start++;
214 end = strchr(start, ']');
215 if (end == NULL || family == AF_INET)
216 xtables_error(PARAMETER_PROBLEM,
217 "Invalid address format");
218
219 *end = '\0';
220 colon = strchr(end + 1, ':');
221 }
222
223 if (colon) {
224 parse_ports(colon + 1, portok, range);
225
226 /* Starts with colon or [] colon? No IP info...*/
227 if (colon == arg || colon == arg + 2) {
228 free(arg);
229 return;
230 }
231 *colon = '\0';
232 }
233
234 range->flags |= NF_NAT_RANGE_MAP_IPS;
235 dash = strchr(start, '-');
236 if (colon && dash && dash > colon)
237 dash = NULL;
238
239 if (dash)
240 *dash = '\0';
241
242 if (!inet_pton(family, start, &range->min_addr))
243 xtables_error(PARAMETER_PROBLEM,
244 "Bad IP address \"%s\"", start);
245 if (dash) {
246 if (!inet_pton(family, dash + 1, &range->max_addr))
247 xtables_error(PARAMETER_PROBLEM,
248 "Bad IP address \"%s\"", dash + 1);
249 } else {
250 range->max_addr = range->min_addr;
251 }
252 free(arg);
253 return;
254 }
255
__NAT_parse(struct xt_option_call * cb,__u16 proto,struct nf_nat_range2 * range,int family)256 static void __NAT_parse(struct xt_option_call *cb, __u16 proto,
257 struct nf_nat_range2 *range, int family)
258 {
259 bool portok = proto == IPPROTO_TCP ||
260 proto == IPPROTO_UDP ||
261 proto == IPPROTO_SCTP ||
262 proto == IPPROTO_DCCP ||
263 proto == IPPROTO_ICMP;
264
265 xtables_option_parse(cb);
266 switch (cb->entry->id) {
267 case O_TO_DEST:
268 case O_TO_SRC:
269 parse_to(cb->arg, portok, range, family);
270 break;
271 case O_TO_PORTS:
272 parse_ports(cb->arg, portok, range);
273 break;
274 case O_PERSISTENT:
275 range->flags |= NF_NAT_RANGE_PERSISTENT;
276 break;
277 case O_RANDOM:
278 range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
279 break;
280 case O_RANDOM_FULLY:
281 range->flags |= NF_NAT_RANGE_PROTO_RANDOM_FULLY;
282 break;
283 }
284 }
285
NAT_parse(struct xt_option_call * cb)286 static void NAT_parse(struct xt_option_call *cb)
287 {
288 struct nf_nat_ipv4_multi_range_compat *mr = (void *)cb->data;
289 const struct ipt_entry *entry = cb->xt_entry;
290 struct nf_nat_range2 range = {};
291
292 __NAT_parse(cb, entry->ip.proto, &range, AF_INET);
293
294 switch (cb->entry->id) {
295 case O_TO_DEST:
296 case O_TO_SRC:
297 mr->range->min_ip = range.min_addr.ip;
298 mr->range->max_ip = range.max_addr.ip;
299 /* fall through */
300 case O_TO_PORTS:
301 mr->range->min = range.min_proto;
302 mr->range->max = range.max_proto;
303 /* fall through */
304 case O_PERSISTENT:
305 case O_RANDOM:
306 case O_RANDOM_FULLY:
307 mr->range->flags |= range.flags;
308 break;
309 }
310 }
311
NAT_parse6(struct xt_option_call * cb)312 static void NAT_parse6(struct xt_option_call *cb)
313 {
314 struct nf_nat_range2 range = RANGE2_INIT_FROM_RANGE(cb->data);
315 struct nf_nat_range *range_v1 = (void *)cb->data;
316 const struct ip6t_entry *entry = cb->xt_entry;
317
318 __NAT_parse(cb, entry->ipv6.proto, &range, AF_INET6);
319 memcpy(range_v1, &range, sizeof(*range_v1));
320 }
321
DNAT_parse_v2(struct xt_option_call * cb)322 static void DNAT_parse_v2(struct xt_option_call *cb)
323 {
324 const struct ipt_entry *entry = cb->xt_entry;
325
326 __NAT_parse(cb, entry->ip.proto, cb->data, AF_INET);
327 }
328
DNAT_parse6_v2(struct xt_option_call * cb)329 static void DNAT_parse6_v2(struct xt_option_call *cb)
330 {
331 const struct ip6t_entry *entry = cb->xt_entry;
332
333 __NAT_parse(cb, entry->ipv6.proto, cb->data, AF_INET6);
334 }
335
SNAT_fcheck(struct xt_fcheck_call * cb)336 static void SNAT_fcheck(struct xt_fcheck_call *cb)
337 {
338 struct nf_nat_ipv4_multi_range_compat *mr = cb->data;
339
340 mr->rangesize = 1;
341 }
342
DNAT_fcheck(struct xt_fcheck_call * cb)343 static void DNAT_fcheck(struct xt_fcheck_call *cb)
344 {
345 struct nf_nat_ipv4_multi_range_compat *mr = cb->data;
346
347 mr->rangesize = 1;
348
349 if (mr->range[0].flags & NF_NAT_RANGE_PROTO_OFFSET)
350 xtables_error(PARAMETER_PROBLEM,
351 "Shifted portmap ranges not supported with this kernel");
352 }
353
DNAT_fcheck6(struct xt_fcheck_call * cb)354 static void DNAT_fcheck6(struct xt_fcheck_call *cb)
355 {
356 struct nf_nat_range *range = (void *)cb->data;
357
358 if (range->flags & NF_NAT_RANGE_PROTO_OFFSET)
359 xtables_error(PARAMETER_PROBLEM,
360 "Shifted portmap ranges not supported with this kernel");
361 }
362
sprint_range(const struct nf_nat_range2 * r,int family)363 static char *sprint_range(const struct nf_nat_range2 *r, int family)
364 {
365 bool brackets = family == AF_INET6 &&
366 r->flags & NF_NAT_RANGE_PROTO_SPECIFIED;
367 static char buf[INET6_ADDRSTRLEN * 2 + 3 + 6 * 3];
368
369 buf[0] = '\0';
370
371 if (r->flags & NF_NAT_RANGE_MAP_IPS) {
372 if (brackets)
373 strcat(buf, "[");
374 inet_ntop(family, &r->min_addr,
375 buf + strlen(buf), INET6_ADDRSTRLEN);
376 if (memcmp(&r->min_addr, &r->max_addr, sizeof(r->min_addr))) {
377 strcat(buf, "-");
378 inet_ntop(family, &r->max_addr,
379 buf + strlen(buf), INET6_ADDRSTRLEN);
380 }
381 if (brackets)
382 strcat(buf, "]");
383 }
384 if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
385 sprintf(buf + strlen(buf), ":%hu",
386 ntohs(r->min_proto.tcp.port));
387 if (r->max_proto.tcp.port != r->min_proto.tcp.port)
388 sprintf(buf + strlen(buf), "-%hu",
389 ntohs(r->max_proto.tcp.port));
390 if (r->flags & NF_NAT_RANGE_PROTO_OFFSET)
391 sprintf(buf + strlen(buf), "/%hu",
392 ntohs(r->base_proto.tcp.port));
393 }
394 return buf;
395 }
396
__NAT_print(const struct nf_nat_range2 * r,int family,const char * rangeopt,const char * flag_pfx,bool skip_colon)397 static void __NAT_print(const struct nf_nat_range2 *r, int family,
398 const char *rangeopt, const char *flag_pfx,
399 bool skip_colon)
400 {
401 char *range_str = sprint_range(r, family);
402
403 if (strlen(range_str)) {
404 if (range_str[0] == ':' && skip_colon)
405 range_str++;
406 printf(" %s%s", rangeopt, range_str);
407 }
408 if (r->flags & NF_NAT_RANGE_PROTO_RANDOM)
409 printf(" %srandom", flag_pfx);
410 if (r->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY)
411 printf(" %srandom-fully", flag_pfx);
412 if (r->flags & NF_NAT_RANGE_PERSISTENT)
413 printf(" %spersistent", flag_pfx);
414 }
415
416 static int
__NAT_xlate(struct xt_xlate * xl,const struct nf_nat_range2 * r,int family,const char * tgt)417 __NAT_xlate(struct xt_xlate *xl, const struct nf_nat_range2 *r,
418 int family, const char *tgt)
419 {
420 char *range_str = sprint_range(r, family);
421 const char *sep = " ";
422
423 /* shifted portmap ranges are not supported by nftables */
424 if (r->flags & NF_NAT_RANGE_PROTO_OFFSET)
425 return 0;
426
427 xt_xlate_add(xl, "%s", tgt);
428 if (strlen(range_str))
429 xt_xlate_add(xl, " to %s", range_str);
430 if (r->flags & NF_NAT_RANGE_PROTO_RANDOM) {
431 xt_xlate_add(xl, "%srandom", sep);
432 sep = ",";
433 }
434 if (r->flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) {
435 xt_xlate_add(xl, "%sfully-random", sep);
436 sep = ",";
437 }
438 if (r->flags & NF_NAT_RANGE_PERSISTENT) {
439 xt_xlate_add(xl, "%spersistent", sep);
440 sep = ",";
441 }
442 return 1;
443 }
444
445 #define PSX_GEN(name, converter, family, \
446 print_rangeopt, save_rangeopt, skip_colon, xlate) \
447 static void name##_print(const void *ip, const struct xt_entry_target *target, \
448 int numeric) \
449 { \
450 struct nf_nat_range2 range = converter(target->data); \
451 \
452 __NAT_print(&range, family, print_rangeopt, "", skip_colon); \
453 } \
454 static void name##_save(const void *ip, const struct xt_entry_target *target) \
455 { \
456 struct nf_nat_range2 range = converter(target->data); \
457 \
458 __NAT_print(&range, family, save_rangeopt, "--", skip_colon); \
459 } \
460 static int name##_xlate(struct xt_xlate *xl, \
461 const struct xt_xlate_tg_params *params) \
462 { \
463 struct nf_nat_range2 range = converter(params->target->data); \
464 \
465 return __NAT_xlate(xl, &range, family, xlate); \
466 }
467
468 PSX_GEN(DNAT, RANGE2_INIT_FROM_IPV4_MRC, \
469 AF_INET, "to:", "--to-destination ", false, "dnat")
470
471 PSX_GEN(DNATv2, *(struct nf_nat_range2 *), \
472 AF_INET, "to:", "--to-destination ", false, "dnat")
473
474 PSX_GEN(DNAT6, RANGE2_INIT_FROM_RANGE, \
475 AF_INET6, "to:", "--to-destination ", false, "dnat")
476
477 PSX_GEN(DNAT6v2, *(struct nf_nat_range2 *), \
478 AF_INET6, "to:", "--to-destination ", false, "dnat")
479
480 PSX_GEN(REDIRECT, RANGE2_INIT_FROM_IPV4_MRC, \
481 AF_INET, "redir ports ", "--to-ports ", true, "redirect")
482
483 PSX_GEN(REDIRECT6, RANGE2_INIT_FROM_RANGE, \
484 AF_INET6, "redir ports ", "--to-ports ", true, "redirect")
485
486 PSX_GEN(SNAT, RANGE2_INIT_FROM_IPV4_MRC, \
487 AF_INET, "to:", "--to-source ", false, "snat")
488
489 PSX_GEN(SNAT6, RANGE2_INIT_FROM_RANGE, \
490 AF_INET6, "to:", "--to-source ", false, "snat")
491
492 PSX_GEN(MASQUERADE, RANGE2_INIT_FROM_IPV4_MRC, \
493 AF_INET, "masq ports: ", "--to-ports ", true, "masquerade")
494
495 PSX_GEN(MASQUERADE6, RANGE2_INIT_FROM_RANGE, \
496 AF_INET6, "masq ports: ", "--to-ports ", true, "masquerade")
497
498 static struct xtables_target nat_tg_reg[] = {
499 {
500 .name = "SNAT",
501 .version = XTABLES_VERSION,
502 .family = NFPROTO_IPV4,
503 .size = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
504 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
505 .help = SNAT_help,
506 .x6_parse = NAT_parse,
507 .x6_fcheck = SNAT_fcheck,
508 .print = SNAT_print,
509 .save = SNAT_save,
510 .x6_options = SNAT_opts,
511 .xlate = SNAT_xlate,
512 },
513 {
514 .name = "DNAT",
515 .version = XTABLES_VERSION,
516 .family = NFPROTO_IPV4,
517 .revision = 0,
518 .size = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
519 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
520 .help = DNAT_help,
521 .print = DNAT_print,
522 .save = DNAT_save,
523 .x6_parse = NAT_parse,
524 .x6_fcheck = DNAT_fcheck,
525 .x6_options = DNAT_opts,
526 .xlate = DNAT_xlate,
527 },
528 {
529 .name = "MASQUERADE",
530 .version = XTABLES_VERSION,
531 .family = NFPROTO_IPV4,
532 .size = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
533 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
534 .help = MASQUERADE_help,
535 .x6_parse = NAT_parse,
536 .x6_fcheck = SNAT_fcheck,
537 .print = MASQUERADE_print,
538 .save = MASQUERADE_save,
539 .x6_options = MASQUERADE_opts,
540 .xlate = MASQUERADE_xlate,
541 },
542 {
543 .name = "MASQUERADE",
544 .version = XTABLES_VERSION,
545 .family = NFPROTO_IPV6,
546 .size = XT_ALIGN(sizeof(struct nf_nat_range)),
547 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_range)),
548 .help = MASQUERADE_help,
549 .x6_parse = NAT_parse6,
550 .print = MASQUERADE6_print,
551 .save = MASQUERADE6_save,
552 .x6_options = MASQUERADE_opts,
553 .xlate = MASQUERADE6_xlate,
554 },
555 {
556 .name = "REDIRECT",
557 .version = XTABLES_VERSION,
558 .family = NFPROTO_IPV4,
559 .revision = 0,
560 .size = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
561 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
562 .help = REDIRECT_help,
563 .print = REDIRECT_print,
564 .save = REDIRECT_save,
565 .x6_parse = NAT_parse,
566 .x6_fcheck = DNAT_fcheck,
567 .x6_options = REDIRECT_opts,
568 .xlate = REDIRECT_xlate,
569 },
570 {
571 .name = "SNAT",
572 .version = XTABLES_VERSION,
573 .family = NFPROTO_IPV6,
574 .revision = 1,
575 .size = XT_ALIGN(sizeof(struct nf_nat_range)),
576 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_range)),
577 .help = SNAT_help,
578 .x6_parse = NAT_parse6,
579 .print = SNAT6_print,
580 .save = SNAT6_save,
581 .x6_options = SNAT_opts,
582 .xlate = SNAT6_xlate,
583 },
584 {
585 .name = "DNAT",
586 .version = XTABLES_VERSION,
587 .family = NFPROTO_IPV6,
588 .revision = 1,
589 .size = XT_ALIGN(sizeof(struct nf_nat_range)),
590 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_range)),
591 .help = DNAT_help,
592 .print = DNAT6_print,
593 .save = DNAT6_save,
594 .x6_parse = NAT_parse6,
595 .x6_fcheck = DNAT_fcheck6,
596 .x6_options = DNAT_opts,
597 .xlate = DNAT6_xlate,
598 },
599 {
600 .name = "REDIRECT",
601 .version = XTABLES_VERSION,
602 .family = NFPROTO_IPV6,
603 .size = XT_ALIGN(sizeof(struct nf_nat_range)),
604 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_range)),
605 .help = REDIRECT_help,
606 .print = REDIRECT6_print,
607 .save = REDIRECT6_save,
608 .x6_parse = NAT_parse6,
609 .x6_fcheck = DNAT_fcheck6,
610 .x6_options = REDIRECT_opts,
611 .xlate = REDIRECT6_xlate,
612 },
613 {
614 .name = "DNAT",
615 .version = XTABLES_VERSION,
616 .family = NFPROTO_IPV4,
617 .revision = 2,
618 .size = XT_ALIGN(sizeof(struct nf_nat_range2)),
619 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_range2)),
620 .help = DNAT_help_v2,
621 .print = DNATv2_print,
622 .save = DNATv2_save,
623 .x6_parse = DNAT_parse_v2,
624 .x6_options = DNAT_opts,
625 .xlate = DNATv2_xlate,
626 },
627 {
628 .name = "DNAT",
629 .version = XTABLES_VERSION,
630 .family = NFPROTO_IPV6,
631 .revision = 2,
632 .size = XT_ALIGN(sizeof(struct nf_nat_range2)),
633 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_range2)),
634 .help = DNAT_help_v2,
635 .print = DNAT6v2_print,
636 .save = DNAT6v2_save,
637 .x6_parse = DNAT_parse6_v2,
638 .x6_options = DNAT_opts,
639 .xlate = DNAT6v2_xlate,
640 },
641 };
642
_init(void)643 void _init(void)
644 {
645 xtables_register_targets(nat_tg_reg, ARRAY_SIZE(nat_tg_reg));
646 }
647