libipt_DNAT.c revision 73866357e4a7a0fdc1b293bf8863fee2bd56da9e
1/* Shared library add-on to iptables to add destination-NAT support. */
2#include <stdbool.h>
3#include <stdio.h>
4#include <netdb.h>
5#include <string.h>
6#include <stdlib.h>
7#include <getopt.h>
8#include <xtables.h>
9#include <iptables.h> /* get_kernel_version */
10#include <limits.h> /* INT_MAX in ip_tables.h */
11#include <linux/netfilter_ipv4/ip_tables.h>
12#include <net/netfilter/nf_nat.h>
13
14#define IPT_DNAT_OPT_DEST 0x1
15#define IPT_DNAT_OPT_RANDOM 0x2
16
17/* Dest NAT data consists of a multi-range, indicating where to map
18   to. */
19struct ipt_natinfo
20{
21	struct xt_entry_target t;
22	struct nf_nat_multi_range mr;
23};
24
25static void DNAT_help(void)
26{
27	printf(
28"DNAT target options:\n"
29" --to-destination <ipaddr>[-<ipaddr>][:port-port]\n"
30"				Address to map destination to.\n"
31"[--random] [--persistent]\n");
32}
33
34static const struct option DNAT_opts[] = {
35	{.name = "to-destination", .has_arg = true,  .val = '1'},
36	{.name = "random",         .has_arg = false, .val = '2'},
37	{.name = "persistent",     .has_arg = false, .val = '3'},
38	XT_GETOPT_TABLEEND,
39};
40
41static struct ipt_natinfo *
42append_range(struct ipt_natinfo *info, const struct nf_nat_range *range)
43{
44	unsigned int size;
45
46	/* One rangesize already in struct ipt_natinfo */
47	size = XT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
48
49	info = realloc(info, size);
50	if (!info)
51		xtables_error(OTHER_PROBLEM, "Out of memory\n");
52
53	info->t.u.target_size = size;
54	info->mr.range[info->mr.rangesize] = *range;
55	info->mr.rangesize++;
56
57	return info;
58}
59
60/* Ranges expected in network order. */
61static struct xt_entry_target *
62parse_to(char *arg, int portok, struct ipt_natinfo *info)
63{
64	struct nf_nat_range range;
65	char *colon, *dash, *error;
66	const struct in_addr *ip;
67
68	memset(&range, 0, sizeof(range));
69	colon = strchr(arg, ':');
70
71	if (colon) {
72		int port;
73
74		if (!portok)
75			xtables_error(PARAMETER_PROBLEM,
76				   "Need TCP, UDP, SCTP or DCCP with port specification");
77
78		range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
79
80		port = atoi(colon+1);
81		if (port <= 0 || port > 65535)
82			xtables_error(PARAMETER_PROBLEM,
83				   "Port `%s' not valid\n", colon+1);
84
85		error = strchr(colon+1, ':');
86		if (error)
87			xtables_error(PARAMETER_PROBLEM,
88				   "Invalid port:port syntax - use dash\n");
89
90		dash = strchr(colon, '-');
91		if (!dash) {
92			range.min.tcp.port
93				= range.max.tcp.port
94				= htons(port);
95		} else {
96			int maxport;
97
98			maxport = atoi(dash + 1);
99			if (maxport <= 0 || maxport > 65535)
100				xtables_error(PARAMETER_PROBLEM,
101					   "Port `%s' not valid\n", dash+1);
102			if (maxport < port)
103				/* People are stupid. */
104				xtables_error(PARAMETER_PROBLEM,
105					   "Port range `%s' funky\n", colon+1);
106			range.min.tcp.port = htons(port);
107			range.max.tcp.port = htons(maxport);
108		}
109		/* Starts with a colon? No IP info...*/
110		if (colon == arg)
111			return &(append_range(info, &range)->t);
112		*colon = '\0';
113	}
114
115	range.flags |= IP_NAT_RANGE_MAP_IPS;
116	dash = strchr(arg, '-');
117	if (colon && dash && dash > colon)
118		dash = NULL;
119
120	if (dash)
121		*dash = '\0';
122
123	ip = xtables_numeric_to_ipaddr(arg);
124	if (!ip)
125		xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
126			   arg);
127	range.min_ip = ip->s_addr;
128	if (dash) {
129		ip = xtables_numeric_to_ipaddr(dash+1);
130		if (!ip)
131			xtables_error(PARAMETER_PROBLEM, "Bad IP address \"%s\"\n",
132				   dash+1);
133		range.max_ip = ip->s_addr;
134	} else
135		range.max_ip = range.min_ip;
136
137	return &(append_range(info, &range)->t);
138}
139
140static int DNAT_parse(int c, char **argv, int invert, unsigned int *flags,
141                      const void *e, struct xt_entry_target **target)
142{
143	const struct ipt_entry *entry = e;
144	struct ipt_natinfo *info = (void *)*target;
145	int portok;
146
147	if (entry->ip.proto == IPPROTO_TCP
148	    || entry->ip.proto == IPPROTO_UDP
149	    || entry->ip.proto == IPPROTO_SCTP
150	    || entry->ip.proto == IPPROTO_DCCP
151	    || entry->ip.proto == IPPROTO_ICMP)
152		portok = 1;
153	else
154		portok = 0;
155
156	switch (c) {
157	case '1':
158		if (xtables_check_inverse(optarg, &invert, NULL, 0, argv))
159			xtables_error(PARAMETER_PROBLEM,
160				   "Unexpected `!' after --to-destination");
161
162		if (*flags & IPT_DNAT_OPT_DEST) {
163			if (!kernel_version)
164				get_kernel_version();
165			if (kernel_version > LINUX_VERSION(2, 6, 10))
166				xtables_error(PARAMETER_PROBLEM,
167					   "Multiple --to-destination not supported");
168		}
169		*target = parse_to(optarg, portok, info);
170		/* WTF do we need this for?? */
171		if (*flags & IPT_DNAT_OPT_RANDOM)
172			info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
173		*flags |= IPT_DNAT_OPT_DEST;
174		return 1;
175
176	case '2':
177		if (*flags & IPT_DNAT_OPT_DEST) {
178			info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
179			*flags |= IPT_DNAT_OPT_RANDOM;
180		} else
181			*flags |= IPT_DNAT_OPT_RANDOM;
182		return 1;
183
184	case '3':
185		info->mr.range[0].flags |= IP_NAT_RANGE_PERSISTENT;
186		return 1;
187	}
188	return 0;
189}
190
191static void DNAT_check(unsigned int flags)
192{
193	if (!flags)
194		xtables_error(PARAMETER_PROBLEM,
195			   "You must specify --to-destination");
196}
197
198static void print_range(const struct nf_nat_range *r)
199{
200	if (r->flags & IP_NAT_RANGE_MAP_IPS) {
201		struct in_addr a;
202
203		a.s_addr = r->min_ip;
204		printf("%s", xtables_ipaddr_to_numeric(&a));
205		if (r->max_ip != r->min_ip) {
206			a.s_addr = r->max_ip;
207			printf("-%s", xtables_ipaddr_to_numeric(&a));
208		}
209	}
210	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
211		printf(":");
212		printf("%hu", ntohs(r->min.tcp.port));
213		if (r->max.tcp.port != r->min.tcp.port)
214			printf("-%hu", ntohs(r->max.tcp.port));
215	}
216}
217
218static void DNAT_print(const void *ip, const struct xt_entry_target *target,
219                       int numeric)
220{
221	const struct ipt_natinfo *info = (const void *)target;
222	unsigned int i = 0;
223
224	printf(" to:");
225	for (i = 0; i < info->mr.rangesize; i++) {
226		print_range(&info->mr.range[i]);
227		if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
228			printf(" random");
229		if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT)
230			printf(" persistent");
231	}
232}
233
234static void DNAT_save(const void *ip, const struct xt_entry_target *target)
235{
236	const struct ipt_natinfo *info = (const void *)target;
237	unsigned int i = 0;
238
239	for (i = 0; i < info->mr.rangesize; i++) {
240		printf(" --to-destination ");
241		print_range(&info->mr.range[i]);
242		if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
243			printf(" --random");
244		if (info->mr.range[i].flags & IP_NAT_RANGE_PERSISTENT)
245			printf(" --persistent");
246	}
247}
248
249static struct xtables_target dnat_tg_reg = {
250	.name		= "DNAT",
251	.version	= XTABLES_VERSION,
252	.family		= NFPROTO_IPV4,
253	.size		= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
254	.userspacesize	= XT_ALIGN(sizeof(struct nf_nat_multi_range)),
255	.help		= DNAT_help,
256	.parse		= DNAT_parse,
257	.final_check	= DNAT_check,
258	.print		= DNAT_print,
259	.save		= DNAT_save,
260	.extra_opts	= DNAT_opts,
261};
262
263void _init(void)
264{
265	xtables_register_target(&dnat_tg_reg);
266}
267