libipt_SNAT.c revision 36d870c76621b94d51816d09eb8fd05e0fb0a0ab
1/* Shared library add-on to iptables to add source-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/* Source 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"SNAT v%s options:\n"
25" --to-source <ipaddr>[-<ipaddr>][:port-port]\n"
26"				Address to map source to.\n"
27"				(You can use this more than once)\n\n",
28IPTABLES_VERSION);
29}
30
31static struct option opts[] = {
32	{ "to-source", 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-source");
157
158		*target = parse_to(optarg, portok, info);
159		*flags = 1;
160		return 1;
161
162	default:
163		return 0;
164	}
165}
166
167/* Final check; must have specfied --to-source. */
168static void final_check(unsigned int flags)
169{
170	if (!flags)
171		exit_error(PARAMETER_PROBLEM,
172			   "You must specify --to-source");
173}
174
175static void print_range(const struct ip_nat_range *r)
176{
177	if (r->flags & IP_NAT_RANGE_MAP_IPS) {
178		struct in_addr a;
179
180		a.s_addr = r->min_ip;
181		printf("%s", addr_to_dotted(&a));
182		if (r->max_ip != r->min_ip) {
183			a.s_addr = r->max_ip;
184			printf("-%s", addr_to_dotted(&a));
185		}
186	}
187	if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
188		printf(":");
189		printf("%hu", ntohs(r->min.tcp.port));
190		if (r->max.tcp.port != r->min.tcp.port)
191			printf("-%hu", ntohs(r->max.tcp.port));
192	}
193}
194
195/* Prints out the targinfo. */
196static void
197print(const struct ipt_ip *ip,
198      const struct ipt_entry_target *target,
199      int numeric)
200{
201	struct ipt_natinfo *info = (void *)target;
202	unsigned int i = 0;
203
204	printf("to:");
205	for (i = 0; i < info->mr.rangesize; i++) {
206		print_range(&info->mr.range[i]);
207		printf(" ");
208	}
209}
210
211/* Saves the union ipt_targinfo in parsable form to stdout. */
212static void
213save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
214{
215	struct ipt_natinfo *info = (void *)target;
216	unsigned int i = 0;
217
218	for (i = 0; i < info->mr.rangesize; i++) {
219		printf("--to-source ");
220		print_range(&info->mr.range[i]);
221		printf(" ");
222	}
223}
224
225static struct iptables_target snat = {
226	.next		= NULL,
227	.name		= "SNAT",
228	.version	= IPTABLES_VERSION,
229	.size		= IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
230	.userspacesize	= IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
231	.help		= &help,
232	.parse		= &parse,
233	.final_check	= &final_check,
234	.print		= &print,
235	.save		= &save,
236	.extra_opts	= opts
237};
238
239void _init(void)
240{
241	register_target(&snat);
242}
243