From 6b27a6665c288937eb5028063064e3350dcab545 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Wed, 15 May 2013 00:04:33 +0200 Subject: [PATCH] Drop iptables-restore and create rules through libiptc and libxtables --- CMakeLists.txt | 2 +- defaults.c | 254 +++++++---- defaults.h | 14 +- forwards.c | 36 +- forwards.h | 4 +- iptables.c | 1112 +++++++++++++++++++++++++++++++++++++++++++++++- iptables.h | 114 ++++- main.c | 82 ++-- options.c | 16 +- options.h | 5 + redirects.c | 278 +++++++----- redirects.h | 5 +- rules.c | 116 ++--- rules.h | 4 +- utils.c | 26 ++ utils.h | 3 + zones.c | 368 +++++++++------- zones.h | 8 +- 18 files changed, 1922 insertions(+), 525 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 50b1c3f..e5d2dc3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ IF(APPLE) ENDIF() ADD_EXECUTABLE(firewall3 main.c options.c defaults.c zones.c forwards.c rules.c redirects.c utils.c ubus.c ipsets.c includes.c iptables.c) -TARGET_LINK_LIBRARIES(firewall3 uci ubox ubus ip4tc ip6tc) +TARGET_LINK_LIBRARIES(firewall3 uci ubox ubus ip4tc ip6tc xtables m ${CMAKE_SOURCE_DIR}/libext.a ${CMAKE_SOURCE_DIR}/libext4.a ${CMAKE_SOURCE_DIR}/libext6.a) SET(CMAKE_INSTALL_PREFIX /usr) diff --git a/defaults.c b/defaults.c index 20c57d6..3ed6cfa 100644 --- a/defaults.c +++ b/defaults.c @@ -45,22 +45,6 @@ static const struct fw3_rule_spec default_chains[] = { { } }; -static const struct fw3_rule_spec toplevel_rules[] = { - C(ANY, FILTER, UNSPEC, "INPUT -j delegate_input"), - C(ANY, FILTER, UNSPEC, "OUTPUT -j delegate_output"), - C(ANY, FILTER, UNSPEC, "FORWARD -j delegate_forward"), - - C(V4, NAT, UNSPEC, "PREROUTING -j delegate_prerouting"), - C(V4, NAT, UNSPEC, "POSTROUTING -j delegate_postrouting"), - - C(ANY, MANGLE, UNSPEC, "FORWARD -j mssfix"), - C(ANY, MANGLE, UNSPEC, "PREROUTING -j fwmark"), - - C(ANY, RAW, UNSPEC, "PREROUTING -j notrack"), - - { } -}; - const struct fw3_option fw3_flag_opts[] = { FW3_OPT("input", target, defaults, policy_input), FW3_OPT("forward", target, defaults, policy_forward), @@ -142,110 +126,186 @@ fw3_load_defaults(struct fw3_state *state, struct uci_package *p) } void -fw3_print_default_chains(struct fw3_state *state, enum fw3_family family, - enum fw3_table table, bool reload) +fw3_print_default_chains(struct fw3_ipt_handle *handle, struct fw3_state *state, + bool reload) { - bool rv; struct fw3_defaults *defs = &state->defaults; - uint32_t custom_mask = ~0; + const struct fw3_rule_spec *c; #define policy(t) \ - ((t == FW3_FLAG_REJECT) ? "DROP" : fw3_flag_names[t]) + ((t == FW3_FLAG_REJECT) ? FW3_FLAG_DROP : t) - if (family == FW3_FAMILY_V6 && defs->disable_ipv6) + if (handle->family == FW3_FAMILY_V6 && defs->disable_ipv6) return; - if (table == FW3_TABLE_FILTER) + if (handle->table == FW3_TABLE_FILTER) { - fw3_pr(":INPUT %s [0:0]\n", policy(defs->policy_input)); - fw3_pr(":FORWARD %s [0:0]\n", policy(defs->policy_forward)); - fw3_pr(":OUTPUT %s [0:0]\n", policy(defs->policy_output)); + fw3_ipt_set_policy(handle, "INPUT", policy(defs->policy_input)); + fw3_ipt_set_policy(handle, "OUTPUT", policy(defs->policy_output)); + fw3_ipt_set_policy(handle, "FORWARD", policy(defs->policy_forward)); } - /* Don't touch user chains on reload */ - if (reload) - delbit(custom_mask, FW3_FLAG_CUSTOM_CHAINS); - if (defs->custom_chains) - set(defs->flags, family, FW3_FLAG_CUSTOM_CHAINS); + set(defs->flags, handle->family, FW3_FLAG_CUSTOM_CHAINS); if (defs->syn_flood) - set(defs->flags, family, FW3_FLAG_SYN_FLOOD); + set(defs->flags, handle->family, FW3_FLAG_SYN_FLOOD); + + for (c = default_chains; c->format; c++) + { + /* don't touch user chains on selective stop */ + if (reload && c->flag == FW3_FLAG_CUSTOM_CHAINS) + continue; + + if (!fw3_is_family(c, handle->family)) + continue; + + if (c->table != handle->table) + continue; - rv = fw3_pr_rulespec(table, family, defs->flags, custom_mask, - default_chains, ":%s - [0:0]\n"); + if (c->flag && + !hasbit(defs->flags[handle->family == FW3_FAMILY_V6], c->flag)) + continue; + + fw3_ipt_create_chain(handle, c->format); + } - if (rv) - set(defs->flags, family, table); + set(defs->flags, handle->family, handle->table); } + +struct toplevel_rule { + enum fw3_table table; + const char *chain; + const char *target; +}; + void -fw3_print_default_head_rules(struct fw3_state *state, enum fw3_family family, - enum fw3_table table, bool reload) +fw3_print_default_head_rules(struct fw3_ipt_handle *handle, + struct fw3_state *state, bool reload) { int i; struct fw3_defaults *defs = &state->defaults; + struct fw3_device lodev = { .set = true }; + struct fw3_protocol tcp = { .protocol = 6 }; + struct fw3_ipt_rule *r; + struct toplevel_rule *tr; + const char *chains[] = { - "input", "input", - "output", "output", - "forward", "forwarding", + "delegate_input", "input", + "delegate_output", "output", + "delegate_forward", "forwarding", }; - fw3_pr_rulespec(table, family, NULL, 0, toplevel_rules, "-A %s\n"); + struct toplevel_rule rules[] = { + { FW3_TABLE_FILTER, "INPUT", "delegate_input" }, + { FW3_TABLE_FILTER, "OUTPUT", "delegate_output" }, + { FW3_TABLE_FILTER, "FORWARD", "delegate_forward" }, + + { FW3_TABLE_NAT, "PREROUTING", "delegate_prerouting" }, + { FW3_TABLE_NAT, "POSTROUTING", "delegate_postrouting" }, + + { FW3_TABLE_MANGLE, "FORWARD", "mssfix" }, + { FW3_TABLE_MANGLE, "PREROUTING", "fwmark" }, + + { FW3_TABLE_RAW, "PREROUTING", "notrack" }, + + { 0, NULL }, + }; - switch (table) + for (tr = rules; tr->chain; tr++) + { + if (tr->table != handle->table) + continue; + + r = fw3_ipt_rule_new(handle); + fw3_ipt_rule_target(r, tr->target); + fw3_ipt_rule_append(r, tr->chain); + } + + switch (handle->table) { case FW3_TABLE_FILTER: - fw3_pr("-A delegate_input -i lo -j ACCEPT\n"); - fw3_pr("-A delegate_output -o lo -j ACCEPT\n"); + + sprintf(lodev.name, "lo"); + + r = fw3_ipt_rule_create(handle, NULL, &lodev, NULL, NULL, NULL); + fw3_ipt_rule_target(r, "ACCEPT"); + fw3_ipt_rule_append(r, "delegate_input"); + + r = fw3_ipt_rule_create(handle, NULL, NULL, &lodev, NULL, NULL); + fw3_ipt_rule_target(r, "ACCEPT"); + fw3_ipt_rule_append(r, "delegate_output"); if (defs->custom_chains) { for (i = 0; i < ARRAY_SIZE(chains); i += 2) { - fw3_pr("-A delegate_%s -m comment " - "--comment \"user chain for %s\" -j %s_rule\n", - chains[i], chains[i+1], chains[i+1]); + r = fw3_ipt_rule_new(handle); + fw3_ipt_rule_comment(r, "user chain for %s", chains[i+1]); + fw3_ipt_rule_target(r, chains[i+1]); + fw3_ipt_rule_append(r, chains[i]); } } for (i = 0; i < ARRAY_SIZE(chains); i += 2) { - fw3_pr("-A delegate_%s -m conntrack --ctstate RELATED,ESTABLISHED " - "-j ACCEPT\n", chains[i]); + r = fw3_ipt_rule_new(handle); + fw3_ipt_rule_extra(r, "-m conntrack --ctstate RELATED,ESTABLISHED"); + fw3_ipt_rule_target(r, "ACCEPT"); + fw3_ipt_rule_append(r, chains[i]); if (defs->drop_invalid) { - fw3_pr("-A delegate_%s -m conntrack --ctstate INVALID -j DROP\n", - chains[i]); + r = fw3_ipt_rule_new(handle); + fw3_ipt_rule_extra(r, "-m conntrack --ctstate INVALID"); + fw3_ipt_rule_target(r, "DROP"); + fw3_ipt_rule_append(r, chains[i]); } } if (defs->syn_flood) { - fw3_pr("-A syn_flood -p tcp --syn"); - fw3_format_limit(&defs->syn_flood_rate); - fw3_pr(" -j RETURN\n"); - - fw3_pr("-A syn_flood -j DROP\n"); - fw3_pr("-A delegate_input -p tcp --syn -j syn_flood\n"); + r = fw3_ipt_rule_create(handle, &tcp, NULL, NULL, NULL, NULL); + fw3_ipt_rule_extra(r, "--syn"); + fw3_ipt_rule_limit(r, &defs->syn_flood_rate); + fw3_ipt_rule_target(r, "RETURN"); + fw3_ipt_rule_append(r, "syn_flood"); + + r = fw3_ipt_rule_new(handle); + fw3_ipt_rule_target(r, "DROP"); + fw3_ipt_rule_append(r, "syn_flood"); + + r = fw3_ipt_rule_create(handle, &tcp, NULL, NULL, NULL, NULL); + fw3_ipt_rule_extra(r, "--syn"); + fw3_ipt_rule_target(r, "syn_flood"); + fw3_ipt_rule_append(r, "delegate_input"); } - fw3_pr("-A reject -p tcp -j REJECT --reject-with tcp-reset\n"); - fw3_pr("-A reject -j REJECT --reject-with port-unreach\n"); + r = fw3_ipt_rule_create(handle, &tcp, NULL, NULL, NULL, NULL); + fw3_ipt_rule_target(r, "REJECT"); + fw3_ipt_rule_addarg(r, false, "--reject-with", "tcp-reset"); + fw3_ipt_rule_append(r, "reject"); + + r = fw3_ipt_rule_new(handle); + fw3_ipt_rule_target(r, "REJECT"); + fw3_ipt_rule_addarg(r, false, "--reject-with", "port-unreach"); + fw3_ipt_rule_append(r, "reject"); break; case FW3_TABLE_NAT: if (defs->custom_chains) { - fw3_pr("-A delegate_prerouting " - "-m comment --comment \"user chain for prerouting\" " - "-j prerouting_rule\n"); - - fw3_pr("-A delegate_postrouting " - "-m comment --comment \"user chain for postrouting\" " - "-j postrouting_rule\n"); + r = fw3_ipt_rule_new(handle); + fw3_ipt_rule_comment(r, "user chain for prerouting"); + fw3_ipt_rule_target(r, "prerouting_rule"); + fw3_ipt_rule_append(r, "delegate_prerouting"); + + r = fw3_ipt_rule_new(handle); + fw3_ipt_rule_comment(r, "user chain for postrouting"); + fw3_ipt_rule_target(r, "postrouting_rule"); + fw3_ipt_rule_append(r, "delegate_postrouting"); } break; @@ -255,22 +315,47 @@ fw3_print_default_head_rules(struct fw3_state *state, enum fw3_family family, } void -fw3_print_default_tail_rules(struct fw3_state *state, enum fw3_family family, - enum fw3_table table, bool reload) +fw3_print_default_tail_rules(struct fw3_ipt_handle *handle, + struct fw3_state *state, bool reload) { struct fw3_defaults *defs = &state->defaults; + struct fw3_ipt_rule *r; - if (table != FW3_TABLE_FILTER) + if (handle->table != FW3_TABLE_FILTER) return; if (defs->policy_input == FW3_FLAG_REJECT) - fw3_pr("-A delegate_input -j reject\n"); + { + r = fw3_ipt_rule_new(handle); + + if (!r) + return; + + fw3_ipt_rule_target(r, "reject"); + fw3_ipt_rule_append(r, "delegate_input"); + } if (defs->policy_output == FW3_FLAG_REJECT) - fw3_pr("-A delegate_output -j reject\n"); + { + r = fw3_ipt_rule_new(handle); + + if (!r) + return; + + fw3_ipt_rule_target(r, "reject"); + fw3_ipt_rule_append(r, "delegate_output"); + } if (defs->policy_forward == FW3_FLAG_REJECT) - fw3_pr("-A delegate_forward -j reject\n"); + { + r = fw3_ipt_rule_new(handle); + + if (!r) + return; + + fw3_ipt_rule_target(r, "reject"); + fw3_ipt_rule_append(r, "delegate_forward"); + } } static void @@ -305,16 +390,24 @@ void fw3_flush_rules(struct fw3_ipt_handle *handle, struct fw3_state *state, bool reload) { + enum fw3_flag policy = reload ? FW3_FLAG_DROP : FW3_FLAG_ACCEPT; struct fw3_defaults *defs = &state->defaults; const struct fw3_rule_spec *c; if (!has(defs->flags, handle->family, handle->table)) return; + if (handle->table == FW3_TABLE_FILTER) + { + fw3_ipt_set_policy(handle, "INPUT", policy); + fw3_ipt_set_policy(handle, "OUTPUT", policy); + fw3_ipt_set_policy(handle, "FORWARD", policy); + } + for (c = default_chains; c->format; c++) { /* don't touch user chains on selective stop */ - if (reload && hasbit(c->flag, FW3_FLAG_CUSTOM_CHAINS)) + if (reload && c->flag == FW3_FLAG_CUSTOM_CHAINS) continue; if (!fw3_is_family(c, handle->family)) @@ -323,7 +416,10 @@ fw3_flush_rules(struct fw3_ipt_handle *handle, struct fw3_state *state, if (c->table != handle->table) continue; - fw3_ipt_set_policy(handle, reload ? FW3_FLAG_DROP : FW3_FLAG_ACCEPT); + if (c->flag && + !hasbit(defs->flags[handle->family == FW3_FAMILY_V6], c->flag)) + continue; + fw3_ipt_delete_rules(handle, c->format); fw3_ipt_delete_chain(handle, c->format); } @@ -334,6 +430,12 @@ fw3_flush_rules(struct fw3_ipt_handle *handle, struct fw3_state *state, void fw3_flush_all(struct fw3_ipt_handle *handle) { - fw3_ipt_set_policy(handle, FW3_FLAG_ACCEPT); + if (handle->table == FW3_TABLE_FILTER) + { + fw3_ipt_set_policy(handle, "INPUT", FW3_FLAG_ACCEPT); + fw3_ipt_set_policy(handle, "OUTPUT", FW3_FLAG_ACCEPT); + fw3_ipt_set_policy(handle, "FORWARD", FW3_FLAG_ACCEPT); + } + fw3_ipt_flush(handle); } diff --git a/defaults.h b/defaults.h index a89a36e..649e329 100644 --- a/defaults.h +++ b/defaults.h @@ -26,16 +26,14 @@ extern const struct fw3_option fw3_flag_opts[]; void fw3_load_defaults(struct fw3_state *state, struct uci_package *p); -void fw3_print_default_chains(struct fw3_state *state, enum fw3_family family, - enum fw3_table table, bool reload); +void fw3_print_default_chains(struct fw3_ipt_handle *handle, + struct fw3_state *state, bool reload); -void fw3_print_default_head_rules(struct fw3_state *state, - enum fw3_family family, enum fw3_table table, - bool reload); +void fw3_print_default_head_rules(struct fw3_ipt_handle *handle, + struct fw3_state *state, bool reload); -void fw3_print_default_tail_rules(struct fw3_state *state, - enum fw3_family family, enum fw3_table table, - bool reload); +void fw3_print_default_tail_rules(struct fw3_ipt_handle *handle, + struct fw3_state *state, bool reload); void fw3_set_defaults(struct fw3_state *state); diff --git a/forwards.c b/forwards.c index 6ea0497..c7e7ba1 100644 --- a/forwards.c +++ b/forwards.c @@ -106,32 +106,32 @@ fw3_load_forwards(struct fw3_state *state, struct uci_package *p) static void -print_chain(struct fw3_forward *forward) +append_chain(struct fw3_ipt_rule *r, struct fw3_forward *forward) { if (forward->src.any || !forward->src.set) - fw3_pr("-A delegate_forward"); + fw3_ipt_rule_append(r, "delegate_forward"); else - fw3_pr("-A zone_%s_forward", forward->src.name); + fw3_ipt_rule_append(r, "zone_%s_forward", forward->src.name); } -static void print_target(struct fw3_forward *forward) +static void set_target(struct fw3_ipt_rule *r, struct fw3_forward *forward) { if (forward->dest.any || !forward->dest.set) - fw3_pr(" -j ACCEPT\n"); + fw3_ipt_rule_target(r, "ACCEPT"); else - fw3_pr(" -j zone_%s_dest_ACCEPT\n", forward->dest.name); + fw3_ipt_rule_target(r, "zone_%s_dest_ACCEPT", forward->dest.name); } static void -print_forward(struct fw3_forward *forward, enum fw3_family family, - enum fw3_table table) +print_forward(struct fw3_ipt_handle *handle, struct fw3_forward *forward) { const char *s, *d; + struct fw3_ipt_rule *r; - if (table != FW3_TABLE_FILTER) + if (handle->table != FW3_TABLE_FILTER) return; - if (!fw3_is_family(forward, family)) + if (!fw3_is_family(forward, handle->family)) return; s = forward->_src ? forward->_src->name : "*"; @@ -139,24 +139,24 @@ print_forward(struct fw3_forward *forward, enum fw3_family family, info(" * Forward '%s' -> '%s'", s, d); - if (!fw3_is_family(forward->_src, family) || - !fw3_is_family(forward->_dest, family)) + if (!fw3_is_family(forward->_src, handle->family) || + !fw3_is_family(forward->_dest, handle->family)) { info(" ! Skipping due to different family of zone"); return; } - print_chain(forward); - fw3_format_comment("forwarding ", s, "->", d); - print_target(forward); + r = fw3_ipt_rule_new(handle); + fw3_ipt_rule_comment(r, "forwarding %s -> %s", s, d); + set_target(r, forward); + append_chain(r, forward); } void -fw3_print_forwards(struct fw3_state *state, enum fw3_family family, - enum fw3_table table) +fw3_print_forwards(struct fw3_ipt_handle *handle, struct fw3_state *state) { struct fw3_forward *forward; list_for_each_entry(forward, &state->forwards, list) - print_forward(forward, family, table); + print_forward(handle, forward); } diff --git a/forwards.h b/forwards.h index b8784bc..d299718 100644 --- a/forwards.h +++ b/forwards.h @@ -22,12 +22,12 @@ #include "options.h" #include "zones.h" #include "utils.h" +#include "iptables.h" extern const struct fw3_option fw3_forward_opts[]; void fw3_load_forwards(struct fw3_state *state, struct uci_package *p); -void fw3_print_forwards(struct fw3_state *state, enum fw3_family family, - enum fw3_table table); +void fw3_print_forwards(struct fw3_ipt_handle *handle, struct fw3_state *state); #define fw3_free_forward(forward) \ fw3_free_object(forward, fw3_forward_opts) diff --git a/iptables.c b/iptables.c index f5f4952..9c5f80a 100644 --- a/iptables.c +++ b/iptables.c @@ -19,27 +19,67 @@ #include "iptables.h" +static struct option base_opts[] = { + { .name = "match", .has_arg = 1, .val = 'm' }, + { .name = "jump", .has_arg = 1, .val = 'j' }, + { .name = "append", .has_arg = 1, .val = 'A' }, + { NULL } +}; + +static struct xtables_globals xtg = { + .option_offset = 0, + .program_version = "4", + .orig_opts = base_opts, +}; + +static struct xtables_globals xtg6 = { + .option_offset = 0, + .program_version = "6", + .orig_opts = base_opts, +}; + +/* Required by certain extensions like SNAT and DNAT */ +int kernel_version; + +void +get_kernel_version(void) +{ + static struct utsname uts; + int x = 0, y = 0, z = 0; + + if (uname(&uts) == -1) + sprintf(uts.release, "3.0.0"); + + sscanf(uts.release, "%d.%d.%d", &x, &y, &z); + kernel_version = LINUX_VERSION(x, y, z); +} + struct fw3_ipt_handle * fw3_ipt_open(enum fw3_family family, enum fw3_table table) { struct fw3_ipt_handle *h; - h = malloc(sizeof(*h)); + h = fw3_alloc(sizeof(*h)); - if (!h) - return NULL; + xtables_init(); if (family == FW3_FAMILY_V6) { h->family = FW3_FAMILY_V6; h->table = table; h->handle = ip6tc_init(fw3_flag_names[table]); + + xtables_set_params(&xtg6); + xtables_set_nfproto(NFPROTO_IPV6); } else { h->family = FW3_FAMILY_V4; h->table = table; h->handle = iptc_init(fw3_flag_names[table]); + + xtables_set_params(&xtg); + xtables_set_nfproto(NFPROTO_IPV4); } if (!h->handle) @@ -48,30 +88,38 @@ fw3_ipt_open(enum fw3_family family, enum fw3_table table) return NULL; } + xtables_pending_matches = NULL; + xtables_pending_targets = NULL; + + xtables_matches = NULL; + xtables_targets = NULL; + + init_extensions(); + init_extensions4(); + init_extensions6(); + return h; } -void fw3_ipt_set_policy(struct fw3_ipt_handle *h, enum fw3_flag policy) +void +fw3_ipt_set_policy(struct fw3_ipt_handle *h, const char *chain, + enum fw3_flag policy) { - if (h->table != FW3_TABLE_FILTER) - return; - if (h->family == FW3_FAMILY_V6) - { - ip6tc_set_policy("INPUT", fw3_flag_names[policy], NULL, h->handle); - ip6tc_set_policy("OUTPUT", fw3_flag_names[policy], NULL, h->handle); - ip6tc_set_policy("FORWARD", fw3_flag_names[policy], NULL, h->handle); - } + ip6tc_set_policy(chain, fw3_flag_names[policy], NULL, h->handle); else - { - iptc_set_policy("INPUT", fw3_flag_names[policy], NULL, h->handle); - iptc_set_policy("OUTPUT", fw3_flag_names[policy], NULL, h->handle); - iptc_set_policy("FORWARD", fw3_flag_names[policy], NULL, h->handle); - } + iptc_set_policy(chain, fw3_flag_names[policy], NULL, h->handle); } -void fw3_ipt_delete_chain(struct fw3_ipt_handle *h, const char *chain) +void +fw3_ipt_delete_chain(struct fw3_ipt_handle *h, const char *chain) { + if (fw3_pr_debug) + { + printf("-F %s\n", chain); + printf("-X %s\n", chain); + } + if (h->family == FW3_FAMILY_V6) { if (ip6tc_flush_entries(chain, h->handle)) @@ -84,7 +132,8 @@ void fw3_ipt_delete_chain(struct fw3_ipt_handle *h, const char *chain) } } -void fw3_ipt_delete_rules(struct fw3_ipt_handle *h, const char *target) +void +fw3_ipt_delete_rules(struct fw3_ipt_handle *h, const char *target) { unsigned int num; const struct ipt_entry *e; @@ -110,6 +159,9 @@ void fw3_ipt_delete_rules(struct fw3_ipt_handle *h, const char *target) if (*t && !strcmp(t, target)) { + if (fw3_pr_debug) + printf("-D %s %u\n", chain, num + 1); + ip6tc_delete_num_entry(chain, num, h->handle); found = true; break; @@ -135,6 +187,9 @@ void fw3_ipt_delete_rules(struct fw3_ipt_handle *h, const char *target) if (*t && !strcmp(t, target)) { + if (fw3_pr_debug) + printf("-D %s %u\n", chain, num + 1); + iptc_delete_num_entry(chain, num, h->handle); found = true; break; @@ -145,7 +200,8 @@ void fw3_ipt_delete_rules(struct fw3_ipt_handle *h, const char *target) } } -void fw3_ipt_flush(struct fw3_ipt_handle *h) +void +fw3_ipt_flush(struct fw3_ipt_handle *h) { const char *chain; @@ -183,12 +239,1022 @@ void fw3_ipt_flush(struct fw3_ipt_handle *h) } } -void fw3_ipt_commit(struct fw3_ipt_handle *h) +void +fw3_ipt_commit(struct fw3_ipt_handle *h) { + int rv; + if (h->family == FW3_FAMILY_V6) - ip6tc_commit(h->handle); + { + rv = ip6tc_commit(h->handle); + if (!rv) + fprintf(stderr, "ip6tc_commit(): %s\n", ip6tc_strerror(errno)); + } else - iptc_commit(h->handle); + { + rv = iptc_commit(h->handle); + if (!rv) + fprintf(stderr, "iptc_commit(): %s\n", iptc_strerror(errno)); + } free(h); } + +struct fw3_ipt_rule * +fw3_ipt_rule_new(struct fw3_ipt_handle *h) +{ + struct fw3_ipt_rule *r; + + r = fw3_alloc(sizeof(*r)); + + r->h = h; + r->argv = fw3_alloc(sizeof(char *)); + r->argv[r->argc++] = "fw3"; + + return r; +} + + +static bool +is_chain(struct fw3_ipt_handle *h, const char *name) +{ + if (h->family == FW3_FAMILY_V6) + return ip6tc_is_chain(name, h->handle); + else + return iptc_is_chain(name, h->handle); +} + +static char * +get_protoname(struct fw3_ipt_rule *r) +{ + const struct xtables_pprot *pp; + + if (r->protocol) + for (pp = xtables_chain_protos; pp->name; pp++) + if (pp->num == r->protocol) + return (char *)pp->name; + + return NULL; +} + +static struct xtables_match * +find_match(struct fw3_ipt_rule *r, const char *name) +{ + return xtables_find_match(name, XTF_TRY_LOAD, &r->matches); +} + +static void +init_match(struct fw3_ipt_rule *r, struct xtables_match *m, bool no_clone) +{ + size_t s; + struct xtables_globals *g; + + if (!m) + return; + + s = XT_ALIGN(sizeof(struct xt_entry_match)) + m->size; + + m->m = fw3_alloc(s); + strcpy(m->m->u.user.name, m->real_name ? m->real_name : m->name); + m->m->u.user.revision = m->revision; + m->m->u.match_size = s; + + /* free previous userspace data */ + if (m->udata_size) + { + free(m->udata); + m->udata = fw3_alloc(m->udata_size); + } + + if (m->init) + m->init(m->m); + + /* don't merge options if no_clone is set and this match is a clone */ + if (no_clone && (m == m->next)) + return; + + /* merge option table */ + g = (r->h->family == FW3_FAMILY_V6) ? &xtg6 : &xtg; + + if (m->x6_options) + g->opts = xtables_options_xfrm(g->orig_opts, g->opts, + m->x6_options, &m->option_offset); + + if (m->extra_opts) + g->opts = xtables_merge_options(g->orig_opts, g->opts, + m->extra_opts, &m->option_offset); +} + +static bool +need_protomatch(struct fw3_ipt_rule *r, const char *pname) +{ + if (!pname) + return false; + + if (!xtables_find_match(pname, XTF_DONT_LOAD, NULL)) + return true; + + return !r->protocol_loaded; +} + +static struct xtables_match * +load_protomatch(struct fw3_ipt_rule *r) +{ + const char *pname = get_protoname(r); + + if (!need_protomatch(r, pname)) + return NULL; + + return find_match(r, pname); +} + +static struct xtables_target * +get_target(struct fw3_ipt_rule *r, const char *name) +{ + size_t s; + struct xtables_target *t; + struct xtables_globals *g; + + bool chain = is_chain(r->h, name); + + if (chain) + t = xtables_find_target(XT_STANDARD_TARGET, XTF_LOAD_MUST_SUCCEED); + else + t = xtables_find_target(name, XTF_TRY_LOAD); + + if (!t) + return NULL; + + s = XT_ALIGN(sizeof(struct xt_entry_target)) + t->size; + t->t = fw3_alloc(s); + + if (!t->real_name) + strcpy(t->t->u.user.name, name); + else + strcpy(t->t->u.user.name, t->real_name); + + t->t->u.user.revision = t->revision; + t->t->u.target_size = s; + + if (t->udata_size) + { + free(t->udata); + t->udata = fw3_alloc(t->udata_size); + } + + if (t->init) + t->init(t->t); + + /* merge option table */ + g = (r->h->family == FW3_FAMILY_V6) ? &xtg6 : &xtg; + + if (t->x6_options) + g->opts = xtables_options_xfrm(g->orig_opts, g->opts, + t->x6_options, &t->option_offset); + else + g->opts = xtables_merge_options(g->orig_opts, g->opts, + t->extra_opts, &t->option_offset); + + r->target = t; + + return t; +} + +void +fw3_ipt_rule_proto(struct fw3_ipt_rule *r, struct fw3_protocol *proto) +{ + uint32_t pr; + + if (!proto || proto->any) + return; + + pr = proto->protocol; + + if (r->h->family == FW3_FAMILY_V6) + { + if (pr == 1) + pr = 58; + + r->e6.ipv6.proto = pr; + r->e6.ipv6.flags |= IP6T_F_PROTO; + + if (proto->invert) + r->e6.ipv6.invflags |= XT_INV_PROTO; + } + else + { + r->e.ip.proto = pr; + + if (proto->invert) + r->e.ip.invflags |= XT_INV_PROTO; + } + + r->protocol = pr; +} + +void +fw3_ipt_rule_in_out(struct fw3_ipt_rule *r, + struct fw3_device *in, struct fw3_device *out) +{ + if (r->h->family == FW3_FAMILY_V6) + { + if (in && !in->any) + { + xtables_parse_interface(in->name, r->e6.ipv6.iniface, + r->e6.ipv6.iniface_mask); + + if (in->invert) + r->e6.ipv6.invflags |= IP6T_INV_VIA_IN; + } + + if (out && !out->any) + { + xtables_parse_interface(out->name, r->e6.ipv6.outiface, + r->e6.ipv6.outiface_mask); + + if (out->invert) + r->e6.ipv6.invflags |= IP6T_INV_VIA_OUT; + } + } + else + { + if (in && !in->any) + { + xtables_parse_interface(in->name, r->e.ip.iniface, + r->e.ip.iniface_mask); + + if (in->invert) + r->e.ip.invflags |= IPT_INV_VIA_IN; + } + + if (out && !out->any) + { + xtables_parse_interface(out->name, r->e.ip.outiface, + r->e.ip.outiface_mask); + + if (out->invert) + r->e.ip.invflags |= IPT_INV_VIA_OUT; + } + } +} + + +static void +ip4prefix2mask(int prefix, struct in_addr *mask) +{ + mask->s_addr = htonl(~((1 << (32 - prefix)) - 1)); +} + +static void +ip6prefix2mask(int prefix, struct in6_addr *mask) +{ + char *p = (char *)mask; + + if (prefix > 0) + { + memset(p, 0xff, prefix / 8); + memset(p + (prefix / 8) + 1, 0, (128 - prefix) / 8); + p[prefix / 8] = 0xff << (8 - (prefix & 7)); + } + else + { + memset(mask, 0, sizeof(*mask)); + } +} + +void +fw3_ipt_rule_src_dest(struct fw3_ipt_rule *r, + struct fw3_address *src, struct fw3_address *dest) +{ + int i; + + if ((src && src->range) || (dest && dest->range)) + { + fw3_ipt_rule_addarg(r, false, "-m", "iprange"); + } + + if (src && src->set) + { + if (src->range) + { + fw3_ipt_rule_addarg(r, src->invert, "--src-range", + fw3_address_to_string(src, false)); + } + else if (r->h->family == FW3_FAMILY_V6) + { + r->e6.ipv6.src = src->address.v6; + ip6prefix2mask(src->mask, &r->e6.ipv6.smsk); + + for (i = 0; i < 4; i++) + r->e6.ipv6.src.s6_addr32[i] &= r->e6.ipv6.smsk.s6_addr32[i]; + + if (src->invert) + r->e6.ipv6.invflags |= IP6T_INV_SRCIP; + } + else + { + r->e.ip.src = src->address.v4; + ip4prefix2mask(src->mask, &r->e.ip.smsk); + + r->e.ip.src.s_addr &= r->e.ip.smsk.s_addr; + + if (src->invert) + r->e.ip.invflags |= IPT_INV_SRCIP; + } + } + + if (dest && dest->set) + { + if (dest->range) + { + fw3_ipt_rule_addarg(r, dest->invert, "--dst-range", + fw3_address_to_string(dest, false)); + } + else if (r->h->family == FW3_FAMILY_V6) + { + r->e6.ipv6.dst = dest->address.v6; + ip6prefix2mask(dest->mask, &r->e6.ipv6.dmsk); + + for (i = 0; i < 4; i++) + r->e6.ipv6.dst.s6_addr32[i] &= r->e6.ipv6.dmsk.s6_addr32[i]; + + if (dest->invert) + r->e6.ipv6.invflags |= IP6T_INV_DSTIP; + } + else + { + r->e.ip.dst = dest->address.v4; + ip4prefix2mask(dest->mask, &r->e.ip.dmsk); + + r->e.ip.dst.s_addr &= r->e.ip.dmsk.s_addr; + + if (dest->invert) + r->e.ip.invflags |= IPT_INV_DSTIP; + } + } +} + +void +fw3_ipt_rule_sport_dport(struct fw3_ipt_rule *r, + struct fw3_port *sp, struct fw3_port *dp) +{ + char buf[sizeof("65535:65535\0")]; + + if ((!sp || !sp->set) && (!dp || !dp->set)) + return; + + if (!get_protoname(r)) + return; + + if (sp && sp->set) + { + if (sp->port_min == sp->port_max) + sprintf(buf, "%u", sp->port_min); + else + sprintf(buf, "%u:%u", sp->port_min, sp->port_max); + + fw3_ipt_rule_addarg(r, sp->invert, "--sport", buf); + } + + if (dp && dp->set) + { + if (dp->port_min == dp->port_max) + sprintf(buf, "%u", dp->port_min); + else + sprintf(buf, "%u:%u", dp->port_min, dp->port_max); + + fw3_ipt_rule_addarg(r, dp->invert, "--dport", buf); + } +} + +void +fw3_ipt_rule_mac(struct fw3_ipt_rule *r, struct fw3_mac *mac) +{ + if (!mac) + return; + + fw3_ipt_rule_addarg(r, false, "-m", "mac"); + fw3_ipt_rule_addarg(r, mac->invert, "--mac-source", ether_ntoa(&mac->mac)); +} + +void +fw3_ipt_rule_icmptype(struct fw3_ipt_rule *r, struct fw3_icmptype *icmp) +{ + char buf[sizeof("255/255\0")]; + + if (!icmp) + return; + + if (r->h->family == FW3_FAMILY_V6) + { + if (icmp->code6_min == 0 && icmp->code6_max == 0xFF) + sprintf(buf, "%u", icmp->type6); + else + sprintf(buf, "%u/%u", icmp->type6, icmp->code6_min); + + fw3_ipt_rule_addarg(r, icmp->invert, "--icmpv6-type", buf); + } + else + { + if (icmp->code_min == 0 && icmp->code_max == 0xFF) + sprintf(buf, "%u", icmp->type); + else + sprintf(buf, "%u/%u", icmp->type, icmp->code_min); + + fw3_ipt_rule_addarg(r, icmp->invert, "--icmp-type", buf); + } +} + +void +fw3_ipt_rule_limit(struct fw3_ipt_rule *r, struct fw3_limit *limit) +{ + char buf[sizeof("-4294967296/second\0")]; + + if (!limit || limit->rate <= 0) + return; + + fw3_ipt_rule_addarg(r, false, "-m", "limit"); + + sprintf(buf, "%u/%s", limit->rate, fw3_limit_units[limit->unit]); + fw3_ipt_rule_addarg(r, limit->invert, "--limit", buf); + + if (limit->burst > 0) + { + sprintf(buf, "%u", limit->burst); + fw3_ipt_rule_addarg(r, limit->invert, "--limit-burst", buf); + } +} + +void +fw3_ipt_rule_ipset(struct fw3_ipt_rule *r, struct fw3_ipset *ipset, + bool invert) +{ + char buf[sizeof("dst,dst,dst\0")]; + char *p = buf; + + struct fw3_ipset_datatype *type; + + if (!ipset) + return; + + list_for_each_entry(type, &ipset->datatypes, list) + { + if (p > buf) + *p++ = ','; + + p += sprintf(p, "%s", type->dest ? "dst" : "src"); + } + + fw3_ipt_rule_addarg(r, false, "-m", "set"); + + fw3_ipt_rule_addarg(r, invert, "--match-set", + ipset->external ? ipset->external : ipset->name); + + fw3_ipt_rule_addarg(r, false, buf, NULL); +} + +void +fw3_ipt_rule_time(struct fw3_ipt_rule *r, struct fw3_time *time) +{ + int i; + struct tm empty = { 0 }; + + char buf[84]; /* sizeof("1,2,3,...,30,31\0") */ + char *p; + + bool d1 = memcmp(&time->datestart, &empty, sizeof(empty)); + bool d2 = memcmp(&time->datestop, &empty, sizeof(empty)); + + if (!d1 && !d2 && !time->timestart && !time->timestop && + !(time->monthdays & 0xFFFFFFFE) && !(time->weekdays & 0xFE)) + { + return; + } + + fw3_ipt_rule_addarg(r, false, "-m", "time"); + + if (time->utc) + fw3_ipt_rule_addarg(r, false, "--utc", NULL); + + if (d1) + { + strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &time->datestart); + fw3_ipt_rule_addarg(r, false, "--datestart", buf); + } + + if (d2) + { + strftime(buf, sizeof(buf), "%Y-%m-%dT%H:%M:%S", &time->datestop); + fw3_ipt_rule_addarg(r, false, "--datestop", buf); + } + + if (time->timestart) + { + sprintf(buf, "%02d:%02d:%02d", + time->timestart / 3600, + time->timestart % 3600 / 60, + time->timestart % 60); + + fw3_ipt_rule_addarg(r, false, "--timestart", buf); + } + + if (time->timestop) + { + sprintf(buf, "%02d:%02d:%02d", + time->timestop / 3600, + time->timestop % 3600 / 60, + time->timestop % 60); + + fw3_ipt_rule_addarg(r, false, "--timestop", buf); + } + + if (time->monthdays & 0xFFFFFFFE) + { + for (i = 1, p = buf; i < 32; i++) + { + if (hasbit(time->monthdays, i)) + { + if (p > buf) + *p++ = ','; + + p += sprintf(p, "%u", i); + } + } + + fw3_ipt_rule_addarg(r, hasbit(time->monthdays, 0), "--monthdays", buf); + } + + if (time->weekdays & 0xFE) + { + for (i = 1, p = buf; i < 8; i++) + { + if (hasbit(time->weekdays, i)) + { + if (p > buf) + *p++ = ','; + + p += sprintf(p, "%u", i); + } + } + + fw3_ipt_rule_addarg(r, hasbit(time->weekdays, 0), "--weekdays", buf); + } +} + +void +fw3_ipt_rule_mark(struct fw3_ipt_rule *r, struct fw3_mark *mark) +{ + char buf[sizeof("0xFFFFFFFF/0xFFFFFFFF\0")]; + + if (!mark || !mark->set) + return; + + if (mark->mask < 0xFFFFFFFF) + sprintf(buf, "0x%x/0x%x", mark->mark, mark->mask); + else + sprintf(buf, "0x%x", mark->mark); + + fw3_ipt_rule_addarg(r, false, "-m", "mark"); + fw3_ipt_rule_addarg(r, mark->invert, "--mark", buf); +} + +void +fw3_ipt_rule_comment(struct fw3_ipt_rule *r, const char *fmt, ...) +{ + va_list ap; + char buf[256]; + + if (!fmt || !*fmt) + return; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf) - 1, fmt, ap); + va_end(ap); + + fw3_ipt_rule_addarg(r, false, "-m", "comment"); + fw3_ipt_rule_addarg(r, false, "--comment", buf); +} + +void +fw3_ipt_rule_extra(struct fw3_ipt_rule *r, const char *extra) +{ + char *p, **tmp, *s; + + if (!extra || !*extra) + return; + + s = fw3_strdup(extra); + + for (p = strtok(s, " \t"); p; p = strtok(NULL, " \t")) + { + tmp = realloc(r->argv, (r->argc + 1) * sizeof(*r->argv)); + + if (!tmp) + break; + + r->argv = tmp; + r->argv[r->argc++] = fw3_strdup(p); + } + + free(s); +} + +static void +rule_print6(struct ip6t_entry *e) +{ + char buf[INET6_ADDRSTRLEN]; + char *pname; + + if (e->ipv6.flags & IP6T_F_PROTO) + { + if (e->ipv6.flags & XT_INV_PROTO) + printf(" !"); + + pname = get_protoname(container_of(e, struct fw3_ipt_rule, e6)); + + if (pname) + printf(" -p %s", pname); + else + printf(" -p %u", e->ipv6.proto); + } + + if (e->ipv6.iniface[0]) + { + if (e->ipv6.flags & IP6T_INV_VIA_IN) + printf(" !"); + + printf(" -i %s", e->ipv6.iniface); + } + + if (e->ipv6.outiface[0]) + { + if (e->ipv6.flags & IP6T_INV_VIA_OUT) + printf(" !"); + + printf(" -o %s", e->ipv6.outiface); + } + + if (memcmp(&e->ipv6.src, &in6addr_any, sizeof(struct in6_addr))) + { + if (e->ipv6.flags & IP6T_INV_SRCIP) + printf(" !"); + + printf(" -s %s/%u", inet_ntop(AF_INET6, &e->ipv6.src, buf, sizeof(buf)), + xtables_ip6mask_to_cidr(&e->ipv6.smsk)); + } + + if (memcmp(&e->ipv6.dst, &in6addr_any, sizeof(struct in6_addr))) + { + if (e->ipv6.flags & IP6T_INV_DSTIP) + printf(" !"); + + printf(" -d %s/%u", inet_ntop(AF_INET6, &e->ipv6.dst, buf, sizeof(buf)), + xtables_ip6mask_to_cidr(&e->ipv6.dmsk)); + } +} + +static void +rule_print4(struct ipt_entry *e) +{ + struct in_addr in_zero = { 0 }; + char buf[sizeof("255.255.255.255\0")]; + char *pname; + + if (e->ip.proto) + { + if (e->ip.flags & XT_INV_PROTO) + printf(" !"); + + pname = get_protoname(container_of(e, struct fw3_ipt_rule, e)); + + if (pname) + printf(" -p %s", pname); + else + printf(" -p %u", e->ip.proto); + } + + if (e->ip.iniface[0]) + { + if (e->ip.flags & IPT_INV_VIA_IN) + printf(" !"); + + printf(" -i %s", e->ip.iniface); + } + + if (e->ip.outiface[0]) + { + if (e->ip.flags & IPT_INV_VIA_OUT) + printf(" !"); + + printf(" -o %s", e->ip.outiface); + } + + if (memcmp(&e->ip.src, &in_zero, sizeof(struct in_addr))) + { + if (e->ip.flags & IPT_INV_SRCIP) + printf(" !"); + + printf(" -s %s/%u", inet_ntop(AF_INET, &e->ip.src, buf, sizeof(buf)), + xtables_ipmask_to_cidr(&e->ip.smsk)); + } + + if (memcmp(&e->ip.dst, &in_zero, sizeof(struct in_addr))) + { + if (e->ip.flags & IPT_INV_DSTIP) + printf(" !"); + + printf(" -d %s/%u", inet_ntop(AF_INET, &e->ip.dst, buf, sizeof(buf)), + xtables_ipmask_to_cidr(&e->ip.dmsk)); + } +} + +static void +rule_print(struct fw3_ipt_rule *r, const char *chain) +{ + struct xtables_rule_match *rm; + struct xtables_match *m; + struct xtables_target *t; + + printf("-A %s", chain); + + if (r->h->family == FW3_FAMILY_V6) + rule_print6(&r->e6); + else + rule_print4(&r->e); + + for (rm = r->matches; rm; rm = rm->next) + { + m = rm->match; + printf(" -m %s", m->alias ? m->alias(m->m) : m->m->u.user.name); + + if (m->save) + m->save(&r->e.ip, m->m); + } + + if (r->target) + { + t = r->target; + printf(" -j %s", t->alias ? t->alias(t->t) : t->t->u.user.name); + + if (t->save) + t->save(&r->e.ip, t->t); + } + + printf("\n"); +} + +static bool +parse_option(struct fw3_ipt_rule *r, int optc, bool inv) +{ + struct xtables_rule_match *m; + struct xtables_match *em; + + /* is a target option */ + if (r->target && (r->target->parse || r->target->x6_parse) && + optc >= r->target->option_offset && + optc < (r->target->option_offset + 256)) + { + xtables_option_tpcall(optc, r->argv, inv, r->target, &r->e); + return false; + } + + /* try to dispatch argument to one of the match parsers */ + for (m = r->matches; m; m = m->next) + { + em = m->match; + + if (m->completed || (!em->parse && !em->x6_parse)) + continue; + + if (optc < em->option_offset || + optc >= (em->option_offset + 256)) + continue; + + xtables_option_mpcall(optc, r->argv, inv, em, &r->e); + return false; + } + + /* unhandled option, might belong to a protocol match */ + if ((em = load_protomatch(r)) != NULL) + { + init_match(r, em, false); + + r->protocol_loaded = true; + optind--; + + return true; + } + + if (optc == ':') + fprintf(stderr, "parse_option(): option '%s' needs argument\n", + r->argv[optind-1]); + + if (optc == '?') + fprintf(stderr, "parse_option(): unknown option '%s'\n", + r->argv[optind-1]); + + return false; +} + +void +fw3_ipt_rule_addarg(struct fw3_ipt_rule *r, bool inv, + const char *k, const char *v) +{ + int n; + char **tmp; + + if (!k) + return; + + n = inv + !!k + !!v; + tmp = realloc(r->argv, (r->argc + n) * sizeof(*tmp)); + + if (!tmp) + return; + + r->argv = tmp; + + if (inv) + r->argv[r->argc++] = fw3_strdup("!"); + + r->argv[r->argc++] = fw3_strdup(k); + + if (v) + r->argv[r->argc++] = fw3_strdup(v); +} + +void +fw3_ipt_rule_append(struct fw3_ipt_rule *r, const char *fmt, ...) +{ + size_t s; + struct xtables_rule_match *m; + struct xtables_match *em; + struct xtables_target *et; + struct xtables_globals *g; + struct ipt_entry *e; + struct ip6t_entry *e6; + + int i, optc; + bool inv = false; + char buf[32]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf) - 1, fmt, ap); + va_end(ap); + + g = (r->h->family == FW3_FAMILY_V6) ? &xtg6 : &xtg; + g->opts = g->orig_opts; + + optind = 0; + opterr = 0; + + while ((optc = getopt_long(r->argc, r->argv, "m:j:", g->opts, NULL)) != -1) + { + switch (optc) + { + case 'm': + em = find_match(r, optarg); + + if (!em) + { + fprintf(stderr, "fw3_ipt_rule_append(): Can't find match '%s'\n", optarg); + return; + } + + init_match(r, em, true); + break; + + case 'j': + et = get_target(r, optarg); + + if (!et) + { + fprintf(stderr, "fw3_ipt_rule_append(): Can't find target '%s'\n", optarg); + return; + } + + break; + + case 1: + if ((optarg[0] == '!') && (optarg[1] == '\0')) + { + inv = true; + continue; + } + + fprintf(stderr, "fw3_ipt_rule_append(): Bad argument '%s'\n", optarg); + return; + + default: + if (parse_option(r, optc, inv)) + continue; + break; + } + + inv = false; + } + + for (m = r->matches; m; m = m->next) + xtables_option_mfcall(m->match); + + if (r->target) + xtables_option_tfcall(r->target); + + if (fw3_pr_debug) + rule_print(r, buf); + + if (r->h->family == FW3_FAMILY_V6) + { + s = XT_ALIGN(sizeof(struct ip6t_entry)); + + for (m = r->matches; m; m = m->next) + s += m->match->m->u.match_size; + + e6 = fw3_alloc(s + r->target->t->u.target_size); + + memcpy(e6, &r->e6, sizeof(struct ip6t_entry)); + + e6->target_offset = s; + e6->next_offset = s + r->target->t->u.target_size; + + s = 0; + + for (m = r->matches; m; m = m->next) + { + memcpy(e6->elems + s, m->match->m, m->match->m->u.match_size); + s += m->match->m->u.match_size; + } + + memcpy(e6->elems + s, r->target->t, r->target->t->u.target_size); + ip6tc_append_entry(buf, e6, r->h->handle); + free(e6); + } + else + { + s = XT_ALIGN(sizeof(struct ipt_entry)); + + for (m = r->matches; m; m = m->next) + s += m->match->m->u.match_size; + + e = fw3_alloc(s + r->target->t->u.target_size); + + memcpy(e, &r->e, sizeof(struct ipt_entry)); + + e->target_offset = s; + e->next_offset = s + r->target->t->u.target_size; + + s = 0; + + for (m = r->matches; m; m = m->next) + { + memcpy(e->elems + s, m->match->m, m->match->m->u.match_size); + s += m->match->m->u.match_size; + } + + memcpy(e->elems + s, r->target->t, r->target->t->u.target_size); + + if (!iptc_append_entry(buf, e, r->h->handle)) + fprintf(stderr, "iptc_append_entry(): %s\n", iptc_strerror(errno)); + + free(e); + } + + for (i = 1; i < r->argc; i++) + free(r->argv[i]); + + free(r->argv); + + xtables_rule_matches_free(&r->matches); + + free(r->target->t); + free(r); + + /* reset all targets and matches */ + for (em = xtables_matches; em; em = em->next) + em->mflags = 0; + + for (et = xtables_targets; et; et = et->next) + { + et->tflags = 0; + et->used = 0; + } + + xtables_free_opts(1); +} + +struct fw3_ipt_rule * +fw3_ipt_rule_create(struct fw3_ipt_handle *handle, struct fw3_protocol *proto, + struct fw3_device *in, struct fw3_device *out, + struct fw3_address *src, struct fw3_address *dest) +{ + struct fw3_ipt_rule *r; + + r = fw3_ipt_rule_new(handle); + + fw3_ipt_rule_proto(r, proto); + fw3_ipt_rule_in_out(r, in, out); + fw3_ipt_rule_src_dest(r, src, dest); + + return r; +} diff --git a/iptables.h b/iptables.h index d809c1d..31d3268 100644 --- a/iptables.h +++ b/iptables.h @@ -21,23 +21,133 @@ #include #include +#include + +#include +#include +#include #include "options.h" +extern struct xtables_match *xtables_pending_matches; +extern struct xtables_target *xtables_pending_targets; + +/* libext.a interface */ +void init_extensions(void); +void init_extensions4(void); +void init_extensions6(void); + +/* Required by certain extensions like SNAT and DNAT */ +extern int kernel_version; +void get_kernel_version(void); + struct fw3_ipt_handle { enum fw3_family family; enum fw3_table table; struct xtc_handle *handle; }; -struct fw3_ipt_handle *fw3_ipt_open(enum fw3_family family, enum fw3_table table); +struct fw3_ipt_rule { + struct fw3_ipt_handle *h; + + union { + struct ipt_entry e; + struct ip6t_entry e6; + }; + + struct xtables_rule_match *matches; + struct xtables_target *target; + + int argc; + char **argv; + + uint32_t protocol; + bool protocol_loaded; +}; + +struct fw3_ipt_handle *fw3_ipt_open(enum fw3_family family, + enum fw3_table table); + +void fw3_ipt_set_policy(struct fw3_ipt_handle *h, const char *chain, + enum fw3_flag policy); -void fw3_ipt_set_policy(struct fw3_ipt_handle *h, enum fw3_flag policy); void fw3_ipt_delete_chain(struct fw3_ipt_handle *h, const char *chain); void fw3_ipt_delete_rules(struct fw3_ipt_handle *h, const char *target); + +static inline void +fw3_ipt_create_chain(struct fw3_ipt_handle *h, const char *fmt, ...) +{ + char buf[32]; + va_list ap; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf) - 1, fmt, ap); + va_end(ap); + + if (fw3_pr_debug) + printf("-N %s\n", buf); + + iptc_create_chain(buf, h->handle); +} + void fw3_ipt_flush(struct fw3_ipt_handle *h); void fw3_ipt_commit(struct fw3_ipt_handle *h); +struct fw3_ipt_rule *fw3_ipt_rule_new(struct fw3_ipt_handle *h); + +void fw3_ipt_rule_proto(struct fw3_ipt_rule *r, struct fw3_protocol *proto); + +void fw3_ipt_rule_in_out(struct fw3_ipt_rule *r, + struct fw3_device *in, struct fw3_device *out); + +void fw3_ipt_rule_src_dest(struct fw3_ipt_rule *r, + struct fw3_address *src, struct fw3_address *dest); + +void fw3_ipt_rule_sport_dport(struct fw3_ipt_rule *r, + struct fw3_port *sp, struct fw3_port *dp); + +void fw3_ipt_rule_mac(struct fw3_ipt_rule *r, struct fw3_mac *mac); + +void fw3_ipt_rule_icmptype(struct fw3_ipt_rule *r, struct fw3_icmptype *icmp); + +void fw3_ipt_rule_limit(struct fw3_ipt_rule *r, struct fw3_limit *limit); + +void fw3_ipt_rule_ipset(struct fw3_ipt_rule *r, struct fw3_ipset *ipset, + bool invert); + +void fw3_ipt_rule_time(struct fw3_ipt_rule *r, struct fw3_time *time); + +void fw3_ipt_rule_mark(struct fw3_ipt_rule *r, struct fw3_mark *mark); + +void fw3_ipt_rule_comment(struct fw3_ipt_rule *r, const char *fmt, ...); + +void fw3_ipt_rule_extra(struct fw3_ipt_rule *r, const char *extra); + +void fw3_ipt_rule_addarg(struct fw3_ipt_rule *r, bool inv, + const char *k, const char *v); + +struct fw3_ipt_rule * fw3_ipt_rule_create(struct fw3_ipt_handle *handle, + struct fw3_protocol *proto, + struct fw3_device *in, + struct fw3_device *out, + struct fw3_address *src, + struct fw3_address *dest); + +void fw3_ipt_rule_append(struct fw3_ipt_rule *r, const char *fmt, ...); + +static inline void +fw3_ipt_rule_target(struct fw3_ipt_rule *r, const char *fmt, ...) +{ + va_list ap; + char buf[32]; + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf) - 1, fmt, ap); + va_end(ap); + + fw3_ipt_rule_addarg(r, false, "-j", buf); +} + #endif diff --git a/main.c b/main.c index 09de782..a2b80be 100644 --- a/main.c +++ b/main.c @@ -139,25 +139,6 @@ free_state(struct fw3_state *state) } -static bool -restore_pipe(enum fw3_family family, bool silent) -{ - 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(enum fw3_family family) { @@ -261,6 +242,7 @@ start(void) int rv = 1; enum fw3_family family; enum fw3_table table; + struct fw3_ipt_handle *handle; if (!print_rules) { @@ -285,32 +267,31 @@ start(void) continue; } - if (!restore_pipe(family, false)) - 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_pr("*%s\n", fw3_flag_names[table]); - fw3_print_default_chains(cfg_state, family, table, false); - fw3_print_zone_chains(cfg_state, family, table, false); - fw3_print_default_head_rules(cfg_state, family, table, false); - fw3_print_rules(cfg_state, family, table); - fw3_print_redirects(cfg_state, family, table); - fw3_print_forwards(cfg_state, family, table); - fw3_print_zone_rules(cfg_state, family, table, false); - fw3_print_default_tail_rules(cfg_state, family, table, false); - fw3_pr("COMMIT\n"); + 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_forwards(handle, cfg_state); + fw3_print_zone_rules(handle, cfg_state, false); + fw3_print_default_tail_rules(handle, cfg_state, false); + + fw3_ipt_commit(handle); } - fw3_print_includes(cfg_state, family, false); + //fw3_print_includes(cfg_state, family, false); - fw3_command_close(); family_set(run_state, family, true); family_set(cfg_state, family, true); @@ -373,41 +354,38 @@ reload(void) family_set(cfg_state, family, false); start: - if (!restore_pipe(family, true)) - continue; - if (family == FW3_FAMILY_V6 && cfg_state->defaults.disable_ipv6) - goto skip; + 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_pr("*%s\n", fw3_flag_names[table]); - fw3_print_default_chains(cfg_state, family, table, true); - fw3_print_zone_chains(cfg_state, family, table, true); - fw3_print_default_head_rules(cfg_state, family, table, true); - fw3_print_rules(cfg_state, family, table); - fw3_print_redirects(cfg_state, family, table); - fw3_print_forwards(cfg_state, family, table); - fw3_print_zone_rules(cfg_state, family, table, true); - fw3_print_default_tail_rules(cfg_state, family, table, true); - fw3_pr("COMMIT\n"); + 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_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_print_includes(cfg_state, family, true); + //fw3_print_includes(cfg_state, family, true); family_set(run_state, family, true); family_set(cfg_state, family, true); rv = 0; - -skip: - fw3_command_close(); } if (!rv) diff --git a/options.c b/options.c index 3d30c57..0cd9068 100644 --- a/options.c +++ b/options.c @@ -84,7 +84,7 @@ const char *fw3_flag_names[__FW3_FLAG_MAX] = { "DROP", }; -static const char *limit_units[] = { +const char *fw3_limit_units[__FW3_LIMIT_UNIT_MAX] = { "second", "minute", "hour", @@ -191,7 +191,7 @@ fw3_parse_limit(void *ptr, const char *val, bool is_list) if (!strlen(e)) return false; - if (!parse_enum(&u, e, limit_units, 0, FW3_LIMIT_UNIT_DAY)) + if (!parse_enum(&u, e, fw3_limit_units, 0, FW3_LIMIT_UNIT_DAY)) return false; limit->rate = n; @@ -893,16 +893,6 @@ fw3_parse_options(void *s, const struct fw3_option *opts, } -void -fw3_format_in_out(struct fw3_device *in, struct fw3_device *out) -{ - if (in && !in->any) - fw3_pr(" %s-i %s", in->invert ? "! " : "", in->name); - - if (out && !out->any) - fw3_pr(" %s-o %s", out->invert ? "! " : "", out->name); -} - const char * fw3_address_to_string(struct fw3_address *address, bool allow_invert) { @@ -1040,7 +1030,7 @@ fw3_format_limit(struct fw3_limit *limit) { fw3_pr(" -m limit %s--limit %u/%s", limit->invert ? "! " : "", - limit->rate, limit_units[limit->unit]); + limit->rate, fw3_limit_units[limit->unit]); if (limit->burst > 0) fw3_pr(" --limit-burst %u", limit->burst); diff --git a/options.h b/options.h index 0a22cb6..f6168f5 100644 --- a/options.h +++ b/options.h @@ -92,8 +92,13 @@ enum fw3_limit_unit FW3_LIMIT_UNIT_MINUTE = 1, FW3_LIMIT_UNIT_HOUR = 2, FW3_LIMIT_UNIT_DAY = 3, + + __FW3_LIMIT_UNIT_MAX }; +extern const char *fw3_limit_units[__FW3_LIMIT_UNIT_MAX]; + + enum fw3_ipset_method { FW3_IPSET_METHOD_UNSPEC = 0, diff --git a/redirects.c b/redirects.c index 3c0ffad..aec47fd 100644 --- a/redirects.c +++ b/redirects.c @@ -259,84 +259,207 @@ fw3_load_redirects(struct fw3_state *state, struct uci_package *p) } static void -print_chain_nat(struct fw3_redirect *redir) +append_chain_nat(struct fw3_ipt_rule *r, struct fw3_redirect *redir) { if (redir->target == FW3_FLAG_DNAT) - fw3_pr("-A zone_%s_prerouting", redir->src.name); + fw3_ipt_rule_append(r, "zone_%s_prerouting", redir->src.name); else - fw3_pr("-A zone_%s_postrouting", redir->dest.name); + fw3_ipt_rule_append(r, "zone_%s_postrouting", redir->dest.name); } static void -print_snat_dnat(enum fw3_flag target, - struct fw3_address *addr, struct fw3_port *port) +set_snat_dnat(struct fw3_ipt_rule *r, enum fw3_flag target, + struct fw3_address *addr, struct fw3_port *port) { - char s[sizeof("255.255.255.255 ")]; + char buf[sizeof("255.255.255.255:65535-65535\0")]; - if (target == FW3_FLAG_DNAT) - fw3_pr(" -j DNAT --to-destination "); - else - fw3_pr(" -j SNAT --to-source "); + buf[0] = '\0'; if (addr && addr->set) { - inet_ntop(AF_INET, &addr->address.v4, s, sizeof(s)); - fw3_pr(s); + inet_ntop(AF_INET, &addr->address.v4, buf, sizeof(buf)); } if (port && port->set) { if (port->port_min == port->port_max) - fw3_pr(":%u", port->port_min); + sprintf(buf + strlen(buf), ":%u", port->port_min); else - fw3_pr(":%u-%u", port->port_min, port->port_max); + sprintf(buf + strlen(buf), ":%u-%u", + port->port_min, port->port_max); } - fw3_pr("\n"); + if (target == FW3_FLAG_DNAT) + { + fw3_ipt_rule_target(r, "DNAT"); + fw3_ipt_rule_addarg(r, false, "--to-destination", buf); + } + else + { + fw3_ipt_rule_target(r, "SNAT"); + fw3_ipt_rule_addarg(r, false, "--to-source", buf); + } } static void -print_target_nat(struct fw3_redirect *redir) +set_target_nat(struct fw3_ipt_rule *r, struct fw3_redirect *redir) { if (redir->target == FW3_FLAG_DNAT) - print_snat_dnat(redir->target, &redir->ip_redir, &redir->port_redir); + set_snat_dnat(r, redir->target, &redir->ip_redir, &redir->port_redir); else - print_snat_dnat(redir->target, &redir->ip_dest, &redir->port_dest); + set_snat_dnat(r, redir->target, &redir->ip_dest, &redir->port_dest); } static void -print_chain_filter(struct fw3_redirect *redir) +append_chain_filter(struct fw3_ipt_rule *r, struct fw3_redirect *redir) { if (redir->target == FW3_FLAG_DNAT) { /* XXX: check for local ip */ if (!redir->ip_redir.set) - fw3_pr("-A zone_%s_input", redir->src.name); + fw3_ipt_rule_append(r, "zone_%s_input", redir->src.name); else - fw3_pr("-A zone_%s_forward", redir->src.name); + fw3_ipt_rule_append(r, "zone_%s_forward", redir->src.name); } else { if (redir->src.set && !redir->src.any) - fw3_pr("-A zone_%s_forward", redir->src.name); + fw3_ipt_rule_append(r, "zone_%s_forward", redir->src.name); else - fw3_pr("-A delegate_forward"); + fw3_ipt_rule_append(r, "delegate_forward"); } } static void -print_target_filter(struct fw3_redirect *redir) +set_target_filter(struct fw3_ipt_rule *r, struct fw3_redirect *redir) { /* XXX: check for local ip */ if (redir->target == FW3_FLAG_DNAT && !redir->ip_redir.set) - fw3_pr(" -m conntrack --ctstate DNAT -j ACCEPT\n"); + fw3_ipt_rule_extra(r, "-m conntrack --ctstate DNAT"); + + fw3_ipt_rule_target(r, "ACCEPT"); +} + +static void +set_comment(struct fw3_ipt_rule *r, const char *name, int num, bool ref) +{ + if (name) + { + if (ref) + fw3_ipt_rule_comment(r, "%s (reflection)", name); + else + fw3_ipt_rule_comment(r, name); + } else - fw3_pr(" -j ACCEPT\n"); + { + if (ref) + fw3_ipt_rule_comment(r, "@redirect[%u] (reflection)", num); + else + fw3_ipt_rule_comment(r, "@redirect[%u]", num); + } +} + +static void +print_redirect(struct fw3_ipt_handle *h, struct fw3_state *state, + struct fw3_redirect *redir, int num, + struct fw3_protocol *proto, struct fw3_mac *mac) +{ + struct fw3_ipt_rule *r; + struct fw3_address *src, *dst; + struct fw3_port *spt, *dpt; + + switch (h->table) + { + case FW3_TABLE_NAT: + src = &redir->ip_src; + dst = &redir->ip_dest; + spt = &redir->port_src; + dpt = &redir->port_dest; + + if (redir->target == FW3_FLAG_SNAT) + { + dst = &redir->ip_redir; + dpt = &redir->port_redir; + } + + r = fw3_ipt_rule_create(h, proto, NULL, NULL, src, dst); + fw3_ipt_rule_sport_dport(r, spt, dpt); + fw3_ipt_rule_mac(r, mac); + fw3_ipt_rule_ipset(r, redir->_ipset, redir->ipset.invert); + fw3_ipt_rule_time(r, &redir->time); + fw3_ipt_rule_mark(r, &redir->mark); + set_target_nat(r, redir); + fw3_ipt_rule_extra(r, redir->extra); + set_comment(r, redir->name, num, false); + append_chain_nat(r, redir); + break; + + case FW3_TABLE_FILTER: + src = &redir->ip_src; + dst = &redir->ip_redir; + spt = &redir->port_src; + dpt = &redir->port_redir; + + r = fw3_ipt_rule_create(h, proto, NULL, NULL, src, dst); + fw3_ipt_rule_sport_dport(r, spt, dpt); + fw3_ipt_rule_mac(r, mac); + fw3_ipt_rule_ipset(r, redir->_ipset, redir->ipset.invert); + fw3_ipt_rule_time(r, &redir->time); + fw3_ipt_rule_mark(r, &redir->mark); + set_target_filter(r, redir); + fw3_ipt_rule_extra(r, redir->extra); + set_comment(r, redir->name, num, false); + append_chain_filter(r, redir); + break; + + default: + break; + } } static void -print_redirect(struct fw3_state *state, enum fw3_family family, - enum fw3_table table, struct fw3_redirect *redir, int num) +print_reflection(struct fw3_ipt_handle *h, struct fw3_state *state, + struct fw3_redirect *redir, int num, + struct fw3_protocol *proto, struct fw3_address *ra, + struct fw3_address *ia, struct fw3_address *ea) +{ + struct fw3_ipt_rule *r; + + switch (h->table) + { + case FW3_TABLE_NAT: + r = fw3_ipt_rule_create(h, proto, NULL, NULL, ia, ea); + fw3_ipt_rule_sport_dport(r, NULL, &redir->port_dest); + fw3_ipt_rule_time(r, &redir->time); + set_comment(r, redir->name, num, true); + set_snat_dnat(r, FW3_FLAG_DNAT, &redir->ip_redir, &redir->port_redir); + fw3_ipt_rule_append(r, "zone_%s_prerouting", redir->dest.name); + + r = fw3_ipt_rule_create(h, proto, NULL, NULL, ia, &redir->ip_redir); + fw3_ipt_rule_sport_dport(r, NULL, &redir->port_redir); + fw3_ipt_rule_time(r, &redir->time); + set_comment(r, redir->name, num, true); + set_snat_dnat(r, FW3_FLAG_SNAT, ra, NULL); + fw3_ipt_rule_append(r, "zone_%s_postrouting", redir->dest.name); + break; + + case FW3_TABLE_FILTER: + r = fw3_ipt_rule_create(h, proto, NULL, NULL, ia, &redir->ip_redir); + fw3_ipt_rule_sport_dport(r, NULL, &redir->port_redir); + fw3_ipt_rule_time(r, &redir->time); + set_comment(r, redir->name, num, true); + fw3_ipt_rule_target(r, "zone_%s_dest_ACCEPT", redir->dest.name); + fw3_ipt_rule_append(r, "zone_%s_forward", redir->dest.name); + break; + + default: + break; + } +} + +static void +expand_redirect(struct fw3_ipt_handle *handle, struct fw3_state *state, + struct fw3_redirect *redir, int num) { struct list_head *ext_addrs, *int_addrs; struct fw3_address *ext_addr, *int_addr, ref_addr; @@ -349,16 +472,16 @@ print_redirect(struct fw3_state *state, enum fw3_family family, else info(" * Redirect #%u", num); - if (!fw3_is_family(redir->_src, family) || - !fw3_is_family(redir->_dest, family)) + if (!fw3_is_family(redir->_src, handle->family) || + !fw3_is_family(redir->_dest, handle->family)) { info(" ! Skipping due to different family of zone"); return; } - if (!fw3_is_family(&redir->ip_src, family) || - !fw3_is_family(&redir->ip_dest, family) || - !fw3_is_family(&redir->ip_redir, family)) + if (!fw3_is_family(&redir->ip_src, handle->family) || + !fw3_is_family(&redir->ip_dest, handle->family) || + !fw3_is_family(&redir->ip_redir, handle->family)) { info(" ! Skipping due to different family of ip address"); return; @@ -366,7 +489,7 @@ print_redirect(struct fw3_state *state, enum fw3_family family, if (redir->_ipset) { - if (!fw3_is_family(redir->_ipset, family)) + if (!fw3_is_family(redir->_ipset, handle->family)) { info(" ! Skipping due to different family in ipset"); return; @@ -380,51 +503,12 @@ print_redirect(struct fw3_state *state, enum fw3_family family, return; } - set(redir->_ipset->flags, family, family); + set(redir->_ipset->flags, handle->family, handle->family); } fw3_foreach(proto, &redir->proto) fw3_foreach(mac, &redir->mac_src) - { - if (table == FW3_TABLE_NAT) - { - print_chain_nat(redir); - fw3_format_ipset(redir->_ipset, redir->ipset.invert); - fw3_format_protocol(proto, family); - - if (redir->target == FW3_FLAG_DNAT) - { - fw3_format_src_dest(&redir->ip_src, &redir->ip_dest); - fw3_format_sport_dport(&redir->port_src, &redir->port_dest); - } - else - { - fw3_format_src_dest(&redir->ip_src, &redir->ip_redir); - fw3_format_sport_dport(&redir->port_src, &redir->port_redir); - } - - fw3_format_mac(mac); - fw3_format_time(&redir->time); - fw3_format_mark(&redir->mark); - fw3_format_extra(redir->extra); - fw3_format_comment(redir->name); - print_target_nat(redir); - } - else if (table == FW3_TABLE_FILTER) - { - print_chain_filter(redir); - fw3_format_ipset(redir->_ipset, redir->ipset.invert); - fw3_format_protocol(proto, family); - fw3_format_src_dest(&redir->ip_src, &redir->ip_redir); - fw3_format_sport_dport(&redir->port_src, &redir->port_redir); - fw3_format_mac(mac); - fw3_format_time(&redir->time); - fw3_format_mark(&redir->mark); - fw3_format_extra(redir->extra); - fw3_format_comment(redir->name); - print_target_filter(redir); - } - } + print_redirect(handle, state, redir, num, proto, mac); /* reflection rules */ if (redir->target != FW3_FLAG_DNAT || !redir->reflection) @@ -451,8 +535,8 @@ print_redirect(struct fw3_state *state, enum fw3_family family, fw3_foreach(int_addr, int_addrs) fw3_foreach(proto, &redir->proto) { - if (!fw3_is_family(int_addr, family) || - !fw3_is_family(ext_addr, family)) + if (!fw3_is_family(int_addr, handle->family) || + !fw3_is_family(ext_addr, handle->family)) continue; if (!proto || (proto->protocol != 6 && proto->protocol != 17)) @@ -466,35 +550,8 @@ print_redirect(struct fw3_state *state, enum fw3_family family, ref_addr.mask = 32; ext_addr->mask = 32; - if (table == FW3_TABLE_NAT) - { - fw3_pr("-A zone_%s_prerouting", redir->dest.name); - fw3_format_protocol(proto, family); - fw3_format_src_dest(int_addr, ext_addr); - fw3_format_sport_dport(NULL, &redir->port_dest); - fw3_format_time(&redir->time); - fw3_format_comment(redir->name, " (reflection)"); - print_snat_dnat(FW3_FLAG_DNAT, - &redir->ip_redir, &redir->port_redir); - - fw3_pr("-A zone_%s_postrouting", redir->dest.name); - fw3_format_protocol(proto, family); - fw3_format_src_dest(int_addr, &redir->ip_redir); - fw3_format_sport_dport(NULL, &redir->port_redir); - fw3_format_time(&redir->time); - fw3_format_comment(redir->name, " (reflection)"); - print_snat_dnat(FW3_FLAG_SNAT, &ref_addr, NULL); - } - else if (table == FW3_TABLE_FILTER) - { - fw3_pr("-A zone_%s_forward", redir->dest.name); - fw3_format_protocol(proto, family); - fw3_format_src_dest(int_addr, &redir->ip_redir); - fw3_format_sport_dport(NULL, &redir->port_redir); - fw3_format_time(&redir->time); - fw3_format_comment(redir->name, " (reflection)"); - fw3_pr(" -j zone_%s_dest_ACCEPT\n", redir->dest.name); - } + print_reflection(handle, state, redir, num, proto, + &ref_addr, int_addr, ext_addr); } fw3_ubus_address_free(int_addrs); @@ -505,18 +562,17 @@ print_redirect(struct fw3_state *state, enum fw3_family family, } void -fw3_print_redirects(struct fw3_state *state, enum fw3_family family, - enum fw3_table table) +fw3_print_redirects(struct fw3_ipt_handle *handle, struct fw3_state *state) { int num = 0; struct fw3_redirect *redir; - if (family == FW3_FAMILY_V6) + if (handle->family == FW3_FAMILY_V6) return; - if (table != FW3_TABLE_FILTER && table != FW3_TABLE_NAT) + if (handle->table != FW3_TABLE_FILTER && handle->table != FW3_TABLE_NAT) return; list_for_each_entry(redir, &state->redirects, list) - print_redirect(state, family, table, redir, num++); + expand_redirect(handle, state, redir, num++); } diff --git a/redirects.h b/redirects.h index 14f90bb..b7001a4 100644 --- a/redirects.h +++ b/redirects.h @@ -23,12 +23,13 @@ #include "zones.h" #include "ipsets.h" #include "ubus.h" +#include "iptables.h" extern const struct fw3_option fw3_redirect_opts[]; void fw3_load_redirects(struct fw3_state *state, struct uci_package *p); -void fw3_print_redirects(struct fw3_state *state, enum fw3_family family, - enum fw3_table table); +void fw3_print_redirects(struct fw3_ipt_handle *handle, + struct fw3_state *state); #define fw3_free_redirect(redir) \ fw3_free_object(redir, fw3_redirect_opts) diff --git a/rules.c b/rules.c index 7f748eb..53268e2 100644 --- a/rules.c +++ b/rules.c @@ -208,19 +208,19 @@ fw3_load_rules(struct fw3_state *state, struct uci_package *p) static void -print_chain(struct fw3_rule *rule) +append_chain(struct fw3_ipt_rule *r, struct fw3_rule *rule) { - char chain[256]; + char chain[32]; - sprintf(chain, "delegate_output"); + snprintf(chain, sizeof(chain), "delegate_output"); if (rule->target == FW3_FLAG_NOTRACK) { - sprintf(chain, "zone_%s_notrack", rule->src.name); + snprintf(chain, sizeof(chain), "zone_%s_notrack", rule->src.name); } else if (rule->target == FW3_FLAG_MARK) { - sprintf(chain, "fwmark"); + snprintf(chain, sizeof(chain), "fwmark"); } else { @@ -229,98 +229,103 @@ print_chain(struct fw3_rule *rule) if (!rule->src.any) { if (rule->dest.set) - sprintf(chain, "zone_%s_forward", rule->src.name); + snprintf(chain, sizeof(chain), "zone_%s_forward", + rule->src.name); else - sprintf(chain, "zone_%s_input", rule->src.name); + snprintf(chain, sizeof(chain), "zone_%s_input", + rule->src.name); } else { if (rule->dest.set) - sprintf(chain, "delegate_forward"); + snprintf(chain, sizeof(chain), "delegate_forward"); else - sprintf(chain, "delegate_input"); + snprintf(chain, sizeof(chain), "delegate_input"); } } if (rule->dest.set && !rule->src.set) - sprintf(chain, "zone_%s_output", rule->dest.name); + snprintf(chain, sizeof(chain), "zone_%s_output", rule->dest.name); } - fw3_pr("-A %s", chain); + fw3_ipt_rule_append(r, chain); } -static void print_target(struct fw3_rule *rule) +static void set_target(struct fw3_ipt_rule *r, struct fw3_rule *rule) { - const char *target; + const char *name; + struct fw3_mark *mark; + char buf[sizeof("0xFFFFFFFF/0xFFFFFFFF\0")]; switch(rule->target) { case FW3_FLAG_MARK: - if (rule->set_mark.set) - fw3_pr(" -j MARK --set-mark 0x%x/0x%x\n", - rule->set_mark.mark, rule->set_mark.mask); - else - fw3_pr(" -j MARK --set-xmark 0x%x/0x%x\n", - rule->set_xmark.mark, rule->set_xmark.mask); + name = rule->set_mark.set ? "--set-mark" : "--set-xmark"; + mark = rule->set_mark.set ? &rule->set_mark : &rule->set_xmark; + sprintf(buf, "0x%x/0x%x", mark->mark, mark->mask); + + fw3_ipt_rule_target(r, "MARK"); + fw3_ipt_rule_addarg(r, false, name, buf); return; case FW3_FLAG_ACCEPT: case FW3_FLAG_DROP: case FW3_FLAG_NOTRACK: - target = fw3_flag_names[rule->target]; + name = fw3_flag_names[rule->target]; break; default: - target = fw3_flag_names[FW3_FLAG_REJECT]; + name = fw3_flag_names[FW3_FLAG_REJECT]; break; } if (rule->dest.set && !rule->dest.any) - fw3_pr(" -j zone_%s_dest_%s\n", rule->dest.name, target); + fw3_ipt_rule_target(r, "zone_%s_dest_%s", rule->dest.name, name); else if (rule->target == FW3_FLAG_REJECT) - fw3_pr(" -j reject\n"); + fw3_ipt_rule_target(r, "reject"); else - fw3_pr(" -j %s\n", target); + fw3_ipt_rule_target(r, name); } static void -print_rule(struct fw3_state *state, enum fw3_family family, - enum fw3_table table, struct fw3_rule *rule, - struct fw3_protocol *proto, +print_rule(struct fw3_ipt_handle *handle, struct fw3_state *state, + struct fw3_rule *rule, struct fw3_protocol *proto, struct fw3_address *sip, struct fw3_address *dip, struct fw3_port *sport, struct fw3_port *dport, struct fw3_mac *mac, struct fw3_icmptype *icmptype) { - if (!fw3_is_family(sip, family) || !fw3_is_family(dip, family)) + struct fw3_ipt_rule *r; + + if (!fw3_is_family(sip, handle->family) || + !fw3_is_family(dip, handle->family)) { info(" ! Skipping due to different family of ip address"); return; } - if (proto->protocol == 58 && family == FW3_FAMILY_V4) + if (proto->protocol == 58 && handle->family == FW3_FAMILY_V4) { info(" ! Skipping due to different family of protocol"); return; } - print_chain(rule); - fw3_format_ipset(rule->_ipset, rule->ipset.invert); - fw3_format_protocol(proto, family); - fw3_format_src_dest(sip, dip); - fw3_format_sport_dport(sport, dport); - fw3_format_icmptype(icmptype, family); - fw3_format_mac(mac); - fw3_format_limit(&rule->limit); - fw3_format_time(&rule->time); - fw3_format_mark(&rule->mark); - fw3_format_extra(rule->extra); - fw3_format_comment(rule->name); - print_target(rule); + r = fw3_ipt_rule_create(handle, proto, NULL, NULL, sip, dip); + fw3_ipt_rule_sport_dport(r, sport, dport); + fw3_ipt_rule_icmptype(r, icmptype); + fw3_ipt_rule_mac(r, mac); + fw3_ipt_rule_ipset(r, rule->_ipset, rule->ipset.invert); + fw3_ipt_rule_limit(r, &rule->limit); + fw3_ipt_rule_time(r, &rule->time); + fw3_ipt_rule_mark(r, &rule->mark); + set_target(r, rule); + fw3_ipt_rule_extra(r, rule->extra); + fw3_ipt_rule_comment(r, rule->name); + append_chain(r, rule); } static void -expand_rule(struct fw3_state *state, enum fw3_family family, - enum fw3_table table, struct fw3_rule *rule, int num) +expand_rule(struct fw3_ipt_handle *handle, struct fw3_state *state, + struct fw3_rule *rule, int num) { struct fw3_protocol *proto; struct fw3_address *sip; @@ -337,12 +342,12 @@ expand_rule(struct fw3_state *state, enum fw3_family family, struct list_head empty; INIT_LIST_HEAD(&empty); - if (!fw3_is_family(rule, family)) + if (!fw3_is_family(rule, handle->family)) return; - if ((rule->target == FW3_FLAG_NOTRACK && table != FW3_TABLE_RAW) || - (rule->target == FW3_FLAG_MARK && table != FW3_TABLE_MANGLE) || - (rule->target < FW3_FLAG_NOTRACK && table != FW3_TABLE_FILTER)) + if ((rule->target == FW3_FLAG_NOTRACK && handle->table != FW3_TABLE_RAW) || + (rule->target == FW3_FLAG_MARK && handle->table != FW3_TABLE_MANGLE) || + (rule->target < FW3_FLAG_NOTRACK && handle->table != FW3_TABLE_FILTER)) return; if (rule->name) @@ -350,8 +355,8 @@ expand_rule(struct fw3_state *state, enum fw3_family family, else info(" * Rule #%u", num); - if (!fw3_is_family(rule->_src, family) || - !fw3_is_family(rule->_dest, family)) + if (!fw3_is_family(rule->_src, handle->family) || + !fw3_is_family(rule->_dest, handle->family)) { info(" ! Skipping due to different family of zone"); return; @@ -359,7 +364,7 @@ expand_rule(struct fw3_state *state, enum fw3_family family, if (rule->_ipset) { - if (!fw3_is_family(rule->_ipset, family)) + if (!fw3_is_family(rule->_ipset, handle->family)) { info(" ! Skipping due to different family in ipset"); return; @@ -373,7 +378,7 @@ expand_rule(struct fw3_state *state, enum fw3_family family, return; } - set(rule->_ipset->flags, family, family); + set(rule->_ipset->flags, handle->family, handle->family); } list_for_each_entry(proto, &rule->proto, list) @@ -398,18 +403,17 @@ expand_rule(struct fw3_state *state, enum fw3_family family, fw3_foreach(dport, dports) fw3_foreach(mac, &rule->mac_src) fw3_foreach(icmptype, icmptypes) - print_rule(state, family, table, rule, proto, sip, dip, + print_rule(handle, state, rule, proto, sip, dip, sport, dport, mac, icmptype); } } void -fw3_print_rules(struct fw3_state *state, enum fw3_family family, - enum fw3_table table) +fw3_print_rules(struct fw3_ipt_handle *handle, struct fw3_state *state) { int num = 0; struct fw3_rule *rule; list_for_each_entry(rule, &state->rules, list) - expand_rule(state, family, table, rule, num++); + expand_rule(handle, state, rule, num++); } diff --git a/rules.h b/rules.h index 44a40f2..6d332fc 100644 --- a/rules.h +++ b/rules.h @@ -23,12 +23,12 @@ #include "zones.h" #include "ipsets.h" #include "utils.h" +#include "iptables.h" extern const struct fw3_option fw3_rule_opts[]; void fw3_load_rules(struct fw3_state *state, struct uci_package *p); -void fw3_print_rules(struct fw3_state *state, enum fw3_family family, - enum fw3_table table); +void fw3_print_rules(struct fw3_ipt_handle *handle, struct fw3_state *state); #define fw3_free_rule(rule) \ fw3_free_object(rule, fw3_rule_opts) diff --git a/utils.c b/utils.c index fc8c201..19c0eda 100644 --- a/utils.c +++ b/utils.c @@ -132,6 +132,32 @@ info(const char* format, ...) fprintf(stderr, "\n"); } +void * +fw3_alloc(size_t size) +{ + void *mem; + + mem = calloc(1, size); + + if (!mem) + error("Out of memory while allocating %d bytes", size); + + return mem; +} + +char * +fw3_strdup(const char *s) +{ + char *ns; + + ns = strdup(s); + + if (!ns) + error("Out of memory while duplicating string '%s'", s); + + return ns; +} + const char * fw3_find_command(const char *cmd) { diff --git a/utils.h b/utils.h index 3573e14..ea6a105 100644 --- a/utils.h +++ b/utils.h @@ -69,6 +69,9 @@ void info(const char *format, ...); (1<family)) return; - set(zone->flags, family, table); + info(" * Zone '%s'", zone->name); - /* Don't touch user chains on reload */ - if (reload) - delbit(custom_mask, FW3_FLAG_CUSTOM_CHAINS); + set(zone->flags, handle->family, handle->table); if (zone->custom_chains) - set(zone->flags, family, FW3_FLAG_CUSTOM_CHAINS); + set(zone->flags, handle->family, FW3_FLAG_CUSTOM_CHAINS); if (!zone->conntrack && !state->defaults.drop_invalid) - set(zone->flags, family, FW3_FLAG_NOTRACK); + set(zone->flags, handle->family, FW3_FLAG_NOTRACK); - c = fw3_pr_rulespec(table, family, zone->flags, custom_mask, zone_chains, - ":%s - [0:0]\n", zone->name); + for (c = zone_chains; c->format; c++) + { + /* don't touch user chains on selective stop */ + if (reload && c->flag == FW3_FLAG_CUSTOM_CHAINS) + continue; - r = fw3_pr_rulespec(table, family, zone->flags, 0, zone_rules, - "-A %s\n", zone->name); + if (!fw3_is_family(c, handle->family)) + continue; - if (c || r) - { - info(" * Zone '%s'", zone->name); + if (c->table != handle->table) + continue; - set(zone->flags, family, table); + if (c->flag && + !hasbit(zone->flags[handle->family == FW3_FAMILY_V6], c->flag)) + continue; + + fw3_ipt_create_chain(handle, c->format, zone->name); } + + if (zone->custom_chains) + { + if (handle->table == FW3_TABLE_FILTER) + { + for (i = 0; i < sizeof(flt_chains)/sizeof(flt_chains[0]); i += 2) + { + r = fw3_ipt_rule_new(handle); + fw3_ipt_rule_comment(r, "user chain for %s", flt_chains[i+1]); + fw3_ipt_rule_target(r, "%s_%s_rule", flt_chains[i+1], zone->name); + fw3_ipt_rule_append(r, "zone_%s_%s", zone->name, flt_chains[i]); + } + } + else if (handle->table == FW3_TABLE_NAT) + { + for (i = 0; i < sizeof(nat_chains)/sizeof(nat_chains[0]); i += 2) + { + r = fw3_ipt_rule_new(handle); + fw3_ipt_rule_comment(r, "user chain for %s", nat_chains[i+1]); + fw3_ipt_rule_target(r, "%s_%s_rule", nat_chains[i+1], zone->name); + fw3_ipt_rule_append(r, "zone_%s_%s", zone->name, nat_chains[i]); + } + } + } + + set(zone->flags, handle->family, handle->table); } static void -print_interface_rule(struct fw3_state *state, enum fw3_family family, - enum fw3_table table, bool reload, struct fw3_zone *zone, +print_interface_rule(struct fw3_ipt_handle *handle, struct fw3_state *state, + bool reload, struct fw3_zone *zone, struct fw3_device *dev, struct fw3_address *sub) { bool disable_notrack = state->defaults.drop_invalid; - + struct fw3_protocol tcp = { .protocol = 6 }; + struct fw3_ipt_rule *r; enum fw3_flag t; + char buf[32]; + + int i; + + const char *chains[] = { + "input", + "output", + "forward", + }; + #define jump_target(t) \ ((t == FW3_FLAG_REJECT) ? "reject" : fw3_flag_names[t]) - if (table == FW3_TABLE_FILTER) + if (handle->table == FW3_TABLE_FILTER) { for (t = FW3_FLAG_ACCEPT; t <= FW3_FLAG_DROP; t++) { - if (has(zone->flags, family, fw3_to_src_target(t))) + if (has(zone->flags, handle->family, fw3_to_src_target(t))) { - fw3_pr("-A zone_%s_src_%s", zone->name, fw3_flag_names[t]); - fw3_format_in_out(dev, NULL); - fw3_format_src_dest(sub, NULL); - fw3_format_extra(zone->extra_src); - fw3_pr(" -j %s\n", jump_target(t)); + r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL); + fw3_ipt_rule_target(r, jump_target(t)); + fw3_ipt_rule_extra(r, zone->extra_src); + fw3_ipt_rule_append(r, "zone_%s_src_%s", zone->name, + fw3_flag_names[t]); } - if (has(zone->flags, family, t)) + if (has(zone->flags, handle->family, t)) { - fw3_pr("-A zone_%s_dest_%s", zone->name, fw3_flag_names[t]); - fw3_format_in_out(NULL, dev); - fw3_format_src_dest(NULL, sub); - fw3_format_extra(zone->extra_dest); - fw3_pr(" -j %s\n", jump_target(t)); + r = fw3_ipt_rule_create(handle, NULL, NULL, dev, NULL, sub); + fw3_ipt_rule_target(r, jump_target(t)); + fw3_ipt_rule_extra(r, zone->extra_dest); + fw3_ipt_rule_append(r, "zone_%s_dest_%s", zone->name, + fw3_flag_names[t]); } } - fw3_pr("-A delegate_input"); - fw3_format_in_out(dev, NULL); - fw3_format_src_dest(sub, NULL); - fw3_format_extra(zone->extra_src); - fw3_pr(" -j zone_%s_input\n", zone->name); - - fw3_pr("-A delegate_forward"); - fw3_format_in_out(dev, NULL); - fw3_format_src_dest(sub, NULL); - fw3_format_extra(zone->extra_src); - fw3_pr(" -j zone_%s_forward\n", zone->name); - - fw3_pr("-A delegate_output"); - fw3_format_in_out(NULL, dev); - fw3_format_src_dest(NULL, sub); - fw3_format_extra(zone->extra_dest); - fw3_pr(" -j zone_%s_output\n", zone->name); + for (i = 0; i < sizeof(chains)/sizeof(chains[0]); i++) + { + if (*chains[i] == 'o') + r = fw3_ipt_rule_create(handle, NULL, NULL, dev, NULL, sub); + else + r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL); + + fw3_ipt_rule_target(r, "zone_%s_%s", zone->name, chains[i]); + + if (*chains[i] == 'o') + fw3_ipt_rule_extra(r, zone->extra_dest); + else + fw3_ipt_rule_extra(r, zone->extra_src); + + fw3_ipt_rule_append(r, "delegate_%s", chains[i]); + } } - else if (table == FW3_TABLE_NAT) + else if (handle->table == FW3_TABLE_NAT) { - if (has(zone->flags, family, FW3_FLAG_DNAT)) + if (has(zone->flags, handle->family, FW3_FLAG_DNAT)) { - fw3_pr("-A delegate_prerouting"); - fw3_format_in_out(dev, NULL); - fw3_format_src_dest(sub, NULL); - fw3_format_extra(zone->extra_src); - fw3_pr(" -j zone_%s_prerouting\n", zone->name); + r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL); + fw3_ipt_rule_target(r, "zone_%s_prerouting", zone->name); + fw3_ipt_rule_extra(r, zone->extra_src); + fw3_ipt_rule_append(r, "delegate_prerouting"); } - if (has(zone->flags, family, FW3_FLAG_SNAT)) + if (has(zone->flags, handle->family, FW3_FLAG_SNAT)) { - fw3_pr("-A delegate_postrouting"); - fw3_format_in_out(NULL, dev); - fw3_format_src_dest(NULL, sub); - fw3_format_extra(zone->extra_dest); - fw3_pr(" -j zone_%s_postrouting\n", zone->name); + r = fw3_ipt_rule_create(handle, NULL, NULL, dev, NULL, sub); + fw3_ipt_rule_target(r, "zone_%s_postrouting", zone->name); + fw3_ipt_rule_extra(r, zone->extra_dest); + fw3_ipt_rule_append(r, "delegate_postrouting"); } } - else if (table == FW3_TABLE_MANGLE) + else if (handle->table == FW3_TABLE_MANGLE) { if (zone->mtu_fix) { if (zone->log) { - fw3_pr("-A mssfix"); - fw3_format_in_out(NULL, dev); - fw3_format_src_dest(NULL, sub); - fw3_pr(" -p tcp --tcp-flags SYN,RST SYN"); - fw3_format_limit(&zone->log_limit); - fw3_format_comment(zone->name, " (mtu_fix logging)"); - fw3_pr(" -j LOG --log-prefix \"MSSFIX(%s): \"\n", zone->name); + snprintf(buf, sizeof(buf) - 1, "MSSFIX(%s): ", zone->name); + + r = fw3_ipt_rule_create(handle, &tcp, NULL, dev, NULL, sub); + fw3_ipt_rule_addarg(r, false, "--tcp-flags", "SYN,RST"); + fw3_ipt_rule_addarg(r, false, "SYN", NULL); + fw3_ipt_rule_limit(r, &zone->log_limit); + fw3_ipt_rule_comment(r, "%s (mtu_fix logging)", zone->name); + fw3_ipt_rule_target(r, "LOG"); + fw3_ipt_rule_addarg(r, false, "--log-prefix", buf); + fw3_ipt_rule_append(r, "mssfix"); } - fw3_pr("-A mssfix"); - fw3_format_in_out(NULL, dev); - fw3_format_src_dest(NULL, sub); - fw3_pr(" -p tcp --tcp-flags SYN,RST SYN"); - fw3_format_comment(zone->name, " (mtu_fix)"); - fw3_pr(" -j TCPMSS --clamp-mss-to-pmtu\n"); + r = fw3_ipt_rule_create(handle, &tcp, NULL, dev, NULL, sub); + fw3_ipt_rule_addarg(r, false, "--tcp-flags", "SYN,RST"); + fw3_ipt_rule_addarg(r, false, "SYN", NULL); + fw3_ipt_rule_comment(r, "%s (mtu_fix)", zone->name); + fw3_ipt_rule_target(r, "TCPMSS"); + fw3_ipt_rule_addarg(r, false, "--clamp-mss-to-pmtu", NULL); + fw3_ipt_rule_append(r, "mssfix"); } } - else if (table == FW3_TABLE_RAW) + else if (handle->table == FW3_TABLE_RAW) { if (!zone->conntrack && !disable_notrack) { - fw3_pr("-A notrack"); - fw3_format_in_out(dev, NULL); - fw3_format_src_dest(sub, NULL); - fw3_format_extra(zone->extra_src); - fw3_format_comment(zone->name, " (notrack)"); - fw3_pr(" -j CT --notrack\n", zone->name); + r = fw3_ipt_rule_create(handle, NULL, dev, NULL, sub, NULL); + fw3_ipt_rule_target(r, "CT"); + fw3_ipt_rule_addarg(r, false, "--notrack", NULL); + fw3_ipt_rule_extra(r, zone->extra_src); + fw3_ipt_rule_append(r, "notrack"); } } } static void -print_interface_rules(struct fw3_state *state, enum fw3_family family, - enum fw3_table table, bool reload, struct fw3_zone *zone) +print_interface_rules(struct fw3_ipt_handle *handle, struct fw3_state *state, + bool reload, struct fw3_zone *zone) { struct fw3_device *dev; struct fw3_address *sub; @@ -398,76 +435,97 @@ print_interface_rules(struct fw3_state *state, enum fw3_family family, fw3_foreach(dev, &zone->devices) fw3_foreach(sub, &zone->subnets) { - if (!fw3_is_family(sub, family)) + if (!fw3_is_family(sub, handle->family)) continue; if (!dev && !sub) continue; - print_interface_rule(state, family, table, reload, zone, dev, sub); + print_interface_rule(handle, state, reload, zone, dev, sub); } } static void -print_zone_rule(struct fw3_state *state, enum fw3_family family, - enum fw3_table table, bool reload, struct fw3_zone *zone) +print_zone_rule(struct fw3_ipt_handle *handle, struct fw3_state *state, + bool reload, struct fw3_zone *zone) { struct fw3_address *msrc; struct fw3_address *mdest; + struct fw3_ipt_rule *r; enum fw3_flag t; + char buf[32]; - if (!fw3_is_family(zone, family)) + if (!fw3_is_family(zone, handle->family)) return; - switch (table) + switch (handle->table) { case FW3_TABLE_FILTER: - fw3_pr("-A zone_%s_input -j zone_%s_src_%s\n", - zone->name, zone->name, fw3_flag_names[zone->policy_input]); + r = fw3_ipt_rule_new(handle); + fw3_ipt_rule_target(r, "zone_%s_src_%s", zone->name, + fw3_flag_names[zone->policy_input]); + fw3_ipt_rule_append(r, "zone_%s_input", zone->name); - fw3_pr("-A zone_%s_forward -j zone_%s_dest_%s\n", - zone->name, zone->name, fw3_flag_names[zone->policy_forward]); + r = fw3_ipt_rule_new(handle); + fw3_ipt_rule_target(r, "zone_%s_dest_%s", zone->name, + fw3_flag_names[zone->policy_forward]); + fw3_ipt_rule_append(r, "zone_%s_forward", zone->name); - fw3_pr("-A zone_%s_output -j zone_%s_dest_%s\n", - zone->name, zone->name, fw3_flag_names[zone->policy_output]); + r = fw3_ipt_rule_new(handle); + fw3_ipt_rule_target(r, "zone_%s_dest_%s", zone->name, + fw3_flag_names[zone->policy_output]); + fw3_ipt_rule_append(r, "zone_%s_output", zone->name); if (zone->log) { for (t = FW3_FLAG_REJECT; t <= FW3_FLAG_DROP; t++) { - if (has(zone->flags, family, fw3_to_src_target(t))) + if (has(zone->flags, handle->family, fw3_to_src_target(t))) { - fw3_pr("-A zone_%s_src_%s", zone->name, fw3_flag_names[t]); - fw3_format_limit(&zone->log_limit); - fw3_pr(" -j LOG --log-prefix \"%s(src %s)\"\n", - fw3_flag_names[t], zone->name); + r = fw3_ipt_rule_new(handle); + + snprintf(buf, sizeof(buf) - 1, "%s(src %s)", + fw3_flag_names[t], zone->name); + + fw3_ipt_rule_limit(r, &zone->log_limit); + fw3_ipt_rule_target(r, "LOG"); + fw3_ipt_rule_addarg(r, false, "--log-prefix", buf); + fw3_ipt_rule_append(r, "zone_%s_src_%s", + zone->name, fw3_flag_names[t]); } - if (has(zone->flags, family, t)) + if (has(zone->flags, handle->family, t)) { - fw3_pr("-A zone_%s_dest_%s", zone->name, fw3_flag_names[t]); - fw3_format_limit(&zone->log_limit); - fw3_pr(" -j LOG --log-prefix \"%s(dest %s)\"\n", - fw3_flag_names[t], zone->name); + r = fw3_ipt_rule_new(handle); + + snprintf(buf, sizeof(buf) - 1, "%s(dest %s)", + fw3_flag_names[t], zone->name); + + fw3_ipt_rule_limit(r, &zone->log_limit); + fw3_ipt_rule_target(r, "LOG"); + fw3_ipt_rule_addarg(r, false, "--log-prefix", buf); + fw3_ipt_rule_append(r, "zone_%s_dest_%s", + zone->name, fw3_flag_names[t]); } } } break; case FW3_TABLE_NAT: - if (zone->masq && family == FW3_FAMILY_V4) + if (zone->masq && handle->family == FW3_FAMILY_V4) { fw3_foreach(msrc, &zone->masq_src) fw3_foreach(mdest, &zone->masq_dest) { - if (!fw3_is_family(msrc, family) || - !fw3_is_family(mdest, family)) + if (!fw3_is_family(msrc, handle->family) || + !fw3_is_family(mdest, handle->family)) continue; - fw3_pr("-A zone_%s_postrouting", zone->name); - fw3_format_src_dest(msrc, mdest); - fw3_pr(" -j MASQUERADE\n"); + r = fw3_ipt_rule_new(handle); + fw3_ipt_rule_src_dest(r, msrc, mdest); + fw3_ipt_rule_target(r, "MASQUERADE"); + fw3_ipt_rule_append(r, "zone_%s_postrouting", zone->name); } } break; @@ -477,27 +535,27 @@ print_zone_rule(struct fw3_state *state, enum fw3_family family, break; } - print_interface_rules(state, family, table, reload, zone); + print_interface_rules(handle, state, reload, zone); } void -fw3_print_zone_chains(struct fw3_state *state, enum fw3_family family, - enum fw3_table table, bool reload) +fw3_print_zone_chains(struct fw3_ipt_handle *handle, struct fw3_state *state, + bool reload) { struct fw3_zone *zone; list_for_each_entry(zone, &state->zones, list) - print_zone_chain(state, family, table, reload, zone); + print_zone_chain(handle, state, reload, zone); } void -fw3_print_zone_rules(struct fw3_state *state, enum fw3_family family, - enum fw3_table table, bool reload) +fw3_print_zone_rules(struct fw3_ipt_handle *handle, struct fw3_state *state, + bool reload) { struct fw3_zone *zone; list_for_each_entry(zone, &state->zones, list) - print_zone_rule(state, family, table, reload, zone); + print_zone_rule(handle, state, reload, zone); } void diff --git a/zones.h b/zones.h index 353f089..b78aa32 100644 --- a/zones.h +++ b/zones.h @@ -28,11 +28,11 @@ struct fw3_zone * fw3_alloc_zone(void); void fw3_load_zones(struct fw3_state *state, struct uci_package *p); -void fw3_print_zone_chains(struct fw3_state *state, enum fw3_family family, - enum fw3_table table, bool reload); +void fw3_print_zone_chains(struct fw3_ipt_handle *handle, + struct fw3_state *state, bool reload); -void fw3_print_zone_rules(struct fw3_state *state, enum fw3_family family, - enum fw3_table table, bool reload); +void fw3_print_zone_rules(struct fw3_ipt_handle *handle, + struct fw3_state *state, bool reload); void fw3_flush_zones(struct fw3_ipt_handle *handle, struct fw3_state *state, bool reload); -- 2.25.1