blob: e3c37222159d174798f5592c29a02bae0c5d103d [file] [log] [blame]
Marc Bouchere6869a82000-03-20 06:03:29 +00001/* 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. */
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"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",
28NETFILTER_VERSION);
29}
30
31static struct option opts[] = {
32 { "to-destination", 1, 0, '1' },
33 { 0 }
34};
35
36/* Initialize the target. */
37static void
38init(struct ipt_entry_target *t, unsigned int *nfcache)
39{
40 /* Can't cache this */
41 *nfcache |= NFC_UNKNOWN;
42}
43
44static struct ipt_natinfo *
45append_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.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. */
64static struct ipt_entry_target *
65parse_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 */
140static int
141parse(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))
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. */
170static void final_check(unsigned int flags)
171{
172 if (!flags)
173 exit_error(PARAMETER_PROBLEM,
174 "You must specify --to-destination");
175}
176
177static 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. */
198static void
199print(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. */
214static void
215save(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
227struct iptables_target dnat
228= { NULL,
229 "DNAT",
230 NETFILTER_VERSION,
231 sizeof(struct ip_nat_multi_range),
232 &help,
233 &init,
234 &parse,
235 &final_check,
236 &print,
237 &save,
238 opts
239};
240
241void _init(void)
242{
243 register_target(&dnat);
244}