libipt_SNAT.c revision 467fa9fe70f08342a50b859ddd431c848a956679
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 <xtables.h>
8#include <iptables.h>
9#include <limits.h> /* INT_MAX in ip_tables.h */
10#include <linux/netfilter_ipv4/ip_tables.h>
11#include <net/netfilter/nf_nat.h>
12
13#define IPT_SNAT_OPT_SOURCE 0x01
14#define IPT_SNAT_OPT_RANDOM 0x02
15
16/* Source NAT data consists of a multi-range, indicating where to map
17   to. */
18struct ipt_natinfo
19{
20	struct xt_entry_target t;
21	struct nf_nat_multi_range mr;
22};
23
24static void SNAT_help(void)
25{
26	printf(
27"SNAT target options:\n"
28" --to-source <ipaddr>[-<ipaddr>][:port-port]\n"
29"				Address to map source to.\n"
30"[--random] [--persistent]\n");
31}
32
33static const struct option SNAT_opts[] = {
34	{ "to-source", 1, NULL, '1' },
35	{ "random", 0, NULL, '2' },
36	{ "persistent", 0, NULL, '3' },
37	{ .name = NULL }
38};
39
40static struct ipt_natinfo *
41append_range(struct ipt_natinfo *info, const struct nf_nat_range *range)
42{
43	unsigned int size;
44
45	/* One rangesize already in struct ipt_natinfo */
46	size = XT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
47
48	info = realloc(info, size);
49	if (!info)
50		xtables_error(OTHER_PROBLEM, "Out of memory\n");
51
52	info->t.u.target_size = size;
53	info->mr.range[info->mr.rangesize] = *range;
54	info->mr.rangesize++;
55
56	return info;
57}
58
59/* Ranges expected in network order. */
60static struct xt_entry_target *
61parse_to(char *arg, int portok, struct ipt_natinfo *info)
62{
63	struct nf_nat_range range;
64	char *colon, *dash, *error;
65	const struct in_addr *ip;
66
67	memset(&range, 0, sizeof(range));
68	colon = strchr(arg, ':');
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 |= IP_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.tcp.port
92				= range.max.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.tcp.port = htons(port);
106			range.max.tcp.port = htons(maxport);
107		}
108		/* Starts with a colon? No IP info...*/
109		if (colon == arg)
110			return &(append_range(info, &range)->t);
111		*colon = '\0';
112	}
113
114	range.flags |= IP_NAT_RANGE_MAP_IPS;
115	dash = strchr(arg, '-');
116	if (colon && dash && dash > colon)
117		dash = NULL;
118
119	if (dash)
120		*dash = '\0';
121
122	ip = xtables_numeric_to_ipaddr(arg);
123	if (!ip)
124		xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
125			   arg);
126	range.min_ip = ip->s_addr;
127	if (dash) {
128		ip = xtables_numeric_to_ipaddr(dash+1);
129		if (!ip)
130			xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
131				   dash+1);
132		range.max_ip = ip->s_addr;
133	} else
134		range.max_ip = range.min_ip;
135
136	return &(append_range(info, &range)->t);
137}
138
139static int SNAT_parse(int c, char **argv, int invert, unsigned int *flags,
140                      const void *e, struct xt_entry_target **target)
141{
142	const struct ipt_entry *entry = e;
143	struct ipt_natinfo *info = (void *)*target;
144	int portok;
145
146	if (entry->ip.proto == IPPROTO_TCP
147	    || entry->ip.proto == IPPROTO_UDP
148	    || entry->ip.proto == IPPROTO_SCTP
149	    || entry->ip.proto == IPPROTO_DCCP
150	    || entry->ip.proto == IPPROTO_ICMP)
151		portok = 1;
152	else
153		portok = 0;
154
155	switch (c) {
156	case '1':
157		if (xtables_check_inverse(optarg, &invert, NULL, 0))
158			xtables_error(PARAMETER_PROBLEM,
159				   "Unexpected `!' after --to-source");
160
161		if (*flags & IPT_SNAT_OPT_SOURCE) {
162			if (!kernel_version)
163				get_kernel_version();
164			if (kernel_version > LINUX_VERSION(2, 6, 10))
165				xtables_error(PARAMETER_PROBLEM,
166					   "Multiple --to-source not supported");
167		}
168		*target = parse_to(optarg, portok, info);
169		/* WTF do we need this for?? */
170		if (*flags & IPT_SNAT_OPT_RANDOM)
171			info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
172		*flags |= IPT_SNAT_OPT_SOURCE;
173		return 1;
174
175	case '2':
176		if (*flags & IPT_SNAT_OPT_SOURCE) {
177			info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
178			*flags |= IPT_SNAT_OPT_RANDOM;
179		} else
180			*flags |= IPT_SNAT_OPT_RANDOM;
181		return 1;
182
183	case '3':
184		info->mr.range[0].flags |= IP_NAT_RANGE_PERSISTENT;
185		return 1;
186
187	default:
188		return 0;
189	}
190}
191
192static void SNAT_check(unsigned int flags)
193{
194	if (!(flags & IPT_SNAT_OPT_SOURCE))
195		xtables_error(PARAMETER_PROBLEM,
196			   "You must specify --to-source");
197}
198
199static void print_range(const struct nf_nat_range *r)
200{
201	if (r->flags & IP_NAT_RANGE_MAP_IPS) {
202		struct in_addr a;
203
204		a.s_addr = r->min_ip;
205		printf("%s", xtables_ipaddr_to_numeric(&a));
206		if (r->max_ip != r->min_ip) {
207			a.s_addr = r->max_ip;
208			printf("-%s", xtables_ipaddr_to_numeric(&a));
209		}
210	}
211	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
212		printf(":");
213		printf("%hu", ntohs(r->min.tcp.port));
214		if (r->max.tcp.port != r->min.tcp.port)
215			printf("-%hu", ntohs(r->max.tcp.port));
216	}
217}
218
219static void SNAT_print(const void *ip, const struct xt_entry_target *target,
220                       int numeric)
221{
222	struct ipt_natinfo *info = (void *)target;
223	unsigned int i = 0;
224
225	printf("to:");
226	for (i = 0; i < info->mr.rangesize; i++) {
227		print_range(&info->mr.range[i]);
228		printf(" ");
229		if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
230			printf("random ");
231		if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT)
232			printf("persistent ");
233	}
234}
235
236static void SNAT_save(const void *ip, const struct xt_entry_target *target)
237{
238	struct ipt_natinfo *info = (void *)target;
239	unsigned int i = 0;
240
241	for (i = 0; i < info->mr.rangesize; i++) {
242		printf("--to-source ");
243		print_range(&info->mr.range[i]);
244		printf(" ");
245		if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
246			printf("--random ");
247		if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT)
248			printf("--persistent ");
249	}
250}
251
252static struct xtables_target snat_tg_reg = {
253	.name		= "SNAT",
254	.version	= XTABLES_VERSION,
255	.family		= NFPROTO_IPV4,
256	.size		= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
257	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
258	.help		= SNAT_help,
259	.parse		= SNAT_parse,
260	.final_check	= SNAT_check,
261	.print		= SNAT_print,
262	.save		= SNAT_save,
263	.extra_opts	= SNAT_opts,
264};
265
266void _init(void)
267{
268	xtables_register_target(&snat_tg_reg);
269}
270