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