1 #include <stdbool.h> 2 #include <stdint.h> 3 #include <stdio.h> 4 #include <string.h> 5 #include <netdb.h> 6 #include <xtables.h> 7 #include <linux/netfilter/xt_policy.h> 8 9 enum { 10 O_DIRECTION = 0, 11 O_POLICY, 12 O_STRICT, 13 O_REQID, 14 O_SPI, 15 O_PROTO, 16 O_MODE, 17 O_TUNNELSRC, 18 O_TUNNELDST, 19 O_NEXT, 20 F_STRICT = 1 << O_STRICT, 21 }; 22 23 static 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 "These options may be used repeatedly, to describe policy elements:\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 } 41 42 static const struct xt_option_entry policy_opts[] = { 43 {.name = "dir", .id = O_DIRECTION, .type = XTTYPE_STRING, 44 .flags = XTOPT_INVERT}, 45 {.name = "pol", .id = O_POLICY, .type = XTTYPE_STRING}, 46 {.name = "strict", .id = O_STRICT, .type = XTTYPE_NONE}, 47 {.name = "reqid", .id = O_REQID, .type = XTTYPE_UINT32, 48 .flags = XTOPT_MULTI | XTOPT_INVERT}, 49 {.name = "spi", .id = O_SPI, .type = XTTYPE_UINT32, 50 .flags = XTOPT_MULTI | XTOPT_INVERT}, 51 {.name = "tunnel-src", .id = O_TUNNELSRC, .type = XTTYPE_HOSTMASK, 52 .flags = XTOPT_MULTI | XTOPT_INVERT}, 53 {.name = "tunnel-dst", .id = O_TUNNELDST, .type = XTTYPE_HOSTMASK, 54 .flags = XTOPT_MULTI | XTOPT_INVERT}, 55 {.name = "proto", .id = O_PROTO, .type = XTTYPE_PROTOCOL, 56 .flags = XTOPT_MULTI | XTOPT_INVERT}, 57 {.name = "mode", .id = O_MODE, .type = XTTYPE_STRING, 58 .flags = XTOPT_MULTI | XTOPT_INVERT}, 59 {.name = "next", .id = O_NEXT, .type = XTTYPE_NONE, 60 .flags = XTOPT_MULTI, .also = F_STRICT}, 61 XTOPT_TABLEEND, 62 }; 63 64 static int parse_direction(const char *s) 65 { 66 if (strcmp(s, "in") == 0) 67 return XT_POLICY_MATCH_IN; 68 if (strcmp(s, "out") == 0) 69 return XT_POLICY_MATCH_OUT; 70 xtables_error(PARAMETER_PROBLEM, "policy_match: invalid dir \"%s\"", s); 71 } 72 73 static int parse_policy(const char *s) 74 { 75 if (strcmp(s, "none") == 0) 76 return XT_POLICY_MATCH_NONE; 77 if (strcmp(s, "ipsec") == 0) 78 return 0; 79 xtables_error(PARAMETER_PROBLEM, "policy match: invalid policy \"%s\"", s); 80 } 81 82 static int parse_mode(const char *s) 83 { 84 if (strcmp(s, "transport") == 0) 85 return XT_POLICY_MODE_TRANSPORT; 86 if (strcmp(s, "tunnel") == 0) 87 return XT_POLICY_MODE_TUNNEL; 88 xtables_error(PARAMETER_PROBLEM, "policy match: invalid mode \"%s\"", s); 89 } 90 91 static void policy_parse(struct xt_option_call *cb) 92 { 93 struct xt_policy_info *info = cb->data; 94 struct xt_policy_elem *e = &info->pol[info->len]; 95 96 xtables_option_parse(cb); 97 switch (cb->entry->id) { 98 case O_DIRECTION: 99 info->flags |= parse_direction(cb->arg); 100 break; 101 case O_POLICY: 102 info->flags |= parse_policy(cb->arg); 103 break; 104 case O_STRICT: 105 info->flags |= XT_POLICY_MATCH_STRICT; 106 break; 107 case O_REQID: 108 if (e->match.reqid) 109 xtables_error(PARAMETER_PROBLEM, 110 "policy match: double --reqid option"); 111 e->match.reqid = 1; 112 e->invert.reqid = cb->invert; 113 e->reqid = cb->val.u32; 114 break; 115 case O_SPI: 116 if (e->match.spi) 117 xtables_error(PARAMETER_PROBLEM, 118 "policy match: double --spi option"); 119 e->match.spi = 1; 120 e->invert.spi = cb->invert; 121 e->spi = cb->val.u32; 122 break; 123 case O_TUNNELSRC: 124 if (e->match.saddr) 125 xtables_error(PARAMETER_PROBLEM, 126 "policy match: double --tunnel-src option"); 127 128 e->match.saddr = 1; 129 e->invert.saddr = cb->invert; 130 memcpy(&e->saddr, &cb->val.haddr, sizeof(cb->val.haddr)); 131 memcpy(&e->smask, &cb->val.hmask, sizeof(cb->val.hmask)); 132 break; 133 case O_TUNNELDST: 134 if (e->match.daddr) 135 xtables_error(PARAMETER_PROBLEM, 136 "policy match: double --tunnel-dst option"); 137 e->match.daddr = 1; 138 e->invert.daddr = cb->invert; 139 memcpy(&e->daddr, &cb->val.haddr, sizeof(cb->val.haddr)); 140 memcpy(&e->dmask, &cb->val.hmask, sizeof(cb->val.hmask)); 141 break; 142 case O_PROTO: 143 if (e->match.proto) 144 xtables_error(PARAMETER_PROBLEM, 145 "policy match: double --proto option"); 146 e->proto = cb->val.protocol; 147 if (e->proto != IPPROTO_AH && e->proto != IPPROTO_ESP && 148 e->proto != IPPROTO_COMP) 149 xtables_error(PARAMETER_PROBLEM, 150 "policy match: protocol must be ah/esp/ipcomp"); 151 e->match.proto = 1; 152 e->invert.proto = cb->invert; 153 break; 154 case O_MODE: 155 if (e->match.mode) 156 xtables_error(PARAMETER_PROBLEM, 157 "policy match: double --mode option"); 158 e->match.mode = 1; 159 e->invert.mode = cb->invert; 160 e->mode = parse_mode(cb->arg); 161 break; 162 case O_NEXT: 163 if (++info->len == XT_POLICY_MAX_ELEM) 164 xtables_error(PARAMETER_PROBLEM, 165 "policy match: maximum policy depth reached"); 166 break; 167 } 168 } 169 170 static void policy_check(struct xt_fcheck_call *cb) 171 { 172 struct xt_policy_info *info = cb->data; 173 const struct xt_policy_elem *e; 174 int i; 175 176 /* 177 * The old "no parameters given" check is carried out 178 * by testing for --dir. 179 */ 180 if (!(info->flags & (XT_POLICY_MATCH_IN | XT_POLICY_MATCH_OUT))) 181 xtables_error(PARAMETER_PROBLEM, 182 "policy match: neither --dir in nor --dir out specified"); 183 184 if (info->flags & XT_POLICY_MATCH_NONE) { 185 if (info->flags & XT_POLICY_MATCH_STRICT) 186 xtables_error(PARAMETER_PROBLEM, 187 "policy match: policy none but --strict given"); 188 189 if (info->len != 0) 190 xtables_error(PARAMETER_PROBLEM, 191 "policy match: policy none but policy given"); 192 } else 193 info->len++; /* increase len by 1, no --next after last element */ 194 195 /* 196 * This is already represented with O_NEXT requiring F_STRICT in the 197 * options table, but will keep this code as a comment for reference. 198 * 199 if (!(info->flags & XT_POLICY_MATCH_STRICT) && info->len > 1) 200 xtables_error(PARAMETER_PROBLEM, 201 "policy match: multiple elements but no --strict"); 202 */ 203 204 for (i = 0; i < info->len; i++) { 205 e = &info->pol[i]; 206 207 if (info->flags & XT_POLICY_MATCH_STRICT && 208 !(e->match.reqid || e->match.spi || e->match.saddr || 209 e->match.daddr || e->match.proto || e->match.mode)) 210 xtables_error(PARAMETER_PROBLEM, 211 "policy match: empty policy element %u. " 212 "--strict is in effect, but at least one of " 213 "reqid, spi, tunnel-src, tunnel-dst, proto or " 214 "mode is required.", i); 215 216 if ((e->match.saddr || e->match.daddr) 217 && ((e->mode == XT_POLICY_MODE_TUNNEL && e->invert.mode) || 218 (e->mode == XT_POLICY_MODE_TRANSPORT && !e->invert.mode))) 219 xtables_error(PARAMETER_PROBLEM, 220 "policy match: --tunnel-src/--tunnel-dst " 221 "is only valid in tunnel mode"); 222 } 223 } 224 225 static void print_mode(const char *prefix, uint8_t mode, int numeric) 226 { 227 printf(" %smode ", prefix); 228 229 switch (mode) { 230 case XT_POLICY_MODE_TRANSPORT: 231 printf("transport"); 232 break; 233 case XT_POLICY_MODE_TUNNEL: 234 printf("tunnel"); 235 break; 236 default: 237 printf("???"); 238 break; 239 } 240 } 241 242 static void print_proto(const char *prefix, uint8_t proto, int numeric) 243 { 244 const struct protoent *p = NULL; 245 246 printf(" %sproto ", prefix); 247 if (!numeric) 248 p = getprotobynumber(proto); 249 if (p != NULL) 250 printf("%s", p->p_name); 251 else 252 printf("%u", proto); 253 } 254 255 #define PRINT_INVERT(x) \ 256 do { \ 257 if (x) \ 258 printf(" !"); \ 259 } while(0) 260 261 static void print_entry(const char *prefix, const struct xt_policy_elem *e, 262 bool numeric, uint8_t family) 263 { 264 if (e->match.reqid) { 265 PRINT_INVERT(e->invert.reqid); 266 printf(" %sreqid %u", prefix, e->reqid); 267 } 268 if (e->match.spi) { 269 PRINT_INVERT(e->invert.spi); 270 printf(" %sspi 0x%x", prefix, e->spi); 271 } 272 if (e->match.proto) { 273 PRINT_INVERT(e->invert.proto); 274 print_proto(prefix, e->proto, numeric); 275 } 276 if (e->match.mode) { 277 PRINT_INVERT(e->invert.mode); 278 print_mode(prefix, e->mode, numeric); 279 } 280 if (e->match.daddr) { 281 PRINT_INVERT(e->invert.daddr); 282 if (family == NFPROTO_IPV6) 283 printf(" %stunnel-dst %s%s", prefix, 284 xtables_ip6addr_to_numeric(&e->daddr.a6), 285 xtables_ip6mask_to_numeric(&e->dmask.a6)); 286 else 287 printf(" %stunnel-dst %s%s", prefix, 288 xtables_ipaddr_to_numeric(&e->daddr.a4), 289 xtables_ipmask_to_numeric(&e->dmask.a4)); 290 } 291 if (e->match.saddr) { 292 PRINT_INVERT(e->invert.saddr); 293 if (family == NFPROTO_IPV6) 294 printf(" %stunnel-src %s%s", prefix, 295 xtables_ip6addr_to_numeric(&e->saddr.a6), 296 xtables_ip6mask_to_numeric(&e->smask.a6)); 297 else 298 printf(" %stunnel-src %s%s", prefix, 299 xtables_ipaddr_to_numeric(&e->saddr.a4), 300 xtables_ipmask_to_numeric(&e->smask.a4)); 301 } 302 } 303 304 static void print_flags(const char *prefix, const struct xt_policy_info *info) 305 { 306 if (info->flags & XT_POLICY_MATCH_IN) 307 printf(" %sdir in", prefix); 308 else 309 printf(" %sdir out", prefix); 310 311 if (info->flags & XT_POLICY_MATCH_NONE) 312 printf(" %spol none", prefix); 313 else 314 printf(" %spol ipsec", prefix); 315 316 if (info->flags & XT_POLICY_MATCH_STRICT) 317 printf(" %sstrict", prefix); 318 } 319 320 static void policy4_print(const void *ip, const struct xt_entry_match *match, 321 int numeric) 322 { 323 const struct xt_policy_info *info = (void *)match->data; 324 unsigned int i; 325 326 printf(" policy match"); 327 print_flags("", info); 328 for (i = 0; i < info->len; i++) { 329 if (info->len > 1) 330 printf(" [%u]", i); 331 print_entry("", &info->pol[i], numeric, NFPROTO_IPV4); 332 } 333 } 334 335 static void policy6_print(const void *ip, const struct xt_entry_match *match, 336 int numeric) 337 { 338 const struct xt_policy_info *info = (void *)match->data; 339 unsigned int i; 340 341 printf(" policy match"); 342 print_flags("", info); 343 for (i = 0; i < info->len; i++) { 344 if (info->len > 1) 345 printf(" [%u]", i); 346 print_entry("", &info->pol[i], numeric, NFPROTO_IPV6); 347 } 348 } 349 350 static void policy4_save(const void *ip, const struct xt_entry_match *match) 351 { 352 const struct xt_policy_info *info = (void *)match->data; 353 unsigned int i; 354 355 print_flags("--", info); 356 for (i = 0; i < info->len; i++) { 357 print_entry("--", &info->pol[i], false, NFPROTO_IPV4); 358 if (i + 1 < info->len) 359 printf(" --next"); 360 } 361 } 362 363 static void policy6_save(const void *ip, const struct xt_entry_match *match) 364 { 365 const struct xt_policy_info *info = (void *)match->data; 366 unsigned int i; 367 368 print_flags("--", info); 369 for (i = 0; i < info->len; i++) { 370 print_entry("--", &info->pol[i], false, NFPROTO_IPV6); 371 if (i + 1 < info->len) 372 printf(" --next"); 373 } 374 } 375 376 static struct xtables_match policy_mt_reg[] = { 377 { 378 .name = "policy", 379 .version = XTABLES_VERSION, 380 .family = NFPROTO_IPV4, 381 .size = XT_ALIGN(sizeof(struct xt_policy_info)), 382 .userspacesize = XT_ALIGN(sizeof(struct xt_policy_info)), 383 .help = policy_help, 384 .x6_parse = policy_parse, 385 .x6_fcheck = policy_check, 386 .print = policy4_print, 387 .save = policy4_save, 388 .x6_options = policy_opts, 389 }, 390 { 391 .name = "policy", 392 .version = XTABLES_VERSION, 393 .family = NFPROTO_IPV6, 394 .size = XT_ALIGN(sizeof(struct xt_policy_info)), 395 .userspacesize = XT_ALIGN(sizeof(struct xt_policy_info)), 396 .help = policy_help, 397 .x6_parse = policy_parse, 398 .x6_fcheck = policy_check, 399 .print = policy6_print, 400 .save = policy6_save, 401 .x6_options = policy_opts, 402 }, 403 }; 404 405 void _init(void) 406 { 407 xtables_register_matches(policy_mt_reg, ARRAY_SIZE(policy_mt_reg)); 408 } 409