• 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 
37 static const struct xt_option_entry DNAT_opts[] = {
38 	{.name = "to-destination", .id = O_TO_DEST, .type = XTTYPE_STRING,
39 	 .flags = XTOPT_MAND | XTOPT_MULTI},
40 	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
41 	{.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
42 	XTOPT_TABLEEND,
43 };
44 
45 /* Ranges expected in network order. */
46 static void
parse_to(const char * orig_arg,int portok,struct nf_nat_range * range)47 parse_to(const char *orig_arg, int portok, struct nf_nat_range *range)
48 {
49 	char *arg, *start, *end = NULL, *colon = NULL, *dash, *error;
50 	const struct in6_addr *ip;
51 
52 	arg = strdup(orig_arg);
53 	if (arg == NULL)
54 		xtables_error(RESOURCE_PROBLEM, "strdup");
55 
56 	start = strchr(arg, '[');
57 	if (start == NULL) {
58 		start = arg;
59 		/* Lets assume one colon is port information. Otherwise its an IPv6 address */
60 		colon = strchr(arg, ':');
61 		if (colon && strchr(colon+1, ':'))
62 			colon = NULL;
63 	}
64 	else {
65 		start++;
66 		end = strchr(start, ']');
67 		if (end == NULL)
68 			xtables_error(PARAMETER_PROBLEM,
69 				      "Invalid address format");
70 
71 		*end = '\0';
72 		colon = strchr(end + 1, ':');
73 	}
74 
75 	if (colon) {
76 		int port;
77 
78 		if (!portok)
79 			xtables_error(PARAMETER_PROBLEM,
80 				   "Need TCP, UDP, SCTP or DCCP with port specification");
81 
82 		range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
83 
84 		port = atoi(colon+1);
85 		if (port <= 0 || port > 65535)
86 			xtables_error(PARAMETER_PROBLEM,
87 				   "Port `%s' not valid\n", colon+1);
88 
89 		error = strchr(colon+1, ':');
90 		if (error)
91 			xtables_error(PARAMETER_PROBLEM,
92 				   "Invalid port:port syntax - use dash\n");
93 
94 		dash = strchr(colon, '-');
95 		if (!dash) {
96 			range->min_proto.tcp.port
97 				= range->max_proto.tcp.port
98 				= htons(port);
99 		} else {
100 			int maxport;
101 
102 			maxport = atoi(dash + 1);
103 			if (maxport <= 0 || maxport > 65535)
104 				xtables_error(PARAMETER_PROBLEM,
105 					   "Port `%s' not valid\n", dash+1);
106 			if (maxport < port)
107 				/* People are stupid. */
108 				xtables_error(PARAMETER_PROBLEM,
109 					   "Port range `%s' funky\n", colon+1);
110 			range->min_proto.tcp.port = htons(port);
111 			range->max_proto.tcp.port = htons(maxport);
112 		}
113 		/* Starts with colon or [] colon? No IP info...*/
114 		if (colon == arg || colon == arg+2) {
115 			free(arg);
116 			return;
117 		}
118 		*colon = '\0';
119 	}
120 
121 	range->flags |= NF_NAT_RANGE_MAP_IPS;
122 	dash = strchr(start, '-');
123 	if (colon && dash && dash > colon)
124 		dash = NULL;
125 
126 	if (dash)
127 		*dash = '\0';
128 
129 	ip = xtables_numeric_to_ip6addr(start);
130 	if (!ip)
131 		xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
132 			      start);
133 	range->min_addr.in6 = *ip;
134 	if (dash) {
135 		ip = xtables_numeric_to_ip6addr(dash + 1);
136 		if (!ip)
137 			xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
138 				      dash+1);
139 		range->max_addr.in6 = *ip;
140 	} else
141 		range->max_addr = range->min_addr;
142 
143 	free(arg);
144 	return;
145 }
146 
DNAT_parse(struct xt_option_call * cb)147 static void DNAT_parse(struct xt_option_call *cb)
148 {
149 	const struct ip6t_entry *entry = cb->xt_entry;
150 	struct nf_nat_range *range = cb->data;
151 	int portok;
152 
153 	if (entry->ipv6.proto == IPPROTO_TCP ||
154 	    entry->ipv6.proto == IPPROTO_UDP ||
155 	    entry->ipv6.proto == IPPROTO_SCTP ||
156 	    entry->ipv6.proto == IPPROTO_DCCP ||
157 	    entry->ipv6.proto == IPPROTO_ICMP)
158 		portok = 1;
159 	else
160 		portok = 0;
161 
162 	xtables_option_parse(cb);
163 	switch (cb->entry->id) {
164 	case O_TO_DEST:
165 		if (cb->xflags & F_X_TO_DEST) {
166 			if (!kernel_version)
167 				get_kernel_version();
168 			if (kernel_version > LINUX_VERSION(2, 6, 10))
169 				xtables_error(PARAMETER_PROBLEM,
170 					   "DNAT: Multiple --to-destination not supported");
171 		}
172 		parse_to(cb->arg, portok, range);
173 		break;
174 	case O_PERSISTENT:
175 		range->flags |= NF_NAT_RANGE_PERSISTENT;
176 		break;
177 	}
178 }
179 
DNAT_fcheck(struct xt_fcheck_call * cb)180 static void DNAT_fcheck(struct xt_fcheck_call *cb)
181 {
182 	static const unsigned int f = F_TO_DEST | F_RANDOM;
183 	struct nf_nat_range *mr = cb->data;
184 
185 	if ((cb->xflags & f) == f)
186 		mr->flags |= NF_NAT_RANGE_PROTO_RANDOM;
187 }
188 
print_range(const struct nf_nat_range * range)189 static void print_range(const struct nf_nat_range *range)
190 {
191 	if (range->flags & NF_NAT_RANGE_MAP_IPS) {
192 		if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
193 			printf("[");
194 		printf("%s", xtables_ip6addr_to_numeric(&range->min_addr.in6));
195 		if (memcmp(&range->min_addr, &range->max_addr,
196 			   sizeof(range->min_addr)))
197 			printf("-%s", xtables_ip6addr_to_numeric(&range->max_addr.in6));
198 		if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
199 			printf("]");
200 	}
201 	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
202 		printf(":");
203 		printf("%hu", ntohs(range->min_proto.tcp.port));
204 		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
205 			printf("-%hu", ntohs(range->max_proto.tcp.port));
206 	}
207 }
208 
DNAT_print(const void * ip,const struct xt_entry_target * target,int numeric)209 static void DNAT_print(const void *ip, const struct xt_entry_target *target,
210                        int numeric)
211 {
212 	const struct nf_nat_range *range = (const void *)target->data;
213 
214 	printf(" to:");
215 	print_range(range);
216 	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
217 		printf(" random");
218 	if (range->flags & NF_NAT_RANGE_PERSISTENT)
219 		printf(" persistent");
220 }
221 
DNAT_save(const void * ip,const struct xt_entry_target * target)222 static void DNAT_save(const void *ip, const struct xt_entry_target *target)
223 {
224 	const struct nf_nat_range *range = (const void *)target->data;
225 
226 	printf(" --to-destination ");
227 	print_range(range);
228 	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
229 		printf(" --random");
230 	if (range->flags & NF_NAT_RANGE_PERSISTENT)
231 		printf(" --persistent");
232 }
233 
print_range_xlate(const struct nf_nat_range * range,struct xt_xlate * xl)234 static void print_range_xlate(const struct nf_nat_range *range,
235 			      struct xt_xlate *xl)
236 {
237 	bool proto_specified = range->flags & NF_NAT_RANGE_PROTO_SPECIFIED;
238 
239 	if (range->flags & NF_NAT_RANGE_MAP_IPS) {
240 		xt_xlate_add(xl, "%s%s%s",
241 			     proto_specified ? "[" : "",
242 			     xtables_ip6addr_to_numeric(&range->min_addr.in6),
243 			     proto_specified ? "]" : "");
244 
245 		if (memcmp(&range->min_addr, &range->max_addr,
246 			   sizeof(range->min_addr))) {
247 			xt_xlate_add(xl, "-%s%s%s",
248 				     proto_specified ? "[" : "",
249 				     xtables_ip6addr_to_numeric(&range->max_addr.in6),
250 				     proto_specified ? "]" : "");
251 		}
252 	}
253 	if (proto_specified) {
254 		xt_xlate_add(xl, ":%hu", ntohs(range->min_proto.tcp.port));
255 
256 		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
257 			xt_xlate_add(xl, "-%hu",
258 				   ntohs(range->max_proto.tcp.port));
259 	}
260 }
261 
DNAT_xlate(struct xt_xlate * xl,const struct xt_xlate_tg_params * params)262 static int DNAT_xlate(struct xt_xlate *xl,
263 		      const struct xt_xlate_tg_params *params)
264 {
265 	const struct nf_nat_range *range = (const void *)params->target->data;
266 	bool sep_need = false;
267 	const char *sep = " ";
268 
269 	xt_xlate_add(xl, "dnat to ");
270 	print_range_xlate(range, xl);
271 	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM) {
272 		xt_xlate_add(xl, " random");
273 		sep_need = true;
274 	}
275 	if (range->flags & NF_NAT_RANGE_PERSISTENT) {
276 		if (sep_need)
277 			sep = ",";
278 		xt_xlate_add(xl, "%spersistent", sep);
279 	}
280 
281 	return 1;
282 }
283 
284 static struct xtables_target snat_tg_reg = {
285 	.name		= "DNAT",
286 	.version	= XTABLES_VERSION,
287 	.family		= NFPROTO_IPV6,
288 	.revision	= 1,
289 	.size		= XT_ALIGN(sizeof(struct nf_nat_range)),
290 	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_range)),
291 	.help		= DNAT_help,
292 	.x6_parse	= DNAT_parse,
293 	.x6_fcheck	= DNAT_fcheck,
294 	.print		= DNAT_print,
295 	.save		= DNAT_save,
296 	.x6_options	= DNAT_opts,
297 	.xlate		= DNAT_xlate,
298 };
299 
_init(void)300 void _init(void)
301 {
302 	xtables_register_target(&snat_tg_reg);
303 }
304