This commit was manufactured by cvs2svn to create branch
[iptables.git] / extensions / libipt_policy.c
1 /* Shared library add-on to iptables to add policy support. */
2 #include <stdio.h>
3 #include <netdb.h>
4 #include <string.h>
5 #include <stdlib.h>
6 #include <syslog.h>
7 #include <getopt.h>
8 #include <netdb.h>
9 #include <errno.h>
10 #include <sys/socket.h>
11 #include <netinet/in.h>
12 #include <arpa/inet.h>
13 #include <iptables.h>
14
15 #include <linux/netfilter_ipv4/ip_tables.h>
16 #include "../include/linux/netfilter_ipv4/ipt_policy.h"
17
18 /*
19  * HACK: global pointer to current matchinfo for making
20  * final checks and adjustments in final_check.
21  */
22 static struct ipt_policy_info *policy_info;
23
24 static void help(void)
25 {
26         printf(
27 "policy v%s options:\n"
28 "  --dir in|out                 match policy applied during decapsulation/\n"
29 "                               policy to be applied during encapsulation\n"
30 "  --pol none|ipsec             match policy\n"
31 "  --strict                     match entire policy instead of single element\n"
32 "                               at any position\n"
33 "[!] --reqid reqid              match reqid\n"
34 "[!] --spi spi                  match SPI\n"
35 "[!] --proto proto              match protocol (ah/esp/ipcomp)\n"
36 "[!] --mode mode                match mode (transport/tunnel)\n"
37 "[!] --tunnel-src addr/mask     match tunnel source\n"
38 "[!] --tunnel-dst addr/mask     match tunnel destination\n"
39 "  --next                       begin next element in policy\n",
40         IPTABLES_VERSION);
41 }
42
43 static struct option 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         { }
94 };
95
96 static void init(struct ipt_entry_match *m, unsigned int *nfcache)
97 {
98         *nfcache |= NFC_UNKNOWN;
99 }
100
101 static int parse_direction(char *s)
102 {
103         if (strcmp(s, "in") == 0)
104                 return IPT_POLICY_MATCH_IN;
105         if (strcmp(s, "out") == 0)
106                 return IPT_POLICY_MATCH_OUT;
107         exit_error(PARAMETER_PROBLEM, "policy_match: invalid dir `%s'", s);
108 }
109
110 static int parse_policy(char *s)
111 {
112         if (strcmp(s, "none") == 0)
113                 return IPT_POLICY_MATCH_NONE;
114         if (strcmp(s, "ipsec") == 0)
115                 return 0;
116         exit_error(PARAMETER_PROBLEM, "policy match: invalid policy `%s'", s);
117 }
118
119 static int parse_mode(char *s)
120 {
121         if (strcmp(s, "transport") == 0)
122                 return IPT_POLICY_MODE_TRANSPORT;
123         if (strcmp(s, "tunnel") == 0)
124                 return IPT_POLICY_MODE_TUNNEL;
125         exit_error(PARAMETER_PROBLEM, "policy match: invalid mode `%s'", s);
126 }
127
128 static int parse(int c, char **argv, int invert, unsigned int *flags,
129                  const struct ipt_entry *entry,
130                  unsigned int *nfcache,
131                  struct ipt_entry_match **match)
132 {
133         struct ipt_policy_info *info = (void *)(*match)->data;
134         struct ipt_policy_elem *e = &info->pol[info->len];
135         struct in_addr *addr = NULL, mask;
136         unsigned int naddr = 0;
137         int mode;
138
139         check_inverse(optarg, &invert, &optind, 0);
140
141         switch (c) {
142         case '1':
143                 if (info->flags & (IPT_POLICY_MATCH_IN|IPT_POLICY_MATCH_OUT))
144                         exit_error(PARAMETER_PROBLEM,
145                                    "policy match: double --dir option");
146                 if (invert)
147                         exit_error(PARAMETER_PROBLEM,
148                                    "policy match: can't invert --dir option");
149
150                 info->flags |= parse_direction(argv[optind-1]);
151                 break;
152         case '2':
153                 if (invert)
154                         exit_error(PARAMETER_PROBLEM,
155                                    "policy match: can't invert --policy option");
156
157                 info->flags |= parse_policy(argv[optind-1]);
158                 break;
159         case '3':
160                 if (info->flags & IPT_POLICY_MATCH_STRICT)
161                         exit_error(PARAMETER_PROBLEM,
162                                    "policy match: double --strict option");
163
164                 if (invert)
165                         exit_error(PARAMETER_PROBLEM,
166                                    "policy match: can't invert --strict option");
167
168                 info->flags |= IPT_POLICY_MATCH_STRICT;
169                 break;
170         case '4':
171                 if (e->match.reqid)
172                         exit_error(PARAMETER_PROBLEM,
173                                    "policy match: double --reqid option");
174
175                 e->match.reqid = 1;
176                 e->invert.reqid = invert;
177                 e->reqid = strtol(argv[optind-1], NULL, 10);
178                 break;
179         case '5':
180                 if (e->match.spi)
181                         exit_error(PARAMETER_PROBLEM,
182                                    "policy match: double --spi option");
183
184                 e->match.spi = 1;
185                 e->invert.spi = invert;
186                 e->spi = strtol(argv[optind-1], NULL, 0x10);
187                 break;
188         case '6':
189                 if (e->match.saddr)
190                         exit_error(PARAMETER_PROBLEM,
191                                    "policy match: double --tunnel-src option");
192
193                 parse_hostnetworkmask(argv[optind-1], &addr, &mask, &naddr);
194                 if (naddr > 1)
195                         exit_error(PARAMETER_PROBLEM,
196                                    "policy match: name resolves to multiple IPs");
197
198                 e->match.saddr = 1;
199                 e->invert.saddr = invert;
200                 e->saddr.a4 = addr[0];
201                 e->smask.a4 = mask;
202                 break;
203         case '7':
204                 if (e->match.daddr)
205                         exit_error(PARAMETER_PROBLEM,
206                                    "policy match: double --tunnel-dst option");
207
208                 parse_hostnetworkmask(argv[optind-1], &addr, &mask, &naddr);
209                 if (naddr > 1)
210                         exit_error(PARAMETER_PROBLEM,
211                                    "policy match: name resolves to multiple IPs");
212
213                 e->match.daddr = 1;
214                 e->invert.daddr = invert;
215                 e->daddr.a4 = addr[0];
216                 e->dmask.a4 = mask;
217                 break;
218         case '8':
219                 if (e->match.proto)
220                         exit_error(PARAMETER_PROBLEM,
221                                    "policy match: double --proto option");
222
223                 e->proto = parse_protocol(argv[optind-1]);
224                 if (e->proto != IPPROTO_AH && e->proto != IPPROTO_ESP &&
225                     e->proto != IPPROTO_COMP)
226                         exit_error(PARAMETER_PROBLEM,
227                                    "policy match: protocol must ah/esp/ipcomp");
228                 e->match.proto = 1;
229                 e->invert.proto = invert;
230                 break;
231         case '9':
232                 if (e->match.mode)
233                         exit_error(PARAMETER_PROBLEM,
234                                    "policy match: double --mode option");
235
236                 mode = parse_mode(argv[optind-1]);
237                 e->match.mode = 1;
238                 e->invert.mode = invert;
239                 e->mode = mode;
240                 break;
241         case 'a':
242                 if (invert)
243                         exit_error(PARAMETER_PROBLEM,
244                                    "policy match: can't invert --next option");
245
246                 if (++info->len == IPT_POLICY_MAX_ELEM)
247                         exit_error(PARAMETER_PROBLEM,
248                                    "policy match: maximum policy depth reached");
249                 break;
250         default:
251                 return 0;
252         }
253
254         policy_info = info;
255         return 1;
256 }
257
258 static void final_check(unsigned int flags)
259 {
260         struct ipt_policy_info *info = policy_info;
261         struct ipt_policy_elem *e;
262         int i;
263
264         if (info == NULL)
265                 exit_error(PARAMETER_PROBLEM,
266                            "policy match: no parameters given");
267
268         if (!(info->flags & (IPT_POLICY_MATCH_IN|IPT_POLICY_MATCH_OUT)))
269                 exit_error(PARAMETER_PROBLEM,
270                            "policy match: neither --in nor --out specified");
271
272         if (info->flags & IPT_POLICY_MATCH_NONE) {
273                 if (info->flags & IPT_POLICY_MATCH_STRICT)
274                         exit_error(PARAMETER_PROBLEM,
275                                    "policy match: policy none but --strict given");
276
277                 if (info->len != 0)
278                         exit_error(PARAMETER_PROBLEM,
279                                    "policy match: policy none but policy given");
280         } else
281                 info->len++;    /* increase len by 1, no --next after last element */
282
283         if (!(info->flags & IPT_POLICY_MATCH_STRICT) && info->len > 1)
284                 exit_error(PARAMETER_PROBLEM,
285                            "policy match: multiple elements but no --strict");
286
287         for (i = 0; i < info->len; i++) {
288                 e = &info->pol[i];
289
290                 if (info->flags & IPT_POLICY_MATCH_STRICT &&
291                     !(e->match.reqid || e->match.spi || e->match.saddr ||
292                       e->match.daddr || e->match.proto || e->match.mode))
293                         exit_error(PARAMETER_PROBLEM,
294                                    "policy match: empty policy element");
295
296                 if ((e->match.saddr || e->match.daddr)
297                     && ((e->mode == IPT_POLICY_MODE_TUNNEL && e->invert.mode) ||
298                         (e->mode == IPT_POLICY_MODE_TRANSPORT && !e->invert.mode)))
299                         exit_error(PARAMETER_PROBLEM,
300                                    "policy match: --tunnel-src/--tunnel-dst "
301                                    "is only valid in tunnel mode");
302         }
303 }
304
305 static void print_mode(char *prefix, u_int8_t mode, int numeric)
306 {
307         printf("%smode ", prefix);
308
309         switch (mode) {
310         case IPT_POLICY_MODE_TRANSPORT:
311                 printf("transport ");
312                 break;
313         case IPT_POLICY_MODE_TUNNEL:
314                 printf("tunnel ");
315                 break;
316         default:
317                 printf("??? ");
318                 break;
319         }
320 }
321
322 static void print_proto(char *prefix, u_int8_t proto, int numeric)
323 {
324         struct protoent *p = NULL;
325
326         printf("%sproto ", prefix);
327         if (!numeric)
328                 p = getprotobynumber(proto);
329         if (p != NULL)
330                 printf("%s ", p->p_name);
331         else
332                 printf("%u ", proto);
333 }
334
335 #define PRINT_INVERT(x)         \
336 do {                            \
337         if (x)                  \
338                 printf("! ");   \
339 } while(0)
340
341 static void print_entry(char *prefix, const struct ipt_policy_elem *e,
342                         int numeric)
343 {
344         if (e->match.reqid) {
345                 PRINT_INVERT(e->invert.reqid);
346                 printf("%sreqid %u ", prefix, e->reqid);
347         }
348         if (e->match.spi) {
349                 PRINT_INVERT(e->invert.spi);
350                 printf("%sspi 0x%x ", prefix, e->spi);
351         }
352         if (e->match.proto) {
353                 PRINT_INVERT(e->invert.proto);
354                 print_proto(prefix, e->proto, numeric);
355         }
356         if (e->match.mode) {
357                 PRINT_INVERT(e->invert.mode);
358                 print_mode(prefix, e->mode, numeric);
359         }
360         if (e->match.daddr) {
361                 PRINT_INVERT(e->invert.daddr);
362                 printf("%stunnel-dst %s%s ", prefix,
363                        addr_to_dotted((struct in_addr *)&e->daddr),
364                        mask_to_dotted((struct in_addr *)&e->dmask));
365         }
366         if (e->match.saddr) {
367                 PRINT_INVERT(e->invert.saddr);
368                 printf("%stunnel-src %s%s ", prefix,
369                        addr_to_dotted((struct in_addr *)&e->saddr),
370                        mask_to_dotted((struct in_addr *)&e->smask));
371         }
372 }
373
374 static void print_flags(char *prefix, const struct ipt_policy_info *info)
375 {
376         if (info->flags & IPT_POLICY_MATCH_IN)
377                 printf("%sdir in ", prefix);
378         else
379                 printf("%sdir out ", prefix);
380
381         if (info->flags & IPT_POLICY_MATCH_NONE)
382                 printf("%spol none ", prefix);
383         else
384                 printf("%spol ipsec ", prefix);
385
386         if (info->flags & IPT_POLICY_MATCH_STRICT)
387                 printf("%sstrict ", prefix);
388 }
389
390 static void print(const struct ipt_ip *ip,
391                   const struct ipt_entry_match *match,
392                   int numeric)
393 {
394         const struct ipt_policy_info *info = (void *)match->data;
395         unsigned int i;
396
397         printf("policy match ");
398         print_flags("", info);
399         for (i = 0; i < info->len; i++) {
400                 if (info->len > 1)
401                         printf("[%u] ", i);
402                 print_entry("", &info->pol[i], numeric);
403         }
404 }
405
406 static void save(const struct ipt_ip *ip, const struct ipt_entry_match *match)
407 {
408         const struct ipt_policy_info *info = (void *)match->data;
409         unsigned int i;
410
411         print_flags("--", info);
412         for (i = 0; i < info->len; i++) {
413                 print_entry("--", &info->pol[i], 0);
414                 if (i + 1 < info->len)
415                         printf("--next ");
416         }
417 }
418
419 struct iptables_match policy = {
420         .name           = "policy",
421         .version        = IPTABLES_VERSION,
422         .size           = IPT_ALIGN(sizeof(struct ipt_policy_info)),
423         .userspacesize  = IPT_ALIGN(sizeof(struct ipt_policy_info)),
424         .help           = help,
425         .init           = init,
426         .parse          = parse,
427         .final_check    = final_check,
428         .print          = print,
429         .save           = save,
430         .extra_opts     = opts
431 };
432
433 void _init(void)
434 {
435         register_match(&policy);
436 }