libipt_DNAT.c revision 8cf65913bb6353bf0e92eab0669d1c4c53b43623
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_ipv4/ip_nat_rule.h>
10
11/* Dest NAT data consists of a multi-range, indicating where to map
12   to. */
13struct ipt_natinfo
14{
15	struct ipt_entry_target t;
16	struct ip_nat_multi_range mr;
17};
18
19/* Function which prints out usage message. */
20static void
21help(void)
22{
23	printf(
24"DNAT v%s options:\n"
25" --to-destination <ipaddr>[-<ipaddr>][:port-port]\n"
26"				Address to map destination to.\n"
27"				(You can use this more than once)\n\n",
28IPTABLES_VERSION);
29}
30
31static struct option opts[] = {
32	{ "to-destination", 1, 0, '1' },
33	{ 0 }
34};
35
36static struct ipt_natinfo *
37append_range(struct ipt_natinfo *info, const struct ip_nat_range *range)
38{
39	unsigned int size;
40
41	/* One rangesize already in struct ipt_natinfo */
42	size = IPT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
43
44	info = realloc(info, size);
45	if (!info)
46		exit_error(OTHER_PROBLEM, "Out of memory\n");
47
48	info->t.u.target_size = size;
49	info->mr.range[info->mr.rangesize] = *range;
50	info->mr.rangesize++;
51
52	return info;
53}
54
55/* Ranges expected in network order. */
56static struct ipt_entry_target *
57parse_to(char *arg, int portok, struct ipt_natinfo *info)
58{
59	struct ip_nat_range range;
60	char *colon, *dash, *error;
61	struct in_addr *ip;
62
63	memset(&range, 0, sizeof(range));
64	colon = strchr(arg, ':');
65
66	if (colon) {
67		int port;
68
69		if (!portok)
70			exit_error(PARAMETER_PROBLEM,
71				   "Need TCP or UDP with port specification");
72
73		range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
74
75		port = atoi(colon+1);
76		if (port <= 0 || port > 65535)
77			exit_error(PARAMETER_PROBLEM,
78				   "Port `%s' not valid\n", colon+1);
79
80		error = strchr(colon+1, ':');
81		if (error)
82			exit_error(PARAMETER_PROBLEM,
83				   "Invalid port:port syntax - use dash\n");
84
85		dash = strchr(colon, '-');
86		if (!dash) {
87			range.min.tcp.port
88				= range.max.tcp.port
89				= htons(port);
90		} else {
91			int maxport;
92
93			maxport = atoi(dash + 1);
94			if (maxport <= 0 || maxport > 65535)
95				exit_error(PARAMETER_PROBLEM,
96					   "Port `%s' not valid\n", dash+1);
97			if (maxport < port)
98				/* People are stupid. */
99				exit_error(PARAMETER_PROBLEM,
100					   "Port range `%s' funky\n", colon+1);
101			range.min.tcp.port = htons(port);
102			range.max.tcp.port = htons(maxport);
103		}
104		/* Starts with a colon? No IP info...*/
105		if (colon == arg)
106			return &(append_range(info, &range)->t);
107		*colon = '\0';
108	}
109
110	range.flags |= IP_NAT_RANGE_MAP_IPS;
111	dash = strchr(arg, '-');
112	if (colon && dash && dash > colon)
113		dash = NULL;
114
115	if (dash)
116		*dash = '\0';
117
118	ip = dotted_to_addr(arg);
119	if (!ip)
120		exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
121			   arg);
122	range.min_ip = ip->s_addr;
123	if (dash) {
124		ip = dotted_to_addr(dash+1);
125		if (!ip)
126			exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
127				   dash+1);
128		range.max_ip = ip->s_addr;
129	} else
130		range.max_ip = range.min_ip;
131
132	return &(append_range(info, &range)->t);
133}
134
135/* Function which parses command options; returns true if it
136   ate an option */
137static int
138parse(int c, char **argv, int invert, unsigned int *flags,
139      const struct ipt_entry *entry,
140      struct ipt_entry_target **target)
141{
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_ICMP)
148		portok = 1;
149	else
150		portok = 0;
151
152	switch (c) {
153	case '1':
154		if (check_inverse(optarg, &invert, NULL, 0))
155			exit_error(PARAMETER_PROBLEM,
156				   "Unexpected `!' after --to-destination");
157
158		if (*flags) {
159			if (!kernel_version)
160				get_kernel_version();
161			if (kernel_version > LINUX_VERSION(2, 6, 10))
162				exit_error(PARAMETER_PROBLEM,
163					   "Multiple --to-destination not supported");
164		}
165		*target = parse_to(optarg, portok, info);
166		*flags = 1;
167		return 1;
168
169	default:
170		return 0;
171	}
172}
173
174/* Final check; must have specfied --to-source. */
175static void final_check(unsigned int flags)
176{
177	if (!flags)
178		exit_error(PARAMETER_PROBLEM,
179			   "You must specify --to-destination");
180}
181
182static void print_range(const struct ip_nat_range *r)
183{
184	if (r->flags & IP_NAT_RANGE_MAP_IPS) {
185		struct in_addr a;
186
187		a.s_addr = r->min_ip;
188		printf("%s", addr_to_dotted(&a));
189		if (r->max_ip != r->min_ip) {
190			a.s_addr = r->max_ip;
191			printf("-%s", addr_to_dotted(&a));
192		}
193	}
194	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
195		printf(":");
196		printf("%hu", ntohs(r->min.tcp.port));
197		if (r->max.tcp.port != r->min.tcp.port)
198			printf("-%hu", ntohs(r->max.tcp.port));
199	}
200}
201
202/* Prints out the targinfo. */
203static void
204print(const struct ipt_ip *ip,
205      const struct ipt_entry_target *target,
206      int numeric)
207{
208	struct ipt_natinfo *info = (void *)target;
209	unsigned int i = 0;
210
211	printf("to:");
212	for (i = 0; i < info->mr.rangesize; i++) {
213		print_range(&info->mr.range[i]);
214		printf(" ");
215	}
216}
217
218/* Saves the union ipt_targinfo in parsable form to stdout. */
219static void
220save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
221{
222	struct ipt_natinfo *info = (void *)target;
223	unsigned int i = 0;
224
225	for (i = 0; i < info->mr.rangesize; i++) {
226		printf("--to-destination ");
227		print_range(&info->mr.range[i]);
228		printf(" ");
229	}
230}
231
232static struct iptables_target dnat = {
233	.next		= NULL,
234	.name		= "DNAT",
235	.version	= IPTABLES_VERSION,
236	.size		= IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
237	.userspacesize	= IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
238	.help		= &help,
239	.parse		= &parse,
240	.final_check	= &final_check,
241	.print		= &print,
242	.save		= &save,
243	.extra_opts	= opts
244};
245
246void _init(void)
247{
248	register_target(&dnat);
249}
250