From: Mikhail Sennikovsky <mikhail.sennikovskii@cloud.ionos.com>
To: netfilter-devel@vger.kernel.org
Cc: Mikhail Sennikovsky <mikhail.sennikovskii@cloud.ionos.com>
Subject: [PATCH 3/8] conntrack: accept parameters from stdin
Date: Fri, 25 Sep 2020 14:49:14 +0200 [thread overview]
Message-ID: <20200925124919.9389-4-mikhail.sennikovskii@cloud.ionos.com> (raw)
In-Reply-To: <20200925124919.9389-1-mikhail.sennikovskii@cloud.ionos.com>
This commit allows accepting multiple sets of ct entry-related
parameters on stdin.
This is useful when one needs to add/update/delete a large
set of ct entries with a single conntrack tool invocation.
Expected syntax is "conntrack [-I|-D|-U] [table] -".
When invoked like that, conntrack expects ct entry parameters
to be passed to the stdin, each line presenting a separate parameter
set.
Signed-off-by: Mikhail Sennikovsky <mikhail.sennikovskii@cloud.ionos.com>
---
src/conntrack.c | 196 +++++++++++++++++++++++++++++++++++++++---------
1 file changed, 161 insertions(+), 35 deletions(-)
diff --git a/src/conntrack.c b/src/conntrack.c
index a26fa60..5834f2d 100644
--- a/src/conntrack.c
+++ b/src/conntrack.c
@@ -96,15 +96,18 @@ static struct {
struct nfct_bitmask *label_modify;
} tmpl;
+int cur_argc;
+char **cur_argv;
+
static int alloc_tmpl_objects(void)
{
+ memset(&tmpl, 0, sizeof(tmpl));
+
tmpl.ct = nfct_new();
tmpl.exptuple = nfct_new();
tmpl.mask = nfct_new();
tmpl.exp = nfexp_new();
- memset(&tmpl.mark, 0, sizeof(tmpl.mark));
-
return tmpl.ct != NULL && tmpl.exptuple != NULL &&
tmpl.mask != NULL && tmpl.exp != NULL;
}
@@ -685,12 +688,18 @@ static void free_options(void)
void __attribute__((noreturn))
exit_error(enum exittype status, const char *msg, ...)
{
+ int i;
va_list args;
free_options();
va_start(args, msg);
fprintf(stderr,"%s v%s (conntrack-tools): ", PROGNAME, VERSION);
vfprintf(stderr, msg, args);
+ if (cur_argc) {
+ fprintf(stderr, "\nargs:");
+ for (i = 1; i < cur_argc; ++i)
+ fprintf(stderr, " %s", cur_argv[i]);
+ }
fprintf(stderr, "\n");
va_end(args);
if (status == PARAMETER_PROBLEM)
@@ -2317,23 +2326,63 @@ nfct_set_nat_details(const int opt, struct nf_conntrack *ct,
nfct_set_attr_u16(ct, ATTR_DNAT_PORT,
ntohs((uint16_t)atoi(port_str)));
}
+}
+
+static int line_to_argcv(char *cmd, char *line, char ***pargv, size_t *pargv_size)
+{
+ char *arg;
+ int argc;
+ char **argv = *pargv;
+ size_t argv_size = *pargv_size;
+ int argc_max = argv_size / sizeof(*argv);
+
+#define _ARG_ADD(_arg) do { \
+ if (argc == argc_max) { \
+ argc_max += 20; \
+ argv_size = argc_max * sizeof (argv[0]); \
+ argv = realloc(argv, argv_size); \
+ if (!argv) \
+ exit_error(OTHER_PROBLEM, "out of memory"); \
+ } \
+ argv[argc] = _arg; \
+ ++argc; \
+} while (0)
+
+#define _ARG_SEP " \t\n\r"
+ for (argc = 0, arg = strtok (line, _ARG_SEP);
+ arg;
+ arg = strtok (NULL, _ARG_SEP)) {
+ /*
+ * getopt_long expects argv[0] to be the command name,
+ * and would always skip it so we need to include it here
+ */
+ if (!argc && cmd)
+ _ARG_ADD(cmd);
+ _ARG_ADD(arg);
+ }
+
+#undef _ARG_ADD
+#undef _ARG_SEP
+ *pargv = argv;
+ *pargv_size = argv_size;
+
+ return argc;
}
int main(int argc, char *argv[])
{
int c, cmd;
- unsigned int type = 0, event_mask = 0, l4flags = 0, status = 0;
+ unsigned int type = 0, event_mask = 0, l4flags, status = 0;
int res = 0, partial;
- size_t socketbuffersize = 0;
- int family = AF_UNSPEC;
- int protonum = 0;
+ size_t socketbuffersize;
+ int family;
+ int protonum;
union ct_address ad;
unsigned int command = 0;
-
- /* we release these objects in the exit_error() path. */
- if (!alloc_tmpl_objects())
- exit_error(OTHER_PROBLEM, "out of memory");
+ FILE *opts_file = NULL;
+ char **argv_buf = NULL, *getline_buf = NULL;
+ size_t argv_buf_size = 0, getline_buf_size = 0;
register_tcp();
register_udp();
@@ -2348,6 +2397,23 @@ int main(int argc, char *argv[])
/* disable explicit missing arguments error output from getopt_long */
opterr = 0;
+parse_opts:
+
+ options = 0;
+ filter_family = 0;
+ memset(dir2network, 0, sizeof(dir2network));
+ /* all allocate-able objects get freed zero-inited
+ * at the end of each iteration */
+
+ l4flags = 0;
+ family = AF_UNSPEC;
+ socketbuffersize = 0;
+ protonum = 0;
+
+ /* we release these objects in the exit_error() path. */
+ if (!alloc_tmpl_objects())
+ exit_error(OTHER_PROBLEM, "out of memory");
+
while ((c = getopt_long(argc, argv, getopt_str, opts, NULL)) != -1) {
switch(c) {
/* commands */
@@ -2585,6 +2651,26 @@ int main(int argc, char *argv[])
"`--dst-nat' with `--any-nat'");
}
cmd = bit2cmd(command);
+
+ if (!opts_file && optind == argc - 1 && !strcmp(argv[optind], "-")) {
+ switch (command) {
+ case CT_CREATE:
+ case EXP_CREATE:
+ case CT_UPDATE:
+ case CT_DELETE:
+ case EXP_DELETE:
+ break;
+ default:
+ exit_error(PARAMETER_PROBLEM, "stdin mode not supported "
+ "for this command!");
+ }
+ if (options)
+ exit_error(PARAMETER_PROBLEM, "no extra options are expected "
+ "with stdin read mode!");
+ opts_file = stdin;
+ goto next_opts;
+ }
+
res = generic_opt_check(options, NUMBER_OF_OPT,
commands_v_options[cmd], optflags,
addr_valid_flags, ADDR_VALID_FLAGS_MAX,
@@ -2670,8 +2756,8 @@ int main(int argc, char *argv[])
printf("</conntrack>\n");
fflush(stdout);
}
-
nfct_close(cth);
+ cth = NULL;
break;
case EXP_LIST:
@@ -2681,12 +2767,13 @@ int main(int argc, char *argv[])
nfexp_callback_register(cth, NFCT_T_ALL, dump_exp_cb, NULL);
res = nfexp_query(cth, NFCT_Q_DUMP, &family);
- nfct_close(cth);
if (dump_xml_header_done == 0) {
printf("</expect>\n");
fflush(stdout);
}
+ nfct_close(cth);
+ cth = NULL;
break;
case CT_CREATE:
@@ -2702,14 +2789,15 @@ int main(int argc, char *argv[])
nfct_set_attr(tmpl.ct, ATTR_CONNLABELS,
xnfct_bitmask_clone(tmpl.label_modify));
- cth = nfct_open(CONNTRACK, 0);
- if (!cth)
- exit_error(OTHER_PROBLEM, "Can't open handler");
+ if (!cth) {
+ cth = nfct_open(CONNTRACK, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+ }
res = nfct_query(cth, NFCT_Q_CREATE, tmpl.ct);
if (res != -1)
counter++;
- nfct_close(cth);
break;
case EXP_CREATE:
@@ -2717,18 +2805,21 @@ int main(int argc, char *argv[])
nfexp_set_attr(tmpl.exp, ATTR_EXP_EXPECTED, tmpl.exptuple);
nfexp_set_attr(tmpl.exp, ATTR_EXP_MASK, tmpl.mask);
- cth = nfct_open(EXPECT, 0);
- if (!cth)
- exit_error(OTHER_PROBLEM, "Can't open handler");
+ if (!cth) {
+ cth = nfct_open(EXPECT, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+ }
res = nfexp_query(cth, NFCT_Q_CREATE, tmpl.exp);
- nfct_close(cth);
break;
case CT_UPDATE:
- cth = nfct_open(CONNTRACK, 0);
+ if (!cth)
+ cth = nfct_open(CONNTRACK, 0);
/* internal handler for delete_cb, otherwise we hit EILSEQ */
- ith = nfct_open(CONNTRACK, 0);
+ if (!ith)
+ ith = nfct_open(CONNTRACK, 0);
if (!cth || !ith)
exit_error(OTHER_PROBLEM, "Can't open handler");
@@ -2737,13 +2828,13 @@ int main(int argc, char *argv[])
nfct_callback_register(cth, NFCT_T_ALL, update_cb, tmpl.ct);
res = nfct_query(cth, NFCT_Q_DUMP, &family);
- nfct_close(ith);
- nfct_close(cth);
break;
case CT_DELETE:
- cth = nfct_open(CONNTRACK, 0);
- ith = nfct_open(CONNTRACK, 0);
+ if (!cth)
+ cth = nfct_open(CONNTRACK, 0);
+ if (!ith)
+ ith = nfct_open(CONNTRACK, 0);
if (!cth || !ith)
exit_error(OTHER_PROBLEM, "Can't open handler");
@@ -2768,19 +2859,18 @@ int main(int argc, char *argv[])
nfct_filter_dump_destroy(filter_dump);
- nfct_close(ith);
- nfct_close(cth);
break;
case EXP_DELETE:
nfexp_set_attr(tmpl.exp, ATTR_EXP_EXPECTED, tmpl.ct);
- cth = nfct_open(EXPECT, 0);
- if (!cth)
- exit_error(OTHER_PROBLEM, "Can't open handler");
+ if (!cth) {
+ cth = nfct_open(EXPECT, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+ }
res = nfexp_query(cth, NFCT_Q_DESTROY, tmpl.exp);
- nfct_close(cth);
break;
case CT_GET:
@@ -2791,6 +2881,7 @@ int main(int argc, char *argv[])
nfct_callback_register(cth, NFCT_T_ALL, dump_cb, tmpl.ct);
res = nfct_query(cth, NFCT_Q_GET, tmpl.ct);
nfct_close(cth);
+ cth = NULL;
break;
case EXP_GET:
@@ -2803,6 +2894,7 @@ int main(int argc, char *argv[])
nfexp_callback_register(cth, NFCT_T_ALL, dump_exp_cb, NULL);
res = nfexp_query(cth, NFCT_Q_GET, tmpl.exp);
nfct_close(cth);
+ cth = NULL;
break;
case CT_FLUSH:
@@ -2810,9 +2902,10 @@ int main(int argc, char *argv[])
if (!cth)
exit_error(OTHER_PROBLEM, "Can't open handler");
res = nfct_query(cth, NFCT_Q_FLUSH, &family);
- nfct_close(cth);
fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION);
fprintf(stderr,"connection tracking table has been emptied.\n");
+ nfct_close(cth);
+ cth = NULL;
break;
case EXP_FLUSH:
@@ -2820,9 +2913,10 @@ int main(int argc, char *argv[])
if (!cth)
exit_error(OTHER_PROBLEM, "Can't open handler");
res = nfexp_query(cth, NFCT_Q_FLUSH, &family);
- nfct_close(cth);
fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION);
fprintf(stderr,"expectation table has been emptied.\n");
+ nfct_close(cth);
+ cth = NULL;
break;
case CT_EVENT:
@@ -2894,6 +2988,8 @@ int main(int argc, char *argv[])
res = mnl_cb_run(buf, res, 0, 0, event_cb, tmpl.ct);
}
mnl_socket_close(sock.mnl);
+ nfct_close(cth);
+ cth = NULL;
break;
case EXP_EVENT:
@@ -2922,6 +3018,7 @@ int main(int argc, char *argv[])
nfexp_callback_register(cth, NFCT_T_ALL, event_exp_cb, NULL);
res = nfexp_catch(cth);
nfct_close(cth);
+ cth = NULL;
break;
case CT_COUNT:
/* If we fail with netlink, fall back to /proc to ensure
@@ -2966,6 +3063,7 @@ try_proc_count:
nfexp_callback_register(cth, NFCT_T_ALL, count_exp_cb, NULL);
res = nfexp_query(cth, NFCT_Q_DUMP, &family);
nfct_close(cth);
+ cth = NULL;
printf("%d\n", counter);
break;
case CT_STATS:
@@ -3024,10 +3122,35 @@ try_proc:
exit_error(OTHER_PROBLEM, "Operation failed: %s",
err2str(errno, command));
+next_opts:
free_tmpl_objects();
free_options();
- if (labelmap)
+ if (labelmap) {
nfct_labelmap_destroy(labelmap);
+ labelmap = NULL;
+ }
+
+ if (opts_file) {
+ while ((res = getline(&getline_buf, &getline_buf_size, opts_file)) >= 0) {
+ if (!res)
+ continue;
+ argc = line_to_argcv(argv[0], getline_buf, &argv_buf, &argv_buf_size);
+ if (!argc)
+ continue;
+ argv = argv_buf;
+
+ cur_argc = argc;
+ cur_argv = argv;
+
+ optind = 0;
+ goto parse_opts;
+ }
+ }
+
+ if (ith)
+ nfct_close(ith);
+ if (cth)
+ nfct_close(cth);
if (command && exit_msg[cmd][0]) {
fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION);
@@ -3036,5 +3159,8 @@ try_proc:
return EXIT_FAILURE;
}
+ free(argv_buf);
+ free(getline_buf);
+
return EXIT_SUCCESS;
}
--
2.25.1
next prev parent reply other threads:[~2020-09-25 12:58 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-09-25 12:49 [PATCH 0/8] Fast bulk transfers of large sets of ct entries Mikhail Sennikovsky
2020-09-25 12:49 ` [PATCH 1/8] tests: icmp entry create/delete Mikhail Sennikovsky
2020-10-13 18:28 ` Pablo Neira Ayuso
2020-09-25 12:49 ` [PATCH 2/8] conntrack: fix icmp entry creation Mikhail Sennikovsky
2020-10-13 18:29 ` Pablo Neira Ayuso
2020-09-25 12:49 ` Mikhail Sennikovsky [this message]
2020-11-04 13:56 ` [PATCH 3/8] conntrack: accept parameters from stdin Pablo Neira Ayuso
2020-09-25 12:49 ` [PATCH 4/8] conntrack.8: man update for stdin params support Mikhail Sennikovsky
2020-09-25 12:49 ` [PATCH 5/8] tests: conntrack parameters from stdin Mikhail Sennikovsky
2020-09-25 12:49 ` [PATCH 6/8] conntrack: implement options output format Mikhail Sennikovsky
2020-10-22 12:36 ` Pablo Neira Ayuso
2020-10-22 12:37 ` Pablo Neira Ayuso
2020-10-23 11:07 ` Mikhail Sennikovsky
2020-09-25 12:49 ` [PATCH 7/8] conntrack.8: man update for opts format support Mikhail Sennikovsky
2020-09-25 12:49 ` [PATCH 8/8] tests: dumping ct entries in opts format Mikhail Sennikovsky
2020-09-25 13:28 ` [PATCH 0/8] Fast bulk transfers of large sets of ct entries Florian Westphal
2020-09-26 18:19 ` Pablo Neira Ayuso
2020-09-29 11:20 ` [PATCH 0/2] conntrack: -L/-D both ipv4/6 if no family is given Mikhail Sennikovsky
2020-09-29 11:20 ` [PATCH 1/2] " Mikhail Sennikovsky
2020-09-29 11:20 ` [PATCH 2/2] tests: conntrack -L/-D ip family filtering Mikhail Sennikovsky
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20200925124919.9389-4-mikhail.sennikovskii@cloud.ionos.com \
--to=mikhail.sennikovskii@cloud.ionos.com \
--cc=netfilter-devel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).