changing trunk/trunk to trunk
[iptables.git] / extensions / libip6t_policy.c
1 /* Shared library add-on to iptables to add policy support. */
2
3 #include <stdio.h>
4 #include <netdb.h>
5 #include <string.h>
6 #include <stdlib.h>
7 #include <syslog.h>
8 #include <getopt.h>
9 #include <netdb.h>
10 #include <errno.h>
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 #include <ip6tables.h>
15
16 #include <linux/netfilter_ipv6/ip6_tables.h>
17 #include "../include/linux/netfilter_ipv6/ip6t_policy.h"
18
19 /*
20  * HACK: global pointer to current matchinfo for making
21  * final checks and adjustments in final_check.
22  */
23 static struct ip6t_policy_info *policy_info;
24
25 static void policy_help(void)
26 {
27         printf(
28 "policy match options:\n"
29 "  --dir in|out                 match policy applied during decapsulation/\n"
30 "                               policy to be applied during encapsulation\n"
31 "  --pol none|ipsec             match policy\n"
32 "  --strict                     match entire policy instead of single element\n"
33 "                               at any position\n"
34 "[!] --reqid reqid              match reqid\n"
35 "[!] --spi spi                  match SPI\n"
36 "[!] --proto proto              match protocol (ah/esp/ipcomp)\n"
37 "[!] --mode mode                match mode (transport/tunnel)\n"
38 "[!] --tunnel-src addr/masklen  match tunnel source\n"
39 "[!] --tunnel-dst addr/masklen  match tunnel destination\n"
40 "  --next                       begin next element in policy\n");
41 }
42
43 static const struct option policy_opts[] =
44 {
45         {
46                 .name           = "dir",
47                 .has_arg        = 1,
48                 .val            = '1',
49         },
50         {
51                 .name           = "pol",
52                 .has_arg        = 1,
53                 .val            = '2',
54         },
55         {
56                 .name           = "strict",
57                 .val            = '3'
58         },
59         {
60                 .name           = "reqid",
61                 .has_arg        = 1,
62                 .val            = '4',
63         },
64         {
65                 .name           = "spi",
66                 .has_arg        = 1,
67                 .val            = '5'
68         },
69         {
70                 .name           = "tunnel-src",
71                 .has_arg        = 1,
72                 .val            = '6'
73         },
74         {
75                 .name           = "tunnel-dst",
76                 .has_arg        = 1,
77                 .val            = '7'
78         },
79         {
80                 .name           = "proto",
81                 .has_arg        = 1,
82                 .val            = '8'
83         },
84         {
85                 .name           = "mode",
86                 .has_arg        = 1,
87                 .val            = '9'
88         },
89         {
90                 .name           = "next",
91                 .val            = 'a'
92         },
93         { .name = NULL }
94 };
95
96 /* FIXME - Duplicated code from ip6tables.c */
97 /* Duplicated to stop too many changes in other files .... */
98 static void
99 in6addrcpy(struct in6_addr *dst, struct in6_addr *src)
100 {
101         memcpy(dst, src, sizeof(struct in6_addr));
102         /* dst->s6_addr = src->s6_addr; */
103 }
104
105 static char *
106 addr_to_numeric(const struct in6_addr *addrp)
107 {
108         /* 0000:0000:0000:0000:0000:000.000.000.000
109          * 0000:0000:0000:0000:0000:0000:0000:0000 */
110         static char buf[50+1];
111         return (char *)inet_ntop(AF_INET6, addrp, buf, sizeof(buf));
112 }
113
114 static char *
115 mask_to_numeric(const struct in6_addr *addrp)
116 {
117         static char buf[50+2];
118         int l = ipv6_prefix_length(addrp);
119         if (l == -1) {
120                 strcpy(buf, "/");
121                 strcat(buf, addr_to_numeric(addrp));
122                 return buf;
123         }
124         sprintf(buf, "/%d", l);
125         return buf;
126 }
127
128 static int parse_direction(char *s)
129 {
130         if (strcmp(s, "in") == 0)
131                 return IP6T_POLICY_MATCH_IN;
132         if (strcmp(s, "out") == 0)
133                 return IP6T_POLICY_MATCH_OUT;
134         exit_error(PARAMETER_PROBLEM, "policy_match: invalid dir `%s'", s);
135 }
136
137 static int parse_policy(char *s)
138 {
139         if (strcmp(s, "none") == 0)
140                 return IP6T_POLICY_MATCH_NONE;
141         if (strcmp(s, "ipsec") == 0)
142                 return 0;
143         exit_error(PARAMETER_PROBLEM, "policy match: invalid policy `%s'", s);
144 }
145
146 static int parse_mode(char *s)
147 {
148         if (strcmp(s, "transport") == 0)
149                 return IP6T_POLICY_MODE_TRANSPORT;
150         if (strcmp(s, "tunnel") == 0)
151                 return IP6T_POLICY_MODE_TUNNEL;
152         exit_error(PARAMETER_PROBLEM, "policy match: invalid mode `%s'", s);
153 }
154
155 static int policy_parse(int c, char **argv, int invert, unsigned int *flags,
156                         const void *entry, struct xt_entry_match **match)
157 {
158         struct ip6t_policy_info *info = (void *)(*match)->data;
159         struct ip6t_policy_elem *e = &info->pol[info->len];
160         struct in6_addr *addr = NULL, mask;
161         unsigned int naddr = 0;
162         int mode;
163
164         check_inverse(optarg, &invert, &optind, 0);
165
166         switch (c) {
167         case '1':
168                 if (info->flags & (IP6T_POLICY_MATCH_IN|IP6T_POLICY_MATCH_OUT))
169                         exit_error(PARAMETER_PROBLEM,
170                                    "policy match: double --dir option");
171                 if (invert)
172                         exit_error(PARAMETER_PROBLEM,
173                                    "policy match: can't invert --dir option");
174
175                 info->flags |= parse_direction(argv[optind-1]);
176                 break;
177         case '2':
178                 if (invert)
179                         exit_error(PARAMETER_PROBLEM,
180                                    "policy match: can't invert --policy option");
181
182                 info->flags |= parse_policy(argv[optind-1]);
183                 break;
184         case '3':
185                 if (info->flags & IP6T_POLICY_MATCH_STRICT)
186                         exit_error(PARAMETER_PROBLEM,
187                                    "policy match: double --strict option");
188
189                 if (invert)
190                         exit_error(PARAMETER_PROBLEM,
191                                    "policy match: can't invert --strict option");
192
193                 info->flags |= IP6T_POLICY_MATCH_STRICT;
194                 break;
195         case '4':
196                 if (e->match.reqid)
197                         exit_error(PARAMETER_PROBLEM,
198                                    "policy match: double --reqid option");
199
200                 e->match.reqid = 1;
201                 e->invert.reqid = invert;
202                 e->reqid = strtol(argv[optind-1], NULL, 10);
203                 break;
204         case '5':
205                 if (e->match.spi)
206                         exit_error(PARAMETER_PROBLEM,
207                                    "policy match: double --spi option");
208
209                 e->match.spi = 1;
210                 e->invert.spi = invert;
211                 e->spi = strtol(argv[optind-1], NULL, 0x10);
212                 break;
213         case '6':
214                 if (e->match.saddr)
215                         exit_error(PARAMETER_PROBLEM,
216                                    "policy match: double --tunnel-src option");
217
218                 ip6parse_hostnetworkmask(argv[optind-1], &addr, &mask, &naddr);
219                 if (naddr > 1)
220                         exit_error(PARAMETER_PROBLEM,
221                                    "policy match: name resolves to multiple IPs");
222
223                 e->match.saddr = 1;
224                 e->invert.saddr = invert;
225                 in6addrcpy(&e->saddr.a6, addr);
226                 in6addrcpy(&e->smask.a6, &mask);
227                 break;
228         case '7':
229                 if (e->match.daddr)
230                         exit_error(PARAMETER_PROBLEM,
231                                    "policy match: double --tunnel-dst option");
232
233                 ip6parse_hostnetworkmask(argv[optind-1], &addr, &mask, &naddr);
234                 if (naddr > 1)
235                         exit_error(PARAMETER_PROBLEM,
236                                    "policy match: name resolves to multiple IPs");
237
238                 e->match.daddr = 1;
239                 e->invert.daddr = invert;
240                 in6addrcpy(&e->daddr.a6, addr);
241                 in6addrcpy(&e->dmask.a6, &mask);
242                 break;
243         case '8':
244                 if (e->match.proto)
245                         exit_error(PARAMETER_PROBLEM,
246                                    "policy match: double --proto option");
247
248                 e->proto = parse_protocol(argv[optind-1]);
249                 if (e->proto != IPPROTO_AH && e->proto != IPPROTO_ESP &&
250                     e->proto != IPPROTO_COMP)
251                         exit_error(PARAMETER_PROBLEM,
252                                    "policy match: protocol must ah/esp/ipcomp");
253                 e->match.proto = 1;
254                 e->invert.proto = invert;
255                 break;
256         case '9':
257                 if (e->match.mode)
258                         exit_error(PARAMETER_PROBLEM,
259                                    "policy match: double --mode option");
260
261                 mode = parse_mode(argv[optind-1]);
262                 e->match.mode = 1;
263                 e->invert.mode = invert;
264                 e->mode = mode;
265                 break;
266         case 'a':
267                 if (invert)
268                         exit_error(PARAMETER_PROBLEM,
269                                    "policy match: can't invert --next option");
270
271                 if (++info->len == IP6T_POLICY_MAX_ELEM)
272                         exit_error(PARAMETER_PROBLEM,
273                                    "policy match: maximum policy depth reached");
274                 break;
275         default:
276                 return 0;
277         }
278
279         policy_info = info;
280         return 1;
281 }
282
283 static void policy_check(unsigned int flags)
284 {
285         struct ip6t_policy_info *info = policy_info;
286         struct ip6t_policy_elem *e;
287         int i;
288
289         if (info == NULL)
290                 exit_error(PARAMETER_PROBLEM,
291                            "policy match: no parameters given");
292
293         if (!(info->flags & (IP6T_POLICY_MATCH_IN|IP6T_POLICY_MATCH_OUT)))
294                 exit_error(PARAMETER_PROBLEM,
295                            "policy match: neither --in nor --out specified");
296
297         if (info->flags & IP6T_POLICY_MATCH_NONE) {
298                 if (info->flags & IP6T_POLICY_MATCH_STRICT)
299                         exit_error(PARAMETER_PROBLEM,
300                                    "policy match: policy none but --strict given");
301
302                 if (info->len != 0)
303                         exit_error(PARAMETER_PROBLEM,
304                                    "policy match: policy none but policy given");
305         } else
306                 info->len++;    /* increase len by 1, no --next after last element */
307
308         if (!(info->flags & IP6T_POLICY_MATCH_STRICT) && info->len > 1)
309                 exit_error(PARAMETER_PROBLEM,
310                            "policy match: multiple elements but no --strict");
311
312         for (i = 0; i < info->len; i++) {
313                 e = &info->pol[i];
314
315                 if (info->flags & IP6T_POLICY_MATCH_STRICT &&
316                     !(e->match.reqid || e->match.spi || e->match.saddr ||
317                       e->match.daddr || e->match.proto || e->match.mode))
318                         exit_error(PARAMETER_PROBLEM,
319                                    "policy match: empty policy element");
320
321                 if ((e->match.saddr || e->match.daddr)
322                     && ((e->mode == IP6T_POLICY_MODE_TUNNEL && e->invert.mode) ||
323                         (e->mode == IP6T_POLICY_MODE_TRANSPORT && !e->invert.mode)))
324                         exit_error(PARAMETER_PROBLEM,
325                                    "policy match: --tunnel-src/--tunnel-dst "
326                                    "is only valid in tunnel mode");
327         }
328 }
329
330 static void print_mode(char *prefix, u_int8_t mode, int numeric)
331 {
332         printf("%smode ", prefix);
333
334         switch (mode) {
335         case IP6T_POLICY_MODE_TRANSPORT:
336                 printf("transport ");
337                 break;
338         case IP6T_POLICY_MODE_TUNNEL:
339                 printf("tunnel ");
340                 break;
341         default:
342                 printf("??? ");
343                 break;
344         }
345 }
346
347 static void print_proto(char *prefix, u_int8_t proto, int numeric)
348 {
349         struct protoent *p = NULL;
350
351         printf("%sproto ", prefix);
352         if (!numeric)
353                 p = getprotobynumber(proto);
354         if (p != NULL)
355                 printf("%s ", p->p_name);
356         else
357                 printf("%u ", proto);
358 }
359
360 #define PRINT_INVERT(x)         \
361 do {                            \
362         if (x)                  \
363                 printf("! ");   \
364 } while(0)
365
366 static void print_entry(char *prefix, const struct ip6t_policy_elem *e,
367                         int numeric)
368 {
369         if (e->match.reqid) {
370                 PRINT_INVERT(e->invert.reqid);
371                 printf("%sreqid %u ", prefix, e->reqid);
372         }
373         if (e->match.spi) {
374                 PRINT_INVERT(e->invert.spi);
375                 printf("%sspi 0x%x ", prefix, e->spi);
376         }
377         if (e->match.proto) {
378                 PRINT_INVERT(e->invert.proto);
379                 print_proto(prefix, e->proto, numeric);
380         }
381         if (e->match.mode) {
382                 PRINT_INVERT(e->invert.mode);
383                 print_mode(prefix, e->mode, numeric);
384         }
385         if (e->match.daddr) {
386                 PRINT_INVERT(e->invert.daddr);
387                 printf("%stunnel-dst %s%s ", prefix,
388                        addr_to_numeric((struct in6_addr *)&e->daddr),
389                        mask_to_numeric((struct in6_addr *)&e->dmask));
390         }
391         if (e->match.saddr) {
392                 PRINT_INVERT(e->invert.saddr);
393                 printf("%stunnel-src %s%s ", prefix,
394                        addr_to_numeric((struct in6_addr *)&e->saddr),
395                        mask_to_numeric((struct in6_addr *)&e->smask));
396         }
397 }
398
399 static void print_flags(char *prefix, const struct ip6t_policy_info *info)
400 {
401         if (info->flags & IP6T_POLICY_MATCH_IN)
402                 printf("%sdir in ", prefix);
403         else
404                 printf("%sdir out ", prefix);
405
406         if (info->flags & IP6T_POLICY_MATCH_NONE)
407                 printf("%spol none ", prefix);
408         else
409                 printf("%spol ipsec ", prefix);
410
411         if (info->flags & IP6T_POLICY_MATCH_STRICT)
412                 printf("%sstrict ", prefix);
413 }
414
415 static void policy_print(const void *ip, const struct xt_entry_match *match,
416                          int numeric)
417 {
418         const struct ip6t_policy_info *info = (void *)match->data;
419         unsigned int i;
420
421         printf("policy match ");
422         print_flags("", info);
423         for (i = 0; i < info->len; i++) {
424                 if (info->len > 1)
425                         printf("[%u] ", i);
426                 print_entry("", &info->pol[i], numeric);
427         }
428
429         printf("\n");
430 }
431
432 static void policy_save(const void *ip, const struct xt_entry_match *match)
433 {
434         const struct ip6t_policy_info *info = (void *)match->data;
435         unsigned int i;
436
437         print_flags("--", info);
438         for (i = 0; i < info->len; i++) {
439                 print_entry("--", &info->pol[i], 0);
440                 if (i + 1 < info->len)
441                         printf("--next ");
442         }
443 }
444
445 static struct xtables_match policy_mt6_reg = {
446         .name           = "policy",
447         .version        = XTABLES_VERSION,
448         .family         = PF_INET6,
449         .size           = XT_ALIGN(sizeof(struct ip6t_policy_info)),
450         .userspacesize  = XT_ALIGN(sizeof(struct ip6t_policy_info)),
451         .help           = policy_help,
452         .parse          = policy_parse,
453         .final_check    = policy_check,
454         .print          = policy_print,
455         .save           = policy_save,
456         .extra_opts     = policy_opts,
457 };
458
459 void _init(void)
460 {
461         xtables_register_match(&policy_mt6_reg);
462 }