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