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