X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;ds=sidebyside;f=ipsets.c;h=30c6463ff05a73e73fb9a99b2812f738577f7f86;hb=HEAD;hp=7a72fd35a378941304c8308f75f65b91ae6fc8d3;hpb=6039c7f4b0052c4da21520cdd604f04a5a67f50d;p=project%2Ffirewall3.git diff --git a/ipsets.c b/ipsets.c index 7a72fd3..e7cde16 100644 --- a/ipsets.c +++ b/ipsets.c @@ -16,11 +16,16 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include + #include "ipsets.h" const struct fw3_option fw3_ipset_opts[] = { FW3_OPT("enabled", bool, ipset, enabled), + FW3_OPT("reload_set", bool, ipset, reload_set), + FW3_OPT("counters", bool, ipset, counters), + FW3_OPT("comment", bool, ipset, comment), FW3_OPT("name", string, ipset, name), FW3_OPT("family", family, ipset, family), @@ -38,6 +43,9 @@ const struct fw3_option fw3_ipset_opts[] = { FW3_OPT("external", string, ipset, external), + FW3_LIST("entry", setentry, ipset, entries), + FW3_OPT("loadfile", string, ipset, loadfile), + { } }; @@ -95,7 +103,7 @@ check_types(struct uci_element *e, struct fw3_ipset *ipset) { if (i >= 3) { - warn_elem(e, "must not have more than 3 datatypes assigned"); + warn_section("ipset", ipset, e, "must not have more than 3 datatypes assigned"); return false; } @@ -116,8 +124,8 @@ check_types(struct uci_element *e, struct fw3_ipset *ipset) { ipset->method = ipset_types[i].method; - warn_elem(e, "defines no storage method, assuming '%s'", - fw3_ipset_method_names[ipset->method]); + warn_section("ipset", ipset, e, "defines no storage method, assuming '%s'", + fw3_ipset_method_names[ipset->method]); break; } @@ -136,56 +144,56 @@ check_types(struct uci_element *e, struct fw3_ipset *ipset) if ((ipset_types[i].required & OPT_IPRANGE) && !ipset->iprange.set) { - warn_elem(e, "requires an ip range"); + warn_section("ipset", ipset, e, "requires an ip range"); return false; } if ((ipset_types[i].required & OPT_PORTRANGE) && !ipset->portrange.set) { - warn_elem(e, "requires a port range"); + warn_section("ipset", ipset, e, "requires a port range"); return false; } if (!(ipset_types[i].required & OPT_IPRANGE) && ipset->iprange.set) { - warn_elem(e, "iprange ignored"); + warn_section("ipset", ipset, e, "iprange ignored"); ipset->iprange.set = false; } if (!(ipset_types[i].required & OPT_PORTRANGE) && ipset->portrange.set) { - warn_elem(e, "portrange ignored"); + warn_section("ipset", ipset, e, "portrange ignored"); ipset->portrange.set = false; } if (!(ipset_types[i].optional & OPT_NETMASK) && ipset->netmask > 0) { - warn_elem(e, "netmask ignored"); + warn_section("ipset", ipset, e, "netmask ignored"); ipset->netmask = 0; } if (!(ipset_types[i].optional & OPT_HASHSIZE) && ipset->hashsize > 0) { - warn_elem(e, "hashsize ignored"); + warn_section("ipset", ipset, e, "hashsize ignored"); ipset->hashsize = 0; } if (!(ipset_types[i].optional & OPT_MAXELEM) && ipset->maxelem > 0) { - warn_elem(e, "maxelem ignored"); + warn_section("ipset", ipset, e, "maxelem ignored"); ipset->maxelem = 0; } if (!(ipset_types[i].optional & OPT_FAMILY) && ipset->family != FW3_FAMILY_V4) { - warn_elem(e, "family ignored"); + warn_section("ipset", ipset, e, "family ignored"); ipset->family = FW3_FAMILY_V4; } } @@ -194,12 +202,55 @@ check_types(struct uci_element *e, struct fw3_ipset *ipset) } } - warn_elem(e, "has an invalid combination of storage method and matches"); + warn_section("ipset", ipset, e, "has an invalid combination of storage method and matches"); return false; } -struct fw3_ipset * -fw3_alloc_ipset(void) +static bool +check_ipset(struct fw3_state *state, struct fw3_ipset *ipset, struct uci_element *e) +{ + if (!ipset->enabled) { + return false; + } + + if (ipset->external) + { + if (!*ipset->external) + ipset->external = NULL; + else if (!ipset->name) + ipset->name = ipset->external; + } + + if (!ipset->name || !*ipset->name) + { + warn_section("ipset", ipset, e, "ipset must have a name assigned"); + } + //else if (fw3_lookup_ipset(state, ipset->name) != NULL) + //{ + // warn_section("ipset", ipset, e, "has duplicated set name", ipset->name); + //} + else if (ipset->family == FW3_FAMILY_ANY) + { + warn_section("ipset", ipset, e, "must not have family 'any'"); + } + else if (ipset->iprange.set && ipset->family != ipset->iprange.family) + { + warn_section("ipset", ipset, e, "has iprange of wrong address family"); + } + else if (list_empty(&ipset->datatypes)) + { + warn_section("ipset", ipset, e, "has no datatypes assigned"); + } + else if (check_types(e, ipset)) + { + return true; + } + + return false; +} + +static struct fw3_ipset * +fw3_alloc_ipset(struct fw3_state *state) { struct fw3_ipset *ipset; @@ -208,25 +259,61 @@ fw3_alloc_ipset(void) return NULL; INIT_LIST_HEAD(&ipset->datatypes); + INIT_LIST_HEAD(&ipset->entries); + + ipset->comment = false; + ipset->counters = false; + ipset->enabled = true; + ipset->family = FW3_FAMILY_V4; + ipset->reload_set = false; + ipset->timeout = -1; /* no timeout by default */ - ipset->enabled = true; - ipset->family = FW3_FAMILY_V4; + list_add_tail(&ipset->list, &state->ipsets); return ipset; } void -fw3_load_ipsets(struct fw3_state *state, struct uci_package *p) +fw3_load_ipsets(struct fw3_state *state, struct uci_package *p, + struct blob_attr *a) { struct uci_section *s; struct uci_element *e; struct fw3_ipset *ipset; + struct blob_attr *entry; + unsigned rem; INIT_LIST_HEAD(&state->ipsets); if (state->disable_ipsets) return; + blob_for_each_attr(entry, a, rem) + { + const char *type; + const char *name = "ubus ipset"; + + if (!fw3_attr_parse_name_type(entry, &name, &type)) + continue; + + if (strcmp(type, "ipset")) + continue; + + ipset = fw3_alloc_ipset(state); + if (!ipset) + continue; + + if (!fw3_parse_blob_options(ipset, fw3_ipset_opts, entry, name)) + { + warn_section("ipset", ipset, NULL, "skipped due to invalid options"); + fw3_free_ipset(ipset); + continue; + } + + if (!check_ipset(state, ipset, NULL)) + fw3_free_ipset(ipset); + } + uci_foreach_element(&p->sections, e) { s = uci_to_section(e); @@ -234,7 +321,7 @@ fw3_load_ipsets(struct fw3_state *state, struct uci_package *p) if (strcmp(s->type, "ipset")) continue; - ipset = fw3_alloc_ipset(); + ipset = fw3_alloc_ipset(state); if (!ipset) continue; @@ -242,50 +329,47 @@ fw3_load_ipsets(struct fw3_state *state, struct uci_package *p) if (!fw3_parse_options(ipset, fw3_ipset_opts, s)) warn_elem(e, "has invalid options"); - if (ipset->external) - { - if (!*ipset->external) - ipset->external = NULL; - else if (!ipset->name) - ipset->name = ipset->external; - } + if (!check_ipset(state, ipset, e)) + fw3_free_ipset(ipset); + } +} - if (!ipset->name || !*ipset->name) - { - warn_elem(e, "must have a name assigned"); - } - //else if (fw3_lookup_ipset(state, ipset->name) != NULL) - //{ - // warn_elem(e, "has duplicated set name '%s'", ipset->name); - //} - else if (ipset->family == FW3_FAMILY_ANY) - { - warn_elem(e, "must not have family 'any'"); - } - else if (ipset->iprange.set && ipset->family != ipset->iprange.family) - { - warn_elem(e, "has iprange of wrong address family"); - } - else if (list_empty(&ipset->datatypes)) - { - warn_elem(e, "has no datatypes assigned"); - } - else if (check_types(e, ipset)) - { - list_add_tail(&ipset->list, &state->ipsets); - continue; - } - fw3_free_ipset(ipset); +static void +load_file(struct fw3_ipset *ipset) +{ + FILE *f; + char line[128]; + char *p; + + if (!ipset->loadfile) + return; + + info(" * Loading file %s", ipset->loadfile); + + f = fopen(ipset->loadfile, "r"); + + if (!f) { + info(" ! Skipping due to open error: %s", strerror(errno)); + return; + } + + while (fgets(line, sizeof(line), f)) { + p = line; + while (isspace(*p)) + p++; + if (*p && *p != '#') + fw3_pr("add %s %s", ipset->name, line); } -} + fclose(f); +} static void create_ipset(struct fw3_ipset *ipset, struct fw3_state *state) { bool first = true; - + struct fw3_setentry *entry; struct fw3_ipset_datatype *type; info(" * Creating ipset %s", ipset->name); @@ -312,7 +396,7 @@ create_ipset(struct fw3_ipset *ipset, struct fw3_state *state) ipset->portrange.port_min, ipset->portrange.port_max); } - if (ipset->timeout > 0) + if (ipset->timeout >= 0) fw3_pr(" timeout %u", ipset->timeout); if (ipset->maxelem > 0) @@ -324,13 +408,25 @@ create_ipset(struct fw3_ipset *ipset, struct fw3_state *state) if (ipset->hashsize > 0) fw3_pr(" hashsize %u", ipset->hashsize); + if (ipset->counters) + fw3_pr(" counters"); + + if (ipset->comment) + fw3_pr(" comment"); + fw3_pr("\n"); + + list_for_each_entry(entry, &ipset->entries, list) + fw3_pr("add %s %s\n", ipset->name, entry->value); + + load_file(ipset); } void -fw3_create_ipsets(struct fw3_state *state) +fw3_create_ipsets(struct fw3_state *state, enum fw3_family family, + bool reload_set) { - int tries; + unsigned int delay, tries; bool exec = false; struct fw3_ipset *ipset; @@ -340,9 +436,16 @@ fw3_create_ipsets(struct fw3_state *state) /* spawn ipsets */ list_for_each_entry(ipset, &state->ipsets, list) { + if (ipset->family != family) + continue; + if (ipset->external) continue; + if (fw3_check_ipset(ipset) && + (reload_set && !ipset->reload_set)) + continue; + if (!exec) { exec = fw3_command_pipe(false, "ipset", "-exist", "-"); @@ -360,27 +463,36 @@ fw3_create_ipsets(struct fw3_state *state) fw3_command_close(); } - /* wait for ipsets to appear */ + /* wait a little expontially for ipsets to appear */ list_for_each_entry(ipset, &state->ipsets, list) { if (ipset->external) continue; + delay = 5; for (tries = 0; !fw3_check_ipset(ipset) && tries < 10; tries++) - usleep(50000); + usleep(delay<<1); } } void -fw3_destroy_ipsets(struct fw3_state *state) +fw3_destroy_ipsets(struct fw3_state *state, enum fw3_family family, + bool reload_set) { - int tries; + unsigned int delay, tries; bool exec = false; struct fw3_ipset *ipset; + if (state->disable_ipsets) + return; + /* destroy ipsets */ list_for_each_entry(ipset, &state->ipsets, list) { + if (ipset->family != family || + (reload_set && !ipset->reload_set)) + continue; + if (!exec) { exec = fw3_command_pipe(false, "ipset", "-exist", "-"); @@ -407,8 +519,9 @@ fw3_destroy_ipsets(struct fw3_state *state) if (ipset->external) continue; + delay = 5; for (tries = 0; fw3_check_ipset(ipset) && tries < 10; tries++) - usleep(50000); + usleep(delay<<1); } } @@ -467,3 +580,43 @@ out: return rv; } + +void +fw3_ipsets_update_run_state(enum fw3_family family, struct fw3_state *run_state, + struct fw3_state *cfg_state) +{ + struct fw3_ipset *ipset_run, *ipset_cfg; + bool in_cfg; + + list_for_each_entry(ipset_run, &run_state->ipsets, list) { + if (ipset_run->family != family) + continue; + + in_cfg = false; + + list_for_each_entry(ipset_cfg, &cfg_state->ipsets, list) { + if (ipset_cfg->family != family) + continue; + + if (strlen(ipset_run->name) == + strlen(ipset_cfg->name) && + !strcmp(ipset_run->name, ipset_cfg->name)) { + in_cfg = true; + break; + } + } + + /* If a set is found in run_state, but not in cfg_state then the + * set has been deleted/renamed. Set reload_set to true to force + * the old set to be destroyed in the "stop" fase of the reload. + * If the set is found, then copy the reload_set value from the + * configuration state. This ensures that the elements are + * always updated according to the configuration, and not the + * runtime state (which the user might have forgotten). + */ + if (!in_cfg) + ipset_run->reload_set = true; + else + ipset_run->reload_set = ipset_cfg->reload_set; + } +}