This commit implements the --load-file option which allows processing conntrack commands stored in file. Most often this would be used as a counter-part for the -o save option, which outputs conntrack entries in the format of the conntrack tool options. This could be useful when one needs to add/update/delete a large set of ct entries with a single conntrack tool invocation. Expected syntax is "conntrack --load-file file". If "-" is given as a file name, stdin is used. No other commands or options are allowed to be specified in conjunction with the --load-file command. It is however possible to specify multiple --load-file file pairs. Example: Copy all entries from ct zone 11 to ct zone 12: conntrack -L -w 11 -o save | sed "s/-w 11/-w 12/g" | \ conntrack --load-file - Signed-off-by: Mikhail Sennikovsky <mikhail.sennikovskii@xxxxxxxxx> --- src/conntrack.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 178 insertions(+), 2 deletions(-) diff --git a/src/conntrack.c b/src/conntrack.c index 6040828..905d3a7 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -615,6 +615,17 @@ static unsigned int addr_valid_flags[ADDR_VALID_FLAGS_MAX] = { CT_OPT_REPL_SRC | CT_OPT_REPL_DST, }; +#define CT_COMMANDS_LOAD_FILE_ALLOWED ( 0 \ + | CT_CREATE \ + | CT_UPDATE_BIT \ + | CT_DELETE \ + | CT_FLUSH \ + ) + +static unsigned int cmd_allowed = ~0; + +static bool print_stats_allowed = true; + static LIST_HEAD(proto_list); static struct nfct_labelmap *labelmap; @@ -1267,6 +1278,9 @@ add_command(unsigned int *cmd, const int newcmd) { if (*cmd) exit_error(PARAMETER_PROBLEM, "Invalid commands combination"); + if (!(cmd_allowed & newcmd)) + exit_error(PARAMETER_PROBLEM, + "Command can not be used in the current mode"); *cmd |= newcmd; } @@ -2798,7 +2812,7 @@ nfct_set_nat_details(const int opt, struct nf_conntrack *ct, static int print_stats(const struct ct_cmd *cmd) { - if (cmd->command && exit_msg[cmd->cmd][0]) { + if (print_stats_allowed && cmd->command && exit_msg[cmd->cmd][0]) { fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION); fprintf(stderr, exit_msg[cmd->cmd], counter); if (counter == 0 && !(cmd->command & (CT_LIST | EXP_LIST))) @@ -3606,6 +3620,168 @@ static void ct_cmd_list_parse_argv(struct ct_cmd_list *list, ct_cmd_list_add(list, ct_cmd_create(argc, argv)); } +#define MAX_ARGC 255 +struct argv_store { + int argc; + char *argv[MAX_ARGC]; + int argvattr[MAX_ARGC]; +}; + +/* function adding one argument to store, updating argc + * returns if argument added, does not return otherwise */ +static void add_argv(struct argv_store *store, const char *what, int quoted) +{ + if (store->argc + 1 >= MAX_ARGC) + exit_error(PARAMETER_PROBLEM, + "Parser cannot handle more arguments\n"); + if (!what) + exit_error(PARAMETER_PROBLEM, + "Trying to store NULL argument\n"); + + store->argv[store->argc] = strdup(what); + store->argvattr[store->argc] = quoted; + store->argv[++store->argc] = NULL; +} + +static void free_argv(struct argv_store *store) +{ + while (store->argc) { + store->argc--; + free(store->argv[store->argc]); + store->argvattr[store->argc] = 0; + } +} + +struct ct_param_buf { + char buffer[1024]; + int len; +}; + +static void add_param(struct ct_param_buf *param, const char *curchar) +{ + param->buffer[param->len++] = *curchar; + if (param->len >= (int)sizeof(param->buffer)) + exit_error(PARAMETER_PROBLEM, "Parameter too long!"); +} + +static void add_param_to_argv(struct argv_store *store, char *parsestart) +{ + int quote_open = 0, escaped = 0, quoted = 0; + struct ct_param_buf param = {}; + char *curchar; + + /* After fighting with strtok enough, here's now + * a 'real' parser. According to Rusty I'm now no + * longer a real hacker, but I can live with that */ + + for (curchar = parsestart; *curchar; curchar++) { + if (quote_open) { + if (escaped) { + add_param(¶m, curchar); + escaped = 0; + continue; + } else if (*curchar == '\\') { + escaped = 1; + continue; + } else if (*curchar == '"') { + quote_open = 0; + } else { + add_param(¶m, curchar); + continue; + } + } else { + if (*curchar == '"') { + quote_open = 1; + quoted = 1; + continue; + } + } + + switch (*curchar) { + case '"': + break; + case ' ': + case '\t': + case '\n': + if (!param.len) { + /* two spaces? */ + continue; + } + break; + default: + /* regular character, copy to buffer */ + add_param(¶m, curchar); + continue; + } + + param.buffer[param.len] = '\0'; + add_argv(store, param.buffer, quoted); + param.len = 0; + quoted = 0; + } + if (param.len) { + param.buffer[param.len] = '\0'; + add_argv(store, param.buffer, 0); + } +} + +static void ct_cmd_list_parse_line(struct ct_cmd_list *list, + const char *progname, char *buffer) +{ + struct argv_store store = {}; + + /* skip prepended tabs and spaces */ + for (; *buffer == ' ' || *buffer == '\t'; buffer++); + + if (buffer[0] == '\n' + || buffer[0] == '#') + return; + + add_argv(&store, progname, false); + + add_param_to_argv(&store, buffer); + + ct_cmd_list_parse_argv(list, store.argc, store.argv); + + free_argv(&store); +} + +static void ct_cmd_list_parse_file(struct ct_cmd_list *list, + const char *progname, + const char *file_name) +{ + char buffer[10240] = {}; + FILE *file; + + if (!strcmp(file_name, "-")) + file_name = "/dev/stdin"; + + file = fopen(file_name, "r"); + if (!file) + exit_error(PARAMETER_PROBLEM, NULL, + "Failed to open file %s for reading", file_name); + + while (fgets(buffer, sizeof(buffer), file)) + ct_cmd_list_parse_line(list, progname, buffer); +} + +static void ct_cmd_list_parse(struct ct_cmd_list *list, int argc, char *argv[]) +{ + if (argc > 2 + && (!strcmp(argv[1], "-R") + || !strcmp(argv[1], "--load-file"))) { + int i; + + cmd_allowed = CT_COMMANDS_LOAD_FILE_ALLOWED; + print_stats_allowed = false; + + for (i = 2; i < argc; ++i) + ct_cmd_list_parse_file(list, argv[0], argv[i]); + return; + } + ct_cmd_list_parse_argv(list, argc, argv); +} + int main(int argc, char *argv[]) { struct ct_cmd_list list; @@ -3622,7 +3798,7 @@ int main(int argc, char *argv[]) ct_cmd_list_init(&list); - ct_cmd_list_parse_argv(&list, argc, argv); + ct_cmd_list_parse(&list, argc, argv); if (ct_cmd_list_apply(&list, argv[0]) < 0) return EXIT_FAILURE; -- 2.25.1