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