123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670 |
- /*
- * firewall3 - 3rd OpenWrt UCI firewall implementation
- *
- * Copyright (C) 2013-2014 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 <stdio.h>
- #include <unistd.h>
- #include "options.h"
- #include "defaults.h"
- #include "zones.h"
- #include "rules.h"
- #include "redirects.h"
- #include "snats.h"
- #include "forwards.h"
- #include "ipsets.h"
- #include "includes.h"
- #include "ubus.h"
- #include "iptables.h"
- #include "helpers.h"
- static enum fw3_family print_family = FW3_FAMILY_ANY;
- static struct fw3_state *run_state = NULL;
- static struct fw3_state *cfg_state = NULL;
- static bool
- build_state(bool runtime)
- {
- struct fw3_state *state = NULL;
- struct uci_package *p = NULL;
- FILE *sf;
- state = calloc(1, sizeof(*state));
- if (!state)
- error("Out of memory");
- state->uci = uci_alloc_context();
- if (!state->uci)
- error("Out of memory");
- if (runtime)
- {
- sf = fopen(FW3_STATEFILE, "r");
- if (sf)
- {
- uci_import(state->uci, sf, "fw3_state", &p, true);
- fclose(sf);
- }
- if (!p)
- {
- uci_free_context(state->uci);
- free(state);
- return false;
- }
- state->statefile = true;
- run_state = state;
- }
- else
- {
- if (!fw3_ubus_connect())
- warn("Failed to connect to ubus");
- if (uci_load(state->uci, "firewall", &p))
- {
- uci_perror(state->uci, NULL);
- error("Failed to load /etc/config/firewall");
- }
- if (!fw3_find_command("ipset"))
- {
- warn("Unable to locate ipset utility, disabling ipset support");
- state->disable_ipsets = true;
- }
- cfg_state = state;
- }
- struct blob_buf b = {NULL, NULL, 0, NULL};
- fw3_ubus_rules(&b);
- fw3_load_defaults(state, p);
- fw3_load_cthelpers(state, p);
- fw3_load_ipsets(state, p, b.head);
- fw3_load_zones(state, p);
- fw3_load_rules(state, p, b.head);
- fw3_load_redirects(state, p, b.head);
- fw3_load_snats(state, p, b.head);
- fw3_load_forwards(state, p, b.head);
- fw3_load_includes(state, p, b.head);
- return true;
- }
- static void
- free_state(struct fw3_state *state)
- {
- struct list_head *cur, *tmp;
- list_for_each_safe(cur, tmp, &state->zones)
- fw3_free_zone((struct fw3_zone *)cur);
- list_for_each_safe(cur, tmp, &state->rules)
- fw3_free_rule((struct fw3_rule *)cur);
- list_for_each_safe(cur, tmp, &state->redirects)
- fw3_free_redirect((struct fw3_redirect *)cur);
- list_for_each_safe(cur, tmp, &state->snats)
- fw3_free_snat((struct fw3_snat *)cur);
- list_for_each_safe(cur, tmp, &state->forwards)
- fw3_free_forward((struct fw3_forward *)cur);
- list_for_each_safe(cur, tmp, &state->ipsets)
- fw3_free_ipset((struct fw3_ipset *)cur);
- list_for_each_safe(cur, tmp, &state->includes)
- fw3_free_include((struct fw3_include *)cur);
- list_for_each_safe(cur, tmp, &state->cthelpers)
- fw3_free_cthelper((struct fw3_cthelper *)cur);
- uci_free_context(state->uci);
- free(state);
- fw3_ubus_disconnect();
- }
- static bool
- family_running(enum fw3_family family)
- {
- return (run_state && has(run_state->defaults.flags, family, family));
- }
- static void
- family_set(struct fw3_state *state, enum fw3_family family, bool set)
- {
- if (!state)
- return;
- if (set)
- set(state->defaults.flags, family, family);
- else
- del(state->defaults.flags, family, family);
- }
- static int
- stop(bool complete)
- {
- int rv = 1;
- enum fw3_family family;
- enum fw3_table table;
- struct fw3_ipt_handle *handle;
- if (!complete && !run_state)
- {
- warn("The firewall appears to be stopped. "
- "Use the 'flush' command to forcefully purge all rules.");
- return rv;
- }
- if (!print_family && run_state)
- fw3_hotplug_zones(run_state, false);
- for (family = FW3_FAMILY_V4; family <= FW3_FAMILY_V6; family++)
- {
- if (!complete && !family_running(family))
- continue;
- for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++)
- {
- if (!fw3_has_table(family == FW3_FAMILY_V6, fw3_flag_names[table]))
- continue;
- if (!(handle = fw3_ipt_open(family, table)))
- continue;
- info(" * %sing %s %s table", complete ? "Flush" : "Clear",
- fw3_flag_names[family], fw3_flag_names[table]);
- if (complete)
- {
- fw3_flush_all(handle);
- }
- else if (run_state)
- {
- fw3_flush_rules(handle, run_state, false);
- fw3_flush_zones(handle, run_state, false);
- }
- fw3_ipt_commit(handle);
- fw3_ipt_close(handle);
- }
- family_set(run_state, family, false);
- family_set(cfg_state, family, false);
- rv = 0;
- }
- if (run_state)
- fw3_destroy_ipsets(run_state);
- if (complete)
- fw3_flush_conntrack(NULL);
- if (!rv && run_state)
- fw3_write_statefile(run_state);
- return rv;
- }
- static int
- start(void)
- {
- int rv = 1;
- enum fw3_family family;
- enum fw3_table table;
- struct fw3_ipt_handle *handle;
- if (!print_family)
- fw3_create_ipsets(cfg_state);
- for (family = FW3_FAMILY_V4; family <= FW3_FAMILY_V6; family++)
- {
- if (family == FW3_FAMILY_V6 && cfg_state->defaults.disable_ipv6)
- continue;
- if (print_family && family != print_family)
- continue;
- if (!print_family && family_running(family))
- {
- warn("The %s firewall appears to be started already. "
- "If it is indeed empty, remove the %s file and retry.",
- fw3_flag_names[family], FW3_STATEFILE);
- continue;
- }
- for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++)
- {
- if (!fw3_has_table(family == FW3_FAMILY_V6, fw3_flag_names[table]))
- continue;
- if (!(handle = fw3_ipt_open(family, table)))
- continue;
- info(" * Populating %s %s table",
- fw3_flag_names[family], fw3_flag_names[table]);
- fw3_print_default_chains(handle, cfg_state, false);
- fw3_print_zone_chains(handle, cfg_state, false);
- fw3_print_default_head_rules(handle, cfg_state, false);
- fw3_print_rules(handle, cfg_state);
- fw3_print_redirects(handle, cfg_state);
- fw3_print_snats(handle, cfg_state);
- fw3_print_forwards(handle, cfg_state);
- fw3_print_zone_rules(handle, cfg_state, false);
- fw3_print_default_tail_rules(handle, cfg_state, false);
- if (!print_family)
- fw3_ipt_commit(handle);
- fw3_ipt_close(handle);
- }
- if (!print_family)
- fw3_print_includes(cfg_state, family, false);
- family_set(run_state, family, true);
- family_set(cfg_state, family, true);
- rv = 0;
- }
- if (!rv)
- {
- fw3_flush_conntrack(run_state);
- fw3_set_defaults(cfg_state);
- if (!print_family)
- {
- fw3_run_includes(cfg_state, false);
- fw3_hotplug_zones(cfg_state, true);
- fw3_write_statefile(cfg_state);
- }
- }
- return rv;
- }
- static int
- reload(void)
- {
- int rv = 1;
- enum fw3_family family;
- enum fw3_table table;
- struct fw3_ipt_handle *handle;
- if (!run_state)
- return start();
- fw3_hotplug_zones(run_state, false);
- for (family = FW3_FAMILY_V4; family <= FW3_FAMILY_V6; family++)
- {
- if (!family_running(family))
- goto start;
- for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++)
- {
- if (!fw3_has_table(family == FW3_FAMILY_V6, fw3_flag_names[table]))
- continue;
- if (!(handle = fw3_ipt_open(family, table)))
- continue;
- info(" * Clearing %s %s table",
- fw3_flag_names[family], fw3_flag_names[table]);
- fw3_flush_rules(handle, run_state, true);
- fw3_flush_zones(handle, run_state, true);
- fw3_ipt_commit(handle);
- fw3_ipt_close(handle);
- }
- family_set(run_state, family, false);
- family_set(cfg_state, family, false);
- start:
- if (family == FW3_FAMILY_V6 && cfg_state->defaults.disable_ipv6)
- continue;
- for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++)
- {
- if (!fw3_has_table(family == FW3_FAMILY_V6, fw3_flag_names[table]))
- continue;
- if (!(handle = fw3_ipt_open(family, table)))
- continue;
- info(" * Populating %s %s table",
- fw3_flag_names[family], fw3_flag_names[table]);
- fw3_print_default_chains(handle, cfg_state, true);
- fw3_print_zone_chains(handle, cfg_state, true);
- fw3_print_default_head_rules(handle, cfg_state, true);
- fw3_print_rules(handle, cfg_state);
- fw3_print_redirects(handle, cfg_state);
- fw3_print_snats(handle, cfg_state);
- fw3_print_forwards(handle, cfg_state);
- fw3_print_zone_rules(handle, cfg_state, true);
- fw3_print_default_tail_rules(handle, cfg_state, true);
- fw3_ipt_commit(handle);
- fw3_ipt_close(handle);
- }
- fw3_print_includes(cfg_state, family, true);
- family_set(run_state, family, true);
- family_set(cfg_state, family, true);
- rv = 0;
- }
- if (!rv)
- {
- fw3_flush_conntrack(run_state);
- fw3_set_defaults(cfg_state);
- fw3_run_includes(cfg_state, true);
- fw3_hotplug_zones(cfg_state, true);
- fw3_write_statefile(cfg_state);
- }
- return rv;
- }
- static int
- gc(void)
- {
- enum fw3_family family;
- enum fw3_table table;
- struct fw3_ipt_handle *handle;
- for (family = FW3_FAMILY_V4; family <= FW3_FAMILY_V6; family++)
- {
- if (family == FW3_FAMILY_V6 && cfg_state->defaults.disable_ipv6)
- continue;
- for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++)
- {
- if (!fw3_has_table(family == FW3_FAMILY_V6, fw3_flag_names[table]))
- continue;
- if (!(handle = fw3_ipt_open(family, table)))
- continue;
- fw3_ipt_gc(handle);
- fw3_ipt_commit(handle);
- fw3_ipt_close(handle);
- }
- }
- return 0;
- }
- static int
- lookup_network(const char *net)
- {
- struct fw3_zone *z;
- struct fw3_device *d;
- list_for_each_entry(z, &cfg_state->zones, list)
- {
- list_for_each_entry(d, &z->networks, list)
- {
- if (!strcmp(d->name, net))
- {
- printf("%s\n", z->name);
- return 0;
- }
- }
- }
- return 1;
- }
- static int
- lookup_device(const char *dev)
- {
- struct fw3_zone *z;
- struct fw3_device *d;
- list_for_each_entry(z, &cfg_state->zones, list)
- {
- list_for_each_entry(d, &z->devices, list)
- {
- if (!strcmp(d->name, dev))
- {
- printf("%s\n", z->name);
- return 0;
- }
- }
- }
- return 1;
- }
- static int
- lookup_zone(const char *zone, const char *device)
- {
- struct fw3_zone *z;
- struct fw3_device *d;
- list_for_each_entry(z, &cfg_state->zones, list)
- {
- if (strcmp(z->name, zone))
- continue;
- list_for_each_entry(d, &z->devices, list)
- {
- if (device && strcmp(device, d->name))
- continue;
- printf("%s\n", d->name);
- if (device)
- return 0;
- }
- if (!device)
- return 0;
- }
- return 1;
- }
- static int
- usage(void)
- {
- fprintf(stderr, "fw3 [-4] [-6] [-q] print\n");
- fprintf(stderr, "fw3 [-q] {start|stop|flush|reload|restart}\n");
- fprintf(stderr, "fw3 [-q] network {net}\n");
- fprintf(stderr, "fw3 [-q] device {dev}\n");
- fprintf(stderr, "fw3 [-q] zone {zone} [dev]\n");
- return 1;
- }
- int main(int argc, char **argv)
- {
- int ch, rv = 1;
- enum fw3_family family = FW3_FAMILY_ANY;
- struct fw3_defaults *defs = NULL;
- while ((ch = getopt(argc, argv, "46dqh")) != -1)
- {
- switch (ch)
- {
- case '4':
- family = FW3_FAMILY_V4;
- break;
- case '6':
- family = FW3_FAMILY_V6;
- break;
- case 'd':
- fw3_pr_debug = true;
- break;
- case 'q':
- if (freopen("/dev/null", "w", stderr)) {}
- break;
- case 'h':
- rv = usage();
- goto out;
- }
- }
- build_state(false);
- defs = &cfg_state->defaults;
- if (optind >= argc)
- {
- rv = usage();
- goto out;
- }
- if (!strcmp(argv[optind], "print"))
- {
- if (family == FW3_FAMILY_ANY)
- {
- family = FW3_FAMILY_V4;
- }
- else if (family == FW3_FAMILY_V6)
- {
- if (defs->disable_ipv6)
- warn("IPv6 rules globally disabled in configuration");
- #ifdef DISABLE_IPV6
- else
- warn("IPv6 support is not compiled in");
- #endif
- }
- if (freopen("/dev/null", "w", stderr)) {};
- cfg_state->disable_ipsets = true;
- print_family = family;
- fw3_pr_debug = true;
- if (fw3_lock())
- {
- build_state(true);
- rv = start();
- fw3_unlock();
- }
- }
- else if (!strcmp(argv[optind], "start"))
- {
- if (fw3_lock())
- {
- build_state(true);
- rv = start();
- fw3_unlock();
- }
- }
- else if (!strcmp(argv[optind], "stop"))
- {
- if (fw3_lock())
- {
- build_state(true);
- rv = stop(false);
- fw3_unlock();
- }
- }
- else if (!strcmp(argv[optind], "flush"))
- {
- if (fw3_lock())
- {
- build_state(true);
- rv = stop(true);
- fw3_unlock();
- }
- }
- else if (!strcmp(argv[optind], "restart"))
- {
- if (fw3_lock())
- {
- build_state(true);
- stop(true);
- rv = start();
- fw3_unlock();
- }
- }
- else if (!strcmp(argv[optind], "reload"))
- {
- if (fw3_lock())
- {
- build_state(true);
- rv = reload();
- fw3_unlock();
- }
- }
- else if (!strcmp(argv[optind], "gc"))
- {
- if (fw3_lock())
- {
- rv = gc();
- fw3_unlock();
- }
- }
- else if (!strcmp(argv[optind], "network") && (optind + 1) < argc)
- {
- rv = lookup_network(argv[optind + 1]);
- }
- else if (!strcmp(argv[optind], "device") && (optind + 1) < argc)
- {
- rv = lookup_device(argv[optind + 1]);
- }
- else if (!strcmp(argv[optind], "zone") && (optind + 1) < argc)
- {
- rv = lookup_zone(argv[optind + 1], argv[optind + 2]);
- }
- else
- {
- rv = usage();
- }
- out:
- if (cfg_state)
- free_state(cfg_state);
- if (run_state)
- free_state(run_state);
- return rv;
- }
|