libipt_SNAT.c revision ae4b0b3aa70c67f2eff303a3e75834e45c3794a7
1/* Shared library add-on to iptables to add source-NAT support. */
2#include <stdio.h>
3#include <netdb.h>
4#include <string.h>
5#include <stdlib.h>
6#include <getopt.h>
7#include <iptables.h>
8#include <linux/netfilter_ipv4/ip_tables.h>
9#include <linux/netfilter_ipv4/ip_nat_rule.h>
10
11#define IPT_SNAT_OPT_SOURCE 0x01
12#ifdef IP_NAT_RANGE_PROTO_RANDOM
13#	define IPT_SNAT_OPT_RANDOM 0x02
14#endif
15
16/* Source NAT data consists of a multi-range, indicating where to map
17   to. */
18struct ipt_natinfo
19{
20	struct ipt_entry_target t;
21	struct ip_nat_multi_range mr;
22};
23
24/* Function which prints out usage message. */
25static void
26help(void)
27{
28	printf(
29"SNAT v%s options:\n"
30" --to-source <ipaddr>[-<ipaddr>][:port-port]"
31#ifdef IP_NAT_RANGE_PROTO_RANDOM
32"[--random]"
33#endif
34"\n"
35"				Address to map source to.\n"
36"				(You can use this more than once)\n\n",
37IPTABLES_VERSION);
38}
39
40static struct option opts[] = {
41	{ "to-source", 1, 0, '1' },
42#ifdef IP_NAT_RANGE_PROTO_RANDOM
43	{ "random", 0, 0, '2' },
44#endif
45	{ 0 }
46};
47
48static struct ipt_natinfo *
49append_range(struct ipt_natinfo *info, const struct ip_nat_range *range)
50{
51	unsigned int size;
52
53	/* One rangesize already in struct ipt_natinfo */
54	size = IPT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
55
56	info = realloc(info, size);
57	if (!info)
58		exit_error(OTHER_PROBLEM, "Out of memory\n");
59
60	info->t.u.target_size = size;
61	info->mr.range[info->mr.rangesize] = *range;
62	info->mr.rangesize++;
63
64	return info;
65}
66
67/* Ranges expected in network order. */
68static struct ipt_entry_target *
69parse_to(char *arg, int portok, struct ipt_natinfo *info)
70{
71	struct ip_nat_range range;
72	char *colon, *dash, *error;
73	struct in_addr *ip;
74
75	memset(&range, 0, sizeof(range));
76	colon = strchr(arg, ':');
77
78	if (colon) {
79		int port;
80
81		if (!portok)
82			exit_error(PARAMETER_PROBLEM,
83				   "Need TCP or UDP with port specification");
84
85		range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
86
87		port = atoi(colon+1);
88		if (port <= 0 || port > 65535)
89			exit_error(PARAMETER_PROBLEM,
90				   "Port `%s' not valid\n", colon+1);
91
92		error = strchr(colon+1, ':');
93		if (error)
94			exit_error(PARAMETER_PROBLEM,
95				   "Invalid port:port syntax - use dash\n");
96
97		dash = strchr(colon, '-');
98		if (!dash) {
99			range.min.tcp.port
100				= range.max.tcp.port
101				= htons(port);
102		} else {
103			int maxport;
104
105			maxport = atoi(dash + 1);
106			if (maxport <= 0 || maxport > 65535)
107				exit_error(PARAMETER_PROBLEM,
108					   "Port `%s' not valid\n", dash+1);
109			if (maxport < port)
110				/* People are stupid. */
111				exit_error(PARAMETER_PROBLEM,
112					   "Port range `%s' funky\n", colon+1);
113			range.min.tcp.port = htons(port);
114			range.max.tcp.port = htons(maxport);
115		}
116		/* Starts with a colon? No IP info...*/
117		if (colon == arg)
118			return &(append_range(info, &range)->t);
119		*colon = '\0';
120	}
121
122	range.flags |= IP_NAT_RANGE_MAP_IPS;
123	dash = strchr(arg, '-');
124	if (colon && dash && dash > colon)
125		dash = NULL;
126
127	if (dash)
128		*dash = '\0';
129
130	ip = dotted_to_addr(arg);
131	if (!ip)
132		exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
133			   arg);
134	range.min_ip = ip->s_addr;
135	if (dash) {
136		ip = dotted_to_addr(dash+1);
137		if (!ip)
138			exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
139				   dash+1);
140		range.max_ip = ip->s_addr;
141	} else
142		range.max_ip = range.min_ip;
143
144	return &(append_range(info, &range)->t);
145}
146
147/* Function which parses command options; returns true if it
148   ate an option */
149static int
150parse(int c, char **argv, int invert, unsigned int *flags,
151      const struct ipt_entry *entry,
152      struct ipt_entry_target **target)
153{
154	struct ipt_natinfo *info = (void *)*target;
155	int portok;
156
157	if (entry->ip.proto == IPPROTO_TCP
158	    || entry->ip.proto == IPPROTO_UDP
159	    || entry->ip.proto == IPPROTO_ICMP)
160		portok = 1;
161	else
162		portok = 0;
163
164	switch (c) {
165	case '1':
166		if (check_inverse(optarg, &invert, NULL, 0))
167			exit_error(PARAMETER_PROBLEM,
168				   "Unexpected `!' after --to-source");
169
170		if (*flags & IPT_SNAT_OPT_SOURCE) {
171			if (!kernel_version)
172				get_kernel_version();
173			if (kernel_version > LINUX_VERSION(2, 6, 10))
174				exit_error(PARAMETER_PROBLEM,
175					   "Multiple --to-source not supported");
176		}
177		*target = parse_to(optarg, portok, info);
178#ifdef IP_NAT_RANGE_PROTO_RANDOM
179		if (*flags & IPT_SNAT_OPT_RANDOM)
180			info->mr.range[0].flags |=  IP_NAT_RANGE_PROTO_RANDOM;
181#endif
182		*flags = IPT_SNAT_OPT_SOURCE;
183		return 1;
184
185#ifdef IP_NAT_RANGE_PROTO_RANDOM
186	case '2':
187		if (*flags & IPT_SNAT_OPT_SOURCE) {
188			info->mr.range[0].flags |=  IP_NAT_RANGE_PROTO_RANDOM;
189			*flags |= IPT_SNAT_OPT_RANDOM;
190		} else
191			*flags |= IPT_SNAT_OPT_RANDOM;
192		return 1;
193#endif
194
195	default:
196		return 0;
197	}
198}
199
200/* Final check; must have specfied --to-source. */
201static void final_check(unsigned int flags)
202{
203	if (!(flags & IPT_SNAT_OPT_SOURCE))
204		exit_error(PARAMETER_PROBLEM,
205			   "You must specify --to-source");
206}
207
208static void print_range(const struct ip_nat_range *r)
209{
210	if (r->flags & IP_NAT_RANGE_MAP_IPS) {
211		struct in_addr a;
212
213		a.s_addr = r->min_ip;
214		printf("%s", addr_to_dotted(&a));
215		if (r->max_ip != r->min_ip) {
216			a.s_addr = r->max_ip;
217			printf("-%s", addr_to_dotted(&a));
218		}
219	}
220	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
221		printf(":");
222		printf("%hu", ntohs(r->min.tcp.port));
223		if (r->max.tcp.port != r->min.tcp.port)
224			printf("-%hu", ntohs(r->max.tcp.port));
225	}
226#ifdef IP_NAT_RANGE_PROTO_RANDOM
227	if (r->flags & IP_NAT_RANGE_PROTO_RANDOM) {
228		printf(" random");
229	}
230#endif
231}
232
233/* Prints out the targinfo. */
234static void
235print(const struct ipt_ip *ip,
236      const struct ipt_entry_target *target,
237      int numeric)
238{
239	struct ipt_natinfo *info = (void *)target;
240	unsigned int i = 0;
241
242	printf("to:");
243	for (i = 0; i < info->mr.rangesize; i++) {
244		print_range(&info->mr.range[i]);
245		printf(" ");
246	}
247}
248
249/* Saves the union ipt_targinfo in parsable form to stdout. */
250static void
251save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
252{
253	struct ipt_natinfo *info = (void *)target;
254	unsigned int i = 0;
255
256	for (i = 0; i < info->mr.rangesize; i++) {
257		printf("--to-source ");
258		print_range(&info->mr.range[i]);
259		printf(" ");
260	}
261}
262
263static struct iptables_target snat = {
264	.next		= NULL,
265	.name		= "SNAT",
266	.version	= IPTABLES_VERSION,
267	.size		= IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
268	.userspacesize	= IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
269	.help		= &help,
270	.parse		= &parse,
271	.final_check	= &final_check,
272	.print		= &print,
273	.save		= &save,
274	.extra_opts	= opts
275};
276
277void _init(void)
278{
279	register_target(&snat);
280}
281