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