changing trunk/trunk to trunk
[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/nf_nat.h>
10
11 #define IPT_SNAT_OPT_SOURCE 0x01
12 #define IPT_SNAT_OPT_RANDOM 0x02
13
14 /* Source 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 SNAT_help(void)
24 {
25         printf(
26 "SNAT target options:\n"
27 " --to-source <ipaddr>[-<ipaddr>][:port-port]\n"
28 "                               Address to map source to.\n"
29 "[--random]\n");
30 }
31
32 static const struct option SNAT_opts[] = {
33         { "to-source", 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 SNAT_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-source");
158
159                 if (*flags & IPT_SNAT_OPT_SOURCE) {
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-source not supported");
165                 }
166                 *target = parse_to(optarg, portok, info);
167                 /* WTF do we need this for?? */
168                 if (*flags & IPT_SNAT_OPT_RANDOM)
169                         info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
170                 *flags |= IPT_SNAT_OPT_SOURCE;
171                 return 1;
172
173         case '2':
174                 if (*flags & IPT_SNAT_OPT_SOURCE) {
175                         info->mr.range[0].flags |= IP_NAT_RANGE_PROTO_RANDOM;
176                         *flags |= IPT_SNAT_OPT_RANDOM;
177                 } else
178                         *flags |= IPT_SNAT_OPT_RANDOM;
179                 return 1;
180
181         default:
182                 return 0;
183         }
184 }
185
186 /* Final check; must have specfied --to-source. */
187 static void SNAT_check(unsigned int flags)
188 {
189         if (!(flags & IPT_SNAT_OPT_SOURCE))
190                 exit_error(PARAMETER_PROBLEM,
191                            "You must specify --to-source");
192 }
193
194 static void print_range(const struct ip_nat_range *r)
195 {
196         if (r->flags & IP_NAT_RANGE_MAP_IPS) {
197                 struct in_addr a;
198
199                 a.s_addr = r->min_ip;
200                 printf("%s", ipaddr_to_numeric(&a));
201                 if (r->max_ip != r->min_ip) {
202                         a.s_addr = r->max_ip;
203                         printf("-%s", ipaddr_to_numeric(&a));
204                 }
205         }
206         if (r->flags & IP_NAT_RANGE_PROTO_SPECIFIED) {
207                 printf(":");
208                 printf("%hu", ntohs(r->min.tcp.port));
209                 if (r->max.tcp.port != r->min.tcp.port)
210                         printf("-%hu", ntohs(r->max.tcp.port));
211         }
212 }
213
214 /* Prints out the targinfo. */
215 static void SNAT_print(const void *ip, const struct xt_entry_target *target,
216                        int numeric)
217 {
218         struct ipt_natinfo *info = (void *)target;
219         unsigned int i = 0;
220
221         printf("to:");
222         for (i = 0; i < info->mr.rangesize; i++) {
223                 print_range(&info->mr.range[i]);
224                 printf(" ");
225                 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
226                         printf("random ");
227         }
228 }
229
230 /* Saves the union ipt_targinfo in parsable form to stdout. */
231 static void SNAT_save(const void *ip, const struct xt_entry_target *target)
232 {
233         struct ipt_natinfo *info = (void *)target;
234         unsigned int i = 0;
235
236         for (i = 0; i < info->mr.rangesize; i++) {
237                 printf("--to-source ");
238                 print_range(&info->mr.range[i]);
239                 printf(" ");
240                 if (info->mr.range[i].flags & IP_NAT_RANGE_PROTO_RANDOM)
241                         printf("--random ");
242         }
243 }
244
245 static struct xtables_target snat_tg_reg = {
246         .name           = "SNAT",
247         .version        = XTABLES_VERSION,
248         .family         = PF_INET,
249         .size           = XT_ALIGN(sizeof(struct ip_nat_multi_range)),
250         .userspacesize  = XT_ALIGN(sizeof(struct ip_nat_multi_range)),
251         .help           = SNAT_help,
252         .parse          = SNAT_parse,
253         .final_check    = SNAT_check,
254         .print          = SNAT_print,
255         .save           = SNAT_save,
256         .extra_opts     = SNAT_opts,
257 };
258
259 void _init(void)
260 {
261         xtables_register_target(&snat_tg_reg);
262 }