libipt_SNAT.c revision 4e41854423b529d3107c23b85434d50a75d08057
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]\n");
31}
32
33static const struct option SNAT_opts[] = {
34	{ "to-source", 1, NULL, '1' },
35	{ "random", 0, NULL, '2' },
36	{ .name = NULL }
37};
38
39static struct ipt_natinfo *
40append_range(struct ipt_natinfo *info, const struct nf_nat_range *range)
41{
42	unsigned int size;
43
44	/* One rangesize already in struct ipt_natinfo */
45	size = XT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
46
47	info = realloc(info, size);
48	if (!info)
49		xtables_error(OTHER_PROBLEM, "Out of memory\n");
50
51	info->t.u.target_size = size;
52	info->mr.range[info->mr.rangesize] = *range;
53	info->mr.rangesize++;
54
55	return info;
56}
57
58/* Ranges expected in network order. */
59static struct xt_entry_target *
60parse_to(char *arg, int portok, struct ipt_natinfo *info)
61{
62	struct nf_nat_range range;
63	char *colon, *dash, *error;
64	const struct in_addr *ip;
65
66	memset(&range, 0, sizeof(range));
67	colon = strchr(arg, ':');
68
69	if (colon) {
70		int port;
71
72		if (!portok)
73			xtables_error(PARAMETER_PROBLEM,
74				   "Need TCP, UDP, SCTP or DCCP with port specification");
75
76		range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
77
78		port = atoi(colon+1);
79		if (port <= 0 || port > 65535)
80			xtables_error(PARAMETER_PROBLEM,
81				   "Port `%s' not valid\n", colon+1);
82
83		error = strchr(colon+1, ':');
84		if (error)
85			xtables_error(PARAMETER_PROBLEM,
86				   "Invalid port:port syntax - use dash\n");
87
88		dash = strchr(colon, '-');
89		if (!dash) {
90			range.min.tcp.port
91				= range.max.tcp.port
92				= htons(port);
93		} else {
94			int maxport;
95
96			maxport = atoi(dash + 1);
97			if (maxport <= 0 || maxport > 65535)
98				xtables_error(PARAMETER_PROBLEM,
99					   "Port `%s' not valid\n", dash+1);
100			if (maxport < port)
101				/* People are stupid. */
102				xtables_error(PARAMETER_PROBLEM,
103					   "Port range `%s' funky\n", colon+1);
104			range.min.tcp.port = htons(port);
105			range.max.tcp.port = htons(maxport);
106		}
107		/* Starts with a colon? No IP info...*/
108		if (colon == arg)
109			return &(append_range(info, &range)->t);
110		*colon = '\0';
111	}
112
113	range.flags |= IP_NAT_RANGE_MAP_IPS;
114	dash = strchr(arg, '-');
115	if (colon && dash && dash > colon)
116		dash = NULL;
117
118	if (dash)
119		*dash = '\0';
120
121	ip = xtables_numeric_to_ipaddr(arg);
122	if (!ip)
123		xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
124			   arg);
125	range.min_ip = ip->s_addr;
126	if (dash) {
127		ip = xtables_numeric_to_ipaddr(dash+1);
128		if (!ip)
129			xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
130				   dash+1);
131		range.max_ip = ip->s_addr;
132	} else
133		range.max_ip = range.min_ip;
134
135	return &(append_range(info, &range)->t);
136}
137
138static int SNAT_parse(int c, char **argv, int invert, unsigned int *flags,
139                      const void *e, struct xt_entry_target **target)
140{
141	const struct ipt_entry *entry = e;
142	struct ipt_natinfo *info = (void *)*target;
143	int portok;
144
145	if (entry->ip.proto == IPPROTO_TCP
146	    || entry->ip.proto == IPPROTO_UDP
147	    || entry->ip.proto == IPPROTO_SCTP
148	    || entry->ip.proto == IPPROTO_DCCP
149	    || entry->ip.proto == IPPROTO_ICMP)
150		portok = 1;
151	else
152		portok = 0;
153
154	switch (c) {
155	case '1':
156		if (xtables_check_inverse(optarg, &invert, NULL, 0))
157			xtables_error(PARAMETER_PROBLEM,
158				   "Unexpected `!' after --to-source");
159
160		if (*flags & IPT_SNAT_OPT_SOURCE) {
161			if (!kernel_version)
162				get_kernel_version();
163			if (kernel_version > LINUX_VERSION(2, 6, 10))
164				xtables_error(PARAMETER_PROBLEM,
165					   "Multiple --to-source not supported");
166		}
167		*target = parse_to(optarg, portok, info);
168		/* WTF do we need this for?? */
169		if (*flags & IPT_SNAT_OPT_RANDOM)
170			info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
171		*flags |= IPT_SNAT_OPT_SOURCE;
172		return 1;
173
174	case '2':
175		if (*flags & IPT_SNAT_OPT_SOURCE) {
176			info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
177			*flags |= IPT_SNAT_OPT_RANDOM;
178		} else
179			*flags |= IPT_SNAT_OPT_RANDOM;
180		return 1;
181
182	default:
183		return 0;
184	}
185}
186
187static void SNAT_check(unsigned int flags)
188{
189	if (!(flags & IPT_SNAT_OPT_SOURCE))
190		xtables_error(PARAMETER_PROBLEM,
191			   "You must specify --to-source");
192}
193
194static void print_range(const struct nf_nat_range *r)
195{
196	if (r->flags & IP_NAT_RANGE_MAP_IPS) {
197		struct in_addr a;
198
199		a.s_addr = r->min_ip;
200		printf("%s", xtables_ipaddr_to_numeric(&a));
201		if (r->max_ip != r->min_ip) {
202			a.s_addr = r->max_ip;
203			printf("-%s", xtables_ipaddr_to_numeric(&a));
204		}
205	}
206	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
207		printf(":");
208		printf("%hu", ntohs(r->min.tcp.port));
209		if (r->max.tcp.port != r->min.tcp.port)
210			printf("-%hu", ntohs(r->max.tcp.port));
211	}
212}
213
214static void SNAT_print(const void *ip, const struct xt_entry_target *target,
215                       int numeric)
216{
217	struct ipt_natinfo *info = (void *)target;
218	unsigned int i = 0;
219
220	printf("to:");
221	for (i = 0; i < info->mr.rangesize; i++) {
222		print_range(&info->mr.range[i]);
223		printf(" ");
224		if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
225			printf("random ");
226	}
227}
228
229static void SNAT_save(const void *ip, const struct xt_entry_target *target)
230{
231	struct ipt_natinfo *info = (void *)target;
232	unsigned int i = 0;
233
234	for (i = 0; i < info->mr.rangesize; i++) {
235		printf("--to-source ");
236		print_range(&info->mr.range[i]);
237		printf(" ");
238		if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
239			printf("--random ");
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	.parse		= SNAT_parse,
251	.final_check	= SNAT_check,
252	.print		= SNAT_print,
253	.save		= SNAT_save,
254	.extra_opts	= SNAT_opts,
255};
256
257void _init(void)
258{
259	xtables_register_target(&snat_tg_reg);
260}
261