libip6t_SNAT.c revision 3672111649732be657cb7566178b7d2618ba6ec5
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
18enum {
19	O_TO_SRC = 0,
20	O_RANDOM,
21	O_PERSISTENT,
22	O_X_TO_SRC,
23	F_TO_SRC   = 1 << O_TO_SRC,
24	F_RANDOM   = 1 << O_RANDOM,
25	F_X_TO_SRC = 1 << O_X_TO_SRC,
26};
27
28static void SNAT_help(void)
29{
30	printf(
31"SNAT target options:\n"
32" --to-source [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
33"				Address to map source to.\n"
34"[--random] [--persistent]\n");
35}
36
37static const struct xt_option_entry SNAT_opts[] = {
38	{.name = "to-source", .id = O_TO_SRC, .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. */
46static void
47parse_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	else {
60		start++;
61		end = strchr(start, ']');
62		if (end == NULL)
63			xtables_error(PARAMETER_PROBLEM,
64				      "Invalid address format");
65
66		*end = '\0';
67		colon = strchr(end + 1, ':');
68	}
69
70	if (colon) {
71		int port;
72
73		if (!portok)
74			xtables_error(PARAMETER_PROBLEM,
75				   "Need TCP, UDP, SCTP or DCCP with port specification");
76
77		range->flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
78
79		port = atoi(colon+1);
80		if (port <= 0 || port > 65535)
81			xtables_error(PARAMETER_PROBLEM,
82				   "Port `%s' not valid\n", colon+1);
83
84		error = strchr(colon+1, ':');
85		if (error)
86			xtables_error(PARAMETER_PROBLEM,
87				   "Invalid port:port syntax - use dash\n");
88
89		dash = strchr(colon, '-');
90		if (!dash) {
91			range->min_proto.tcp.port
92				= range->max_proto.tcp.port
93				= htons(port);
94		} else {
95			int maxport;
96
97			maxport = atoi(dash + 1);
98			if (maxport <= 0 || maxport > 65535)
99				xtables_error(PARAMETER_PROBLEM,
100					   "Port `%s' not valid\n", dash+1);
101			if (maxport < port)
102				/* People are stupid. */
103				xtables_error(PARAMETER_PROBLEM,
104					   "Port range `%s' funky\n", colon+1);
105			range->min_proto.tcp.port = htons(port);
106			range->max_proto.tcp.port = htons(maxport);
107		}
108		/* Starts with a colon? No IP info...*/
109		if (colon == arg) {
110			free(arg);
111			return;
112		}
113		*colon = '\0';
114	}
115
116	range->flags |= NF_NAT_RANGE_MAP_IPS;
117	dash = strchr(start, '-');
118	if (colon && dash && dash > colon)
119		dash = NULL;
120
121	if (dash)
122		*dash = '\0';
123
124	ip = xtables_numeric_to_ip6addr(start);
125	if (!ip)
126		xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
127			      start);
128	range->min_addr.in6 = *ip;
129	if (dash) {
130		ip = xtables_numeric_to_ip6addr(dash + 1);
131		if (!ip)
132			xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
133				      dash+1);
134		range->max_addr.in6 = *ip;
135	} else
136		range->max_addr = range->min_addr;
137
138	free(arg);
139	return;
140}
141
142static void SNAT_parse(struct xt_option_call *cb)
143{
144	const struct ip6t_entry *entry = cb->xt_entry;
145	struct nf_nat_range *range = cb->data;
146	int portok;
147
148	if (entry->ipv6.proto == IPPROTO_TCP ||
149	    entry->ipv6.proto == IPPROTO_UDP ||
150	    entry->ipv6.proto == IPPROTO_SCTP ||
151	    entry->ipv6.proto == IPPROTO_DCCP ||
152	    entry->ipv6.proto == IPPROTO_ICMP)
153		portok = 1;
154	else
155		portok = 0;
156
157	xtables_option_parse(cb);
158	switch (cb->entry->id) {
159	case O_TO_SRC:
160		if (cb->xflags & F_X_TO_SRC) {
161			if (!kernel_version)
162				get_kernel_version();
163			if (kernel_version > LINUX_VERSION(2, 6, 10))
164				xtables_error(PARAMETER_PROBLEM,
165					   "SNAT: Multiple --to-source not supported");
166		}
167		parse_to(cb->arg, portok, range);
168		break;
169	case O_PERSISTENT:
170		range->flags |= NF_NAT_RANGE_PERSISTENT;
171		break;
172	}
173}
174
175static void SNAT_fcheck(struct xt_fcheck_call *cb)
176{
177	static const unsigned int f = F_TO_SRC | F_RANDOM;
178	struct nf_nat_range *range = cb->data;
179
180	if ((cb->xflags & f) == f)
181		range->flags |= NF_NAT_RANGE_PROTO_RANDOM;
182}
183
184static void print_range(const struct nf_nat_range *range)
185{
186	if (range->flags & NF_NAT_RANGE_MAP_IPS) {
187		if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
188			printf("[");
189		printf("%s", xtables_ip6addr_to_numeric(&range->min_addr.in6));
190		if (memcmp(&range->min_addr, &range->max_addr,
191			   sizeof(range->min_addr)))
192			printf("-%s", xtables_ip6addr_to_numeric(&range->max_addr.in6));
193		if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED)
194			printf("]");
195	}
196	if (range->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
197		printf(":");
198		printf("%hu", ntohs(range->min_proto.tcp.port));
199		if (range->max_proto.tcp.port != range->min_proto.tcp.port)
200			printf("-%hu", ntohs(range->max_proto.tcp.port));
201	}
202}
203
204static void SNAT_print(const void *ip, const struct xt_entry_target *target,
205                       int numeric)
206{
207	const struct nf_nat_range *range = (const void *)target->data;
208
209	printf(" to:");
210	print_range(range);
211	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
212		printf(" random");
213	if (range->flags & NF_NAT_RANGE_PERSISTENT)
214		printf(" persistent");
215}
216
217static void SNAT_save(const void *ip, const struct xt_entry_target *target)
218{
219	const struct nf_nat_range *range = (const void *)target->data;
220
221	printf(" --to-source ");
222	print_range(range);
223	if (range->flags & NF_NAT_RANGE_PROTO_RANDOM)
224		printf(" --random");
225	if (range->flags & NF_NAT_RANGE_PERSISTENT)
226		printf(" --persistent");
227}
228
229static struct xtables_target snat_tg_reg = {
230	.name		= "SNAT",
231	.version	= XTABLES_VERSION,
232	.family		= NFPROTO_IPV6,
233	.revision	= 1,
234	.size		= XT_ALIGN(sizeof(struct nf_nat_range)),
235	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_range)),
236	.help		= SNAT_help,
237	.x6_parse	= SNAT_parse,
238	.x6_fcheck	= SNAT_fcheck,
239	.print		= SNAT_print,
240	.save		= SNAT_save,
241	.x6_options	= SNAT_opts,
242};
243
244void _init(void)
245{
246	xtables_register_target(&snat_tg_reg);
247}
248