1 #include <stdio.h>
2 #include <netdb.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <xtables.h>
6 #include <iptables.h> /* get_kernel_version */
7 #include <limits.h> /* INT_MAX in ip_tables.h */
8 #include <linux/netfilter_ipv4/ip_tables.h>
9 #include <linux/netfilter/nf_nat.h>
10
11 enum {
12 O_TO_DEST = 0,
13 O_RANDOM,
14 O_PERSISTENT,
15 O_X_TO_DEST, /* hidden flag */
16 F_TO_DEST = 1 << O_TO_DEST,
17 F_RANDOM = 1 << O_RANDOM,
18 F_X_TO_DEST = 1 << O_X_TO_DEST,
19 };
20
21 /* Dest NAT data consists of a multi-range, indicating where to map
22 to. */
23 struct ipt_natinfo
24 {
25 struct xt_entry_target t;
26 struct nf_nat_ipv4_multi_range_compat mr;
27 };
28
DNAT_help(void)29 static void DNAT_help(void)
30 {
31 printf(
32 "DNAT target options:\n"
33 " --to-destination [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
34 " Address to map destination to.\n"
35 "[--random] [--persistent]\n");
36 }
37
DNAT_help_v2(void)38 static void DNAT_help_v2(void)
39 {
40 printf(
41 "DNAT target options:\n"
42 " --to-destination [<ipaddr>[-<ipaddr>]][:port[-port[/port]]]\n"
43 " Address to map destination to.\n"
44 "[--random] [--persistent]\n");
45 }
46
47 static const struct xt_option_entry DNAT_opts[] = {
48 {.name = "to-destination", .id = O_TO_DEST, .type = XTTYPE_STRING,
49 .flags = XTOPT_MAND | XTOPT_MULTI},
50 {.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
51 {.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
52 XTOPT_TABLEEND,
53 };
54
55 static struct ipt_natinfo *
append_range(struct ipt_natinfo * info,const struct nf_nat_ipv4_range * range)56 append_range(struct ipt_natinfo *info, const struct nf_nat_ipv4_range *range)
57 {
58 unsigned int size;
59
60 /* One rangesize already in struct ipt_natinfo */
61 size = XT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
62
63 info = realloc(info, size);
64 if (!info)
65 xtables_error(OTHER_PROBLEM, "Out of memory\n");
66
67 info->t.u.target_size = size;
68 info->mr.range[info->mr.rangesize] = *range;
69 info->mr.rangesize++;
70
71 return info;
72 }
73
74 /* Ranges expected in network order. */
75 static struct xt_entry_target *
parse_to(const char * orig_arg,int portok,struct ipt_natinfo * info)76 parse_to(const char *orig_arg, int portok, struct ipt_natinfo *info)
77 {
78 struct nf_nat_ipv4_range range;
79 char *arg, *colon, *dash, *error;
80 const struct in_addr *ip;
81
82 arg = strdup(orig_arg);
83 if (arg == NULL)
84 xtables_error(RESOURCE_PROBLEM, "strdup");
85 memset(&range, 0, sizeof(range));
86 colon = strchr(arg, ':');
87
88 if (colon) {
89 int port;
90
91 if (!portok)
92 xtables_error(PARAMETER_PROBLEM,
93 "Need TCP, UDP, SCTP or DCCP with port specification");
94
95 range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
96
97 port = atoi(colon+1);
98 if (port <= 0 || port > 65535)
99 xtables_error(PARAMETER_PROBLEM,
100 "Port `%s' not valid\n", colon+1);
101
102 error = strchr(colon+1, ':');
103 if (error)
104 xtables_error(PARAMETER_PROBLEM,
105 "Invalid port:port syntax - use dash\n");
106
107 dash = strchr(colon, '-');
108 if (!dash) {
109 range.min.tcp.port
110 = range.max.tcp.port
111 = htons(port);
112 } else {
113 int maxport;
114
115 maxport = atoi(dash + 1);
116 if (maxport <= 0 || maxport > 65535)
117 xtables_error(PARAMETER_PROBLEM,
118 "Port `%s' not valid\n", dash+1);
119 if (maxport < port)
120 /* People are stupid. */
121 xtables_error(PARAMETER_PROBLEM,
122 "Port range `%s' funky\n", colon+1);
123 range.min.tcp.port = htons(port);
124 range.max.tcp.port = htons(maxport);
125 }
126 /* Starts with a colon? No IP info...*/
127 if (colon == arg) {
128 free(arg);
129 return &(append_range(info, &range)->t);
130 }
131 *colon = '\0';
132 }
133
134 range.flags |= NF_NAT_RANGE_MAP_IPS;
135 dash = strchr(arg, '-');
136 if (colon && dash && dash > colon)
137 dash = NULL;
138
139 if (dash)
140 *dash = '\0';
141
142 ip = xtables_numeric_to_ipaddr(arg);
143 if (!ip)
144 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
145 arg);
146 range.min_ip = ip->s_addr;
147 if (dash) {
148 ip = xtables_numeric_to_ipaddr(dash+1);
149 if (!ip)
150 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
151 dash+1);
152 range.max_ip = ip->s_addr;
153 } else
154 range.max_ip = range.min_ip;
155
156 free(arg);
157 return &(append_range(info, &range)->t);
158 }
159
DNAT_parse(struct xt_option_call * cb)160 static void DNAT_parse(struct xt_option_call *cb)
161 {
162 const struct ipt_entry *entry = cb->xt_entry;
163 struct ipt_natinfo *info = (void *)(*cb->target);
164 int portok;
165
166 if (entry->ip.proto == IPPROTO_TCP
167 || entry->ip.proto == IPPROTO_UDP
168 || entry->ip.proto == IPPROTO_SCTP
169 || entry->ip.proto == IPPROTO_DCCP
170 || entry->ip.proto == IPPROTO_ICMP)
171 portok = 1;
172 else
173 portok = 0;
174
175 xtables_option_parse(cb);
176 switch (cb->entry->id) {
177 case O_TO_DEST:
178 if (cb->xflags & F_X_TO_DEST) {
179 if (!kernel_version)
180 get_kernel_version();
181 if (kernel_version > LINUX_VERSION(2, 6, 10))
182 xtables_error(PARAMETER_PROBLEM,
183 "DNAT: Multiple --to-destination not supported");
184 }
185 *cb->target = parse_to(cb->arg, portok, info);
186 cb->xflags |= F_X_TO_DEST;
187 break;
188 case O_PERSISTENT:
189 info->mr.range[0].flags |= NF_NAT_RANGE_PERSISTENT;
190 break;
191 }
192 }
193
DNAT_fcheck(struct xt_fcheck_call * cb)194 static void DNAT_fcheck(struct xt_fcheck_call *cb)
195 {
196 static const unsigned int f = F_TO_DEST | F_RANDOM;
197 struct nf_nat_ipv4_multi_range_compat *mr = cb->data;
198
199 if ((cb->xflags & f) == f)
200 mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM;
201 }
202
print_range(const struct nf_nat_ipv4_range * r)203 static void print_range(const struct nf_nat_ipv4_range *r)
204 {
205 if (r->flags & NF_NAT_RANGE_MAP_IPS) {
206 struct in_addr a;
207
208 a.s_addr = r->min_ip;
209 printf("%s", xtables_ipaddr_to_numeric(&a));
210 if (r->max_ip != r->min_ip) {
211 a.s_addr = r->max_ip;
212 printf("-%s", xtables_ipaddr_to_numeric(&a));
213 }
214 }
215 if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
216 printf(":");
217 printf("%hu", ntohs(r->min.tcp.port));
218 if (r->max.tcp.port != r->min.tcp.port)
219 printf("-%hu", ntohs(r->max.tcp.port));
220 }
221 }
222
DNAT_print(const void * ip,const struct xt_entry_target * target,int numeric)223 static void DNAT_print(const void *ip, const struct xt_entry_target *target,
224 int numeric)
225 {
226 const struct ipt_natinfo *info = (const void *)target;
227 unsigned int i = 0;
228
229 printf(" to:");
230 for (i = 0; i < info->mr.rangesize; i++) {
231 print_range(&info->mr.range[i]);
232 if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM)
233 printf(" random");
234 if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT)
235 printf(" persistent");
236 }
237 }
238
DNAT_save(const void * ip,const struct xt_entry_target * target)239 static void DNAT_save(const void *ip, const struct xt_entry_target *target)
240 {
241 const struct ipt_natinfo *info = (const void *)target;
242 unsigned int i = 0;
243
244 for (i = 0; i < info->mr.rangesize; i++) {
245 printf(" --to-destination ");
246 print_range(&info->mr.range[i]);
247 if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM)
248 printf(" --random");
249 if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT)
250 printf(" --persistent");
251 }
252 }
253
print_range_xlate(const struct nf_nat_ipv4_range * r,struct xt_xlate * xl)254 static void print_range_xlate(const struct nf_nat_ipv4_range *r,
255 struct xt_xlate *xl)
256 {
257 if (r->flags & NF_NAT_RANGE_MAP_IPS) {
258 struct in_addr a;
259
260 a.s_addr = r->min_ip;
261 xt_xlate_add(xl, "%s", xtables_ipaddr_to_numeric(&a));
262 if (r->max_ip != r->min_ip) {
263 a.s_addr = r->max_ip;
264 xt_xlate_add(xl, "-%s", xtables_ipaddr_to_numeric(&a));
265 }
266 }
267 if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
268 xt_xlate_add(xl, ":%hu", ntohs(r->min.tcp.port));
269 if (r->max.tcp.port != r->min.tcp.port)
270 xt_xlate_add(xl, "-%hu", ntohs(r->max.tcp.port));
271 }
272 }
273
DNAT_xlate(struct xt_xlate * xl,const struct xt_xlate_tg_params * params)274 static int DNAT_xlate(struct xt_xlate *xl,
275 const struct xt_xlate_tg_params *params)
276 {
277 const struct ipt_natinfo *info = (const void *)params->target;
278 unsigned int i = 0;
279 bool sep_need = false;
280 const char *sep = " ";
281
282 for (i = 0; i < info->mr.rangesize; i++) {
283 xt_xlate_add(xl, "dnat to ");
284 print_range_xlate(&info->mr.range[i], xl);
285 if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM) {
286 xt_xlate_add(xl, " random");
287 sep_need = true;
288 }
289 if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT) {
290 if (sep_need)
291 sep = ",";
292 xt_xlate_add(xl, "%spersistent", sep);
293 }
294 }
295
296 return 1;
297 }
298
299 static void
parse_to_v2(const char * orig_arg,int portok,struct nf_nat_range2 * range)300 parse_to_v2(const char *orig_arg, int portok, struct nf_nat_range2 *range)
301 {
302 char *arg, *colon, *dash, *error;
303 const struct in_addr *ip;
304
305 arg = strdup(orig_arg);
306 if (arg == NULL)
307 xtables_error(RESOURCE_PROBLEM, "strdup");
308
309 colon = strchr(arg, ':');
310 if (colon) {
311 int port;
312
313 if (!portok)
314 xtables_error(PARAMETER_PROBLEM,
315 "Need TCP, UDP, SCTP or DCCP with port specification");
316
317 range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
318
319 port = atoi(colon+1);
320 if (port <= 0 || port > 65535)
321 xtables_error(PARAMETER_PROBLEM,
322 "Port `%s' not valid\n", colon+1);
323
324 error = strchr(colon+1, ':');
325 if (error)
326 xtables_error(PARAMETER_PROBLEM,
327 "Invalid port:port syntax - use dash\n");
328
329 dash = strchr(colon, '-');
330 if (!dash) {
331 range->min_proto.tcp.port
332 = range->max_proto.tcp.port
333 = htons(port);
334 } else {
335 int maxport;
336 char *slash;
337
338 maxport = atoi(dash + 1);
339 if (maxport <= 0 || maxport > 65535)
340 xtables_error(PARAMETER_PROBLEM,
341 "Port `%s' not valid\n", dash+1);
342 if (maxport < port)
343 /* People are stupid. */
344 xtables_error(PARAMETER_PROBLEM,
345 "Port range `%s' funky\n", colon+1);
346 range->min_proto.tcp.port = htons(port);
347 range->max_proto.tcp.port = htons(maxport);
348
349 slash = strchr(dash, '/');
350 if (slash) {
351 int baseport;
352
353 baseport = atoi(slash + 1);
354 if (baseport <= 0 || baseport > 65535)
355 xtables_error(PARAMETER_PROBLEM,
356 "Port `%s' not valid\n", slash+1);
357 range->flags |= NF_NAT_RANGE_PROTO_OFFSET;
358 range->base_proto.tcp.port = htons(baseport);
359 }
360 }
361 /* Starts with a colon? No IP info...*/
362 if (colon == arg) {
363 free(arg);
364 return;
365 }
366 *colon = '\0';
367 }
368
369 range->flags |= NF_NAT_RANGE_MAP_IPS;
370 dash = strchr(arg, '-');
371 if (colon && dash && dash > colon)
372 dash = NULL;
373
374 if (dash)
375 *dash = '\0';
376
377 ip = xtables_numeric_to_ipaddr(arg);
378 if (!ip)
379 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
380 arg);
381 range->min_addr.in = *ip;
382 if (dash) {
383 ip = xtables_numeric_to_ipaddr(dash+1);
384 if (!ip)
385 xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
386 dash+1);
387 range->max_addr.in = *ip;
388 } else
389 range->max_addr = range->min_addr;
390
391 free(arg);
392 return;
393 }
394
DNAT_parse_v2(struct xt_option_call * cb)395 static void DNAT_parse_v2(struct xt_option_call *cb)
396 {
397 const struct ipt_entry *entry = cb->xt_entry;
398 struct nf_nat_range2 *range = cb->data;
399 int portok;
400
401 if (entry->ip.proto == IPPROTO_TCP
402 || entry->ip.proto == IPPROTO_UDP
403 || entry->ip.proto == IPPROTO_SCTP
404 || entry->ip.proto == IPPROTO_DCCP
405 || entry->ip.proto == IPPROTO_ICMP)
406 portok = 1;
407 else
408 portok = 0;
409
410 xtables_option_parse(cb);
411 switch (cb->entry->id) {
412 case O_TO_DEST:
413 if (cb->xflags & F_X_TO_DEST) {
414 xtables_error(PARAMETER_PROBLEM,
415 "DNAT: Multiple --to-destination not supported");
416 }
417 parse_to_v2(cb->arg, portok, range);
418 cb->xflags |= F_X_TO_DEST;
419 break;
420 case O_PERSISTENT:
421 range->flags |= NF_NAT_RANGE_PERSISTENT;
422 break;
423 }
424 }
425
DNAT_fcheck_v2(struct xt_fcheck_call * cb)426 static void DNAT_fcheck_v2(struct xt_fcheck_call *cb)
427 {
428 static const unsigned int f = F_TO_DEST | F_RANDOM;
429 struct nf_nat_range2 *range = cb->data;
430
431 if ((cb->xflags & f) == f)
432 range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
433 }
434
print_range_v2(const struct nf_nat_range2 * range)435 static void print_range_v2(const struct nf_nat_range2 *range)
436 {
437 if (range->flags & NF_NAT_RANGE_MAP_IPS) {
438 printf("%s", xtables_ipaddr_to_numeric(&range->min_addr.in));
439 if (memcmp(&range->min_addr, &range->max_addr,
440 sizeof(range->min_addr)))
441 printf("-%s", xtables_ipaddr_to_numeric(&range->max_addr.in));
442 }
443 if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
444 printf(":");
445 printf("%hu", ntohs(range->min_proto.tcp.port));
446 if (range->max_proto.tcp.port != range->min_proto.tcp.port)
447 printf("-%hu", ntohs(range->max_proto.tcp.port));
448 if (range->flags & NF_NAT_RANGE_PROTO_OFFSET)
449 printf("/%hu", ntohs(range->base_proto.tcp.port));
450 }
451 }
452
DNAT_print_v2(const void * ip,const struct xt_entry_target * target,int numeric)453 static void DNAT_print_v2(const void *ip, const struct xt_entry_target *target,
454 int numeric)
455 {
456 const struct nf_nat_range2 *range = (const void *)target->data;
457
458 printf(" to:");
459 print_range_v2(range);
460 if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
461 printf(" random");
462 if (range->flags & NF_NAT_RANGE_PERSISTENT)
463 printf(" persistent");
464 }
465
DNAT_save_v2(const void * ip,const struct xt_entry_target * target)466 static void DNAT_save_v2(const void *ip, const struct xt_entry_target *target)
467 {
468 const struct nf_nat_range2 *range = (const void *)target->data;
469
470 printf(" --to-destination ");
471 print_range_v2(range);
472 if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
473 printf(" --random");
474 if (range->flags & NF_NAT_RANGE_PERSISTENT)
475 printf(" --persistent");
476 }
477
print_range_xlate_v2(const struct nf_nat_range2 * range,struct xt_xlate * xl)478 static void print_range_xlate_v2(const struct nf_nat_range2 *range,
479 struct xt_xlate *xl)
480 {
481 if (range->flags & NF_NAT_RANGE_MAP_IPS) {
482 xt_xlate_add(xl, "%s", xtables_ipaddr_to_numeric(&range->min_addr.in));
483 if (memcmp(&range->min_addr, &range->max_addr,
484 sizeof(range->min_addr))) {
485 xt_xlate_add(xl, "-%s", xtables_ipaddr_to_numeric(&range->max_addr.in));
486 }
487 }
488 if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
489 xt_xlate_add(xl, ":%hu", ntohs(range->min_proto.tcp.port));
490 if (range->max_proto.tcp.port != range->min_proto.tcp.port)
491 xt_xlate_add(xl, "-%hu", ntohs(range->max_proto.tcp.port));
492 if (range->flags & NF_NAT_RANGE_PROTO_OFFSET)
493 xt_xlate_add(xl, ";%hu", ntohs(range->base_proto.tcp.port));
494 }
495 }
496
DNAT_xlate_v2(struct xt_xlate * xl,const struct xt_xlate_tg_params * params)497 static int DNAT_xlate_v2(struct xt_xlate *xl,
498 const struct xt_xlate_tg_params *params)
499 {
500 const struct nf_nat_range2 *range = (const void *)params->target->data;
501 bool sep_need = false;
502 const char *sep = " ";
503
504 xt_xlate_add(xl, "dnat to ");
505 print_range_xlate_v2(range, xl);
506 if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) {
507 xt_xlate_add(xl, " random");
508 sep_need = true;
509 }
510 if (range->flags & NF_NAT_RANGE_PERSISTENT) {
511 if (sep_need)
512 sep = ",";
513 xt_xlate_add(xl, "%spersistent", sep);
514 }
515
516 return 1;
517 }
518
519 static struct xtables_target dnat_tg_reg[] = {
520 {
521 .name = "DNAT",
522 .version = XTABLES_VERSION,
523 .family = NFPROTO_IPV4,
524 .revision = 0,
525 .size = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
526 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
527 .help = DNAT_help,
528 .print = DNAT_print,
529 .save = DNAT_save,
530 .x6_parse = DNAT_parse,
531 .x6_fcheck = DNAT_fcheck,
532 .x6_options = DNAT_opts,
533 .xlate = DNAT_xlate,
534 },
535 {
536 .name = "DNAT",
537 .version = XTABLES_VERSION,
538 .family = NFPROTO_IPV4,
539 .revision = 2,
540 .size = XT_ALIGN(sizeof(struct nf_nat_range2)),
541 .userspacesize = XT_ALIGN(sizeof(struct nf_nat_range2)),
542 .help = DNAT_help_v2,
543 .print = DNAT_print_v2,
544 .save = DNAT_save_v2,
545 .x6_parse = DNAT_parse_v2,
546 .x6_fcheck = DNAT_fcheck_v2,
547 .x6_options = DNAT_opts,
548 .xlate = DNAT_xlate_v2,
549 },
550 };
551
_init(void)552 void _init(void)
553 {
554 xtables_register_targets(dnat_tg_reg, ARRAY_SIZE(dnat_tg_reg));
555 }
556