diff options
Diffstat (limited to 'helpers.c')
-rw-r--r-- | helpers.c | 277 |
1 files changed, 277 insertions, 0 deletions
diff --git a/helpers.c b/helpers.c new file mode 100644 index 0000000..8cad0b3 --- /dev/null +++ b/helpers.c @@ -0,0 +1,277 @@ +/* + * firewall3 - 3rd OpenWrt UCI firewall implementation + * + * Copyright (C) 2018 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 + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "helpers.h" + + +const struct fw3_option fw3_cthelper_opts[] = { + FW3_OPT("enabled", bool, cthelper, enabled), + FW3_OPT("name", string, cthelper, name), + FW3_OPT("module", string, cthelper, module), + FW3_OPT("description", string, cthelper, description), + FW3_OPT("family", family, cthelper, family), + FW3_OPT("proto", protocol, cthelper, proto), + FW3_OPT("port", port, cthelper, port), + + { } +}; + + +static bool +test_module(struct fw3_cthelper *helper) +{ + struct stat s; + char path[sizeof("/sys/module/nf_conntrack_xxxxxxxxxxxxxxxx")]; + + snprintf(path, sizeof(path), "/sys/module/%s", helper->module); + + if (stat(path, &s) || !S_ISDIR(s.st_mode)) + return false; + + return true; +} + +static bool +check_cthelper(struct fw3_state *state, struct fw3_cthelper *helper, struct uci_element *e) +{ + if (!helper->name || !*helper->name) + { + warn_section("helper", helper, e, "must have a name assigned"); + } + else if (!helper->module || !*helper->module) + { + warn_section("helper", helper, e, "must have a module assigned"); + } + else if (!helper->proto.protocol || helper->proto.any || helper->proto.invert) + { + warn_section("helper", helper, e, "must specify a protocol"); + } + else if (helper->port.set && helper->port.invert) + { + warn_section("helper", helper, e, "must not specify negated ports"); + } + else + { + return true; + } + + return false; +} + +static struct fw3_cthelper * +fw3_alloc_cthelper(struct fw3_state *state) +{ + struct fw3_cthelper *helper; + + helper = calloc(1, sizeof(*helper)); + if (!helper) + return NULL; + + helper->enabled = true; + helper->family = FW3_FAMILY_ANY; + + list_add_tail(&helper->list, &state->cthelpers); + + return helper; +} + +static void +load_cthelpers(struct fw3_state *state, struct uci_package *p) +{ + struct fw3_cthelper *helper; + struct uci_section *s; + struct uci_element *e; + + uci_foreach_element(&p->sections, e) + { + s = uci_to_section(e); + + if (strcmp(s->type, "helper")) + continue; + + helper = fw3_alloc_cthelper(state); + + if (!helper) + continue; + + if (!fw3_parse_options(helper, fw3_cthelper_opts, s)) + warn_elem(e, "has invalid options"); + + if (!check_cthelper(state, helper, e)) + fw3_free_cthelper(helper); + } +} + +void +fw3_load_cthelpers(struct fw3_state *state, struct uci_package *p) +{ + struct uci_package *hp = NULL; + FILE *fp; + + INIT_LIST_HEAD(&state->cthelpers); + + fp = fopen(FW3_HELPERCONF, "r"); + + if (fp) { + uci_import(state->uci, fp, "fw3_ct_helpers", &hp, true); + fclose(fp); + + if (hp) + load_cthelpers(state, hp); + } + + load_cthelpers(state, p); +} + +struct fw3_cthelper * +fw3_lookup_cthelper(struct fw3_state *state, const char *name) +{ + struct fw3_cthelper *h; + + if (list_empty(&state->cthelpers)) + return NULL; + + list_for_each_entry(h, &state->cthelpers, list) + { + if (strcasecmp(h->name, name)) + continue; + + return h; + } + + return NULL; +} + +struct fw3_cthelper * +fw3_lookup_cthelper_by_proto_port(struct fw3_state *state, + struct fw3_protocol *proto, + struct fw3_port *port) +{ + struct fw3_cthelper *h; + + if (list_empty(&state->cthelpers)) + return NULL; + + if (!proto || !proto->protocol || proto->any || proto->invert) + return NULL; + + if (port && port->invert) + return NULL; + + list_for_each_entry(h, &state->cthelpers, list) + { + if (!h->enabled) + continue; + + if (h->proto.protocol != proto->protocol) + continue; + + if (h->port.set && (!port || !port->set)) + continue; + + if (!h->port.set && (!port || !port->set)) + return h; + + if (h->port.set && port && port->set && + h->port.port_min <= port->port_min && + h->port.port_max >= port->port_max) + return h; + } + + return NULL; +} + +static void +print_helper_rule(struct fw3_ipt_handle *handle, struct fw3_cthelper *helper, + struct fw3_zone *zone) +{ + struct fw3_ipt_rule *r; + + r = fw3_ipt_rule_create(handle, &helper->proto, NULL, NULL, NULL, NULL); + + if (helper->description && *helper->description) + fw3_ipt_rule_comment(r, helper->description); + else + fw3_ipt_rule_comment(r, helper->name); + + fw3_ipt_rule_sport_dport(r, NULL, &helper->port); + fw3_ipt_rule_target(r, "CT"); + fw3_ipt_rule_addarg(r, false, "--helper", helper->name); + fw3_ipt_rule_replace(r, "zone_%s_helper", zone->name); +} + +void +fw3_print_cthelpers(struct fw3_ipt_handle *handle, struct fw3_state *state, + struct fw3_zone *zone) +{ + struct fw3_cthelper *helper; + struct fw3_cthelpermatch *match; + + if (handle->table != FW3_TABLE_RAW) + return; + + if (!fw3_is_family(zone, handle->family)) + return; + + if (list_empty(&zone->cthelpers)) + { + if (zone->masq || !zone->auto_helper) + return; + + if (list_empty(&state->cthelpers)) + return; + + info(" - Using automatic conntrack helper attachment"); + + list_for_each_entry(helper, &state->cthelpers, list) + { + if (!helper || !helper->enabled) + continue; + + if (!fw3_is_family(helper, handle->family)) + continue; + + if (!test_module(helper)) + continue; + + print_helper_rule(handle, helper, zone); + } + } + else + { + list_for_each_entry(match, &zone->cthelpers, list) + { + helper = match->ptr; + + if (!helper || !helper->enabled) + continue; + + if (!fw3_is_family(helper, handle->family)) + continue; + + if (!test_module(helper)) + { + info(" ! Conntrack module '%s' for helper '%s' is not loaded", + helper->module, helper->name); + continue; + } + + print_helper_rule(handle, helper, zone); + } + } +} |