summaryrefslogtreecommitdiff
path: root/helpers.c
diff options
context:
space:
mode:
Diffstat (limited to 'helpers.c')
-rw-r--r--helpers.c277
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);
+ }
+ }
+}