From 922508e9156327ccb8e35243781cf85f5787ee40 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 19 Jul 2018 18:32:09 +0200 Subject: xtables: implement ebtables-{save,restore} The code for ebtables-restore was derived from legacy code, ebtables-save is actually a new implementation using the existing infrastructure and trying to adhere to legacy perl script output formatting as much as possible. This introduces a new format flag (FMT_EBT_SAVE) to allow nft_bridge_save_rule() to distinguish between ruleset listing (i.e., ebtables -L) and saving via ebtables-save - the two differ in how counters are being formatted. Odd, but that's how it is. Signed-off-by: Phil Sutter Signed-off-by: Florian Westphal --- include/xtables.h | 1 + iptables/Makefile.am | 1 + iptables/nft-bridge.c | 85 +++++++++++++++++++++----------- iptables/nft.h | 1 + iptables/xtables-eb.c | 4 +- iptables/xtables-multi.h | 2 + iptables/xtables-nft-multi.c | 2 + iptables/xtables-restore.c | 114 +++++++++++++++++++++++++++++++++++++++++++ iptables/xtables-save.c | 67 +++++++++++++++++++++++++ 9 files changed, 246 insertions(+), 31 deletions(-) diff --git a/include/xtables.h b/include/xtables.h index 743906bf..e4d23502 100644 --- a/include/xtables.h +++ b/include/xtables.h @@ -536,6 +536,7 @@ extern void xtables_save_string(const char *value); #define FMT_VIA 0x0040 #define FMT_NONEWLINE 0x0080 #define FMT_LINENUMBERS 0x0100 +#define FMT_EBT_SAVE 0x0200 #define FMT_PRINT_RULE (FMT_NOCOUNTS | FMT_OPTIONS | FMT_VIA \ | FMT_NUMERIC | FMT_NOTABLE) diff --git a/iptables/Makefile.am b/iptables/Makefile.am index 800dd13a..a950d9fd 100644 --- a/iptables/Makefile.am +++ b/iptables/Makefile.am @@ -82,6 +82,7 @@ x_sbin_links = iptables-nft iptables-nft-restore iptables-nft-save \ iptables-restore-translate ip6tables-restore-translate \ arptables-nft arptables \ ebtables-nft ebtables \ + ebtables-nft-restore ebtables-nft-save \ xtables-monitor endif diff --git a/iptables/nft-bridge.c b/iptables/nft-bridge.c index b3bb3666..bbcecd82 100644 --- a/iptables/nft-bridge.c +++ b/iptables/nft-bridge.c @@ -435,52 +435,78 @@ static void print_protocol(uint16_t ethproto, bool invert, unsigned int bitmask) printf("%s ", ent->e_name); } -static void nft_bridge_print_rule(struct nftnl_rule *r, unsigned int num, - unsigned int format) +static void nft_bridge_save_rule(const void *data, unsigned int format) { - struct iptables_command_state cs = {}; - - nft_rule_to_ebtables_command_state(r, &cs); - - if (format & FMT_LINENUMBERS) - printf("%d ", num); + const struct iptables_command_state *cs = data; - print_protocol(cs.eb.ethproto, cs.eb.invflags & EBT_IPROTO, cs.eb.bitmask); - if (cs.eb.bitmask & EBT_ISOURCE) - print_mac('s', cs.eb.sourcemac, cs.eb.sourcemsk, - cs.eb.invflags & EBT_ISOURCE); - if (cs.eb.bitmask & EBT_IDEST) - print_mac('d', cs.eb.destmac, cs.eb.destmsk, - cs.eb.invflags & EBT_IDEST); + if (cs->eb.ethproto) + print_protocol(cs->eb.ethproto, cs->eb.invflags & EBT_IPROTO, + cs->eb.bitmask); + if (cs->eb.bitmask & EBT_ISOURCE) + print_mac('s', cs->eb.sourcemac, cs->eb.sourcemsk, + cs->eb.invflags & EBT_ISOURCE); + if (cs->eb.bitmask & EBT_IDEST) + print_mac('d', cs->eb.destmac, cs->eb.destmsk, + cs->eb.invflags & EBT_IDEST); - print_iface("-i", cs.eb.in, cs.eb.invflags & EBT_IIN); - print_iface("--logical-in", cs.eb.logical_in, cs.eb.invflags & EBT_ILOGICALIN); - print_iface("-o", cs.eb.out, cs.eb.invflags & EBT_IOUT); - print_iface("--logical-out", cs.eb.logical_out, cs.eb.invflags & EBT_ILOGICALOUT); + print_iface("-i", cs->eb.in, cs->eb.invflags & EBT_IIN); + print_iface("--logical-in", cs->eb.logical_in, + cs->eb.invflags & EBT_ILOGICALIN); + print_iface("-o", cs->eb.out, cs->eb.invflags & EBT_IOUT); + print_iface("--logical-out", cs->eb.logical_out, + cs->eb.invflags & EBT_ILOGICALOUT); - print_matches_and_watchers(&cs, format); + print_matches_and_watchers(cs, format); printf("-j "); - if (cs.jumpto != NULL) { - if (strcmp(cs.jumpto, "") != 0) - printf("%s", cs.jumpto); + if (cs->jumpto != NULL) { + if (strcmp(cs->jumpto, "") != 0) + printf("%s", cs->jumpto); else printf("CONTINUE"); } - else if (cs.target != NULL && cs.target->print != NULL) - cs.target->print(&cs.fw, cs.target->t, format & FMT_NUMERIC); + else if (cs->target != NULL && cs->target->print != NULL) + cs->target->print(&cs->fw, cs->target->t, format & FMT_NUMERIC); + + if (!(format & FMT_NOCOUNTS)) { + const char *counter_fmt; - if (!(format & FMT_NOCOUNTS)) - printf(" , pcnt = %"PRIu64" -- bcnt = %"PRIu64"", - (uint64_t)cs.counters.pcnt, (uint64_t)cs.counters.bcnt); + if (format & FMT_EBT_SAVE) + counter_fmt = " -c %"PRIu64" %"PRIu64""; + else + counter_fmt = " , pcnt = %"PRIu64" -- bcnt = %"PRIu64""; + + printf(counter_fmt, + (uint64_t)cs->counters.pcnt, + (uint64_t)cs->counters.bcnt); + } if (!(format & FMT_NONEWLINE)) fputc('\n', stdout); +} + +static void nft_bridge_print_rule(struct nftnl_rule *r, unsigned int num, + unsigned int format) +{ + struct iptables_command_state cs = {}; + + if (format & FMT_LINENUMBERS) + printf("%d ", num); + nft_rule_to_ebtables_command_state(r, &cs); + nft_bridge_save_rule(&cs, format); ebt_cs_clean(&cs); } +static void nft_bridge_save_chain(const struct nftnl_chain *c, + const char *policy) +{ + const char *chain = nftnl_chain_get_str(c, NFTNL_CHAIN_NAME); + + printf(":%s %s\n", chain, policy ?: "ACCEPT"); +} + static bool nft_bridge_is_same(const void *data_a, const void *data_b) { const struct ebt_entry *a = data_a; @@ -730,8 +756,9 @@ struct nft_family_ops nft_family_ops_bridge = { .print_table_header = nft_bridge_print_table_header, .print_header = nft_bridge_print_header, .print_rule = nft_bridge_print_rule, - .save_rule = NULL, + .save_rule = nft_bridge_save_rule, .save_counters = NULL, + .save_chain = nft_bridge_save_chain, .post_parse = NULL, .rule_to_cs = nft_rule_to_ebtables_command_state, .clear_cs = ebt_cs_clean, diff --git a/iptables/nft.h b/iptables/nft.h index f73a61c5..a479cf07 100644 --- a/iptables/nft.h +++ b/iptables/nft.h @@ -146,6 +146,7 @@ int do_commandx(struct nft_handle *h, int argc, char *argv[], char **table, bool int do_commandarp(struct nft_handle *h, int argc, char *argv[], char **table); /* For xtables-eb.c */ int nft_init_eb(struct nft_handle *h); +int ebt_get_current_chain(const char *chain); int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table); /* diff --git a/iptables/xtables-eb.c b/iptables/xtables-eb.c index 44235347..ab0539c8 100644 --- a/iptables/xtables-eb.c +++ b/iptables/xtables-eb.c @@ -203,7 +203,7 @@ delete_entry(struct nft_handle *h, return ret; } -static int get_current_chain(const char *chain) +int ebt_get_current_chain(const char *chain) { if (!chain) return -1; @@ -846,7 +846,7 @@ int do_commandeb(struct nft_handle *h, int argc, char *argv[], char **table) if (optarg && (optarg[0] == '-' || !strcmp(optarg, "!"))) xtables_error(PARAMETER_PROBLEM, "No chain name specified"); chain = optarg; - selected_chain = get_current_chain(chain); + selected_chain = ebt_get_current_chain(chain); flags |= OPT_COMMAND; if (c == 'N') { diff --git a/iptables/xtables-multi.h b/iptables/xtables-multi.h index 82ee9c9d..84457618 100644 --- a/iptables/xtables-multi.h +++ b/iptables/xtables-multi.h @@ -16,6 +16,8 @@ extern int xtables_ip4_xlate_restore_main(int, char **); extern int xtables_ip6_xlate_restore_main(int, char **); extern int xtables_arp_main(int, char **); extern int xtables_eb_main(int, char **); +extern int xtables_eb_restore_main(int, char **); +extern int xtables_eb_save_main(int, char **); extern int xtables_config_main(int, char **); extern int xtables_monitor_main(int, char **); #endif diff --git a/iptables/xtables-nft-multi.c b/iptables/xtables-nft-multi.c index 03690a56..6fb8bd29 100644 --- a/iptables/xtables-nft-multi.c +++ b/iptables/xtables-nft-multi.c @@ -35,6 +35,8 @@ static const struct subcommand multi_subcommands[] = { {"ebtables-translate", xtables_eb_xlate_main}, {"ebtables", xtables_eb_main}, {"ebtables-nft", xtables_eb_main}, + {"ebtables-restore", xtables_eb_restore_main}, + {"ebtables-save", xtables_eb_save_main}, {"xtables-monitor", xtables_monitor_main}, {NULL}, }; diff --git a/iptables/xtables-restore.c b/iptables/xtables-restore.c index f9392457..5c0ae98e 100644 --- a/iptables/xtables-restore.c +++ b/iptables/xtables-restore.c @@ -16,6 +16,7 @@ #include "libiptc/libiptc.h" #include "xtables-multi.h" #include "nft.h" +#include "nft-bridge.h" #include #ifdef DEBUG @@ -581,3 +582,116 @@ int xtables_ip6_restore_main(int argc, char *argv[]) return xtables_restore_main(NFPROTO_IPV6, "ip6tables-restore", argc, argv); } + +static const char *ebt_parse_table_name(const char *input) +{ + if (!strcmp(input, "broute")) + xtables_error(PARAMETER_PROBLEM, "broute table not supported"); + else if (!strcmp(input, "filter")) + return "filter"; + else if (!strcmp(input, "nat")) + return "nat"; + + xtables_error(PARAMETER_PROBLEM, "table '%s' not recognized", input); +} + +static const char *ebt_parse_policy_name(const char *input) +{ + int i; + + for (i = 0; i < NUM_STANDARD_TARGETS; i++) { + if (!strcmp(input, ebt_standard_targets[i])) { + int policy = -i - 1; + + if (policy == EBT_CONTINUE) + i = NUM_STANDARD_TARGETS; + break; + } + } + if (i == NUM_STANDARD_TARGETS) + xtables_error(PARAMETER_PROBLEM, "invalid policy specified"); + return ebt_standard_targets[i]; +} + +static const struct option ebt_restore_options[] = { + {.name = "noflush", .has_arg = 0, .val = 'n'}, + { 0 } +}; + +int xtables_eb_restore_main(int argc, char *argv[]) +{ + char buffer[10240]; + int i, ret, c, flush = 1; + const char *table = NULL; + struct nft_handle h; + + nft_init_eb(&h); + + while ((c = getopt_long(argc, argv, "n", + ebt_restore_options, NULL)) != -1) { + switch(c) { + case 'n': + flush = 0; + break; + default: + fprintf(stderr, + "Usage: ebtables-restore [ --noflush ]\n"); + exit(1); + break; + } + } + + while (fgets(buffer, sizeof(buffer), stdin)) { + if (buffer[0] == '#' || buffer[0] == '\n') + continue; + if (buffer[0] == '*') { + table = ebt_parse_table_name(buffer + 1); + if (flush) + nft_table_flush(&h, table); + continue; + } else if (!table) { + xtables_error(PARAMETER_PROBLEM, "no table specified"); + } + if (buffer[0] == ':') { + char *ch, *chain = buffer; + const char *policy; + + if (!(ch = strchr(buffer, ' '))) + xtables_error(PARAMETER_PROBLEM, "no policy specified"); + *ch = '\0'; + policy = ebt_parse_policy_name(ch + 1); + + /* No need to check chain name for consistency, since + * we're supposed to be reading an automatically generated + * file. */ + if (ebt_get_current_chain(chain) < 0) + nft_chain_user_add(&h, chain, table); + ret = nft_chain_set(&h, table, chain, policy, NULL); + if (ret < 0) + xtables_error(PARAMETER_PROBLEM, "Wrong policy"); + continue; + } + + newargc = 0; + add_argv("ebtables"); + add_argv("-t"); + add_argv(table); + add_param_to_argv(buffer); + + DEBUGP("calling do_commandeb(%u, argv, &%s, handle):\n", + newargc, table); + + for (i = 0; i < newargc; i++) + DEBUGP("argv[%u]: %s\n", i, newargv[i]); + + optind = 0; /* Setting optind = 1 causes serious annoyances */ + if (!do_commandeb(&h, newargc, newargv, &newargv[2])) + return 1; + } + + if (!nft_commit(&h)) { + fprintf(stderr, "%s\n", nft_strerror(errno)); + return 1; + } + return 0; +} diff --git a/iptables/xtables-save.c b/iptables/xtables-save.c index e6bad32f..c9df51d5 100644 --- a/iptables/xtables-save.c +++ b/iptables/xtables-save.c @@ -220,3 +220,70 @@ int xtables_ip6_save_main(int argc, char *argv[]) { return xtables_save_main(NFPROTO_IPV6, "ip6tables-save", argc, argv); } + +static int __ebt_save(struct nft_handle *h, const char *tablename, bool counters) +{ + struct nftnl_chain_list *chain_list; + static bool first = true; + time_t now; + + if (!nft_table_find(h, tablename)) { + printf("Table `%s' does not exist\n", tablename); + return 1; + } + + if (!nft_is_table_compatible(h, tablename)) { + printf("# Table `%s' is incompatible, use 'nft' tool.\n", tablename); + return 0; + } + + chain_list = nft_chain_dump(h); + + if (first) { + now = time(NULL); + printf("# Generated by ebtables-save v%s on %s", + IPTABLES_VERSION, ctime(&now)); + first = false; + } + printf("*%s\n", tablename); + + /* Dump out chain names first, + * thereby preventing dependency conflicts */ + nft_chain_save(h, chain_list, tablename); + nft_rule_save(h, tablename, + FMT_EBT_SAVE | (counters ? 0 : FMT_NOCOUNTS)); + printf("\n"); + return 0; +} + +int xtables_eb_save_main(int argc_, char *argv_[]) +{ + const char *ctr = getenv("EBTABLES_SAVE_COUNTER"); + struct nft_handle h = { + .family = NFPROTO_BRIDGE, + }; + int c; + + if (ctr && strcmp(ctr, "yes")) + ctr = NULL; + + xtables_globals.program_name = "ebtables-save"; + c = xtables_init_all(&xtables_globals, h.family); + if (c < 0) { + fprintf(stderr, "%s/%s Failed to initialize xtables\n", + xtables_globals.program_name, + xtables_globals.program_version); + exit(1); + } + + if (nft_init(&h, xtables_bridge) < 0) { + fprintf(stderr, "%s/%s Failed to initialize nft: %s\n", + xtables_globals.program_name, + xtables_globals.program_version, + strerror(errno)); + exit(EXIT_FAILURE); + } + + nft_for_each_table(&h, __ebt_save, !!ctr); + return 0; +} -- cgit v1.2.3