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 <linux/netfilter/nf_nat.h>
10
11enum {
12	O_TO_SRC = 0,
13	O_RANDOM,
14	O_RANDOM_FULLY,
15	O_PERSISTENT,
16	O_X_TO_SRC,
17	F_TO_SRC       = 1 << O_TO_SRC,
18	F_RANDOM       = 1 << O_RANDOM,
19	F_RANDOM_FULLY = 1 << O_RANDOM_FULLY,
20	F_X_TO_SRC     = 1 << O_X_TO_SRC,
21};
22
23/* Source NAT data consists of a multi-range, indicating where to map
24   to. */
25struct ipt_natinfo
26{
27	struct xt_entry_target t;
28	struct nf_nat_ipv4_multi_range_compat mr;
29};
30
31static void SNAT_help(void)
32{
33	printf(
34"SNAT target options:\n"
35" --to-source [<ipaddr>[-<ipaddr>]][:port[-port]]\n"
36"				Address to map source to.\n"
37"[--random] [--random-fully] [--persistent]\n");
38}
39
40static const struct xt_option_entry SNAT_opts[] = {
41	{.name = "to-source", .id = O_TO_SRC, .type = XTTYPE_STRING,
42	 .flags = XTOPT_MAND | XTOPT_MULTI},
43	{.name = "random", .id = O_RANDOM, .type = XTTYPE_NONE},
44	{.name = "random-fully", .id = O_RANDOM_FULLY, .type = XTTYPE_NONE},
45	{.name = "persistent", .id = O_PERSISTENT, .type = XTTYPE_NONE},
46	XTOPT_TABLEEND,
47};
48
49static struct ipt_natinfo *
50append_range(struct ipt_natinfo *info, const struct nf_nat_ipv4_range *range)
51{
52	unsigned int size;
53
54	/* One rangesize already in struct ipt_natinfo */
55	size = XT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
56
57	info = realloc(info, size);
58	if (!info)
59		xtables_error(OTHER_PROBLEM, "Out of memory\n");
60
61	info->t.u.target_size = size;
62	info->mr.range[info->mr.rangesize] = *range;
63	info->mr.rangesize++;
64
65	return info;
66}
67
68/* Ranges expected in network order. */
69static struct xt_entry_target *
70parse_to(const char *orig_arg, int portok, struct ipt_natinfo *info)
71{
72	struct nf_nat_ipv4_range range;
73	char *arg, *colon, *dash, *error;
74	const struct in_addr *ip;
75
76	arg = strdup(orig_arg);
77	if (arg == NULL)
78		xtables_error(RESOURCE_PROBLEM, "strdup");
79	memset(&range, 0, sizeof(range));
80	colon = strchr(arg, ':');
81
82	if (colon) {
83		int port;
84
85		if (!portok)
86			xtables_error(PARAMETER_PROBLEM,
87				   "Need TCP, UDP, SCTP or DCCP with port specification");
88
89		range.flags |= NF_NAT_RANGE_PROTO_SPECIFIED;
90
91		port = atoi(colon+1);
92		if (port <= 0 || port > 65535)
93			xtables_error(PARAMETER_PROBLEM,
94				   "Port `%s' not valid\n", colon+1);
95
96		error = strchr(colon+1, ':');
97		if (error)
98			xtables_error(PARAMETER_PROBLEM,
99				   "Invalid port:port syntax - use dash\n");
100
101		dash = strchr(colon, '-');
102		if (!dash) {
103			range.min.tcp.port
104				= range.max.tcp.port
105				= htons(port);
106		} else {
107			int maxport;
108
109			maxport = atoi(dash + 1);
110			if (maxport <= 0 || maxport > 65535)
111				xtables_error(PARAMETER_PROBLEM,
112					   "Port `%s' not valid\n", dash+1);
113			if (maxport < port)
114				/* People are stupid. */
115				xtables_error(PARAMETER_PROBLEM,
116					   "Port range `%s' funky\n", colon+1);
117			range.min.tcp.port = htons(port);
118			range.max.tcp.port = htons(maxport);
119		}
120		/* Starts with a colon? No IP info...*/
121		if (colon == arg) {
122			free(arg);
123			return &(append_range(info, &range)->t);
124		}
125		*colon = '\0';
126	}
127
128	range.flags |= NF_NAT_RANGE_MAP_IPS;
129	dash = strchr(arg, '-');
130	if (colon && dash && dash > colon)
131		dash = NULL;
132
133	if (dash)
134		*dash = '\0';
135
136	ip = xtables_numeric_to_ipaddr(arg);
137	if (!ip)
138		xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
139			   arg);
140	range.min_ip = ip->s_addr;
141	if (dash) {
142		ip = xtables_numeric_to_ipaddr(dash+1);
143		if (!ip)
144			xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
145				   dash+1);
146		range.max_ip = ip->s_addr;
147	} else
148		range.max_ip = range.min_ip;
149
150	free(arg);
151	return &(append_range(info, &range)->t);
152}
153
154static void SNAT_parse(struct xt_option_call *cb)
155{
156	const struct ipt_entry *entry = cb->xt_entry;
157	struct ipt_natinfo *info = (void *)(*cb->target);
158	int portok;
159
160	if (entry->ip.proto == IPPROTO_TCP
161	    || entry->ip.proto == IPPROTO_UDP
162	    || entry->ip.proto == IPPROTO_SCTP
163	    || entry->ip.proto == IPPROTO_DCCP
164	    || entry->ip.proto == IPPROTO_ICMP)
165		portok = 1;
166	else
167		portok = 0;
168
169	xtables_option_parse(cb);
170	switch (cb->entry->id) {
171	case O_TO_SRC:
172		if (cb->xflags & F_X_TO_SRC) {
173			if (!kernel_version)
174				get_kernel_version();
175			if (kernel_version > LINUX_VERSION(2, 6, 10))
176				xtables_error(PARAMETER_PROBLEM,
177					   "SNAT: Multiple --to-source not supported");
178		}
179		*cb->target = parse_to(cb->arg, portok, info);
180		cb->xflags |= F_X_TO_SRC;
181		break;
182	case O_PERSISTENT:
183		info->mr.range[0].flags |= NF_NAT_RANGE_PERSISTENT;
184		break;
185	}
186}
187
188static void SNAT_fcheck(struct xt_fcheck_call *cb)
189{
190	static const unsigned int f = F_TO_SRC | F_RANDOM;
191	static const unsigned int r = F_TO_SRC | F_RANDOM_FULLY;
192	struct nf_nat_ipv4_multi_range_compat *mr = cb->data;
193
194	if ((cb->xflags & f) == f)
195		mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM;
196	if ((cb->xflags & r) == r)
197		mr->range[0].flags |= NF_NAT_RANGE_PROTO_RANDOM_FULLY;
198}
199
200static void print_range(const struct nf_nat_ipv4_range *r)
201{
202	if (r->flags & NF_NAT_RANGE_MAP_IPS) {
203		struct in_addr a;
204
205		a.s_addr = r->min_ip;
206		printf("%s", xtables_ipaddr_to_numeric(&a));
207		if (r->max_ip != r->min_ip) {
208			a.s_addr = r->max_ip;
209			printf("-%s", xtables_ipaddr_to_numeric(&a));
210		}
211	}
212	if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
213		printf(":");
214		printf("%hu", ntohs(r->min.tcp.port));
215		if (r->max.tcp.port != r->min.tcp.port)
216			printf("-%hu", ntohs(r->max.tcp.port));
217	}
218}
219
220static void SNAT_print(const void *ip, const struct xt_entry_target *target,
221                       int numeric)
222{
223	const struct ipt_natinfo *info = (const void *)target;
224	unsigned int i = 0;
225
226	printf(" to:");
227	for (i = 0; i < info->mr.rangesize; i++) {
228		print_range(&info->mr.range[i]);
229		if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM)
230			printf(" random");
231		if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY)
232			printf(" random-fully");
233		if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT)
234			printf(" persistent");
235	}
236}
237
238static void SNAT_save(const void *ip, const struct xt_entry_target *target)
239{
240	const struct ipt_natinfo *info = (const void *)target;
241	unsigned int i = 0;
242
243	for (i = 0; i < info->mr.rangesize; i++) {
244		printf(" --to-source ");
245		print_range(&info->mr.range[i]);
246		if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM)
247			printf(" --random");
248		if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY)
249			printf(" --random-fully");
250		if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT)
251			printf(" --persistent");
252	}
253}
254
255static void print_range_xlate(const struct nf_nat_ipv4_range *r,
256			      struct xt_xlate *xl)
257{
258	if (r->flags & NF_NAT_RANGE_MAP_IPS) {
259		struct in_addr a;
260
261		a.s_addr = r->min_ip;
262		xt_xlate_add(xl, "%s", xtables_ipaddr_to_numeric(&a));
263		if (r->max_ip != r->min_ip) {
264			a.s_addr = r->max_ip;
265			xt_xlate_add(xl, "-%s", xtables_ipaddr_to_numeric(&a));
266		}
267	}
268	if (r->flags & NF_NAT_RANGE_PROTO_SPECIFIED) {
269		xt_xlate_add(xl, ":");
270		xt_xlate_add(xl, "%hu", ntohs(r->min.tcp.port));
271		if (r->max.tcp.port != r->min.tcp.port)
272			xt_xlate_add(xl, "-%hu", ntohs(r->max.tcp.port));
273	}
274}
275
276static int SNAT_xlate(struct xt_xlate *xl,
277		      const struct xt_xlate_tg_params *params)
278{
279	const struct ipt_natinfo *info = (const void *)params->target;
280	unsigned int i = 0;
281	bool sep_need = false;
282	const char *sep = " ";
283
284	for (i = 0; i < info->mr.rangesize; i++) {
285		xt_xlate_add(xl, "snat to ");
286		print_range_xlate(&info->mr.range[i], xl);
287		if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM) {
288			xt_xlate_add(xl, " random");
289			sep_need = true;
290		}
291		if (info->mr.range[i].flags & NF_NAT_RANGE_PROTO_RANDOM_FULLY) {
292			if (sep_need)
293				sep = ",";
294			xt_xlate_add(xl, "%sfully-random", sep);
295			sep_need = true;
296		}
297		if (info->mr.range[i].flags & NF_NAT_RANGE_PERSISTENT) {
298			if (sep_need)
299				sep = ",";
300			xt_xlate_add(xl, "%spersistent", sep);
301		}
302	}
303
304	return 1;
305}
306
307static struct xtables_target snat_tg_reg = {
308	.name		= "SNAT",
309	.version	= XTABLES_VERSION,
310	.family		= NFPROTO_IPV4,
311	.size		= XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
312	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_ipv4_multi_range_compat)),
313	.help		= SNAT_help,
314	.x6_parse	= SNAT_parse,
315	.x6_fcheck	= SNAT_fcheck,
316	.print		= SNAT_print,
317	.save		= SNAT_save,
318	.x6_options	= SNAT_opts,
319	.xlate		= SNAT_xlate,
320};
321
322void _init(void)
323{
324	xtables_register_target(&snat_tg_reg);
325}
326