libipt_DNAT.c revision 9ee386a1b6d7704b259460152c959ab0e79e02aa
1/* Shared library add-on to iptables to add destination-NAT support. */
2#include <stdio.h>
3#include <netdb.h>
4#include <string.h>
5#include <stdlib.h>
6#include <getopt.h>
7#include <iptables.h>
8#include <linux/netfilter_ipv4/ip_tables.h>
9#include <linux/netfilter/nf_nat.h>
10
11#define IPT_DNAT_OPT_DEST 0x1
12#define IPT_DNAT_OPT_RANDOM 0x2
13
14/* Dest NAT data consists of a multi-range, indicating where to map
15   to. */
16struct ipt_natinfo
17{
18	struct xt_entry_target t;
19	struct ip_nat_multi_range mr;
20};
21
22/* Function which prints out usage message. */
23static void DNAT_help(void)
24{
25	printf(
26"DNAT v%s options:\n"
27" --to-destination <ipaddr>[-<ipaddr>][:port-port]\n"
28"				Address to map destination to.\n"
29"[--random]\n"
30"\n",
31IPTABLES_VERSION);
32}
33
34static const struct option DNAT_opts[] = {
35	{ "to-destination", 1, NULL, '1' },
36	{ "random", 0, NULL, '2' },
37	{ .name = NULL }
38};
39
40static struct ipt_natinfo *
41append_range(struct ipt_natinfo *info, const struct ip_nat_range *range)
42{
43	unsigned int size;
44
45	/* One rangesize already in struct ipt_natinfo */
46	size = IPT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
47
48	info = realloc(info, size);
49	if (!info)
50		exit_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 ip_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			exit_error(PARAMETER_PROBLEM,
75				   "Need TCP or UDP 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			exit_error(PARAMETER_PROBLEM,
82				   "Port `%s' not valid\n", colon+1);
83
84		error = strchr(colon+1, ':');
85		if (error)
86			exit_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				exit_error(PARAMETER_PROBLEM,
100					   "Port `%s' not valid\n", dash+1);
101			if (maxport < port)
102				/* People are stupid. */
103				exit_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 = numeric_to_ipaddr(arg);
123	if (!ip)
124		exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
125			   arg);
126	range.min_ip = ip->s_addr;
127	if (dash) {
128		ip = numeric_to_ipaddr(dash+1);
129		if (!ip)
130			exit_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
139/* Function which parses command options; returns true if it
140   ate an option */
141static int DNAT_parse(int c, char **argv, int invert, unsigned int *flags,
142                      const void *e, struct xt_entry_target **target)
143{
144	const struct ipt_entry *entry = e;
145	struct ipt_natinfo *info = (void *)*target;
146	int portok;
147
148	if (entry->ip.proto == IPPROTO_TCP
149	    || entry->ip.proto == IPPROTO_UDP
150	    || entry->ip.proto == IPPROTO_ICMP)
151		portok = 1;
152	else
153		portok = 0;
154
155	switch (c) {
156	case '1':
157		if (check_inverse(optarg, &invert, NULL, 0))
158			exit_error(PARAMETER_PROBLEM,
159				   "Unexpected `!' after --to-destination");
160
161		if (*flags) {
162			if (!kernel_version)
163				get_kernel_version();
164			if (kernel_version > LINUX_VERSION(2, 6, 10))
165				exit_error(PARAMETER_PROBLEM,
166					   "Multiple --to-destination not supported");
167		}
168		*target = parse_to(optarg, portok, info);
169		/* WTF do we need this for?? */
170		if (*flags & IPT_DNAT_OPT_RANDOM)
171			info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
172		*flags |= IPT_DNAT_OPT_DEST;
173		return 1;
174
175	case '2':
176		if (*flags & IPT_DNAT_OPT_DEST) {
177			info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
178			*flags |= IPT_DNAT_OPT_RANDOM;
179		} else
180			*flags |= IPT_DNAT_OPT_RANDOM;
181		return 1;
182	default:
183		return 0;
184	}
185}
186
187/* Final check; must have specfied --to-source. */
188static void DNAT_check(unsigned int flags)
189{
190	if (!flags)
191		exit_error(PARAMETER_PROBLEM,
192			   "You must specify --to-destination");
193}
194
195static void print_range(const struct ip_nat_range *r)
196{
197	if (r->flags & IP_NAT_RANGE_MAP_IPS) {
198		struct in_addr a;
199
200		a.s_addr = r->min_ip;
201		printf("%s", ipaddr_to_numeric(&a));
202		if (r->max_ip != r->min_ip) {
203			a.s_addr = r->max_ip;
204			printf("-%s", ipaddr_to_numeric(&a));
205		}
206	}
207	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
208		printf(":");
209		printf("%hu", ntohs(r->min.tcp.port));
210		if (r->max.tcp.port != r->min.tcp.port)
211			printf("-%hu", ntohs(r->max.tcp.port));
212	}
213}
214
215/* Prints out the targinfo. */
216static void DNAT_print(const void *ip, const struct xt_entry_target *target,
217                       int numeric)
218{
219	struct ipt_natinfo *info = (void *)target;
220	unsigned int i = 0;
221
222	printf("to:");
223	for (i = 0; i < info->mr.rangesize; i++) {
224		print_range(&info->mr.range[i]);
225		printf(" ");
226		if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
227			printf("random ");
228	}
229}
230
231/* Saves the union ipt_targinfo in parsable form to stdout. */
232static void DNAT_save(const void *ip, const struct xt_entry_target *target)
233{
234	struct ipt_natinfo *info = (void *)target;
235	unsigned int i = 0;
236
237	for (i = 0; i < info->mr.rangesize; i++) {
238		printf("--to-destination ");
239		print_range(&info->mr.range[i]);
240		printf(" ");
241		if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
242			printf("--random ");
243	}
244}
245
246static struct iptables_target dnat_target = {
247	.name		= "DNAT",
248	.version	= IPTABLES_VERSION,
249	.size		= IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
250	.userspacesize	= IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
251	.help		= DNAT_help,
252	.parse		= DNAT_parse,
253	.final_check	= DNAT_check,
254	.print		= DNAT_print,
255	.save		= DNAT_save,
256	.extra_opts	= DNAT_opts,
257};
258
259void _init(void)
260{
261	register_target(&dnat_target);
262}
263