X-Git-Url: http://git.archive.openwrt.org/?p=project%2Ffirewall3.git;a=blobdiff_plain;f=ipsets.c;h=06aafb77ecab50be9b5ffbdb8f917183e9b176b9;hp=5e0d61f9c3f956c23c542b533fdda8884e31b70f;hb=95cc95c7fec2d68fa8e27cc8e8e4b8dbacababf8;hpb=bb6873d86322a66bc01fc4195512ba5d2c78bddb diff --git a/ipsets.c b/ipsets.c index 5e0d61f..06aafb7 100644 --- a/ipsets.c +++ b/ipsets.c @@ -19,14 +19,16 @@ #include "ipsets.h" -static struct fw3_option ipset_opts[] = { +const struct fw3_option fw3_ipset_opts[] = { + FW3_OPT("enabled", bool, ipset, enabled), + FW3_OPT("name", string, ipset, name), FW3_OPT("family", family, ipset, family), FW3_OPT("storage", ipset_method, ipset, method), FW3_LIST("match", ipset_datatype, ipset, datatypes), - FW3_LIST("iprange", address, ipset, iprange), + FW3_OPT("iprange", address, ipset, iprange), FW3_OPT("portrange", port, ipset, portrange), FW3_OPT("netmask", int, ipset, netmask), @@ -35,6 +37,8 @@ static struct fw3_option ipset_opts[] = { FW3_OPT("timeout", int, ipset, timeout), FW3_OPT("external", string, ipset, external), + + { } }; #define T(m, t1, t2, t3, r, o) \ @@ -42,27 +46,41 @@ static struct fw3_option ipset_opts[] = { FW3_IPSET_TYPE_##t1 | (FW3_IPSET_TYPE_##t2 << 8) | (FW3_IPSET_TYPE_##t3 << 16), \ r, o } -static struct fw3_ipset_settype ipset_types[] = { - T(BITMAP, IP, UNSPEC, UNSPEC, FW3_IPSET_OPT_IPRANGE, - FW3_IPSET_OPT_NETMASK), - T(BITMAP, IP, MAC, UNSPEC, FW3_IPSET_OPT_IPRANGE, 0), - T(BITMAP, PORT, UNSPEC, UNSPEC, FW3_IPSET_OPT_PORTRANGE, 0), +enum ipset_optflag { + OPT_IPRANGE = (1 << 0), + OPT_PORTRANGE = (1 << 1), + OPT_NETMASK = (1 << 2), + OPT_HASHSIZE = (1 << 3), + OPT_MAXELEM = (1 << 4), + OPT_FAMILY = (1 << 5), +}; + +struct ipset_type { + enum fw3_ipset_method method; + uint32_t types; + uint8_t required; + uint8_t optional; +}; + +static struct ipset_type ipset_types[] = { + T(BITMAP, IP, UNSPEC, UNSPEC, OPT_IPRANGE, OPT_NETMASK), + T(BITMAP, IP, MAC, UNSPEC, OPT_IPRANGE, 0), + T(BITMAP, PORT, UNSPEC, UNSPEC, OPT_PORTRANGE, 0), T(HASH, IP, UNSPEC, UNSPEC, 0, - FW3_IPSET_OPT_FAMILY | FW3_IPSET_OPT_HASHSIZE | FW3_IPSET_OPT_MAXELEM | - FW3_IPSET_OPT_NETMASK), + OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM | OPT_NETMASK), T(HASH, NET, UNSPEC, UNSPEC, 0, - FW3_IPSET_OPT_FAMILY | FW3_IPSET_OPT_HASHSIZE | FW3_IPSET_OPT_MAXELEM), + OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM), T(HASH, IP, PORT, UNSPEC, 0, - FW3_IPSET_OPT_FAMILY | FW3_IPSET_OPT_HASHSIZE | FW3_IPSET_OPT_MAXELEM), + OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM), T(HASH, NET, PORT, UNSPEC, 0, - FW3_IPSET_OPT_FAMILY | FW3_IPSET_OPT_HASHSIZE | FW3_IPSET_OPT_MAXELEM), + OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM), T(HASH, IP, PORT, IP, 0, - FW3_IPSET_OPT_FAMILY | FW3_IPSET_OPT_HASHSIZE | FW3_IPSET_OPT_MAXELEM), + OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM), T(HASH, IP, PORT, NET, 0, - FW3_IPSET_OPT_FAMILY | FW3_IPSET_OPT_HASHSIZE | FW3_IPSET_OPT_MAXELEM), + OPT_FAMILY | OPT_HASHSIZE | OPT_MAXELEM), - T(LIST, SET, UNSPEC, UNSPEC, 0, FW3_IPSET_OPT_MAXELEM), + T(LIST, SET, UNSPEC, UNSPEC, 0, OPT_MAXELEM), }; @@ -73,15 +91,6 @@ check_types(struct uci_element *e, struct fw3_ipset *ipset) uint32_t typelist = 0; struct fw3_ipset_datatype *type; - const char *methods[] = { - "(bug)", - "bitmap", - "hash", - "list", - }; - - typelist = 0; - list_for_each_entry(type, &ipset->datatypes, list) { if (i >= 3) @@ -103,7 +112,7 @@ 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'", - methods[ipset->method]); + fw3_ipset_method_names[ipset->method]); break; } @@ -117,62 +126,62 @@ check_types(struct uci_element *e, struct fw3_ipset *ipset) if (ipset_types[i].method == ipset->method && ipset_types[i].types == typelist) { - if (!ipset->external || !*ipset->external) + if (!ipset->external) { - if ((ipset_types[i].required & FW3_IPSET_OPT_IPRANGE) && - list_empty(&ipset->iprange)) + if ((ipset_types[i].required & OPT_IPRANGE) && + !ipset->iprange.set) { warn_elem(e, "requires an ip range"); return false; } - if ((ipset_types[i].required & FW3_IPSET_OPT_PORTRANGE) && + if ((ipset_types[i].required & OPT_PORTRANGE) && !ipset->portrange.set) { warn_elem(e, "requires a port range"); return false; } - if (!(ipset_types[i].required & FW3_IPSET_OPT_IPRANGE) && - !list_empty(&ipset->iprange)) + if (!(ipset_types[i].required & OPT_IPRANGE) && + ipset->iprange.set) { warn_elem(e, "iprange ignored"); - fw3_free_list(&ipset->iprange); + ipset->iprange.set = false; } - if (!(ipset_types[i].required & FW3_IPSET_OPT_PORTRANGE) && + if (!(ipset_types[i].required & OPT_PORTRANGE) && ipset->portrange.set) { warn_elem(e, "portrange ignored"); - memset(&ipset->portrange, 0, sizeof(ipset->portrange)); + ipset->portrange.set = false; } - if (!(ipset_types[i].optional & FW3_IPSET_OPT_NETMASK) && + if (!(ipset_types[i].optional & OPT_NETMASK) && ipset->netmask > 0) { warn_elem(e, "netmask ignored"); ipset->netmask = 0; } - if (!(ipset_types[i].optional & FW3_IPSET_OPT_HASHSIZE) && + if (!(ipset_types[i].optional & OPT_HASHSIZE) && ipset->hashsize > 0) { warn_elem(e, "hashsize ignored"); ipset->hashsize = 0; } - if (!(ipset_types[i].optional & FW3_IPSET_OPT_MAXELEM) && + if (!(ipset_types[i].optional & OPT_MAXELEM) && ipset->maxelem > 0) { warn_elem(e, "maxelem ignored"); ipset->maxelem = 0; } - if (!(ipset_types[i].optional & FW3_IPSET_OPT_FAMILY) && - ipset->family != FW3_FAMILY_ANY) + if (!(ipset_types[i].optional & OPT_FAMILY) && + ipset->family != FW3_FAMILY_V4) { warn_elem(e, "family ignored"); - ipset->family = FW3_FAMILY_ANY; + ipset->family = FW3_FAMILY_V4; } } @@ -184,6 +193,26 @@ check_types(struct uci_element *e, struct fw3_ipset *ipset) return false; } +struct fw3_ipset * +fw3_alloc_ipset(void) +{ + struct fw3_ipset *ipset; + + ipset = malloc(sizeof(*ipset)); + + if (!ipset) + return NULL; + + memset(ipset, 0, sizeof(*ipset)); + + INIT_LIST_HEAD(&ipset->datatypes); + + ipset->enabled = true; + ipset->family = FW3_FAMILY_V4; + + return ipset; +} + void fw3_load_ipsets(struct fw3_state *state, struct uci_package *p) { @@ -203,17 +232,20 @@ fw3_load_ipsets(struct fw3_state *state, struct uci_package *p) if (strcmp(s->type, "ipset")) continue; - ipset = malloc(sizeof(*ipset)); + ipset = fw3_alloc_ipset(); if (!ipset) continue; - memset(ipset, 0, sizeof(*ipset)); - - INIT_LIST_HEAD(&ipset->datatypes); - INIT_LIST_HEAD(&ipset->iprange); + fw3_parse_options(ipset, fw3_ipset_opts, s); - fw3_parse_options(ipset, ipset_opts, ARRAY_SIZE(ipset_opts), s); + if (ipset->external) + { + if (!*ipset->external) + ipset->external = NULL; + else if (!ipset->name) + ipset->name = ipset->external; + } if (!ipset->name || !*ipset->name) { @@ -223,6 +255,10 @@ fw3_load_ipsets(struct fw3_state *state, struct uci_package *p) //{ // 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 (list_empty(&ipset->datatypes)) { warn_elem(e, "has no datatypes assigned"); @@ -239,70 +275,26 @@ fw3_load_ipsets(struct fw3_state *state, struct uci_package *p) static void -create_ipset(struct fw3_ipset *ipset) +create_ipset(struct fw3_ipset *ipset, struct fw3_state *state) { bool first = true; - char s[INET6_ADDRSTRLEN]; struct fw3_ipset_datatype *type; - struct fw3_address *a1, *a2; - - const char *methods[] = { - "(bug)", - "bitmap", - "hash", - "list", - }; - - const char *types[] = { - "(bug)", - "ip", - "port", - "mac", - "net", - "set", - }; - - if (ipset->external && *ipset->external) - return; - info("Creating ipset %s", ipset->name); + info(" * Creating ipset %s", ipset->name); first = true; - fw3_pr("create %s %s", ipset->name, methods[ipset->method]); + fw3_pr("create %s %s", ipset->name, fw3_ipset_method_names[ipset->method]); list_for_each_entry(type, &ipset->datatypes, list) { - fw3_pr("%c%s", first ? ':' : ',', types[type->type]); + fw3_pr("%c%s", first ? ':' : ',', fw3_ipset_type_names[type->type]); first = false; } - if (!list_empty(&ipset->iprange)) + if (ipset->iprange.set) { - a1 = list_first_entry(&ipset->iprange, struct fw3_address, list); - a2 = list_last_entry(&ipset->iprange, struct fw3_address, list); - - if (a1 == a2) - { - inet_ntop(a1->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6, - &a1->address.v6, s, sizeof(s)); - - fw3_pr(" range %s/%u", s, a1->mask); - } - else if (a1->family == a2->family && - fw3_is_family(ipset, a1->family) && - fw3_is_family(ipset, a2->family)) - { - inet_ntop(a1->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6, - &a1->address.v6, s, sizeof(s)); - - fw3_pr(" range %s", s); - - inet_ntop(a2->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6, - &a2->address.v6, s, sizeof(s)); - - fw3_pr("-%s", s); - } + fw3_pr(" range %s", fw3_address_to_string(&ipset->iprange, false)); } else if (ipset->portrange.set) { @@ -310,8 +302,7 @@ create_ipset(struct fw3_ipset *ipset) ipset->portrange.port_min, ipset->portrange.port_max); } - if (ipset->family != FW3_FAMILY_ANY) - fw3_pr(" family inet%s", (ipset->family == FW3_FAMILY_V4) ? "" : "6"); + fw3_pr(" family inet%s", (ipset->family == FW3_FAMILY_V4) ? "" : "6"); if (ipset->timeout > 0) fw3_pr(" timeout %u", ipset->timeout); @@ -328,96 +319,143 @@ create_ipset(struct fw3_ipset *ipset) fw3_pr("\n"); } -static bool -ipset_loaded(struct list_head *statefile, const char *name) +void +fw3_create_ipsets(struct fw3_state *state) { - struct fw3_statefile_entry *e; - int mask = (1 << FW3_FAMILY_V4) | (1 << FW3_FAMILY_V6); + int tries; + bool exec = false; + struct fw3_ipset *ipset; - if (!statefile) - return false; + if (state->disable_ipsets) + return; - list_for_each_entry(e, statefile, list) + /* spawn ipsets */ + list_for_each_entry(ipset, &state->ipsets, list) { - if (e->type != FW3_TYPE_IPSET) + if (ipset->external) continue; - if (!strcmp(e->name, name) && (e->flags[0] & mask)) - return true; - } + if (!exec) + { + exec = fw3_command_pipe(false, "ipset", "-exist", "-"); - return false; -} + if (!exec) + return; + } -void -fw3_create_ipsets(struct fw3_state *state, struct list_head *statefile) -{ - struct fw3_ipset *ipset; + create_ipset(ipset, state); + } - if (state->disable_ipsets) - return; + if (exec) + { + fw3_pr("quit\n"); + fw3_command_close(); + } + /* wait for ipsets to appear */ list_for_each_entry(ipset, &state->ipsets, list) - if (!ipset_loaded(statefile, ipset->name)) - create_ipset(ipset); + { + if (ipset->external) + continue; - fw3_pr("quit\n"); + for (tries = 0; !fw3_check_ipset(ipset) && tries < 10; tries++) + usleep(50000); + } } void -fw3_destroy_ipsets(struct fw3_state *state, struct list_head *statefile) +fw3_destroy_ipsets(struct fw3_state *state) { - struct fw3_ipset *s; - struct fw3_statefile_entry *e; - int mask = (1 << FW3_FAMILY_V4) | (1 << FW3_FAMILY_V6); - - if (!statefile) - return; + int tries; + bool exec = false; + struct fw3_ipset *ipset; - list_for_each_entry(e, statefile, list) + /* destroy ipsets */ + list_for_each_entry(ipset, &state->ipsets, list) { - if (e->type != FW3_TYPE_IPSET) - continue; - - if (!hasbit(state->defaults.flags, FW3_FAMILY_V4)) - delbit(e->flags[0], FW3_FAMILY_V4); + if (!exec) + { + exec = fw3_command_pipe(false, "ipset", "-exist", "-"); - if (!hasbit(state->defaults.flags, FW3_FAMILY_V6)) - delbit(e->flags[0], FW3_FAMILY_V6); + if (!exec) + return; + } - if ((s = fw3_lookup_ipset(state, e->name)) != NULL) - s->flags = e->flags[0]; + info(" * Deleting ipset %s", ipset->name); - if (!(e->flags[0] & mask)) - { - info("Deleting ipset %s", e->name); + fw3_pr("flush %s\n", ipset->name); + fw3_pr("destroy %s\n", ipset->name); + } - fw3_pr("flush %s\n", e->name); - fw3_pr("destroy %s\n", e->name); - } + if (exec) + { + fw3_pr("quit\n"); + fw3_command_close(); } -} -void -fw3_free_ipset(struct fw3_ipset *ipset) -{ - fw3_free_list(&ipset->datatypes); - fw3_free_list(&ipset->iprange); + /* wait for ipsets to disappear */ + list_for_each_entry(ipset, &state->ipsets, list) + { + if (ipset->external) + continue; - free(ipset); + for (tries = 0; fw3_check_ipset(ipset) && tries < 10; tries++) + usleep(50000); + } } struct fw3_ipset * fw3_lookup_ipset(struct fw3_state *state, const char *name) { - struct fw3_ipset *ipset; + struct fw3_ipset *s; if (list_empty(&state->ipsets)) return NULL; - list_for_each_entry(ipset, &state->ipsets, list) - if (!strcmp(ipset->name, name)) - return ipset; + list_for_each_entry(s, &state->ipsets, list) + { + if (strcmp(s->name, name)) + continue; + + return s; + } return NULL; } + +bool +fw3_check_ipset(struct fw3_ipset *set) +{ + bool rv = false; + + socklen_t sz; + int s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); + struct ip_set_req_version req_ver; + struct ip_set_req_get_set req_name; + + if (s < 0 || fcntl(s, F_SETFD, FD_CLOEXEC)) + goto out; + + sz = sizeof(req_ver); + req_ver.op = IP_SET_OP_VERSION; + + if (getsockopt(s, SOL_IP, SO_IP_SET, &req_ver, &sz)) + goto out; + + sz = sizeof(req_name); + req_name.op = IP_SET_OP_GET_BYNAME; + req_name.version = req_ver.version; + snprintf(req_name.set.name, IPSET_MAXNAMELEN - 1, "%s", + set->external ? set->external : set->name); + + if (getsockopt(s, SOL_IP, SO_IP_SET, &req_name, &sz)) + goto out; + + rv = ((sz == sizeof(req_name)) && (req_name.set.index != IPSET_INVALID_ID)); + +out: + if (s >= 0) + close(s); + + return rv; +}