iptables-1.2.9-2.3.1.src.rpm
[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_ipv4/ip_nat_rule.h>
10
11 /* Dest NAT data consists of a multi-range, indicating where to map
12    to. */
13 struct 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. */
20 static void
21 help(void)
22 {
23         printf(
24 "DNAT v%s options:\n"
25 " --to-destination <ipaddr>[-<ipaddr>][:port-port]\n"
26 "                               Address to map destination to.\n"
27 "                               (You can use this more than once)\n\n",
28 IPTABLES_VERSION);
29 }
30
31 static struct option opts[] = {
32         { "to-destination", 1, 0, '1' },
33         { 0 }
34 };
35
36 /* Initialize the target. */
37 static void
38 init(struct ipt_entry_target *t, unsigned int *nfcache)
39 {
40         /* Can't cache this */
41         *nfcache |= NFC_UNKNOWN;
42 }
43
44 static struct ipt_natinfo *
45 append_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
56         info->t.u.target_size = size;
57         info->mr.range[info->mr.rangesize] = *range;
58         info->mr.rangesize++;
59
60         return info;
61 }
62
63 /* Ranges expected in network order. */
64 static struct ipt_entry_target *
65 parse_to(char *arg, int portok, struct ipt_natinfo *info)
66 {
67         struct ip_nat_range range;
68         char *colon, *dash;
69         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
88                 dash = strchr(colon, '-');
89                 if (!dash) {
90                         range.min.tcp.port
91                                 = range.max.tcp.port
92                                 = htons(port);
93                 } else {
94                         int maxport;
95
96                         maxport = atoi(dash + 1);
97                         if (maxport == 0 || maxport > 65535)
98                                 exit_error(PARAMETER_PROBLEM,
99                                            "Port `%s' not valid\n", dash+1);
100                         if (maxport < port)
101                                 /* People are stupid. */
102                                 exit_error(PARAMETER_PROBLEM,
103                                            "Port range `%s' funky\n", colon+1);
104                         range.min.tcp.port = htons(port);
105                         range.max.tcp.port = htons(maxport);
106                 }
107                 /* Starts with a colon? No IP info...*/
108                 if (colon == arg)
109                         return &(append_range(info, &range)->t);
110                 *colon = '\0';
111         }
112
113         range.flags |= IP_NAT_RANGE_MAP_IPS;
114         dash = strchr(arg, '-');
115         if (colon && dash && dash > colon)
116                 dash = NULL;
117
118         if (dash)
119                 *dash = '\0';
120
121         ip = dotted_to_addr(arg);
122         if (!ip)
123                 exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
124                            arg);
125         range.min_ip = ip->s_addr;
126         if (dash) {
127                 ip = dotted_to_addr(dash+1);
128                 if (!ip)
129                         exit_error(PARAMETER_PROBLEM, "Bad IP address `%s'\n",
130                                    dash+1);
131                 range.max_ip = ip->s_addr;
132         } else
133                 range.max_ip = range.min_ip;
134
135         return &(append_range(info, &range)->t);
136 }
137
138 /* Function which parses command options; returns true if it
139    ate an option */
140 static int
141 parse(int c, char **argv, int invert, unsigned int *flags,
142       const struct ipt_entry *entry,
143       struct ipt_entry_target **target)
144 {
145         struct ipt_natinfo *info = (void *)*target;
146         int portok;
147
148         if (entry->ip.proto == IPPROTO_TCP
149             || entry->ip.proto == IPPROTO_UDP)
150                 portok = 1;
151         else
152                 portok = 0;
153
154         switch (c) {
155         case '1':
156                 if (check_inverse(optarg, &invert, NULL, 0))
157                         exit_error(PARAMETER_PROBLEM,
158                                    "Unexpected `!' after --to-destination");
159
160                 *target = parse_to(optarg, portok, info);
161                 *flags = 1;
162                 return 1;
163
164         default:
165                 return 0;
166         }
167 }
168
169 /* Final check; must have specfied --to-source. */
170 static void final_check(unsigned int flags)
171 {
172         if (!flags)
173                 exit_error(PARAMETER_PROBLEM,
174                            "You must specify --to-destination");
175 }
176
177 static void print_range(const struct ip_nat_range *r)
178 {
179         if (r->flags & IP_NAT_RANGE_MAP_IPS) {
180                 struct in_addr a;
181
182                 a.s_addr = r->min_ip;
183                 printf("%s", addr_to_dotted(&a));
184                 if (r->max_ip != r->min_ip) {
185                         a.s_addr = r->max_ip;
186                         printf("-%s", addr_to_dotted(&a));
187                 }
188         }
189         if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
190                 printf(":");
191                 printf("%hu", ntohs(r->min.tcp.port));
192                 if (r->max.tcp.port != r->min.tcp.port)
193                         printf("-%hu", ntohs(r->max.tcp.port));
194         }
195 }
196
197 /* Prints out the targinfo. */
198 static void
199 print(const struct ipt_ip *ip,
200       const struct ipt_entry_target *target,
201       int numeric)
202 {
203         struct ipt_natinfo *info = (void *)target;
204         unsigned int i = 0;
205
206         printf("to:");
207         for (i = 0; i < info->mr.rangesize; i++) {
208                 print_range(&info->mr.range[i]);
209                 printf(" ");
210         }
211 }
212
213 /* Saves the union ipt_targinfo in parsable form to stdout. */
214 static void
215 save(const struct ipt_ip *ip, const struct ipt_entry_target *target)
216 {
217         struct ipt_natinfo *info = (void *)target;
218         unsigned int i = 0;
219
220         for (i = 0; i < info->mr.rangesize; i++) {
221                 printf("--to-destination ");
222                 print_range(&info->mr.range[i]);
223                 printf(" ");
224         }
225 }
226
227 static
228 struct iptables_target dnat
229 = { NULL,
230     "DNAT",
231     IPTABLES_VERSION,
232     IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
233     IPT_ALIGN(sizeof(struct ip_nat_multi_range)),
234     &help,
235     &init,
236     &parse,
237     &final_check,
238     &print,
239     &save,
240     opts
241 };
242
243 void _init(void)
244 {
245         register_target(&dnat);
246 }