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