/*
* firewall3 - 3rd OpenWrt UCI firewall implementation
*
- * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
+ * Copyright (C) 2013 Jo-Philipp Wich <jo@mein.io>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
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),
FW3_OPT("external", string, ipset, external),
+ FW3_LIST("entry", setentry, ipset, entries),
+ FW3_OPT("loadfile", string, ipset, loadfile),
+
{ }
};
{
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;
}
{
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;
}
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;
}
}
}
}
- 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->enabled) {
+ return false;
+ }
- ipset = malloc(sizeof(*ipset));
+ 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;
+
+ 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;
+ ipset->comment = false;
+ ipset->counters = false;
+ ipset->enabled = true;
+ ipset->family = FW3_FAMILY_V4;
+ ipset->reload_set = false;
+
+ 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->external)
+ if (!fw3_parse_blob_options(ipset, fw3_ipset_opts, entry, name))
{
- if (!*ipset->external)
- ipset->external = NULL;
- else if (!ipset->name)
- ipset->name = ipset->external;
+ warn_section("ipset", ipset, NULL, "skipped due to invalid options");
+ fw3_free_ipset(ipset);
+ continue;
}
- 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);
+ 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;
- }
- fw3_free_ipset(ipset);
+ 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;
-
+ struct fw3_setentry *entry;
struct fw3_ipset_datatype *type;
info(" * Creating ipset %s", ipset->name);
if (ipset->iprange.set)
{
- fw3_pr(" range %s", fw3_address_to_string(&ipset->iprange, false));
+ fw3_pr(" range %s", fw3_address_to_string(&ipset->iprange, false, true));
}
else if (ipset->portrange.set)
{
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;
/* 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", "-");
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", "-");
if (ipset->external)
continue;
+ delay = 5;
for (tries = 0; fw3_check_ipset(ipset) && tries < 10; tries++)
- usleep(50000);
+ usleep(delay<<1);
}
}
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;
+ }
+}