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 */ 22static struct ipt_policy_info *policy_info; 23 24static 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 43static 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 96static void init(struct ipt_entry_match *m, unsigned int *nfcache) 97{ 98 *nfcache |= NFC_UNKNOWN; 99} 100 101static 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 110static 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 119static 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 128static 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 258static 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 305static 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 322static 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) \ 336do { \ 337 if (x) \ 338 printf("! "); \ 339} while(0) 340 341static 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 374static 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 390static 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 406static 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 419struct 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 433void ipt_policy_init(void) 434{ 435 register_match(&policy); 436} 437