X-Git-Url: http://git.archive.openwrt.org/?p=project%2Ffirewall3.git;a=blobdiff_plain;f=ipsets.c;h=b73c3d28c64d99d7769f44ca9ad0da6a19241111;hp=af03ddc94c0ef2c773d0f23de7fd3181509d4e84;hb=f742ba7d20cb07f6306ebacedbb165b3fc7064ad;hpb=71d9d828691cefcac19201079473e600ffa596c9 diff --git a/ipsets.c b/ipsets.c index af03ddc..b73c3d2 100644 --- a/ipsets.c +++ b/ipsets.c @@ -1,7 +1,7 @@ /* * firewall3 - 3rd OpenWrt UCI firewall implementation * - * Copyright (C) 2013 Jo-Philipp Wich + * Copyright (C) 2013 Jo-Philipp Wich * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -38,6 +38,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), + { } }; @@ -91,20 +94,11 @@ 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) { - 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,12 +110,17 @@ check_types(struct uci_element *e, struct fw3_ipset *ipset) { for (i = 0; i < ARRAY_SIZE(ipset_types); i++) { + /* skip type for v6 if it does not support family */ + if (ipset->family != FW3_FAMILY_V4 && + !(ipset_types[i].optional & OPT_FAMILY)) + continue; + if (ipset_types[i].types == typelist) { ipset->method = ipset_types[i].method; - warn_elem(e, "defines no storage method, assuming '%s'", - methods[ipset->method]); + warn_section("ipset", ipset, e, "defines no storage method, assuming '%s'", + fw3_ipset_method_names[ipset->method]); break; } @@ -135,62 +134,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 & 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_ANY) + ipset->family != FW3_FAMILY_V4) { - warn_elem(e, "family ignored"); - ipset->family = FW3_FAMILY_ANY; + warn_section("ipset", ipset, e, "family ignored"); + ipset->family = FW3_FAMILY_V4; } } @@ -198,138 +197,179 @@ 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) { - struct fw3_ipset *ipset; + if (ipset->external) + { + if (!*ipset->external) + ipset->external = NULL; + else if (!ipset->name) + ipset->name = ipset->external; + } - ipset = malloc(sizeof(*ipset)); + 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; + + ipset = calloc(1, sizeof(*ipset)); if (!ipset) return NULL; - memset(ipset, 0, sizeof(*ipset)); - INIT_LIST_HEAD(&ipset->datatypes); + INIT_LIST_HEAD(&ipset->entries); + + 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; - uci_foreach_element(&p->sections, e) + blob_for_each_attr(entry, a, rem) { - s = uci_to_section(e); + const char *type; + const char *name = "ubus ipset"; - if (strcmp(s->type, "ipset")) + if (!fw3_attr_parse_name_type(entry, &name, &type)) continue; - ipset = fw3_alloc_ipset(); + if (strcmp(type, "ipset")) + continue; + ipset = fw3_alloc_ipset(state); if (!ipset) continue; - fw3_parse_options(ipset, fw3_ipset_opts, s); - - 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 (list_empty(&ipset->datatypes)) + if (!fw3_parse_blob_options(ipset, fw3_ipset_opts, entry, name)) { - warn_elem(e, "has no datatypes assigned"); - } - else if (check_types(e, ipset)) - { - list_add_tail(&ipset->list, &state->ipsets); + warn_section("ipset", ipset, NULL, "skipped due to invalid options"); + fw3_free_ipset(ipset); continue; } - fw3_free_ipset(ipset); + if (!check_ipset(state, ipset, NULL)) + fw3_free_ipset(ipset); + } + + uci_foreach_element(&p->sections, e) + { + s = uci_to_section(e); + + if (strcmp(s->type, "ipset")) + continue; + + ipset = fw3_alloc_ipset(state); + + if (!ipset) + continue; + + if (!fw3_parse_options(ipset, fw3_ipset_opts, s)) + warn_elem(e, "has invalid options"); + + if (!check_ipset(state, ipset, e)) + fw3_free_ipset(ipset); } } static void +load_file(struct fw3_ipset *ipset) +{ + FILE *f; + char line[128]; + + 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)) + 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; - char s[INET6_ADDRSTRLEN]; - + struct fw3_setentry *entry; struct fw3_ipset_datatype *type; - struct fw3_address *a; - - 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); 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 (ipset->method == FW3_IPSET_METHOD_HASH) + fw3_pr(" family inet%s", (ipset->family == FW3_FAMILY_V4) ? "" : "6"); + if (ipset->iprange.set) { - a = &ipset->iprange; - - if (!a->range) - { - inet_ntop(a->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6, - &a->address.v6, s, sizeof(s)); - - fw3_pr(" range %s/%u", s, a->mask); - } - else - { - inet_ntop(a->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6, - &a->address.v6, s, sizeof(s)); - - fw3_pr(" range %s", s); - - inet_ntop(a->family == FW3_FAMILY_V4 ? AF_INET : AF_INET6, - &a->address2.v6, s, sizeof(s)); - - fw3_pr("-%s", s); - } + fw3_pr(" range %s", fw3_address_to_string(&ipset->iprange, false, true)); } else if (ipset->portrange.set) { @@ -337,9 +377,6 @@ create_ipset(struct fw3_ipset *ipset, struct fw3_state *state) 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"); - if (ipset->timeout > 0) fw3_pr(" timeout %u", ipset->timeout); @@ -354,48 +391,99 @@ create_ipset(struct fw3_ipset *ipset, struct fw3_state *state) fw3_pr("\n"); - fw3_set_running(ipset, &state->running_ipsets); + 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) { + int tries; + bool exec = false; struct fw3_ipset *ipset; if (state->disable_ipsets) return; + /* spawn ipsets */ + list_for_each_entry(ipset, &state->ipsets, list) + { + if (ipset->external) + continue; + + if (!exec) + { + exec = fw3_command_pipe(false, "ipset", "-exist", "-"); + + if (!exec) + return; + } + + create_ipset(ipset, state); + } + + if (exec) + { + fw3_pr("quit\n"); + fw3_command_close(); + } + + /* wait for ipsets to appear */ list_for_each_entry(ipset, &state->ipsets, list) - if (!hasbit(ipset->flags[0], FW3_FLAG_DELETED)) - if (!fw3_lookup_ipset(state, ipset->name, true)) - create_ipset(ipset, state); + { + 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, enum fw3_family family) +fw3_destroy_ipsets(struct fw3_state *state) { - struct fw3_ipset *s, *tmp; + int tries; + bool exec = false; + struct fw3_ipset *ipset; - list_for_each_entry_safe(s, tmp, &state->running_ipsets, running_list) + /* destroy ipsets */ + list_for_each_entry(ipset, &state->ipsets, list) { - del(s->flags, family, family); - - if (fw3_no_family(s->flags[family == FW3_FAMILY_V6])) + if (!exec) { - info(" * Deleting ipset %s", s->name); + exec = fw3_command_pipe(false, "ipset", "-exist", "-"); - fw3_pr("flush %s\n", s->name); - fw3_pr("destroy %s\n", s->name); - - fw3_set_running(s, NULL); + if (!exec) + return; } + + info(" * Deleting ipset %s", ipset->name); + + fw3_pr("flush %s\n", ipset->name); + fw3_pr("destroy %s\n", ipset->name); + } + + if (exec) + { + fw3_pr("quit\n"); + fw3_command_close(); + } + + /* wait for ipsets to disappear */ + list_for_each_entry(ipset, &state->ipsets, list) + { + if (ipset->external) + continue; + + 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, bool running) +fw3_lookup_ipset(struct fw3_state *state, const char *name) { struct fw3_ipset *s; @@ -407,11 +495,45 @@ fw3_lookup_ipset(struct fw3_state *state, const char *name, bool running) if (strcmp(s->name, name)) continue; - if (!running || s->running_list.next) - return s; - - break; + 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; +}