libipt_SNAT.c revision e37d45ce390c2f5a7f1e64742b9100ecef0def54
1#include <stdio.h>
2#include <netdb.h>
3#include <string.h>
4#include <stdlib.h>
5#include <xtables.h>
6#include <iptables.h>
7#include <limits.h> /* INT_MAX in ip_tables.h */
8#include <linux/netfilter_ipv4/ip_tables.h>
9#include <net/netfilter/nf_nat.h>
10
11enum {
12	O_TO_SRC = 0,
13	O_RANDOM,
14	O_PERSISTENT,
15	O_X_TO_SRC,
16	F_TO_SRC   = 1 << O_TO_SRC,
17	F_RANDOM   = 1 << O_RANDOM,
18	F_X_TO_SRC = 1 << O_X_TO_SRC,
19};
20
21/* Source NAT data consists of a multi-range, indicating where to map
22   to. */
23struct ipt_natinfo
24{
25	struct xt_entry_target t;
26	struct nf_nat_multi_range mr;
27};
28
29static void SNAT_help(void)
30{
31	printf(
32"SNAT target options:\n"
33" --to-source [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
34"				Address to map source to.\n"
35"[--random] [--persistent]\n");
36}
37
38static const struct xt_option_entry SNAT_opts[] = {
39	{.name = "to-source", .id = O_TO_SRC, .type = XTTYPE_STRING,
40	 .flags = XTOPT_MAND | XTOPT_MULTI},
41	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
42	{.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
43	XTOPT_TABLEEND,
44};
45
46static struct ipt_natinfo *
47append_range(struct ipt_natinfo *info, const struct nf_nat_range *range)
48{
49	unsigned int size;
50
51	/* One rangesize already in struct ipt_natinfo */
52	size = XT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
53
54	info = realloc(info, size);
55	if (!info)
56		xtables_error(OTHER_PROBLEM, "Out of memory\n");
57
58	info->t.u.target_size = size;
59	info->mr.range[info->mr.rangesize] = *range;
60	info->mr.rangesize++;
61
62	return info;
63}
64
65/* Ranges expected in network order. */
66static struct xt_entry_target *
67parse_to(const char *orig_arg, int portok, struct ipt_natinfo *info)
68{
69	struct nf_nat_range range;
70	char *arg, *colon, *dash, *error;
71	const struct in_addr *ip;
72
73	arg = strdup(orig_arg);
74	if (arg == NULL)
75		xtables_error(RESOURCE_PROBLEM, "strdup");
76	memset(&range, 0, sizeof(range));
77	colon = strchr(arg, ':');
78
79	if (colon) {
80		int port;
81
82		if (!portok)
83			xtables_error(PARAMETER_PROBLEM,
84				   "Need TCP, UDP, SCTP or DCCP with port specification");
85
86		range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
87
88		port = atoi(colon+1);
89		if (port <= 0 || port > 65535)
90			xtables_error(PARAMETER_PROBLEM,
91				   "Port `%s' not valid\n", colon+1);
92
93		error = strchr(colon+1, ':');
94		if (error)
95			xtables_error(PARAMETER_PROBLEM,
96				   "Invalid port:port syntax - use dash\n");
97
98		dash = strchr(colon, '-');
99		if (!dash) {
100			range.min.tcp.port
101				= range.max.tcp.port
102				= htons(port);
103		} else {
104			int maxport;
105
106			maxport = atoi(dash + 1);
107			if (maxport <= 0 || maxport > 65535)
108				xtables_error(PARAMETER_PROBLEM,
109					   "Port `%s' not valid\n", dash+1);
110			if (maxport < port)
111				/* People are stupid. */
112				xtables_error(PARAMETER_PROBLEM,
113					   "Port range `%s' funky\n", colon+1);
114			range.min.tcp.port = htons(port);
115			range.max.tcp.port = htons(maxport);
116		}
117		/* Starts with a colon? No IP info...*/
118		if (colon == arg) {
119			free(arg);
120			return &(append_range(info, &range)->t);
121		}
122		*colon = '\0';
123	}
124
125	range.flags |= IP_NAT_RANGE_MAP_IPS;
126	dash = strchr(arg, '-');
127	if (colon && dash && dash > colon)
128		dash = NULL;
129
130	if (dash)
131		*dash = '\0';
132
133	ip = xtables_numeric_to_ipaddr(arg);
134	if (!ip)
135		xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
136			   arg);
137	range.min_ip = ip->s_addr;
138	if (dash) {
139		ip = xtables_numeric_to_ipaddr(dash+1);
140		if (!ip)
141			xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
142				   dash+1);
143		range.max_ip = ip->s_addr;
144	} else
145		range.max_ip = range.min_ip;
146
147	free(arg);
148	return &(append_range(info, &range)->t);
149}
150
151static void SNAT_parse(struct xt_option_call *cb)
152{
153	const struct ipt_entry *entry = cb->xt_entry;
154	struct ipt_natinfo *info = (void *)(*cb->target);
155	int portok;
156
157	if (entry->ip.proto == IPPROTO_TCP
158	    || entry->ip.proto == IPPROTO_UDP
159	    || entry->ip.proto == IPPROTO_SCTP
160	    || entry->ip.proto == IPPROTO_DCCP
161	    || entry->ip.proto == IPPROTO_ICMP)
162		portok = 1;
163	else
164		portok = 0;
165
166	xtables_option_parse(cb);
167	switch (cb->entry->id) {
168	case O_TO_SRC:
169		if (cb->xflags & F_X_TO_SRC) {
170			if (!kernel_version)
171				get_kernel_version();
172			if (kernel_version > LINUX_VERSION(2, 6, 10))
173				xtables_error(PARAMETER_PROBLEM,
174					   "SNAT: Multiple --to-source not supported");
175		}
176		*cb->target = parse_to(cb->arg, portok, info);
177		/* WTF do we need this for?? */
178		if (cb->xflags & F_RANDOM)
179			info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
180		cb->xflags |= F_X_TO_SRC;
181		break;
182	case O_RANDOM:
183		if (cb->xflags & F_TO_SRC)
184			info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
185		break;
186	case O_PERSISTENT:
187		info->mr.range[0].flags |= IP_NAT_RANGE_PERSISTENT;
188		break;
189	}
190}
191
192static void print_range(const struct nf_nat_range *r)
193{
194	if (r->flags & IP_NAT_RANGE_MAP_IPS) {
195		struct in_addr a;
196
197		a.s_addr = r->min_ip;
198		printf("%s", xtables_ipaddr_to_numeric(&a));
199		if (r->max_ip != r->min_ip) {
200			a.s_addr = r->max_ip;
201			printf("-%s", xtables_ipaddr_to_numeric(&a));
202		}
203	}
204	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
205		printf(":");
206		printf("%hu", ntohs(r->min.tcp.port));
207		if (r->max.tcp.port != r->min.tcp.port)
208			printf("-%hu", ntohs(r->max.tcp.port));
209	}
210}
211
212static void SNAT_print(const void *ip, const struct xt_entry_target *target,
213                       int numeric)
214{
215	const struct ipt_natinfo *info = (const void *)target;
216	unsigned int i = 0;
217
218	printf(" to:");
219	for (i = 0; i < info->mr.rangesize; i++) {
220		print_range(&info->mr.range[i]);
221		if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
222			printf(" random");
223		if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT)
224			printf(" persistent");
225	}
226}
227
228static void SNAT_save(const void *ip, const struct xt_entry_target *target)
229{
230	const struct ipt_natinfo *info = (const void *)target;
231	unsigned int i = 0;
232
233	for (i = 0; i < info->mr.rangesize; i++) {
234		printf(" --to-source ");
235		print_range(&info->mr.range[i]);
236		if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
237			printf(" --random");
238		if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT)
239			printf(" --persistent");
240	}
241}
242
243static struct xtables_target snat_tg_reg = {
244	.name		= "SNAT",
245	.version	= XTABLES_VERSION,
246	.family		= NFPROTO_IPV4,
247	.size		= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
248	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
249	.help		= SNAT_help,
250	.x6_parse	= SNAT_parse,
251	.print		= SNAT_print,
252	.save		= SNAT_save,
253	.x6_options	= SNAT_opts,
254};
255
256void _init(void)
257{
258	xtables_register_target(&snat_tg_reg);
259}
260