@@ -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;
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@ionos.com> --- src/conntrack.c | 180 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 178 insertions(+), 2 deletions(-)