blob: a893a47d1a5ddf221b0b3367383b9a26c08a8b2d [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
Marc Bouchere6869a82000-03-20 06:03:29 +000036static 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
Rusty Russell228e98d2000-04-27 10:28:06 +000048 info->t.u.target_size = size;
Marc Bouchere6869a82000-03-20 06:03:29 +000049 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;
Harald Weltede5ba5d2005-02-01 15:14:15 +000060 char *colon, *dash, *error;
Marc Bouchere6869a82000-03-20 06:03:29 +000061 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);
Yasuyuki KOZAKAIa3a9c0d2005-06-22 12:22:44 +000076 if (port <= 0 || port > 65535)
Marc Bouchere6869a82000-03-20 06:03:29 +000077 exit_error(PARAMETER_PROBLEM,
78 "Port `%s' not valid\n", colon+1);
79
Harald Weltede5ba5d2005-02-01 15:14:15 +000080 error = strchr(colon+1, ':');
81 if (error)
82 exit_error(PARAMETER_PROBLEM,
83 "Invalid port:port syntax - use dash\n");
84
Marc Bouchere6869a82000-03-20 06:03:29 +000085 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);
Yasuyuki KOZAKAIa3a9c0d2005-06-22 12:22:44 +000094 if (maxport <= 0 || maxport > 65535)
Marc Bouchere6869a82000-03-20 06:03:29 +000095 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 portok = 1;
148 else
149 portok = 0;
150
151 switch (c) {
152 case '1':
Harald Welteb77f1da2002-03-14 11:35:58 +0000153 if (check_inverse(optarg, &invert, NULL, 0))
Marc Bouchere6869a82000-03-20 06:03:29 +0000154 exit_error(PARAMETER_PROBLEM,
155 "Unexpected `!' after --to-source");
156
157 *target = parse_to(optarg, portok, info);
158 *flags = 1;
159 return 1;
160
161 default:
162 return 0;
163 }
164}
165
166/* Final check; must have specfied --to-source. */
167static void final_check(unsigned int flags)
168{
169 if (!flags)
170 exit_error(PARAMETER_PROBLEM,
171 "You must specify --to-source");
172}
173
174static void print_range(const struct ip_nat_range *r)
175{
176 if (r->flags & IP_NAT_RANGE_MAP_IPS) {
177 struct in_addr a;
178
179 a.s_addr = r->min_ip;
180 printf("%s", addr_to_dotted(&a));
181 if (r->max_ip != r->min_ip) {
182 a.s_addr = r->max_ip;
183 printf("-%s", addr_to_dotted(&a));
184 }
185 }
186 if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
187 printf(":");
188 printf("%hu", ntohs(r->min.tcp.port));
189 if (r->max.tcp.port != r->min.tcp.port)
190 printf("-%hu", ntohs(r->max.tcp.port));
191 }
192}
193
194/* Prints out the targinfo. */
195static void
196print(const struct ipt_ip *ip,
197 const struct ipt_entry_target *target,
198 int numeric)
199{
200 struct ipt_natinfo *info = (void *)target;
201 unsigned int i = 0;
202
203 printf("to:");
204 for (i = 0; i < info->mr.rangesize; i++) {
205 print_range(&info->mr.range[i]);
206 printf(" ");
207 }
208}
209
210/* Saves the union ipt_targinfo in parsable form to stdout. */
211static void
212save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
213{
214 struct ipt_natinfo *info = (void *)target;
215 unsigned int i = 0;
216
217 for (i = 0; i < info->mr.rangesize; i++) {
218 printf("--to-source ");
219 print_range(&info->mr.range[i]);
220 printf(" ");
221 }
222}
223
Pablo Neira8caee8b2004-12-28 13:11:59 +0000224static struct iptables_target snat = {
225 .next = NULL,
226 .name = "SNAT",
227 .version = IPTABLES_VERSION,
228 .size = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
229 .userspacesize = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
230 .help = &help,
Pablo Neira8caee8b2004-12-28 13:11:59 +0000231 .parse = &parse,
232 .final_check = &final_check,
233 .print = &print,
234 .save = &save,
235 .extra_opts = opts
Marc Bouchere6869a82000-03-20 06:03:29 +0000236};
237
238void _init(void)
239{
240 register_target(&snat);
241}