blob: f10d0418e2ff8d847dbbda7283fde6eb777502bd [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>
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",
Harald Welte80fe35d2002-05-29 13:08:15 +000028IPTABLES_VERSION);
Marc Bouchere6869a82000-03-20 06:03:29 +000029}
30
31static struct option opts[] = {
32 { "to-source", 1, 0, '1' },
33 { 0 }
34};
35
36/* Initialize the target. */
37static void
38init(struct ipt_entry_target *t, unsigned int *nfcache)
39{
40 /* Can't cache this */
41 *nfcache |= NFC_UNKNOWN;
42}
43
44static struct ipt_natinfo *
45append_range(struct ipt_natinfo *info, const struct ip_nat_range *range)
46{
47 unsigned int size;
48
49 /* One rangesize already in struct ipt_natinfo */
50 size = IPT_ALIGN(sizeof(*info) + info->mr.rangesize * sizeof(*range));
51
52 info = realloc(info, size);
53 if (!info)
54 exit_error(OTHER_PROBLEM, "Out of memory\n");
55
Rusty Russell228e98d2000-04-27 10:28:06 +000056 info->t.u.target_size = size;
Marc Bouchere6869a82000-03-20 06:03:29 +000057 info->mr.range[info->mr.rangesize] = *range;
58 info->mr.rangesize++;
59
60 return info;
61}
62
63/* Ranges expected in network order. */
64static struct ipt_entry_target *
65parse_to(char *arg, int portok, struct ipt_natinfo *info)
66{
67 struct ip_nat_range range;
Harald Weltede5ba5d2005-02-01 15:14:15 +000068 char *colon, *dash, *error;
Marc Bouchere6869a82000-03-20 06:03:29 +000069 struct in_addr *ip;
70
71 memset(&range, 0, sizeof(range));
72 colon = strchr(arg, ':');
73
74 if (colon) {
75 int port;
76
77 if (!portok)
78 exit_error(PARAMETER_PROBLEM,
79 "Need TCP or UDP with port specification");
80
81 range.flags |= IP_NAT_RANGE_PROTO_SPECIFIED;
82
83 port = atoi(colon+1);
84 if (port == 0 || port > 65535)
85 exit_error(PARAMETER_PROBLEM,
86 "Port `%s' not valid\n", colon+1);
87
Harald Weltede5ba5d2005-02-01 15:14:15 +000088 error = strchr(colon+1, ':');
89 if (error)
90 exit_error(PARAMETER_PROBLEM,
91 "Invalid port:port syntax - use dash\n");
92
Marc Bouchere6869a82000-03-20 06:03:29 +000093 dash = strchr(colon, '-');
94 if (!dash) {
95 range.min.tcp.port
96 = range.max.tcp.port
97 = htons(port);
98 } else {
99 int maxport;
100
101 maxport = atoi(dash + 1);
102 if (maxport == 0 || maxport > 65535)
103 exit_error(PARAMETER_PROBLEM,
104 "Port `%s' not valid\n", dash+1);
105 if (maxport < port)
106 /* People are stupid. */
107 exit_error(PARAMETER_PROBLEM,
108 "Port range `%s' funky\n", colon+1);
109 range.min.tcp.port = htons(port);
110 range.max.tcp.port = htons(maxport);
111 }
112 /* Starts with a colon? No IP info...*/
113 if (colon == arg)
114 return &(append_range(info, &range)->t);
115 *colon = '\0';
116 }
117
118 range.flags |= IP_NAT_RANGE_MAP_IPS;
119 dash = strchr(arg, '-');
120 if (colon && dash && dash > colon)
121 dash = NULL;
122
123 if (dash)
124 *dash = '\0';
125
126 ip = dotted_to_addr(arg);
127 if (!ip)
128 exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
129 arg);
130 range.min_ip = ip->s_addr;
131 if (dash) {
132 ip = dotted_to_addr(dash+1);
133 if (!ip)
134 exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
135 dash+1);
136 range.max_ip = ip->s_addr;
137 } else
138 range.max_ip = range.min_ip;
139
140 return &(append_range(info, &range)->t);
141}
142
143/* Function which parses command options; returns true if it
144 ate an option */
145static int
146parse(int c, char **argv, int invert, unsigned int *flags,
147 const struct ipt_entry *entry,
148 struct ipt_entry_target **target)
149{
150 struct ipt_natinfo *info = (void *)*target;
151 int portok;
152
153 if (entry->ip.proto == IPPROTO_TCP
154 || entry->ip.proto == IPPROTO_UDP)
155 portok = 1;
156 else
157 portok = 0;
158
159 switch (c) {
160 case '1':
Harald Welteb77f1da2002-03-14 11:35:58 +0000161 if (check_inverse(optarg, &invert, NULL, 0))
Marc Bouchere6869a82000-03-20 06:03:29 +0000162 exit_error(PARAMETER_PROBLEM,
163 "Unexpected `!' after --to-source");
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-source");
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-source ");
227 print_range(&info->mr.range[i]);
228 printf(" ");
229 }
230}
231
Pablo Neira8caee8b2004-12-28 13:11:59 +0000232static struct iptables_target snat = {
233 .next = NULL,
234 .name = "SNAT",
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 .init = &init,
240 .parse = &parse,
241 .final_check = &final_check,
242 .print = &print,
243 .save = &save,
244 .extra_opts = opts
Marc Bouchere6869a82000-03-20 06:03:29 +0000245};
246
247void _init(void)
248{
249 register_target(&snat);
250}