X-Git-Url: http://git.archive.openwrt.org/?p=project%2Ffirewall3.git;a=blobdiff_plain;f=main.c;h=5888ab50285a6e416d3434ea28f9e16901d7106a;hp=3ef7243c33955f92ff54eb25c5a2e32d0d3f6182;hb=0a7d36d8cf56f160b531f3db9f045e3f9315dd15;hpb=2d73c2b436cc16d49ce706920802604150781dc9 diff --git a/main.c b/main.c index 3ef7243..5888ab5 100644 --- a/main.c +++ b/main.c @@ -1,7 +1,7 @@ /* * firewall3 - 3rd OpenWrt UCI firewall implementation * - * Copyright (C) 2013 Jo-Philipp Wich + * Copyright (C) 2013-2014 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 @@ -24,52 +24,92 @@ #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" -static bool print_rules = false; -static enum fw3_family use_family = FW3_FAMILY_ANY; +static enum fw3_family print_family = FW3_FAMILY_ANY; +static struct fw3_state *run_state = NULL; +static struct fw3_state *cfg_state = NULL; -static struct fw3_state * -build_state(void) + +static bool +build_state(bool runtime) { struct fw3_state *state = NULL; struct uci_package *p = NULL; + FILE *sf; - state = malloc(sizeof(*state)); - + state = calloc(1, sizeof(*state)); if (!state) error("Out of memory"); - memset(state, 0, sizeof(*state)); state->uci = uci_alloc_context(); if (!state->uci) error("Out of memory"); - if (uci_load(state->uci, "firewall", &p)) + if (runtime) { - uci_perror(state->uci, NULL); - error("Failed to load /etc/config/firewall"); - } + sf = fopen(FW3_STATEFILE, "r"); + + if (sf) + { + uci_import(state->uci, sf, "fw3_state", &p, true); + fclose(sf); + } - if (!fw3_find_command("ipset")) + if (!p) + { + uci_free_context(state->uci); + free(state); + + return false; + } + + state->statefile = true; + + run_state = state; + } + else { - warn("Unable to locate ipset utility, disabling ipset support"); - state->disable_ipsets = true; + 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_ipsets(state, p); fw3_load_zones(state, p); - fw3_load_rules(state, p); - fw3_load_redirects(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); + fw3_load_includes(state, p); - return state; + return true; } static void @@ -86,9 +126,18 @@ free_state(struct fw3_state *state) 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); + uci_free_context(state->uci); free(state); @@ -98,170 +147,110 @@ free_state(struct fw3_state *state) static bool -restore_pipe(enum fw3_family family, bool silent) +family_running(enum fw3_family family) { - const char *cmd; - - cmd = (family == FW3_FAMILY_V4) ? "iptables-restore" : "ip6tables-restore"; - - if (print_rules) - return fw3_stdout_pipe(); - - if (!fw3_command_pipe(silent, cmd, "--lenient", "--noflush")) - { - warn("Unable to execute %s", cmd); - return false; - } - - return true; -} - -static bool -family_running(struct list_head *statefile, enum fw3_family family) -{ - struct fw3_statefile_entry *e; - - if (statefile) - { - list_for_each_entry(e, statefile, list) - { - if (e->type != FW3_TYPE_DEFAULTS) - continue; - - return hasbit(e->flags[0], family); - } - } - - return false; -} - -static bool -family_used(enum fw3_family family) -{ - return (use_family == FW3_FAMILY_ANY) || (use_family == family); -} - -static bool -family_loaded(struct fw3_state *state, enum fw3_family family) -{ - return hasbit(state->defaults.flags, 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) - setbit(state->defaults.flags, family); + set(state->defaults.flags, family, family); else - delbit(state->defaults.flags, family); + del(state->defaults.flags, family, family); } static int -stop(struct fw3_state *state, bool complete, bool restart) +stop(bool complete) { int rv = 1; enum fw3_family family; enum fw3_table table; + struct fw3_ipt_handle *handle; - struct list_head *statefile = fw3_read_statefile(); - - if (!complete && !statefile) + if (!complete && !run_state) { - if (!restart) - warn("The firewall appears to be stopped. " - "Use the 'flush' command to forcefully purge all rules."); + 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(statefile, family)) + if (!complete && !family_running(family)) continue; - if (!family_used(family) || !restore_pipe(family, true)) - continue; - - info("Removing %s rules ...", fw3_flag_names[family]); - for (table = FW3_TABLE_FILTER; table <= FW3_TABLE_RAW; table++) { if (!fw3_has_table(family == FW3_FAMILY_V6, fw3_flag_names[table])) continue; - info(" * %sing %s table", - complete ? "Flush" : "Clear", fw3_flag_names[table]); + if (!(handle = fw3_ipt_open(family, table))) + continue; - fw3_pr("*%s\n", fw3_flag_names[table]); + info(" * %sing %s %s table", complete ? "Flush" : "Clear", + fw3_flag_names[family], fw3_flag_names[table]); if (complete) { - fw3_flush_all(table); + fw3_flush_all(handle); } - else + else if (run_state) { - /* pass 1 */ - fw3_flush_rules(table, family, false, statefile); - fw3_flush_zones(table, family, false, statefile); - - /* pass 2 */ - fw3_flush_rules(table, family, true, statefile); - fw3_flush_zones(table, family, true, statefile); + fw3_flush_rules(handle, run_state, false); + fw3_flush_zones(handle, run_state, false); } - fw3_pr("COMMIT\n"); + fw3_ipt_commit(handle); + fw3_ipt_close(handle); } - fw3_command_close(); - - if (!restart) - family_set(state, family, false); + family_set(run_state, family, false); + family_set(cfg_state, family, false); rv = 0; } - if (!restart && - !family_loaded(state, FW3_FAMILY_V4) && - !family_loaded(state, FW3_FAMILY_V6) && - fw3_command_pipe(false, "ipset", "-exist", "-")) - { - fw3_destroy_ipsets(statefile); - fw3_command_close(); - } + if (run_state) + fw3_destroy_ipsets(run_state); - fw3_free_statefile(statefile); + if (complete) + fw3_flush_conntrack(NULL); - if (!rv) - fw3_write_statefile(state); + if (!rv && run_state) + fw3_write_statefile(run_state); return rv; } static int -start(struct fw3_state *state, bool restart) +start(void) { int rv = 1; enum fw3_family family; enum fw3_table table; + struct fw3_ipt_handle *handle; - struct list_head *statefile = fw3_read_statefile(); - - if (!print_rules && !restart && - fw3_command_pipe(false, "ipset", "-exist", "-")) - { - fw3_create_ipsets(state); - fw3_command_close(); - } + if (!print_family) + fw3_create_ipsets(cfg_state); for (family = FW3_FAMILY_V4; family <= FW3_FAMILY_V6; family++) { - if (!family_used(family)) + if (family == FW3_FAMILY_V6 && cfg_state->defaults.disable_ipv6) continue; - if (!family_loaded(state, family) || !restore_pipe(family, false)) + if (print_family && family != print_family) continue; - if (!print_rules && !restart && family_running(statefile, family)) + 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.", @@ -270,48 +259,183 @@ start(struct fw3_state *state, bool restart) continue; } - info("Constructing %s rules ...", fw3_flag_names[family]); + 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; - info(" * Populating %s table", fw3_flag_names[table]); - - fw3_pr("*%s\n", fw3_flag_names[table]); - fw3_print_default_chains(table, family, state); - fw3_print_zone_chains(table, family, state); - fw3_print_default_head_rules(table, family, state); - fw3_print_rules(table, family, state); - fw3_print_redirects(table, family, state); - fw3_print_forwards(table, family, state); - fw3_print_zone_rules(table, family, state); - fw3_print_default_tail_rules(table, family, state); - fw3_pr("COMMIT\n"); + 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_command_close(); - family_set(state, family, true); + fw3_print_includes(cfg_state, family, true); + + family_set(run_state, family, true); + family_set(cfg_state, family, true); rv = 0; } - fw3_free_statefile(statefile); + if (!rv) + { + fw3_flush_conntrack(run_state); - if (!rv && !print_rules) - fw3_write_statefile(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 -lookup_network(struct fw3_state *state, const char *net) +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, &state->zones, list) + list_for_each_entry(z, &cfg_state->zones, list) { list_for_each_entry(d, &z->networks, list) { @@ -327,12 +451,12 @@ lookup_network(struct fw3_state *state, const char *net) } static int -lookup_device(struct fw3_state *state, const char *dev) +lookup_device(const char *dev) { struct fw3_zone *z; struct fw3_device *d; - list_for_each_entry(z, &state->zones, list) + list_for_each_entry(z, &cfg_state->zones, list) { list_for_each_entry(d, &z->devices, list) { @@ -348,11 +472,42 @@ lookup_device(struct fw3_state *state, const char *dev) } 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] {start|stop|flush|restart|print}\n"); + 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; } @@ -361,23 +516,27 @@ usage(void) int main(int argc, char **argv) { int ch, rv = 1; - struct fw3_state *state = NULL; + enum fw3_family family = FW3_FAMILY_ANY; struct fw3_defaults *defs = NULL; - while ((ch = getopt(argc, argv, "46qh")) != -1) + while ((ch = getopt(argc, argv, "46dqh")) != -1) { switch (ch) { case '4': - use_family = FW3_FAMILY_V4; + family = FW3_FAMILY_V4; break; case '6': - use_family = FW3_FAMILY_V6; + family = FW3_FAMILY_V6; + break; + + case 'd': + fw3_pr_debug = true; break; case 'q': - freopen("/dev/null", "w", stderr); + if (freopen("/dev/null", "w", stderr)) {} break; case 'h': @@ -386,14 +545,8 @@ int main(int argc, char **argv) } } - if (!fw3_ubus_connect()) - error("Failed to connect to ubus"); - - state = build_state(); - defs = &state->defaults; - - if (!fw3_lock()) - goto out; + build_state(false); + defs = &cfg_state->defaults; if (optind >= argc) { @@ -401,45 +554,100 @@ int main(int argc, char **argv) goto out; } - if (use_family == FW3_FAMILY_V6 && defs->disable_ipv6) - warn("IPv6 rules globally disabled in configuration"); - if (!strcmp(argv[optind], "print")) { - if (use_family == FW3_FAMILY_ANY) - use_family = FW3_FAMILY_V4; + 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 + } - freopen("/dev/null", "w", stderr); + if (freopen("/dev/null", "w", stderr)) {}; - state->disable_ipsets = true; - print_rules = true; + cfg_state->disable_ipsets = true; + print_family = family; + fw3_pr_debug = true; - rv = start(state, false); + if (fw3_lock()) + { + build_state(true); + rv = start(); + fw3_unlock(); + } } else if (!strcmp(argv[optind], "start")) { - rv = start(state, false); + if (fw3_lock()) + { + build_state(true); + rv = start(); + fw3_unlock(); + } } else if (!strcmp(argv[optind], "stop")) { - rv = stop(state, false, false); + if (fw3_lock()) + { + build_state(true); + rv = stop(false); + fw3_unlock(); + } } else if (!strcmp(argv[optind], "flush")) { - rv = stop(state, true, false); + if (fw3_lock()) + { + build_state(true); + rv = stop(true); + fw3_unlock(); + } } else if (!strcmp(argv[optind], "restart")) { - rv = stop(state, false, true); - rv = start(state, !rv); + 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(state, argv[optind + 1]); + rv = lookup_network(argv[optind + 1]); } else if (!strcmp(argv[optind], "device") && (optind + 1) < argc) { - rv = lookup_device(state, argv[optind + 1]); + 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 { @@ -447,10 +655,11 @@ int main(int argc, char **argv) } out: - if (state) - free_state(state); + if (cfg_state) + free_state(cfg_state); - fw3_unlock(); + if (run_state) + free_state(run_state); return rv; }