fix for f12, gcc4.4
[iptables.git] / extensions / libipt_DNAT.c
1 /* Shared library add-on to iptables to add destination-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_DNAT_OPT_DEST 0x1
12 #define IPT_DNAT_OPT_RANDOM 0x2
13
14 /* Dest 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 "DNAT v%s options:\n"
28 " --to-destination <ipaddr>[-<ipaddr>][:port-port]\n"
29 "                               Address to map destination to.\n"
30 "[--random]\n"
31 "\n",
32 IPTABLES_VERSION);
33 }
34
35 static struct option opts[] = {
36         { "to-destination", 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-destination");
162
163                 if (*flags) {
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-destination not supported");
169                 }
170                 *target = parse_to(optarg, portok, info);
171                 /* WTF do we need this for?? */
172                 if (*flags & IPT_DNAT_OPT_RANDOM)
173                         info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
174                 *flags |= IPT_DNAT_OPT_DEST;
175                 return 1;
176
177         case '2':
178                 if (*flags & IPT_DNAT_OPT_DEST) {
179                         info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
180                         *flags |= IPT_DNAT_OPT_RANDOM;
181                 } else
182                         *flags |= IPT_DNAT_OPT_RANDOM;
183         default:
184                 return 0;
185         }
186 }
187
188 /* Final check; must have specfied --to-source. */
189 static void final_check(unsigned int flags)
190 {
191         if (!flags)
192                 exit_error(PARAMETER_PROBLEM,
193                            "You must specify --to-destination");
194 }
195
196 static void print_range(const struct ip_nat_range *r)
197 {
198         if (r->flags & IP_NAT_RANGE_MAP_IPS) {
199                 struct in_addr a;
200
201                 a.s_addr = r->min_ip;
202                 printf("%s", addr_to_dotted(&a));
203                 if (r->max_ip != r->min_ip) {
204                         a.s_addr = r->max_ip;
205                         printf("-%s", addr_to_dotted(&a));
206                 }
207         }
208         if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
209                 printf(":");
210                 printf("%hu", ntohs(r->min.tcp.port));
211                 if (r->max.tcp.port != r->min.tcp.port)
212                         printf("-%hu", ntohs(r->max.tcp.port));
213         }
214 }
215
216 /* Prints out the targinfo. */
217 static void
218 print(const struct ipt_ip *ip,
219       const struct ipt_entry_target *target,
220       int numeric)
221 {
222         struct ipt_natinfo *info = (void *)target;
223         unsigned int i = 0;
224
225         printf("to:");
226         for (i = 0; i < info->mr.rangesize; i++) {
227                 print_range(&info->mr.range[i]);
228                 printf(" ");
229                 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
230                         printf("random ");
231         }
232 }
233
234 /* Saves the union ipt_targinfo in parsable form to stdout. */
235 static void
236 save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
237 {
238         struct ipt_natinfo *info = (void *)target;
239         unsigned int i = 0;
240
241         for (i = 0; i < info->mr.rangesize; i++) {
242                 printf("--to-destination ");
243                 print_range(&info->mr.range[i]);
244                 printf(" ");
245                 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
246                         printf("--random ");
247         }
248 }
249
250 static struct iptables_target dnat = { 
251         .next           = NULL,
252         .name           = "DNAT",
253         .version        = IPTABLES_VERSION,
254         .size           = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
255         .userspacesize  = IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
256         .help           = &help,
257         .parse          = &parse,
258         .final_check    = &final_check,
259         .print          = &print,
260         .save           = &save,
261         .extra_opts     = opts
262 };
263
264 void _init(void)
265 {
266         register_target(&dnat);
267 }