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