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