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