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