blob: f706f98692c6b80f9a5b9deae47d88b7a445ed9e [file] [log] [blame]
Patrick McHardy524bb802005-11-19 09:00:03 +00001/* 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>
Harald Welted3476b22006-02-01 13:05:33 +000017#include "../include/linux/netfilter_ipv6/ip6t_policy.h"
Patrick McHardy524bb802005-11-19 09:00:03 +000018
19/*
20 * HACK: global pointer to current matchinfo for making
21 * final checks and adjustments in final_check.
22 */
23static struct ip6t_policy_info *policy_info;
24
Jan Engelhardt997045f2007-10-04 16:29:21 +000025static void policy_help(void)
Patrick McHardy524bb802005-11-19 09:00:03 +000026{
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
Jan Engelhardt997045f2007-10-04 16:29:21 +000044static const struct option policy_opts[] =
Patrick McHardy524bb802005-11-19 09:00:03 +000045{
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 },
Max Kellermann9ee386a2008-01-29 13:48:05 +000094 { .name = NULL }
Patrick McHardy524bb802005-11-19 09:00:03 +000095};
96
97/* FIXME - Duplicated code from ip6tables.c */
98/* Duplicated to stop too many changes in other files .... */
99static void
100in6addrcpy(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
106static char *
107addr_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
115static char *
116mask_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... */
130extern u_int16_t parse_protocol(const char *s);
Patrick McHardy524bb802005-11-19 09:00:03 +0000131
132/* End duplicated code from ip6tables.c */
133
Patrick McHardy524bb802005-11-19 09:00:03 +0000134static int parse_direction(char *s)
135{
136 if (strcmp(s, "in") == 0)
137 return IP6T_POLICY_MATCH_IN;
138 if (strcmp(s, "out") == 0)
139 return IP6T_POLICY_MATCH_OUT;
140 exit_error(PARAMETER_PROBLEM, "policy_match: invalid dir `%s'", s);
141}
142
143static int parse_policy(char *s)
144{
145 if (strcmp(s, "none") == 0)
146 return IP6T_POLICY_MATCH_NONE;
147 if (strcmp(s, "ipsec") == 0)
148 return 0;
149 exit_error(PARAMETER_PROBLEM, "policy match: invalid policy `%s'", s);
150}
151
152static int parse_mode(char *s)
153{
154 if (strcmp(s, "transport") == 0)
155 return IP6T_POLICY_MODE_TRANSPORT;
156 if (strcmp(s, "tunnel") == 0)
157 return IP6T_POLICY_MODE_TUNNEL;
158 exit_error(PARAMETER_PROBLEM, "policy match: invalid mode `%s'", s);
159}
160
Jan Engelhardt997045f2007-10-04 16:29:21 +0000161static int policy_parse(int c, char **argv, int invert, unsigned int *flags,
162 const void *entry, struct xt_entry_match **match)
Patrick McHardy524bb802005-11-19 09:00:03 +0000163{
164 struct ip6t_policy_info *info = (void *)(*match)->data;
165 struct ip6t_policy_elem *e = &info->pol[info->len];
166 struct in6_addr *addr = NULL, mask;
167 unsigned int naddr = 0;
168 int mode;
169
170 check_inverse(optarg, &invert, &optind, 0);
171
172 switch (c) {
173 case '1':
174 if (info->flags & (IP6T_POLICY_MATCH_IN|IP6T_POLICY_MATCH_OUT))
175 exit_error(PARAMETER_PROBLEM,
176 "policy match: double --dir option");
177 if (invert)
178 exit_error(PARAMETER_PROBLEM,
179 "policy match: can't invert --dir option");
180
181 info->flags |= parse_direction(argv[optind-1]);
182 break;
183 case '2':
184 if (invert)
185 exit_error(PARAMETER_PROBLEM,
186 "policy match: can't invert --policy option");
187
188 info->flags |= parse_policy(argv[optind-1]);
189 break;
190 case '3':
191 if (info->flags & IP6T_POLICY_MATCH_STRICT)
192 exit_error(PARAMETER_PROBLEM,
193 "policy match: double --strict option");
194
195 if (invert)
196 exit_error(PARAMETER_PROBLEM,
197 "policy match: can't invert --strict option");
198
199 info->flags |= IP6T_POLICY_MATCH_STRICT;
200 break;
201 case '4':
202 if (e->match.reqid)
203 exit_error(PARAMETER_PROBLEM,
204 "policy match: double --reqid option");
205
206 e->match.reqid = 1;
207 e->invert.reqid = invert;
208 e->reqid = strtol(argv[optind-1], NULL, 10);
209 break;
210 case '5':
211 if (e->match.spi)
212 exit_error(PARAMETER_PROBLEM,
213 "policy match: double --spi option");
Patrick McHardy1d0f57c2006-01-12 09:12:47 +0000214
Patrick McHardy524bb802005-11-19 09:00:03 +0000215 e->match.spi = 1;
216 e->invert.spi = invert;
217 e->spi = strtol(argv[optind-1], NULL, 0x10);
218 break;
219 case '6':
220 if (e->match.saddr)
221 exit_error(PARAMETER_PROBLEM,
222 "policy match: double --tunnel-src option");
223
Jan Engelhardtbd943842008-01-20 13:38:08 +0000224 ip6parse_hostnetworkmask(argv[optind-1], &addr, &mask, &naddr);
Patrick McHardy524bb802005-11-19 09:00:03 +0000225 if (naddr > 1)
226 exit_error(PARAMETER_PROBLEM,
227 "policy match: name resolves to multiple IPs");
228
229 e->match.saddr = 1;
230 e->invert.saddr = invert;
Patrick McHardy02e88f22006-01-31 18:24:14 +0000231 in6addrcpy(&e->saddr.a6, addr);
232 in6addrcpy(&e->smask.a6, &mask);
Patrick McHardy524bb802005-11-19 09:00:03 +0000233 break;
234 case '7':
235 if (e->match.daddr)
236 exit_error(PARAMETER_PROBLEM,
237 "policy match: double --tunnel-dst option");
238
Jan Engelhardtbd943842008-01-20 13:38:08 +0000239 ip6parse_hostnetworkmask(argv[optind-1], &addr, &mask, &naddr);
Patrick McHardy524bb802005-11-19 09:00:03 +0000240 if (naddr > 1)
241 exit_error(PARAMETER_PROBLEM,
242 "policy match: name resolves to multiple IPs");
243
244 e->match.daddr = 1;
245 e->invert.daddr = invert;
Patrick McHardy02e88f22006-01-31 18:24:14 +0000246 in6addrcpy(&e->daddr.a6, addr);
247 in6addrcpy(&e->dmask.a6, &mask);
Patrick McHardy524bb802005-11-19 09:00:03 +0000248 break;
249 case '8':
250 if (e->match.proto)
251 exit_error(PARAMETER_PROBLEM,
252 "policy match: double --proto option");
253
254 e->proto = parse_protocol(argv[optind-1]);
255 if (e->proto != IPPROTO_AH && e->proto != IPPROTO_ESP &&
256 e->proto != IPPROTO_COMP)
257 exit_error(PARAMETER_PROBLEM,
258 "policy match: protocol must ah/esp/ipcomp");
259 e->match.proto = 1;
260 e->invert.proto = invert;
261 break;
262 case '9':
263 if (e->match.mode)
264 exit_error(PARAMETER_PROBLEM,
265 "policy match: double --mode option");
Patrick McHardy1d0f57c2006-01-12 09:12:47 +0000266
Patrick McHardy524bb802005-11-19 09:00:03 +0000267 mode = parse_mode(argv[optind-1]);
268 e->match.mode = 1;
269 e->invert.mode = invert;
270 e->mode = mode;
271 break;
272 case 'a':
273 if (invert)
274 exit_error(PARAMETER_PROBLEM,
275 "policy match: can't invert --next option");
276
Patrick McHardy524bb802005-11-19 09:00:03 +0000277 if (++info->len == IP6T_POLICY_MAX_ELEM)
278 exit_error(PARAMETER_PROBLEM,
279 "policy match: maximum policy depth reached");
280 break;
281 default:
282 return 0;
283 }
284
285 policy_info = info;
286 return 1;
287}
288
Jan Engelhardt997045f2007-10-04 16:29:21 +0000289static void policy_check(unsigned int flags)
Patrick McHardy524bb802005-11-19 09:00:03 +0000290{
291 struct ip6t_policy_info *info = policy_info;
292 struct ip6t_policy_elem *e;
293 int i;
294
295 if (info == NULL)
296 exit_error(PARAMETER_PROBLEM,
297 "policy match: no parameters given");
298
299 if (!(info->flags & (IP6T_POLICY_MATCH_IN|IP6T_POLICY_MATCH_OUT)))
300 exit_error(PARAMETER_PROBLEM,
301 "policy match: neither --in nor --out specified");
302
303 if (info->flags & IP6T_POLICY_MATCH_NONE) {
304 if (info->flags & IP6T_POLICY_MATCH_STRICT)
305 exit_error(PARAMETER_PROBLEM,
306 "policy match: policy none but --strict given");
307
308 if (info->len != 0)
309 exit_error(PARAMETER_PROBLEM,
310 "policy match: policy none but policy given");
311 } else
312 info->len++; /* increase len by 1, no --next after last element */
313
314 if (!(info->flags & IP6T_POLICY_MATCH_STRICT) && info->len > 1)
315 exit_error(PARAMETER_PROBLEM,
316 "policy match: multiple elements but no --strict");
317
318 for (i = 0; i < info->len; i++) {
319 e = &info->pol[i];
Patrick McHardya46d88d2006-01-12 09:43:18 +0000320
Noticed by Tom Eastep3f347562006-01-22 13:47:07 +0000321 if (info->flags & IP6T_POLICY_MATCH_STRICT &&
322 !(e->match.reqid || e->match.spi || e->match.saddr ||
Patrick McHardya46d88d2006-01-12 09:43:18 +0000323 e->match.daddr || e->match.proto || e->match.mode))
324 exit_error(PARAMETER_PROBLEM,
325 "policy match: empty policy element");
326
Patrick McHardy524bb802005-11-19 09:00:03 +0000327 if ((e->match.saddr || e->match.daddr)
328 && ((e->mode == IP6T_POLICY_MODE_TUNNEL && e->invert.mode) ||
329 (e->mode == IP6T_POLICY_MODE_TRANSPORT && !e->invert.mode)))
330 exit_error(PARAMETER_PROBLEM,
331 "policy match: --tunnel-src/--tunnel-dst "
332 "is only valid in tunnel mode");
333 }
334}
335
336static void print_mode(char *prefix, u_int8_t mode, int numeric)
337{
338 printf("%smode ", prefix);
339
340 switch (mode) {
341 case IP6T_POLICY_MODE_TRANSPORT:
342 printf("transport ");
343 break;
344 case IP6T_POLICY_MODE_TUNNEL:
345 printf("tunnel ");
346 break;
347 default:
348 printf("??? ");
349 break;
350 }
351}
352
353static void print_proto(char *prefix, u_int8_t proto, int numeric)
354{
355 struct protoent *p = NULL;
356
357 printf("%sproto ", prefix);
358 if (!numeric)
359 p = getprotobynumber(proto);
360 if (p != NULL)
361 printf("%s ", p->p_name);
362 else
363 printf("%u ", proto);
364}
365
366#define PRINT_INVERT(x) \
367do { \
368 if (x) \
369 printf("! "); \
370} while(0)
371
372static void print_entry(char *prefix, const struct ip6t_policy_elem *e,
373 int numeric)
374{
375 if (e->match.reqid) {
376 PRINT_INVERT(e->invert.reqid);
377 printf("%sreqid %u ", prefix, e->reqid);
378 }
379 if (e->match.spi) {
380 PRINT_INVERT(e->invert.spi);
381 printf("%sspi 0x%x ", prefix, e->spi);
382 }
383 if (e->match.proto) {
384 PRINT_INVERT(e->invert.proto);
385 print_proto(prefix, e->proto, numeric);
386 }
387 if (e->match.mode) {
388 PRINT_INVERT(e->invert.mode);
389 print_mode(prefix, e->mode, numeric);
390 }
391 if (e->match.daddr) {
392 PRINT_INVERT(e->invert.daddr);
393 printf("%stunnel-dst %s%s ", prefix,
394 addr_to_numeric((struct in6_addr *)&e->daddr),
395 mask_to_numeric((struct in6_addr *)&e->dmask));
396 }
397 if (e->match.saddr) {
398 PRINT_INVERT(e->invert.saddr);
399 printf("%stunnel-src %s%s ", prefix,
400 addr_to_numeric((struct in6_addr *)&e->saddr),
401 mask_to_numeric((struct in6_addr *)&e->smask));
402 }
403}
404
405static void print_flags(char *prefix, const struct ip6t_policy_info *info)
406{
407 if (info->flags & IP6T_POLICY_MATCH_IN)
408 printf("%sdir in ", prefix);
409 else
410 printf("%sdir out ", prefix);
411
412 if (info->flags & IP6T_POLICY_MATCH_NONE)
413 printf("%spol none ", prefix);
414 else
415 printf("%spol ipsec ", prefix);
416
417 if (info->flags & IP6T_POLICY_MATCH_STRICT)
418 printf("%sstrict ", prefix);
419}
420
Jan Engelhardt997045f2007-10-04 16:29:21 +0000421static void policy_print(const void *ip, const struct xt_entry_match *match,
422 int numeric)
Patrick McHardy524bb802005-11-19 09:00:03 +0000423{
424 const struct ip6t_policy_info *info = (void *)match->data;
425 unsigned int i;
426
427 printf("policy match ");
428 print_flags("", info);
429 for (i = 0; i < info->len; i++) {
430 if (info->len > 1)
431 printf("[%u] ", i);
432 print_entry("", &info->pol[i], numeric);
433 }
434
435 printf("\n");
436}
437
Jan Engelhardt997045f2007-10-04 16:29:21 +0000438static void policy_save(const void *ip, const struct xt_entry_match *match)
Patrick McHardy524bb802005-11-19 09:00:03 +0000439{
440 const struct ip6t_policy_info *info = (void *)match->data;
441 unsigned int i;
442
443 print_flags("--", info);
444 for (i = 0; i < info->len; i++) {
445 print_entry("--", &info->pol[i], 0);
446 if (i + 1 < info->len)
447 printf("--next ");
448 }
449}
450
Jan Engelhardt997045f2007-10-04 16:29:21 +0000451static struct ip6tables_match policy_match6 = {
Patrick McHardy524bb802005-11-19 09:00:03 +0000452 .name = "policy",
453 .version = IPTABLES_VERSION,
454 .size = IP6T_ALIGN(sizeof(struct ip6t_policy_info)),
455 .userspacesize = IP6T_ALIGN(sizeof(struct ip6t_policy_info)),
Jan Engelhardt997045f2007-10-04 16:29:21 +0000456 .help = policy_help,
457 .parse = policy_parse,
458 .final_check = policy_check,
459 .print = policy_print,
460 .save = policy_save,
461 .extra_opts = policy_opts,
Patrick McHardy524bb802005-11-19 09:00:03 +0000462};
463
464void _init(void)
465{
Jan Engelhardt997045f2007-10-04 16:29:21 +0000466 register_match6(&policy_match6);
Patrick McHardy524bb802005-11-19 09:00:03 +0000467}