1 /* 2 * firewall3 - 3rd OpenWrt UCI firewall implementation 3 * 4 * Copyright (C) 2013 Jo-Philipp Wich <jo@mein.io> 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <ctype.h> 20 21 #include "ipsets.h" 22 23 24 const struct fw3_option fw3_ipset_opts[] = { 25 FW3_OPT("enabled", bool, ipset, enabled), 26 FW3_OPT("reload_set", bool, ipset, reload_set), 27 FW3_OPT("counters", bool, ipset, counters), 28 FW3_OPT("comment", bool, ipset, comment), 29 30 FW3_OPT("name", string, ipset, name), 31 FW3_OPT("family", family, ipset, family), 32 33 FW3_OPT("storage", ipset_method, ipset, method), 34 FW3_LIST("match", ipset_datatype, ipset, datatypes), 35 36 FW3_OPT("iprange", address, ipset, iprange), 37 FW3_OPT("portrange", port, ipset, portrange), 38 39 FW3_OPT("netmask", int, ipset, netmask), 40 FW3_OPT("maxelem", int, ipset, maxelem), 41 FW3_OPT("hashsize", int, ipset, hashsize), 42 FW3_OPT("timeout", int, ipset, timeout), 43 44 FW3_OPT("external", string, ipset, external), 45 46 FW3_LIST("entry", setentry, ipset, entries), 47 FW3_OPT("loadfile", string, ipset, loadfile), 48 49 { } 50 }; 51 52 #define T(m, t1, t2, t3, r, o) \ 53 { FW3_IPSET_METHOD_##m, \ 54 FW3_IPSET_TYPE_##t1 | (FW3_IPSET_TYPE_##t2 << 8) | (FW3_IPSET_TYPE_##t3 << 16), \ 55 r, o } 56 57 enum ipset_optflag { 58 OPT_IPRANGE = (1 << 0), 59 OPT_PORTRANGE = (1 << 1), 60 OPT_NETMASK = (1 << 2), 61 OPT_HASHSIZE = (1 << 3), 62 OPT_MAXELEM = (1 << 4), 63 OPT_FAMILY = (1 << 5), 64 }; 65 66 struct ipset_type { 67 enum fw3_ipset_method method; 68 uint32_t types; 69 uint8_t required; 70 uint8_t optional; 71 }; 72 73 static struct ipset_type ipset_types[] = { 74 T(BITMAP, IP, UNSPEC, UNSPEC, OPT_IPRANGE, OPT_NETMASK), 75 T(BITMAP, IP, MAC, UNSPEC, OPT_IPRANGE, 0), 76 T(BITMAP, PORT, UNSPEC, UNSPEC, OPT_PORTRANGE, 0), 77 78 T(HASH, IP, UNSPEC, UNSPEC, 0, 79 OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM | OPT_NETMASK), 80 T(HASH, NET, UNSPEC, UNSPEC, 0, 81 OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM), 82 T(HASH, IP, PORT, UNSPEC, 0, 83 OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM), 84 T(HASH, NET, PORT, UNSPEC, 0, 85 OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM), 86 T(HASH, IP, PORT, IP, 0, 87 OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM), 88 T(HASH, IP, PORT, NET, 0, 89 OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM), 90 91 T(LIST, SET, UNSPEC, UNSPEC, 0, OPT_MAXELEM), 92 }; 93 94 95 static bool 96 check_types(struct uci_element *e, struct fw3_ipset *ipset) 97 { 98 int i = 0; 99 uint32_t typelist = 0; 100 struct fw3_ipset_datatype *type; 101 102 list_for_each_entry(type, &ipset->datatypes, list) 103 { 104 if (i >= 3) 105 { 106 warn_section("ipset", ipset, e, "must not have more than 3 datatypes assigned"); 107 return false; 108 } 109 110 typelist |= (type->type << (i++ * 8)); 111 } 112 113 /* find a suitable storage method if none specified */ 114 if (ipset->method == FW3_IPSET_METHOD_UNSPEC) 115 { 116 for (i = 0; i < ARRAY_SIZE(ipset_types); i++) 117 { 118 /* skip type for v6 if it does not support family */ 119 if (ipset->family != FW3_FAMILY_V4 && 120 !(ipset_types[i].optional & OPT_FAMILY)) 121 continue; 122 123 if (ipset_types[i].types == typelist) 124 { 125 ipset->method = ipset_types[i].method; 126 127 warn_section("ipset", ipset, e, "defines no storage method, assuming '%s'", 128 fw3_ipset_method_names[ipset->method]); 129 130 break; 131 } 132 } 133 } 134 135 //typelist |= ipset->method; 136 137 for (i = 0; i < ARRAY_SIZE(ipset_types); i++) 138 { 139 if (ipset_types[i].method == ipset->method && 140 ipset_types[i].types == typelist) 141 { 142 if (!ipset->external) 143 { 144 if ((ipset_types[i].required & OPT_IPRANGE) && 145 !ipset->iprange.set) 146 { 147 warn_section("ipset", ipset, e, "requires an ip range"); 148 return false; 149 } 150 151 if ((ipset_types[i].required & OPT_PORTRANGE) && 152 !ipset->portrange.set) 153 { 154 warn_section("ipset", ipset, e, "requires a port range"); 155 return false; 156 } 157 158 if (!(ipset_types[i].required & OPT_IPRANGE) && 159 ipset->iprange.set) 160 { 161 warn_section("ipset", ipset, e, "iprange ignored"); 162 ipset->iprange.set = false; 163 } 164 165 if (!(ipset_types[i].required & OPT_PORTRANGE) && 166 ipset->portrange.set) 167 { 168 warn_section("ipset", ipset, e, "portrange ignored"); 169 ipset->portrange.set = false; 170 } 171 172 if (!(ipset_types[i].optional & OPT_NETMASK) && 173 ipset->netmask > 0) 174 { 175 warn_section("ipset", ipset, e, "netmask ignored"); 176 ipset->netmask = 0; 177 } 178 179 if (!(ipset_types[i].optional & OPT_HASHSIZE) && 180 ipset->hashsize > 0) 181 { 182 warn_section("ipset", ipset, e, "hashsize ignored"); 183 ipset->hashsize = 0; 184 } 185 186 if (!(ipset_types[i].optional & OPT_MAXELEM) && 187 ipset->maxelem > 0) 188 { 189 warn_section("ipset", ipset, e, "maxelem ignored"); 190 ipset->maxelem = 0; 191 } 192 193 if (!(ipset_types[i].optional & OPT_FAMILY) && 194 ipset->family != FW3_FAMILY_V4) 195 { 196 warn_section("ipset", ipset, e, "family ignored"); 197 ipset->family = FW3_FAMILY_V4; 198 } 199 } 200 201 return true; 202 } 203 } 204 205 warn_section("ipset", ipset, e, "has an invalid combination of storage method and matches"); 206 return false; 207 } 208 209 static bool 210 check_ipset(struct fw3_state *state, struct fw3_ipset *ipset, struct uci_element *e) 211 { 212 if (!ipset->enabled) { 213 return false; 214 } 215 216 if (ipset->external) 217 { 218 if (!*ipset->external) 219 ipset->external = NULL; 220 else if (!ipset->name) 221 ipset->name = ipset->external; 222 } 223 224 if (!ipset->name || !*ipset->name) 225 { 226 warn_section("ipset", ipset, e, "ipset must have a name assigned"); 227 } 228 //else if (fw3_lookup_ipset(state, ipset->name) != NULL) 229 //{ 230 // warn_section("ipset", ipset, e, "has duplicated set name", ipset->name); 231 //} 232 else if (ipset->family == FW3_FAMILY_ANY) 233 { 234 warn_section("ipset", ipset, e, "must not have family 'any'"); 235 } 236 else if (ipset->iprange.set && ipset->family != ipset->iprange.family) 237 { 238 warn_section("ipset", ipset, e, "has iprange of wrong address family"); 239 } 240 else if (list_empty(&ipset->datatypes)) 241 { 242 warn_section("ipset", ipset, e, "has no datatypes assigned"); 243 } 244 else if (check_types(e, ipset)) 245 { 246 return true; 247 } 248 249 return false; 250 } 251 252 static struct fw3_ipset * 253 fw3_alloc_ipset(struct fw3_state *state) 254 { 255 struct fw3_ipset *ipset; 256 257 ipset = calloc(1, sizeof(*ipset)); 258 if (!ipset) 259 return NULL; 260 261 INIT_LIST_HEAD(&ipset->datatypes); 262 INIT_LIST_HEAD(&ipset->entries); 263 264 ipset->comment = false; 265 ipset->counters = false; 266 ipset->enabled = true; 267 ipset->family = FW3_FAMILY_V4; 268 ipset->reload_set = false; 269 ipset->timeout = -1; /* no timeout by default */ 270 271 list_add_tail(&ipset->list, &state->ipsets); 272 273 return ipset; 274 } 275 276 void 277 fw3_load_ipsets(struct fw3_state *state, struct uci_package *p, 278 struct blob_attr *a) 279 { 280 struct uci_section *s; 281 struct uci_element *e; 282 struct fw3_ipset *ipset; 283 struct blob_attr *entry; 284 unsigned rem; 285 286 INIT_LIST_HEAD(&state->ipsets); 287 288 if (state->disable_ipsets) 289 return; 290 291 blob_for_each_attr(entry, a, rem) 292 { 293 const char *type; 294 const char *name = "ubus ipset"; 295 296 if (!fw3_attr_parse_name_type(entry, &name, &type)) 297 continue; 298 299 if (strcmp(type, "ipset")) 300 continue; 301 302 ipset = fw3_alloc_ipset(state); 303 if (!ipset) 304 continue; 305 306 if (!fw3_parse_blob_options(ipset, fw3_ipset_opts, entry, name)) 307 { 308 warn_section("ipset", ipset, NULL, "skipped due to invalid options"); 309 fw3_free_ipset(ipset); 310 continue; 311 } 312 313 if (!check_ipset(state, ipset, NULL)) 314 fw3_free_ipset(ipset); 315 } 316 317 uci_foreach_element(&p->sections, e) 318 { 319 s = uci_to_section(e); 320 321 if (strcmp(s->type, "ipset")) 322 continue; 323 324 ipset = fw3_alloc_ipset(state); 325 326 if (!ipset) 327 continue; 328 329 if (!fw3_parse_options(ipset, fw3_ipset_opts, s)) 330 warn_elem(e, "has invalid options"); 331 332 if (!check_ipset(state, ipset, e)) 333 fw3_free_ipset(ipset); 334 } 335 } 336 337 338 static void 339 load_file(struct fw3_ipset *ipset) 340 { 341 FILE *f; 342 char line[128]; 343 char *p; 344 345 if (!ipset->loadfile) 346 return; 347 348 info(" * Loading file %s", ipset->loadfile); 349 350 f = fopen(ipset->loadfile, "r"); 351 352 if (!f) { 353 info(" ! Skipping due to open error: %s", strerror(errno)); 354 return; 355 } 356 357 while (fgets(line, sizeof(line), f)) { 358 p = line; 359 while (isspace(*p)) 360 p++; 361 if (*p && *p != '#') 362 fw3_pr("add %s %s", ipset->name, line); 363 } 364 365 fclose(f); 366 } 367 368 static void 369 create_ipset(struct fw3_ipset *ipset, struct fw3_state *state) 370 { 371 bool first = true; 372 struct fw3_setentry *entry; 373 struct fw3_ipset_datatype *type; 374 375 info(" * Creating ipset %s", ipset->name); 376 377 first = true; 378 fw3_pr("create %s %s", ipset->name, fw3_ipset_method_names[ipset->method]); 379 380 list_for_each_entry(type, &ipset->datatypes, list) 381 { 382 fw3_pr("%c%s", first ? ':' : ',', fw3_ipset_type_names[type->type]); 383 first = false; 384 } 385 386 if (ipset->method == FW3_IPSET_METHOD_HASH) 387 fw3_pr(" family inet%s", (ipset->family == FW3_FAMILY_V4) ? "" : "6"); 388 389 if (ipset->iprange.set) 390 { 391 fw3_pr(" range %s", fw3_address_to_string(&ipset->iprange, false, true)); 392 } 393 else if (ipset->portrange.set) 394 { 395 fw3_pr(" range %u-%u", 396 ipset->portrange.port_min, ipset->portrange.port_max); 397 } 398 399 if (ipset->timeout >= 0) 400 fw3_pr(" timeout %u", ipset->timeout); 401 402 if (ipset->maxelem > 0) 403 fw3_pr(" maxelem %u", ipset->maxelem); 404 405 if (ipset->netmask > 0) 406 fw3_pr(" netmask %u", ipset->netmask); 407 408 if (ipset->hashsize > 0) 409 fw3_pr(" hashsize %u", ipset->hashsize); 410 411 if (ipset->counters) 412 fw3_pr(" counters"); 413 414 if (ipset->comment) 415 fw3_pr(" comment"); 416 417 fw3_pr("\n"); 418 419 list_for_each_entry(entry, &ipset->entries, list) 420 fw3_pr("add %s %s\n", ipset->name, entry->value); 421 422 load_file(ipset); 423 } 424 425 void 426 fw3_create_ipsets(struct fw3_state *state, enum fw3_family family, 427 bool reload_set) 428 { 429 unsigned int delay, tries; 430 bool exec = false; 431 struct fw3_ipset *ipset; 432 433 if (state->disable_ipsets) 434 return; 435 436 /* spawn ipsets */ 437 list_for_each_entry(ipset, &state->ipsets, list) 438 { 439 if (ipset->family != family) 440 continue; 441 442 if (ipset->external) 443 continue; 444 445 if (fw3_check_ipset(ipset) && 446 (reload_set && !ipset->reload_set)) 447 continue; 448 449 if (!exec) 450 { 451 exec = fw3_command_pipe(false, "ipset", "-exist", "-"); 452 453 if (!exec) 454 return; 455 } 456 457 create_ipset(ipset, state); 458 } 459 460 if (exec) 461 { 462 fw3_pr("quit\n"); 463 fw3_command_close(); 464 } 465 466 /* wait a little expontially for ipsets to appear */ 467 list_for_each_entry(ipset, &state->ipsets, list) 468 { 469 if (ipset->external) 470 continue; 471 472 delay = 5; 473 for (tries = 0; !fw3_check_ipset(ipset) && tries < 10; tries++) 474 usleep(delay<<1); 475 } 476 } 477 478 void 479 fw3_destroy_ipsets(struct fw3_state *state, enum fw3_family family, 480 bool reload_set) 481 { 482 unsigned int delay, tries; 483 bool exec = false; 484 struct fw3_ipset *ipset; 485 486 if (state->disable_ipsets) 487 return; 488 489 /* destroy ipsets */ 490 list_for_each_entry(ipset, &state->ipsets, list) 491 { 492 if (ipset->family != family || 493 (reload_set && !ipset->reload_set)) 494 continue; 495 496 if (!exec) 497 { 498 exec = fw3_command_pipe(false, "ipset", "-exist", "-"); 499 500 if (!exec) 501 return; 502 } 503 504 info(" * Deleting ipset %s", ipset->name); 505 506 fw3_pr("flush %s\n", ipset->name); 507 fw3_pr("destroy %s\n", ipset->name); 508 } 509 510 if (exec) 511 { 512 fw3_pr("quit\n"); 513 fw3_command_close(); 514 } 515 516 /* wait for ipsets to disappear */ 517 list_for_each_entry(ipset, &state->ipsets, list) 518 { 519 if (ipset->external) 520 continue; 521 522 delay = 5; 523 for (tries = 0; fw3_check_ipset(ipset) && tries < 10; tries++) 524 usleep(delay<<1); 525 } 526 } 527 528 struct fw3_ipset * 529 fw3_lookup_ipset(struct fw3_state *state, const char *name) 530 { 531 struct fw3_ipset *s; 532 533 if (list_empty(&state->ipsets)) 534 return NULL; 535 536 list_for_each_entry(s, &state->ipsets, list) 537 { 538 if (strcmp(s->name, name)) 539 continue; 540 541 return s; 542 } 543 544 return NULL; 545 } 546 547 bool 548 fw3_check_ipset(struct fw3_ipset *set) 549 { 550 bool rv = false; 551 552 socklen_t sz; 553 int s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); 554 struct ip_set_req_version req_ver; 555 struct ip_set_req_get_set req_name; 556 557 if (s < 0 || fcntl(s, F_SETFD, FD_CLOEXEC)) 558 goto out; 559 560 sz = sizeof(req_ver); 561 req_ver.op = IP_SET_OP_VERSION; 562 563 if (getsockopt(s, SOL_IP, SO_IP_SET, &req_ver, &sz)) 564 goto out; 565 566 sz = sizeof(req_name); 567 req_name.op = IP_SET_OP_GET_BYNAME; 568 req_name.version = req_ver.version; 569 snprintf(req_name.set.name, IPSET_MAXNAMELEN - 1, "%s", 570 set->external ? set->external : set->name); 571 572 if (getsockopt(s, SOL_IP, SO_IP_SET, &req_name, &sz)) 573 goto out; 574 575 rv = ((sz == sizeof(req_name)) && (req_name.set.index != IPSET_INVALID_ID)); 576 577 out: 578 if (s >= 0) 579 close(s); 580 581 return rv; 582 } 583 584 void 585 fw3_ipsets_update_run_state(enum fw3_family family, struct fw3_state *run_state, 586 struct fw3_state *cfg_state) 587 { 588 struct fw3_ipset *ipset_run, *ipset_cfg; 589 bool in_cfg; 590 591 list_for_each_entry(ipset_run, &run_state->ipsets, list) { 592 if (ipset_run->family != family) 593 continue; 594 595 in_cfg = false; 596 597 list_for_each_entry(ipset_cfg, &cfg_state->ipsets, list) { 598 if (ipset_cfg->family != family) 599 continue; 600 601 if (strlen(ipset_run->name) == 602 strlen(ipset_cfg->name) && 603 !strcmp(ipset_run->name, ipset_cfg->name)) { 604 in_cfg = true; 605 break; 606 } 607 } 608 609 /* If a set is found in run_state, but not in cfg_state then the 610 * set has been deleted/renamed. Set reload_set to true to force 611 * the old set to be destroyed in the "stop" fase of the reload. 612 * If the set is found, then copy the reload_set value from the 613 * configuration state. This ensures that the elements are 614 * always updated according to the configuration, and not the 615 * runtime state (which the user might have forgotten). 616 */ 617 if (!in_cfg) 618 ipset_run->reload_set = true; 619 else 620 ipset_run->reload_set = ipset_cfg->reload_set; 621 } 622 } 623
This page was automatically generated by LXR 0.3.1. • OpenWrt