blob: d52c1810c772d824975946fb986b188d489f0d3b [file] [log] [blame]
Marc Bouchere6869a82000-03-20 06:03:29 +00001/* 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>
Patrick McHardy40d54752007-04-18 07:00:36 +00009#include <linux/netfilter/nf_nat.h>
Marc Bouchere6869a82000-03-20 06:03:29 +000010
Eric Leblondae4b0b32007-02-24 15:11:33 +000011#define IPT_SNAT_OPT_SOURCE 0x01
12#ifdef IP_NAT_RANGE_PROTO_RANDOM
13# define IPT_SNAT_OPT_RANDOM 0x02
14#endif
15
Marc Bouchere6869a82000-03-20 06:03:29 +000016/* Source NAT data consists of a multi-range, indicating where to map
17 to. */
18struct ipt_natinfo
19{
20 struct ipt_entry_target t;
21 struct ip_nat_multi_range mr;
22};
23
24/* Function which prints out usage message. */
25static void
26help(void)
27{
28 printf(
29"SNAT v%s options:\n"
Eric Leblondae4b0b32007-02-24 15:11:33 +000030" --to-source <ipaddr>[-<ipaddr>][:port-port]"
31#ifdef IP_NAT_RANGE_PROTO_RANDOM
32"[--random]"
33#endif
34"\n"
Marc Bouchere6869a82000-03-20 06:03:29 +000035" Address to map source to.\n"
36" (You can use this more than once)\n\n",
Harald Welte80fe35d2002-05-29 13:08:15 +000037IPTABLES_VERSION);
Marc Bouchere6869a82000-03-20 06:03:29 +000038}
39
40static struct option opts[] = {
41 { "to-source", 1, 0, '1' },
Eric Leblondae4b0b32007-02-24 15:11:33 +000042#ifdef IP_NAT_RANGE_PROTO_RANDOM
43 { "random", 0, 0, '2' },
44#endif
Marc Bouchere6869a82000-03-20 06:03:29 +000045 { 0 }
46};
47
Marc Bouchere6869a82000-03-20 06:03:29 +000048static struct ipt_natinfo *
49append_range(struct ipt_natinfo *info, const struct ip_nat_range *range)
50{
51 unsigned int size;
52
53 /* One rangesize already in struct ipt_natinfo */
54 size = IPT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
55
56 info = realloc(info, size);
57 if (!info)
58 exit_error(OTHER_PROBLEM, "Out of memory\n");
59
Rusty Russell228e98d2000-04-27 10:28:06 +000060 info->t.u.target_size = size;
Marc Bouchere6869a82000-03-20 06:03:29 +000061 info->mr.range[info->mr.rangesize] = *range;
62 info->mr.rangesize++;
63
64 return info;
65}
66
67/* Ranges expected in network order. */
68static struct ipt_entry_target *
69parse_to(char *arg, int portok, struct ipt_natinfo *info)
70{
71 struct ip_nat_range range;
Harald Weltede5ba5d2005-02-01 15:14:15 +000072 char *colon, *dash, *error;
Marc Bouchere6869a82000-03-20 06:03:29 +000073 struct in_addr *ip;
74
75 memset(&range, 0, sizeof(range));
76 colon = strchr(arg, ':');
77
78 if (colon) {
79 int port;
80
81 if (!portok)
82 exit_error(PARAMETER_PROBLEM,
83 "Need TCP or UDP with port specification");
84
85 range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
86
87 port = atoi(colon+1);
Yasuyuki KOZAKAIa3a9c0d2005-06-22 12:22:44 +000088 if (port <= 0 || port > 65535)
Marc Bouchere6869a82000-03-20 06:03:29 +000089 exit_error(PARAMETER_PROBLEM,
90 "Port `%s' not valid\n", colon+1);
91
Harald Weltede5ba5d2005-02-01 15:14:15 +000092 error = strchr(colon+1, ':');
93 if (error)
94 exit_error(PARAMETER_PROBLEM,
95 "Invalid port:port syntax - use dash\n");
96
Marc Bouchere6869a82000-03-20 06:03:29 +000097 dash = strchr(colon, '-');
98 if (!dash) {
99 range.min.tcp.port
100 = range.max.tcp.port
101 = htons(port);
102 } else {
103 int maxport;
104
105 maxport = atoi(dash + 1);
Yasuyuki KOZAKAIa3a9c0d2005-06-22 12:22:44 +0000106 if (maxport <= 0 || maxport > 65535)
Marc Bouchere6869a82000-03-20 06:03:29 +0000107 exit_error(PARAMETER_PROBLEM,
108 "Port `%s' not valid\n", dash+1);
109 if (maxport < port)
110 /* People are stupid. */
111 exit_error(PARAMETER_PROBLEM,
112 "Port range `%s' funky\n", colon+1);
113 range.min.tcp.port = htons(port);
114 range.max.tcp.port = htons(maxport);
115 }
116 /* Starts with a colon? No IP info...*/
117 if (colon == arg)
118 return &(append_range(info, &range)->t);
119 *colon = '\0';
120 }
121
122 range.flags |= IP_NAT_RANGE_MAP_IPS;
123 dash = strchr(arg, '-');
124 if (colon && dash && dash > colon)
125 dash = NULL;
126
127 if (dash)
128 *dash = '\0';
129
130 ip = dotted_to_addr(arg);
131 if (!ip)
132 exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
133 arg);
134 range.min_ip = ip->s_addr;
135 if (dash) {
136 ip = dotted_to_addr(dash+1);
137 if (!ip)
138 exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
139 dash+1);
140 range.max_ip = ip->s_addr;
141 } else
142 range.max_ip = range.min_ip;
143
144 return &(append_range(info, &range)->t);
145}
146
147/* Function which parses command options; returns true if it
148 ate an option */
149static int
150parse(int c, char **argv, int invert, unsigned int *flags,
151 const struct ipt_entry *entry,
152 struct ipt_entry_target **target)
153{
154 struct ipt_natinfo *info = (void *)*target;
155 int portok;
156
157 if (entry->ip.proto == IPPROTO_TCP
Patrick McHardy36d870c2005-07-22 06:39:45 +0000158 || entry->ip.proto == IPPROTO_UDP
159 || entry->ip.proto == IPPROTO_ICMP)
Marc Bouchere6869a82000-03-20 06:03:29 +0000160 portok = 1;
161 else
162 portok = 0;
163
164 switch (c) {
165 case '1':
Harald Welteb77f1da2002-03-14 11:35:58 +0000166 if (check_inverse(optarg, &invert, NULL, 0))
Marc Bouchere6869a82000-03-20 06:03:29 +0000167 exit_error(PARAMETER_PROBLEM,
168 "Unexpected `!' after --to-source");
169
Eric Leblondae4b0b32007-02-24 15:11:33 +0000170 if (*flags & IPT_SNAT_OPT_SOURCE) {
Phil Oester8cf65912005-09-19 15:00:33 +0000171 if (!kernel_version)
172 get_kernel_version();
173 if (kernel_version > LINUX_VERSION(2, 6, 10))
174 exit_error(PARAMETER_PROBLEM,
175 "Multiple --to-source not supported");
176 }
Marc Bouchere6869a82000-03-20 06:03:29 +0000177 *target = parse_to(optarg, portok, info);
Eric Leblondae4b0b32007-02-24 15:11:33 +0000178#ifdef IP_NAT_RANGE_PROTO_RANDOM
179 if (*flags & IPT_SNAT_OPT_RANDOM)
180 info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
181#endif
182 *flags = IPT_SNAT_OPT_SOURCE;
Marc Bouchere6869a82000-03-20 06:03:29 +0000183 return 1;
184
Eric Leblondae4b0b32007-02-24 15:11:33 +0000185#ifdef IP_NAT_RANGE_PROTO_RANDOM
186 case '2':
187 if (*flags & IPT_SNAT_OPT_SOURCE) {
188 info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
189 *flags |= IPT_SNAT_OPT_RANDOM;
190 } else
191 *flags |= IPT_SNAT_OPT_RANDOM;
192 return 1;
193#endif
194
Marc Bouchere6869a82000-03-20 06:03:29 +0000195 default:
196 return 0;
197 }
198}
199
200/* Final check; must have specfied --to-source. */
201static void final_check(unsigned int flags)
202{
Eric Leblondae4b0b32007-02-24 15:11:33 +0000203 if (!(flags & IPT_SNAT_OPT_SOURCE))
Marc Bouchere6869a82000-03-20 06:03:29 +0000204 exit_error(PARAMETER_PROBLEM,
205 "You must specify --to-source");
206}
207
208static void print_range(const struct ip_nat_range *r)
209{
210 if (r->flags & IP_NAT_RANGE_MAP_IPS) {
211 struct in_addr a;
212
213 a.s_addr = r->min_ip;
214 printf("%s", addr_to_dotted(&a));
215 if (r->max_ip != r->min_ip) {
216 a.s_addr = r->max_ip;
217 printf("-%s", addr_to_dotted(&a));
218 }
219 }
220 if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
221 printf(":");
222 printf("%hu", ntohs(r->min.tcp.port));
223 if (r->max.tcp.port != r->min.tcp.port)
224 printf("-%hu", ntohs(r->max.tcp.port));
225 }
Eric Leblondae4b0b32007-02-24 15:11:33 +0000226#ifdef IP_NAT_RANGE_PROTO_RANDOM
227 if (r->flags & IP_NAT_RANGE_PROTO_RANDOM) {
228 printf(" random");
229 }
230#endif
Marc Bouchere6869a82000-03-20 06:03:29 +0000231}
232
233/* Prints out the targinfo. */
234static void
235print(const struct ipt_ip *ip,
236 const struct ipt_entry_target *target,
237 int numeric)
238{
239 struct ipt_natinfo *info = (void *)target;
240 unsigned int i = 0;
241
242 printf("to:");
243 for (i = 0; i < info->mr.rangesize; i++) {
244 print_range(&info->mr.range[i]);
245 printf(" ");
246 }
247}
248
249/* Saves the union ipt_targinfo in parsable form to stdout. */
250static void
251save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
252{
253 struct ipt_natinfo *info = (void *)target;
254 unsigned int i = 0;
255
256 for (i = 0; i < info->mr.rangesize; i++) {
257 printf("--to-source ");
258 print_range(&info->mr.range[i]);
259 printf(" ");
260 }
261}
262
Pablo Neira8caee8b2004-12-28 13:11:59 +0000263static struct iptables_target snat = {
264 .next = NULL,
265 .name = "SNAT",
266 .version = IPTABLES_VERSION,
267 .size = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
268 .userspacesize = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
269 .help = &help,
Pablo Neira8caee8b2004-12-28 13:11:59 +0000270 .parse = &parse,
271 .final_check = &final_check,
272 .print = &print,
273 .save = &save,
274 .extra_opts = opts
Marc Bouchere6869a82000-03-20 06:03:29 +0000275};
276
277void _init(void)
278{
279 register_target(&snat);
280}