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