• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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