@@ -5,6 +5,8 @@
#define UNIX_PATH_MAX 108
#endif
+#define DEFAULT_SOCKET_PATH "/var/run/conntrackd.ctl"
+
struct local_conf {
int reuseaddr;
char path[UNIX_PATH_MAX + 1];
@@ -26,11 +28,7 @@ void local_server_destroy(struct local_server *server);
int do_local_server_step(struct local_server *server, void *data,
int (*process)(int fd, void *data));
-/* local client */
-int local_client_create(struct local_conf *conf);
-void local_client_destroy(int fd);
-int do_local_client_step(int fd, void (*process)(char *buf));
-int do_local_request(int, struct local_conf *,void (*step)(char *buf));
-void local_step(char *buf);
+/* local client, conntrackdctl */
+int do_local_request(int request, const char *socket_path);
#endif
@@ -1,6 +1,7 @@
/conntrack
/conntrackd
/nfct
+/conntrackdctl
/read_config_lex.c
/read_config_yy.c
@@ -8,7 +8,7 @@ AM_YFLAGS = -d
CLEANFILES = read_config_yy.c read_config_lex.c
-sbin_PROGRAMS = conntrack conntrackd nfct
+sbin_PROGRAMS = conntrack conntrackd nfct conntrackdctl
conntrack_SOURCES = conntrack.c
conntrack_LDADD = ../extensions/libct_proto_tcp.la ../extensions/libct_proto_udp.la ../extensions/libct_proto_udplite.la ../extensions/libct_proto_icmp.la ../extensions/libct_proto_icmpv6.la ../extensions/libct_proto_sctp.la ../extensions/libct_proto_dccp.la ../extensions/libct_proto_gre.la ../extensions/libct_proto_unknown.la ${LIBNETFILTER_CONNTRACK_LIBS} ${LIBMNL_LIBS} ${LIBNFNETLINK_LIBS}
@@ -54,6 +54,9 @@ conntrackd_SOURCES = alarm.c main.c run.c hash.c queue.c queue_tx.c rbtree.c \
read_config_yy.y read_config_lex.l \
stack.c resync.c
+conntrackdctl_SOURCES = conntrackdctl.c local.c
+conntrackdctl_LDADD = ${libdl_LIBS}
+
if HAVE_CTHELPER
conntrackd_SOURCES += cthelper.c helpers.c utils.c expect.c
endif
new file mode 100644
@@ -0,0 +1,292 @@
+/*
+ * (C) 2006-2011 by Pablo Neira Ayuso <pablo@netfilter.org>
+ * (C) 2011 by Vyatta Inc. <http://www.vyatta.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "conntrackd.h"
+#include "log.h"
+#include "helper.h"
+#include "systemd.h"
+#include "resync.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/utsname.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <limits.h>
+
+static const char usage_client_commands[] =
+ "conntrackdctl options:\n"
+ " -u <path>, full path to the Unix domain socket used to connect to conntrackd. "
+ "Defaults to '/var/run/conntrackd.ctl'\n"
+ " -c [ct|expect], commit external cache to conntrack table\n"
+ " -f [internal|external], flush internal and external cache\n"
+ " -F [ct|expect], flush kernel conntrack table\n"
+ " -i [ct|expect], display content of the internal cache\n"
+ " -e [ct|expect], display the content of the external cache\n"
+ " -k, kill conntrack daemon\n"
+ " -s [network|cache|runtime|link|rsqueue|queue|ct|expect] dump statistics. "
+ "Defaults to 'ct'\n"
+ " -R [ct|expect], resync with kernel conntrack table\n"
+ " -n, request resync with other node (only FT-FW and NOTRACK modes)\n"
+ " -B, force a bulk send to other replica firewalls\n"
+ " -x, dump cache in XML format (requires -i or -e)\n"
+ " -t, reset the kernel timeout (see PurgeTimeout clause)\n"
+ " -v, show version information\n"
+ " -h, show this help message\n";
+
+static void
+show_usage(char *progname)
+{
+ fprintf(stdout, "conntrackdctl utility to interact with conntrackd, v%s\n", VERSION);
+ fprintf(stdout, "Usage: %s [options]\n\n", progname);
+ fprintf(stdout, "%s\n", usage_client_commands);
+}
+
+static void
+show_version(void)
+{
+ fprintf(stdout, "conntrackdctl utilty to interact with conntrackd, v%s. ", VERSION);
+ fprintf(stdout, "Licensed under GPLv2.\n");
+ fprintf(stdout, "(C) 2006-2009 Pablo Neira Ayuso ");
+ fprintf(stdout, "<pablo@netfilter.org>\n");
+}
+
+static int
+set_action_by_table(int i, int argc, char *argv[],
+ int ct_action, int exp_action, int dfl_action, int *action)
+{
+ if (i+1 < argc && argv[i+1][0] != '-') {
+ if (strncmp(argv[i+1], "ct", strlen(argv[i+1])) == 0) {
+ *action = ct_action;
+ i++;
+ } else if (strncmp(argv[i+1], "expect",
+ strlen(argv[i+1])) == 0) {
+ *action = exp_action;
+ i++;
+ }
+ } else
+ *action = dfl_action;
+
+ return i;
+}
+
+static void set_exec_env(void)
+{
+ umask(0177);
+ close(STDIN_FILENO);
+ if (chdir("/"))
+ fprintf(stderr, "WARNING: could not chdir to '/' (root): %s", strerror(errno));
+}
+
+int main(int argc, char *argv[])
+{
+ char socket_path[PATH_MAX + 1] = {};
+ int i, action = -1;
+ struct utsname u;
+ int version, major, minor;
+
+ /* Check kernel version: it must be >= 2.6.18 */
+ if (uname(&u) == -1) {
+ fprintf(stderr, "ERROR: can't retrieve kernel version via uname()");
+ exit(EXIT_FAILURE);
+ }
+ sscanf(u.release, "%d.%d.%d", &version, &major, &minor);
+ if (version < 2 && major < 6 && minor < 18) {
+ fprintf(stderr, "ERROR: Linux kernel version must be >= 2.6.18");
+ exit(EXIT_FAILURE);
+ }
+
+ for (i=1; i<argc; i++) {
+ switch(argv[i][1]) {
+ case 'c':
+ i = set_action_by_table(i, argc, argv,
+ CT_COMMIT, EXP_COMMIT,
+ ALL_COMMIT, &action);
+ break;
+ case 'i':
+ i = set_action_by_table(i, argc, argv,
+ CT_DUMP_INTERNAL,
+ EXP_DUMP_INTERNAL,
+ CT_DUMP_INTERNAL, &action);
+ break;
+ case 'e':
+ i = set_action_by_table(i, argc, argv,
+ CT_DUMP_EXTERNAL,
+ EXP_DUMP_EXTERNAL,
+ CT_DUMP_EXTERNAL, &action);
+ break;
+ case 'F':
+ i = set_action_by_table(i, argc, argv,
+ CT_FLUSH_MASTER,
+ EXP_FLUSH_MASTER,
+ ALL_FLUSH_MASTER, &action);
+ break;
+ case 'f':
+ if (i+1 < argc && argv[i+1][0] != '-') {
+ if (strncmp(argv[i+1], "internal",
+ strlen(argv[i+1])) == 0) {
+ action = CT_FLUSH_INT_CACHE;
+ i++;
+ } else if (strncmp(argv[i+1], "external",
+ strlen(argv[i+1])) == 0) {
+ action = CT_FLUSH_EXT_CACHE;
+ i++;
+ } else {
+ fprintf(stderr, "unknown parameter `%s' "
+ "for option `-f'", argv[i + 1]);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ /* default to general flushing */
+ action = ALL_FLUSH_CACHE;
+ }
+ break;
+ case 'R':
+ i = set_action_by_table(i, argc, argv,
+ CT_RESYNC_MASTER,
+ EXP_RESYNC_MASTER,
+ ALL_RESYNC_MASTER, &action);
+ break;
+ case 'B':
+ action = SEND_BULK;
+ break;
+ case 't':
+ action = RESET_TIMERS;
+ break;
+ case 'k':
+ action = KILL;
+ break;
+ case 's':
+ /* we've got a parameter */
+ if (i+1 < argc && argv[i+1][0] != '-') {
+ if (strncmp(argv[i+1], "network",
+ strlen(argv[i+1])) == 0) {
+ action = STATS_NETWORK;
+ i++;
+ } else if (strncmp(argv[i+1], "cache",
+ strlen(argv[i+1])) == 0) {
+ action = STATS_CACHE;
+ i++;
+ } else if (strncmp(argv[i+1], "runtime",
+ strlen(argv[i+1])) == 0) {
+ action = STATS_RUNTIME;
+ i++;
+ } else if (strncmp(argv[i+1], "multicast",
+ strlen(argv[i+1])) == 0) {
+ fprintf(stderr, "WARNING: use `link' "
+ "instead of `multicast' as "
+ "parameter.");
+ action = STATS_LINK;
+ i++;
+ } else if (strncmp(argv[i+1], "link",
+ strlen(argv[i+1])) == 0) {
+ action = STATS_LINK;
+ i++;
+ } else if (strncmp(argv[i+1], "rsqueue",
+ strlen(argv[i+1])) == 0) {
+ action = STATS_RSQUEUE;
+ i++;
+ } else if (strncmp(argv[i+1], "process",
+ strlen(argv[i+1])) == 0) {
+ action = STATS_PROCESS;
+ i++;
+ } else if (strncmp(argv[i+1], "queue",
+ strlen(argv[i+1])) == 0) {
+ action = STATS_QUEUE;
+ i++;
+ } else if (strncmp(argv[i+1], "ct",
+ strlen(argv[i+1])) == 0) {
+ action = STATS;
+ i++;
+ } else if (strncmp(argv[i+1], "expect",
+ strlen(argv[i+1])) == 0) {
+ action = EXP_STATS;
+ i++;
+ } else {
+ fprintf(stderr, "ERROR: unknown parameter `%s' "
+ "for option `-s'", argv[i + 1]);
+ exit(EXIT_FAILURE);
+ }
+ } else {
+ /* default to general statistics */
+ action = STATS;
+ }
+ break;
+ case 'n':
+ action = REQUEST_DUMP;
+ break;
+ case 'x':
+ if (action == CT_DUMP_INTERNAL)
+ action = CT_DUMP_INT_XML;
+ else if (action == CT_DUMP_EXTERNAL)
+ action = CT_DUMP_EXT_XML;
+ else if (action == EXP_DUMP_INTERNAL)
+ action = EXP_DUMP_INT_XML;
+ else if (action == EXP_DUMP_EXTERNAL)
+ action = EXP_DUMP_EXT_XML;
+ else {
+ show_usage(argv[0]);
+ fprintf(stderr, "ERROR: invalid parameters");
+ exit(EXIT_FAILURE);
+
+ }
+ break;
+ case 'u':
+ if (++i < argc) {
+ if (strlen(argv[i]) > PATH_MAX) {
+ fprintf(stderr, "WARNING: path to Unix socket too long. "
+ "Cutting it down to %d characters\n", PATH_MAX);
+ }
+ snprintf(socket_path, PATH_MAX, "%s", argv[i]);
+ break;
+ }
+ show_usage(argv[0]);
+ fprintf(stderr, "ERROR: missing Unix socket path\n");
+ exit(EXIT_FAILURE);
+ break;
+
+ case 'v':
+ show_version();
+ exit(EXIT_SUCCESS);
+ case 'h':
+ show_usage(argv[0]);
+ exit(EXIT_SUCCESS);
+ default:
+ show_usage(argv[0]);
+ fprintf(stderr, "ERROR: unknown option: %s\n", argv[i]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (!socket_path[0])
+ strcpy(socket_path, DEFAULT_SOCKET_PATH);
+
+ set_exec_env();
+
+ if (do_local_request(action, socket_path) == -1) {
+ fprintf(stderr, "ERROR: unable to contact conntrackd. Check if it is running, "
+ "permissions, and the Unix socket path is right (tried '%s')\n",
+ socket_path);
+ exit(EXIT_FAILURE);
+ }
+ exit(EXIT_SUCCESS);
+}
@@ -91,7 +91,7 @@ int do_local_server_step(struct local_server *server, void *data,
return 0;
}
-int local_client_create(struct local_conf *conf)
+static int local_client_create(const char *socket_path)
{
socklen_t len;
struct sockaddr_un local;
@@ -101,7 +101,7 @@ int local_client_create(struct local_conf *conf)
return -1;
local.sun_family = AF_UNIX;
- strcpy(local.sun_path, conf->path);
+ strcpy(local.sun_path, socket_path);
len = strlen(local.sun_path) + sizeof(local.sun_family);
if (connect(fd, (struct sockaddr *) &local, len) == -1) {
@@ -112,12 +112,12 @@ int local_client_create(struct local_conf *conf)
return fd;
}
-void local_client_destroy(int fd)
+static void local_client_destroy(int fd)
{
close(fd);
}
-int do_local_client_step(int fd, void (*process)(char *buf))
+static int do_local_client_step(int fd, void (*process)(char *buf))
{
char buf[1024];
@@ -132,18 +132,16 @@ int do_local_client_step(int fd, void (*process)(char *buf))
return 0;
}
-void local_step(char *buf)
+static void local_step(char *buf)
{
printf("%s", buf);
}
-int do_local_request(int request,
- struct local_conf *conf,
- void (*step)(char *buf))
+int do_local_request(int request, const char *socket_path)
{
int fd, ret;
- fd = local_client_create(conf);
+ fd = local_client_create(socket_path);
if (fd == -1)
return -1;
@@ -151,7 +149,7 @@ int do_local_request(int request,
if (ret == -1)
goto err1;
- do_local_client_step(fd, step);
+ do_local_client_step(fd, local_step);
local_client_destroy(fd);
@@ -43,29 +43,12 @@ static const char usage_general_commands[] =
" -v, display conntrackd version\n"
" -h, display this help information\n";
-static const char usage_client_commands[] =
- "Client mode commands:\n"
- " -c [ct|expect], commit external cache to conntrack table\n"
- " -f [internal|external], flush internal and external cache\n"
- " -F [ct|expect], flush kernel conntrack table\n"
- " -i [ct|expect], display content of the internal cache\n"
- " -e [ct|expect], display the content of the external cache\n"
- " -k, kill conntrack daemon\n"
- " -s [network|cache|runtime|link|rsqueue|queue|ct|expect], "
- "dump statistics\n"
- " -R [ct|expect], resync with kernel conntrack table\n"
- " -n, request resync with other node (only FT-FW and NOTRACK modes)\n"
- " -B, force a bulk send to other replica firewalls\n"
- " -x, dump cache in XML format (requires -i or -e)\n"
- " -t, reset the kernel timeout (see PurgeTimeout clause)\n";
-
static void
show_usage(char *progname)
{
fprintf(stdout, "Connection tracking userspace daemon v%s\n", VERSION);
fprintf(stdout, "Usage: %s [commands] [options]\n\n", progname);
fprintf(stdout, "%s\n", usage_general_commands);
- fprintf(stdout, "%s\n", usage_client_commands);
}
static void
@@ -77,39 +60,6 @@ show_version(void)
fprintf(stdout, "<pablo@netfilter.org>\n");
}
-static void
-set_operation_mode(int *current, int want, char *argv[])
-{
- if (*current == NOT_SET) {
- *current = want;
- return;
- }
- if (*current != want) {
- show_usage(argv[0]);
- dlog(LOG_ERR, "Invalid parameters");
- exit(EXIT_FAILURE);
- }
-}
-
-static int
-set_action_by_table(int i, int argc, char *argv[],
- int ct_action, int exp_action, int dfl_action, int *action)
-{
- if (i+1 < argc && argv[i+1][0] != '-') {
- if (strncmp(argv[i+1], "ct", strlen(argv[i+1])) == 0) {
- *action = ct_action;
- i++;
- } else if (strncmp(argv[i+1], "expect",
- strlen(argv[i+1])) == 0) {
- *action = exp_action;
- i++;
- }
- } else
- *action = dfl_action;
-
- return i;
-}
-
static void
do_chdir(const char *d)
{
@@ -121,8 +71,7 @@ do_chdir(const char *d)
int main(int argc, char *argv[])
{
char config_file[PATH_MAX + 1] = {};
- int ret, i, action = -1;
- int type = 0;
+ int ret, i;
struct utsname u;
int version, major, minor;
@@ -140,29 +89,8 @@ int main(int argc, char *argv[])
for (i=1; i<argc; i++) {
switch(argv[i][1]) {
case 'd':
- set_operation_mode(&type, DAEMON, argv);
CONFIG(running_mode) = DAEMON;
break;
- case 'c':
- set_operation_mode(&type, REQUEST, argv);
- i = set_action_by_table(i, argc, argv,
- CT_COMMIT, EXP_COMMIT,
- ALL_COMMIT, &action);
- break;
- case 'i':
- set_operation_mode(&type, REQUEST, argv);
- i = set_action_by_table(i, argc, argv,
- CT_DUMP_INTERNAL,
- EXP_DUMP_INTERNAL,
- CT_DUMP_INTERNAL, &action);
- break;
- case 'e':
- set_operation_mode(&type, REQUEST, argv);
- i = set_action_by_table(i, argc, argv,
- CT_DUMP_EXTERNAL,
- EXP_DUMP_EXTERNAL,
- CT_DUMP_EXTERNAL, &action);
- break;
case 'C':
if (++i < argc) {
if (strlen(argv[i]) > PATH_MAX) {
@@ -176,136 +104,26 @@ int main(int argc, char *argv[])
show_usage(argv[0]);
dlog(LOG_ERR, "Missing config filename");
break;
- case 'F':
- set_operation_mode(&type, REQUEST, argv);
- i = set_action_by_table(i, argc, argv,
- CT_FLUSH_MASTER,
- EXP_FLUSH_MASTER,
- ALL_FLUSH_MASTER, &action);
- break;
- case 'f':
- set_operation_mode(&type, REQUEST, argv);
- if (i+1 < argc && argv[i+1][0] != '-') {
- if (strncmp(argv[i+1], "internal",
- strlen(argv[i+1])) == 0) {
- action = CT_FLUSH_INT_CACHE;
- i++;
- } else if (strncmp(argv[i+1], "external",
- strlen(argv[i+1])) == 0) {
- action = CT_FLUSH_EXT_CACHE;
- i++;
- } else {
- dlog(LOG_ERR, "unknown parameter `%s' "
- "for option `-f'", argv[i + 1]);
- exit(EXIT_FAILURE);
- }
- } else {
- /* default to general flushing */
- action = ALL_FLUSH_CACHE;
- }
- break;
- case 'R':
- set_operation_mode(&type, REQUEST, argv);
- i = set_action_by_table(i, argc, argv,
- CT_RESYNC_MASTER,
- EXP_RESYNC_MASTER,
- ALL_RESYNC_MASTER, &action);
- break;
- case 'B':
- set_operation_mode(&type, REQUEST, argv);
- action = SEND_BULK;
- break;
- case 't':
- set_operation_mode(&type, REQUEST, argv);
- action = RESET_TIMERS;
- break;
- case 'k':
- set_operation_mode(&type, REQUEST, argv);
- action = KILL;
- break;
- case 's':
- set_operation_mode(&type, REQUEST, argv);
- /* we've got a parameter */
- if (i+1 < argc && argv[i+1][0] != '-') {
- if (strncmp(argv[i+1], "network",
- strlen(argv[i+1])) == 0) {
- action = STATS_NETWORK;
- i++;
- } else if (strncmp(argv[i+1], "cache",
- strlen(argv[i+1])) == 0) {
- action = STATS_CACHE;
- i++;
- } else if (strncmp(argv[i+1], "runtime",
- strlen(argv[i+1])) == 0) {
- action = STATS_RUNTIME;
- i++;
- } else if (strncmp(argv[i+1], "multicast",
- strlen(argv[i+1])) == 0) {
- dlog(LOG_WARNING, "use `link' "
- "instead of `multicast' as "
- "parameter.");
- action = STATS_LINK;
- i++;
- } else if (strncmp(argv[i+1], "link",
- strlen(argv[i+1])) == 0) {
- action = STATS_LINK;
- i++;
- } else if (strncmp(argv[i+1], "rsqueue",
- strlen(argv[i+1])) == 0) {
- action = STATS_RSQUEUE;
- i++;
- } else if (strncmp(argv[i+1], "process",
- strlen(argv[i+1])) == 0) {
- action = STATS_PROCESS;
- i++;
- } else if (strncmp(argv[i+1], "queue",
- strlen(argv[i+1])) == 0) {
- action = STATS_QUEUE;
- i++;
- } else if (strncmp(argv[i+1], "ct",
- strlen(argv[i+1])) == 0) {
- action = STATS;
- i++;
- } else if (strncmp(argv[i+1], "expect",
- strlen(argv[i+1])) == 0) {
- action = EXP_STATS;
- i++;
- } else {
- dlog(LOG_ERR, "unknown parameter `%s' "
- "for option `-s'", argv[i + 1]);
- exit(EXIT_FAILURE);
- }
- } else {
- /* default to general statistics */
- action = STATS;
- }
- break;
- case 'n':
- set_operation_mode(&type, REQUEST, argv);
- action = REQUEST_DUMP;
- break;
- case 'x':
- if (action == CT_DUMP_INTERNAL)
- action = CT_DUMP_INT_XML;
- else if (action == CT_DUMP_EXTERNAL)
- action = CT_DUMP_EXT_XML;
- else if (action == EXP_DUMP_INTERNAL)
- action = EXP_DUMP_INT_XML;
- else if (action == EXP_DUMP_EXTERNAL)
- action = EXP_DUMP_EXT_XML;
- else {
- show_usage(argv[0]);
- dlog(LOG_ERR, "Invalid parameters");
- exit(EXIT_FAILURE);
-
- }
- break;
case 'v':
show_version();
exit(EXIT_SUCCESS);
case 'h':
show_usage(argv[0]);
exit(EXIT_SUCCESS);
+ case 'c':
+ case 'f':
+ case 'F':
+ case 'i':
+ case 'e':
+ case 'k':
+ case 's':
+ case 'R':
+ case 'n':
+ case 'B':
+ case 'x':
+ case 't':
+ dlog(LOG_WARNING, "ignored option, use conntrackdctl instead");
+ break;
default:
show_usage(argv[0]);
dlog(LOG_ERR, "Unknown option: %s", argv[i]);
@@ -333,15 +151,6 @@ int main(int argc, char *argv[])
exit(EXIT_FAILURE);
}
- if (type == REQUEST) {
- if (do_local_request(action, &conf.local, local_step) == -1) {
- dlog(LOG_ERR, "can't connect: is conntrackd "
- "running? appropriate permissions?");
- exit(EXIT_FAILURE);
- }
- exit(EXIT_SUCCESS);
- }
-
/*
* Setting up logging
*/
@@ -377,7 +186,7 @@ int main(int argc, char *argv[])
sd_ct_watchdog_init();
/* Daemonize conntrackd */
- if (type == DAEMON) {
+ if (CONFIG(running_mode) == DAEMON) {
pid_t pid;
if ((pid = fork()) == -1) {
Separate conntrackd command line client functionalities into a different binary. This should help with several things: * avoid reading and parsing the config file, which can fail in some enviroments, for example if conntrackd is running inside a netns and the referenced interfaces/addresses doesn't exist in the namespace conntrackd client command is running from. * easily update conntrakdctl with more functionalities without polluting the daemon binary * easier code maintenance Signed-off-by: Arturo Borrero Gonzalez <arturo@netfilter.org> --- NOTE: this is just an idea, comments welcome. I have plans to extend this new binary with more options in the future. include/local.h | 10 +- src/.gitignore | 1 src/Makefile.am | 5 + src/conntrackdctl.c | 292 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/local.c | 18 +-- src/main.c | 223 +++------------------------------------ 6 files changed, 325 insertions(+), 224 deletions(-) create mode 100644 src/conntrackdctl.c