summaryrefslogtreecommitdiff
path: root/src/conntrack.c
diff options
context:
space:
mode:
authorroot <root@build-vm.(none)>2010-06-14 15:51:25 -0700
committerroot <root@build-vm.(none)>2010-06-14 15:51:25 -0700
commitc4414d9a8b31bedfb7471cd2365aaf5ea5cf55d5 (patch)
tree86ee5befd8195f56dcd91f9fd4e06368bf507a11 /src/conntrack.c
downloadconntrack-tools-c4414d9a8b31bedfb7471cd2365aaf5ea5cf55d5.tar.gz
conntrack-tools-c4414d9a8b31bedfb7471cd2365aaf5ea5cf55d5.zip
debian conntrack 0.9.14-2
Diffstat (limited to 'src/conntrack.c')
-rw-r--r--src/conntrack.c1504
1 files changed, 1504 insertions, 0 deletions
diff --git a/src/conntrack.c b/src/conntrack.c
new file mode 100644
index 0000000..eec3868
--- /dev/null
+++ b/src/conntrack.c
@@ -0,0 +1,1504 @@
+/*
+ * (C) 2005-2008 by Pablo Neira Ayuso <pablo@netfilter.org>
+ *
+ * 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.
+ *
+ * Note:
+ * Yes, portions of this code has been stolen from iptables ;)
+ * Special thanks to the the Netfilter Core Team.
+ * Thanks to Javier de Miguel Rodriguez <jmiguel at talika.eii.us.es>
+ * for introducing me to advanced firewalling stuff.
+ *
+ * --pablo 13/04/2005
+ *
+ * 2005-04-16 Harald Welte <laforge@netfilter.org>:
+ * Add support for conntrack accounting and conntrack mark
+ * 2005-06-23 Harald Welte <laforge@netfilter.org>:
+ * Add support for expect creation
+ * 2005-09-24 Harald Welte <laforge@netfilter.org>:
+ * Remove remaints of "-A"
+ * 2007-04-22 Pablo Neira Ayuso <pablo@netfilter.org>:
+ * Ported to the new libnetfilter_conntrack API
+ * 2008-04-13 Pablo Neira Ayuso <pablo@netfilter.org>:
+ * Way more flexible update and delete operations
+ */
+
+#include "conntrack.h"
+
+#include <stdio.h>
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <time.h>
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#include <signal.h>
+#include <string.h>
+#include <netdb.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+
+static const char *optflags[NUMBER_OF_OPT] = {
+"src","dst","reply-src","reply-dst","protonum","timeout","status","zero",
+"event-mask","tuple-src","tuple-dst","mask-src","mask-dst","nat-range","mark",
+"id","family","src-nat","dst-nat","output","secmark","buffersize"};
+
+static struct option original_opts[] = {
+ {"dump", 2, 0, 'L'},
+ {"create", 2, 0, 'I'},
+ {"delete", 2, 0, 'D'},
+ {"update", 2, 0, 'U'},
+ {"get", 2, 0, 'G'},
+ {"flush", 2, 0, 'F'},
+ {"event", 2, 0, 'E'},
+ {"counter", 2, 0, 'C'},
+ {"stats", 0, 0, 'S'},
+ {"version", 0, 0, 'V'},
+ {"help", 0, 0, 'h'},
+ {"orig-src", 1, 0, 's'},
+ {"src", 1, 0, 's'},
+ {"orig-dst", 1, 0, 'd'},
+ {"dst", 1, 0, 'd'},
+ {"reply-src", 1, 0, 'r'},
+ {"reply-dst", 1, 0, 'q'},
+ {"protonum", 1, 0, 'p'},
+ {"timeout", 1, 0, 't'},
+ {"status", 1, 0, 'u'},
+ {"zero", 0, 0, 'z'},
+ {"event-mask", 1, 0, 'e'},
+ {"tuple-src", 1, 0, '['},
+ {"tuple-dst", 1, 0, ']'},
+ {"mask-src", 1, 0, '{'},
+ {"mask-dst", 1, 0, '}'},
+ {"nat-range", 1, 0, 'a'}, /* deprecated */
+ {"mark", 1, 0, 'm'},
+ {"secmark", 1, 0, 'c'},
+ {"id", 2, 0, 'i'}, /* deprecated */
+ {"family", 1, 0, 'f'},
+ {"src-nat", 2, 0, 'n'},
+ {"dst-nat", 2, 0, 'g'},
+ {"output", 1, 0, 'o'},
+ {"buffer-size", 1, 0, 'b'},
+ {0, 0, 0, 0}
+};
+
+#define OPTION_OFFSET 256
+
+static struct nfct_handle *cth, *ith;
+static struct option *opts = original_opts;
+static unsigned int global_option_offset = 0;
+
+/* Table of legal combinations of commands and options. If any of the
+ * given commands make an option legal, that option is legal (applies to
+ * CMD_LIST and CMD_ZERO only).
+ * Key:
+ * 0 illegal
+ * 1 compulsory
+ * 2 optional
+ * 3 undecided, see flag combination checkings in generic_opt_check()
+ */
+
+static char commands_v_options[NUMBER_OF_CMD][NUMBER_OF_OPT] =
+/* Well, it's better than "Re: Linux vs FreeBSD" */
+{
+ /* s d r q p t u z e [ ] { } a m i f n g o c b*/
+/*CT_LIST*/ {2,2,2,2,2,0,2,2,0,0,0,0,0,0,2,0,2,2,2,2,2,0},
+/*CT_CREATE*/ {3,3,3,3,1,1,2,0,0,0,0,0,0,2,2,0,0,2,2,0,0,0},
+/*CT_UPDATE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0},
+/*CT_DELETE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0},
+/*CT_GET*/ {3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2,0,0},
+/*CT_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*CT_EVENT*/ {2,2,2,2,2,0,0,0,2,0,0,0,0,0,2,0,0,2,2,2,2,2},
+/*VERSION*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*HELP*/ {0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0},
+/*EXP_CREATE*/{1,1,2,2,1,1,2,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0},
+/*EXP_DELETE*/{1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_GET*/ {1,1,2,2,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_FLUSH*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_EVENT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*CT_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*EXP_COUNT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+/*X_STATS*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
+};
+
+#define ADDR_VALID_FLAGS_MAX 2
+static unsigned int addr_valid_flags[ADDR_VALID_FLAGS_MAX] = {
+ CT_OPT_ORIG_SRC | CT_OPT_ORIG_DST,
+ CT_OPT_REPL_SRC | CT_OPT_REPL_DST,
+};
+
+static LIST_HEAD(proto_list);
+
+static unsigned int options;
+
+#define CT_COMPARISON (CT_OPT_PROTO | CT_OPT_ORIG | CT_OPT_REPL | CT_OPT_MARK |\
+ CT_OPT_SECMARK | CT_OPT_STATUS | CT_OPT_ID)
+
+void register_proto(struct ctproto_handler *h)
+{
+ if (strcmp(h->version, VERSION) != 0) {
+ fprintf(stderr, "plugin `%s': version %s (I'm %s)\n",
+ h->name, h->version, VERSION);
+ exit(1);
+ }
+ list_add(&h->head, &proto_list);
+}
+
+extern struct ctproto_handler ct_proto_unknown;
+
+static struct ctproto_handler *findproto(char *name, int *pnum)
+{
+ struct ctproto_handler *cur;
+ struct protoent *pent;
+ int protonum;
+
+ /* is it in the list of supported protocol? */
+ list_for_each_entry(cur, &proto_list, head) {
+ if (strcmp(cur->name, name) == 0) {
+ *pnum = cur->protonum;
+ return cur;
+ }
+ }
+ /* using the protocol name for an unsupported protocol? */
+ if ((pent = getprotobyname(name))) {
+ *pnum = pent->p_proto;
+ return &ct_proto_unknown;
+ }
+ /* using a protocol number? */
+ protonum = atoi(name);
+ if (protonum > 0 && protonum <= IPPROTO_MAX) {
+ /* try lookup by number, perhaps this protocol is supported */
+ list_for_each_entry(cur, &proto_list, head) {
+ if (cur->protonum == protonum) {
+ *pnum = protonum;
+ return cur;
+ }
+ }
+ *pnum = protonum;
+ return &ct_proto_unknown;
+ }
+
+ return NULL;
+}
+
+static void
+extension_help(struct ctproto_handler *h, int protonum)
+{
+ const char *name;
+
+ if (h == &ct_proto_unknown) {
+ struct protoent *pent;
+
+ pent = getprotobynumber(protonum);
+ if (!pent)
+ name = h->name;
+ else
+ name = pent->p_name;
+ } else {
+ name = h->name;
+ }
+
+ fprintf(stdout, "Proto `%s' help:\n", name);
+ h->help();
+}
+
+static void __attribute__((noreturn))
+exit_tryhelp(int status)
+{
+ fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n",
+ PROGNAME, PROGNAME);
+ exit(status);
+}
+
+static void free_options(void)
+{
+ if (opts != original_opts) {
+ free(opts);
+ opts = original_opts;
+ global_option_offset = 0;
+ }
+}
+
+void __attribute__((noreturn))
+exit_error(enum exittype status, const char *msg, ...)
+{
+ va_list args;
+
+ free_options();
+ va_start(args, msg);
+ fprintf(stderr,"%s v%s (conntrack-tools): ", PROGNAME, VERSION);
+ vfprintf(stderr, msg, args);
+ fprintf(stderr, "\n");
+ va_end(args);
+ if (status == PARAMETER_PROBLEM)
+ exit_tryhelp(status);
+ exit(status);
+}
+
+static int bit2cmd(int command)
+{
+ int i;
+
+ for (i = 0; i < NUMBER_OF_CMD; i++)
+ if (command & (1<<i))
+ break;
+
+ return i;
+}
+
+int generic_opt_check(int local_options, int num_opts,
+ char *optset, const char *optflg[],
+ unsigned int *coupled_flags, int coupled_flags_size,
+ int *partial)
+{
+ int i, matching = -1, special_case = 0;
+
+ for (i = 0; i < num_opts; i++) {
+ if (!(local_options & (1<<i))) {
+ if (optset[i] == 1)
+ exit_error(PARAMETER_PROBLEM,
+ "You need to supply the "
+ "`--%s' option for this "
+ "command", optflg[i]);
+ } else {
+ if (optset[i] == 0)
+ exit_error(PARAMETER_PROBLEM, "Illegal "
+ "option `--%s' with this "
+ "command", optflg[i]);
+ }
+ if (optset[i] == 3)
+ special_case = 1;
+ }
+
+ /* no weird flags combinations, leave */
+ if (!special_case || coupled_flags == NULL)
+ return 1;
+
+ *partial = -1;
+ for (i=0; i<coupled_flags_size; i++) {
+ /* we look for an exact matching to ensure this is correct */
+ if ((local_options & coupled_flags[i]) == coupled_flags[i]) {
+ matching = i;
+ break;
+ }
+ /* ... otherwise look for the first partial matching */
+ if ((local_options & coupled_flags[i]) && *partial < 0) {
+ *partial = i;
+ }
+ }
+
+ /* we found an exact matching, game over */
+ if (matching >= 0)
+ return 1;
+
+ /* report a partial matching to suggest something */
+ return 0;
+}
+
+static struct option *
+merge_options(struct option *oldopts, const struct option *newopts,
+ unsigned int *option_offset)
+{
+ unsigned int num_old, num_new, i;
+ struct option *merge;
+
+ for (num_old = 0; oldopts[num_old].name; num_old++);
+ for (num_new = 0; newopts[num_new].name; num_new++);
+
+ global_option_offset += OPTION_OFFSET;
+ *option_offset = global_option_offset;
+
+ merge = malloc(sizeof(struct option) * (num_new + num_old + 1));
+ if (merge == NULL)
+ return NULL;
+
+ memcpy(merge, oldopts, num_old * sizeof(struct option));
+ for (i = 0; i < num_new; i++) {
+ merge[num_old + i] = newopts[i];
+ merge[num_old + i].val += *option_offset;
+ }
+ memset(merge + num_old + num_new, 0, sizeof(struct option));
+
+ return merge;
+}
+
+/* From linux/errno.h */
+#define ENOTSUPP 524 /* Operation is not supported */
+
+/* Translates errno numbers into more human-readable form than strerror. */
+static const char *
+err2str(int err, enum action command)
+{
+ unsigned int i;
+ struct table_struct {
+ enum action act;
+ int err;
+ const char *message;
+ } table [] =
+ { { CT_LIST, ENOTSUPP, "function not implemented" },
+ { 0xFFFF, EINVAL, "invalid parameters" },
+ { CT_CREATE, EEXIST, "Such conntrack exists, try -U to update" },
+ { CT_CREATE|CT_GET|CT_DELETE, ENOENT,
+ "such conntrack doesn't exist" },
+ { CT_CREATE|CT_GET, ENOMEM, "not enough memory" },
+ { CT_GET, EAFNOSUPPORT, "protocol not supported" },
+ { CT_CREATE, ETIME, "conntrack has expired" },
+ { EXP_CREATE, ENOENT, "master conntrack not found" },
+ { EXP_CREATE, EINVAL, "invalid parameters" },
+ { ~0U, EPERM, "sorry, you must be root or get "
+ "CAP_NET_ADMIN capability to do this"}
+ };
+
+ for (i = 0; i < sizeof(table)/sizeof(struct table_struct); i++) {
+ if ((table[i].act & command) && table[i].err == err)
+ return table[i].message;
+ }
+
+ return strerror(err);
+}
+
+#define PARSE_STATUS 0
+#define PARSE_EVENT 1
+#define PARSE_OUTPUT 2
+#define PARSE_MAX 3
+
+static struct parse_parameter {
+ const char *parameter[6];
+ size_t size;
+ unsigned int value[6];
+} parse_array[PARSE_MAX] = {
+ { {"ASSURED", "SEEN_REPLY", "UNSET", "FIXED_TIMEOUT", "EXPECTED"}, 5,
+ { IPS_ASSURED, IPS_SEEN_REPLY, 0, IPS_FIXED_TIMEOUT, IPS_EXPECTED} },
+ { {"ALL", "NEW", "UPDATES", "DESTROY"}, 4,
+ {~0U, NF_NETLINK_CONNTRACK_NEW, NF_NETLINK_CONNTRACK_UPDATE,
+ NF_NETLINK_CONNTRACK_DESTROY} },
+ { {"xml", "extended", "timestamp", "id" }, 4,
+ { _O_XML, _O_EXT, _O_TMS, _O_ID },
+ },
+};
+
+static int
+do_parse_parameter(const char *str, size_t str_length, unsigned int *value,
+ int parse_type)
+{
+ size_t i;
+ int ret = 0;
+ struct parse_parameter *p = &parse_array[parse_type];
+
+ if (strncasecmp(str, "SRC_NAT", str_length) == 0) {
+ fprintf(stderr, "WARNING: ignoring SRC_NAT, "
+ "use --src-nat instead\n");
+ return 1;
+ }
+
+ if (strncasecmp(str, "DST_NAT", str_length) == 0) {
+ fprintf(stderr, "WARNING: ignoring DST_NAT, "
+ "use --dst-nat instead\n");
+ return 1;
+ }
+
+ for (i = 0; i < p->size; i++)
+ if (strncasecmp(str, p->parameter[i], str_length) == 0) {
+ *value |= p->value[i];
+ ret = 1;
+ break;
+ }
+
+ return ret;
+}
+
+static void
+parse_parameter(const char *arg, unsigned int *status, int parse_type)
+{
+ const char *comma;
+
+ while ((comma = strchr(arg, ',')) != NULL) {
+ if (comma == arg
+ || !do_parse_parameter(arg, comma-arg, status, parse_type))
+ exit_error(PARAMETER_PROBLEM,"Bad parameter `%s'", arg);
+ arg = comma+1;
+ }
+
+ if (strlen(arg) == 0
+ || !do_parse_parameter(arg, strlen(arg), status, parse_type))
+ exit_error(PARAMETER_PROBLEM, "Bad parameter `%s'", arg);
+}
+
+static void
+add_command(unsigned int *cmd, const int newcmd)
+{
+ if (*cmd)
+ exit_error(PARAMETER_PROBLEM, "Invalid commands combination");
+ *cmd |= newcmd;
+}
+
+static unsigned int
+check_type(int argc, char *argv[])
+{
+ char *table = NULL;
+
+ /* Nasty bug or feature in getopt_long ?
+ * It seems that it behaves badly with optional arguments.
+ * Fortunately, I just stole the fix from iptables ;) */
+ if (optarg)
+ return 0;
+ else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ table = argv[optind++];
+
+ if (!table)
+ return 0;
+
+ if (strncmp("expect", table, strlen(table)) == 0)
+ return 1;
+ else if (strncmp("conntrack", table, strlen(table)) == 0)
+ return 0;
+ else
+ exit_error(PARAMETER_PROBLEM, "unknown type `%s'", table);
+
+ return 0;
+}
+
+static void set_family(int *family, int new)
+{
+ if (*family == AF_UNSPEC)
+ *family = new;
+ else if (*family != new)
+ exit_error(PARAMETER_PROBLEM, "mismatched address family");
+}
+
+struct addr_parse {
+ struct in_addr addr;
+ struct in6_addr addr6;
+ unsigned int family;
+};
+
+static int
+parse_inetaddr(const char *cp, struct addr_parse *parse)
+{
+ if (inet_aton(cp, &parse->addr))
+ return AF_INET;
+#ifdef HAVE_INET_PTON_IPV6
+ else if (inet_pton(AF_INET6, cp, &parse->addr6) > 0)
+ return AF_INET6;
+#endif
+
+ exit_error(PARAMETER_PROBLEM, "Invalid IP address `%s'", cp);
+}
+
+union ct_address {
+ uint32_t v4;
+ uint32_t v6[4];
+};
+
+static int
+parse_addr(const char *cp, union ct_address *address)
+{
+ struct addr_parse parse;
+ int ret;
+
+ if ((ret = parse_inetaddr(cp, &parse)) == AF_INET)
+ address->v4 = parse.addr.s_addr;
+ else if (ret == AF_INET6)
+ memcpy(address->v6, &parse.addr6, sizeof(parse.addr6));
+
+ return ret;
+}
+
+/* Shamelessly stolen from libipt_DNAT ;). Ranges expected in network order. */
+static void
+nat_parse(char *arg, int portok, struct nf_conntrack *obj, int type)
+{
+ char *colon, *error;
+ union ct_address parse;
+
+ colon = strchr(arg, ':');
+
+ if (colon) {
+ uint16_t port;
+
+ if (!portok)
+ exit_error(PARAMETER_PROBLEM,
+ "Need TCP or UDP with port specification");
+
+ port = (uint16_t)atoi(colon+1);
+ if (port == 0)
+ exit_error(PARAMETER_PROBLEM,
+ "Port `%s' not valid", colon+1);
+
+ error = strchr(colon+1, ':');
+ if (error)
+ exit_error(PARAMETER_PROBLEM,
+ "Invalid port:port syntax");
+
+ if (type == CT_OPT_SRC_NAT)
+ nfct_set_attr_u16(obj, ATTR_SNAT_PORT, port);
+ else if (type == CT_OPT_DST_NAT)
+ nfct_set_attr_u16(obj, ATTR_DNAT_PORT, port);
+ }
+
+ if (parse_addr(arg, &parse) != AF_INET)
+ return;
+
+ if (type == CT_OPT_SRC_NAT)
+ nfct_set_attr_u32(obj, ATTR_SNAT_IPV4, parse.v4);
+ else if (type == CT_OPT_DST_NAT)
+ nfct_set_attr_u32(obj, ATTR_DNAT_IPV4, parse.v4);
+}
+
+static const char usage_commands[] =
+ "Commands:\n"
+ " -L [table] [options]\t\tList conntrack or expectation table\n"
+ " -G [table] parameters\t\tGet conntrack or expectation\n"
+ " -D [table] parameters\t\tDelete conntrack or expectation\n"
+ " -I [table] parameters\t\tCreate a conntrack or expectation\n"
+ " -U [table] parameters\t\tUpdate a conntrack\n"
+ " -E [table] [options]\t\tShow events\n"
+ " -F [table]\t\t\tFlush table\n"
+ " -C [table]\t\t\tShow counter\n"
+ " -S\t\t\t\tShow statistics\n";
+
+static const char usage_tables[] =
+ "Tables: conntrack, expect\n";
+
+static const char usage_conntrack_parameters[] =
+ "Conntrack parameters and options:\n"
+ " -n, --src-nat ip\t\t\tsource NAT ip\n"
+ " -g, --dst-nat ip\t\t\tdestination NAT ip\n"
+ " -m, --mark mark\t\t\tSet mark\n"
+ " -c, --secmark secmark\t\t\tSet selinux secmark\n"
+ " -e, --event-mask eventmask\t\tEvent mask, eg. NEW,DESTROY\n"
+ " -z, --zero \t\t\t\tZero counters while listing\n"
+ " -o, --output type[,...]\t\tOutput format, eg. xml\n";
+
+static const char usage_expectation_parameters[] =
+ "Expectation parameters and options:\n"
+ " --tuple-src ip\tSource address in expect tuple\n"
+ " --tuple-dst ip\tDestination address in expect tuple\n"
+ " --mask-src ip\t\tSource mask address\n"
+ " --mask-dst ip\t\tDestination mask address\n";
+
+static const char usage_parameters[] =
+ "Common parameters and options:\n"
+ " -s, --orig-src ip\t\tSource address from original direction\n"
+ " -d, --orig-dst ip\t\tDestination address from original direction\n"
+ " -r, --reply-src ip\t\tSource addres from reply direction\n"
+ " -q, --reply-dst ip\t\tDestination address from reply direction\n"
+ " -p, --protonum proto\t\tLayer 4 Protocol, eg. 'tcp'\n"
+ " -f, --family proto\t\tLayer 3 Protocol, eg. 'ipv6'\n"
+ " -t, --timeout timeout\t\tSet timeout\n"
+ " -u, --status status\t\tSet status, eg. ASSURED\n"
+ " -b, --buffer-size\t\tNetlink socket buffer size\n"
+ ;
+
+
+static void
+usage(char *prog)
+{
+ fprintf(stdout, "Command line interface for the connection "
+ "tracking system. Version %s\n", VERSION);
+ fprintf(stdout, "Usage: %s [commands] [options]\n", prog);
+
+ fprintf(stdout, "\n%s", usage_commands);
+ fprintf(stdout, "\n%s", usage_tables);
+ fprintf(stdout, "\n%s", usage_conntrack_parameters);
+ fprintf(stdout, "\n%s", usage_expectation_parameters);
+ fprintf(stdout, "\n%s\n", usage_parameters);
+}
+
+static unsigned int output_mask;
+
+static int
+filter_nat(const struct nf_conntrack *obj, const struct nf_conntrack *ct)
+{
+ uint32_t ip;
+
+ if (options & CT_OPT_SRC_NAT) {
+ if (!nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT))
+ return 1;
+
+ if (nfct_attr_is_set(obj, ATTR_SNAT_IPV4)) {
+ ip = nfct_get_attr_u32(obj, ATTR_SNAT_IPV4);
+ if (ip != nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST))
+ return 1;
+ }
+ }
+ if (options & CT_OPT_DST_NAT) {
+ if (!nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT))
+ return 1;
+
+ if (nfct_attr_is_set(obj, ATTR_DNAT_IPV4)) {
+ ip = nfct_get_attr_u32(obj, ATTR_DNAT_IPV4);
+ if (ip != nfct_get_attr_u32(ct, ATTR_REPL_IPV4_SRC))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int counter;
+static int dump_xml_header_done = 1;
+
+static void __attribute__((noreturn))
+event_sighandler(int s)
+{
+ if (dump_xml_header_done == 0) {
+ printf("</conntrack>\n");
+ fflush(stdout);
+ }
+
+ fprintf(stderr, "%s v%s (conntrack-tools): ", PROGNAME, VERSION);
+ fprintf(stderr, "%d flow events have been shown.\n", counter);
+ nfct_close(cth);
+ exit(0);
+}
+
+static int event_cb(enum nf_conntrack_msg_type type,
+ struct nf_conntrack *ct,
+ void *data)
+{
+ char buf[1024];
+ struct nf_conntrack *obj = data;
+ unsigned int op_type = NFCT_O_DEFAULT;
+ unsigned int op_flags = 0;
+
+ if (filter_nat(obj, ct))
+ return NFCT_CB_CONTINUE;
+
+ if (options & CT_COMPARISON &&
+ !nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK))
+ return NFCT_CB_CONTINUE;
+
+ if (output_mask & _O_XML) {
+ op_type = NFCT_O_XML;
+ if (dump_xml_header_done) {
+ dump_xml_header_done = 0;
+ printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<conntrack>\n");
+ }
+ }
+ if (output_mask & _O_EXT)
+ op_flags = NFCT_OF_SHOW_LAYER3;
+ if (output_mask & _O_TMS) {
+ if (!(output_mask & _O_XML)) {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ printf("[%-8ld.%-6ld]\t", tv.tv_sec, tv.tv_usec);
+ } else
+ op_flags |= NFCT_OF_TIME;
+ }
+ if (output_mask & _O_ID)
+ op_flags |= NFCT_OF_ID;
+
+ nfct_snprintf(buf, sizeof(buf), ct, type, op_type, op_flags);
+
+ printf("%s\n", buf);
+ fflush(stdout);
+
+ counter++;
+
+ return NFCT_CB_CONTINUE;
+}
+
+static int dump_cb(enum nf_conntrack_msg_type type,
+ struct nf_conntrack *ct,
+ void *data)
+{
+ char buf[1024];
+ struct nf_conntrack *obj = data;
+ unsigned int op_type = NFCT_O_DEFAULT;
+ unsigned int op_flags = 0;
+
+ if (filter_nat(obj, ct))
+ return NFCT_CB_CONTINUE;
+
+ if (options & CT_COMPARISON &&
+ !nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK))
+ return NFCT_CB_CONTINUE;
+
+ if (output_mask & _O_XML) {
+ op_type = NFCT_O_XML;
+ if (dump_xml_header_done) {
+ dump_xml_header_done = 0;
+ printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ "<conntrack>\n");
+ }
+ }
+ if (output_mask & _O_EXT)
+ op_flags = NFCT_OF_SHOW_LAYER3;
+ if (output_mask & _O_ID)
+ op_flags |= NFCT_OF_ID;
+
+ nfct_snprintf(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags);
+ printf("%s\n", buf);
+
+ counter++;
+
+ return NFCT_CB_CONTINUE;
+}
+
+static int delete_cb(enum nf_conntrack_msg_type type,
+ struct nf_conntrack *ct,
+ void *data)
+{
+ int res;
+ char buf[1024];
+ struct nf_conntrack *obj = data;
+ unsigned int op_type = NFCT_O_DEFAULT;
+ unsigned int op_flags = 0;
+
+ if (filter_nat(obj, ct))
+ return NFCT_CB_CONTINUE;
+
+ if (options & CT_COMPARISON &&
+ !nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK))
+ return NFCT_CB_CONTINUE;
+
+ res = nfct_query(ith, NFCT_Q_DESTROY, ct);
+ if (res < 0)
+ exit_error(OTHER_PROBLEM,
+ "Operation failed: %s",
+ err2str(errno, CT_DELETE));
+
+ if (output_mask & _O_XML)
+ op_type = NFCT_O_XML;
+ if (output_mask & _O_EXT)
+ op_flags = NFCT_OF_SHOW_LAYER3;
+ if (output_mask & _O_ID)
+ op_flags |= NFCT_OF_ID;
+
+ nfct_snprintf(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags);
+ printf("%s\n", buf);
+
+ counter++;
+
+ return NFCT_CB_CONTINUE;
+}
+
+static int print_cb(enum nf_conntrack_msg_type type,
+ struct nf_conntrack *ct,
+ void *data)
+{
+ char buf[1024];
+ unsigned int op_type = NFCT_O_DEFAULT;
+ unsigned int op_flags = 0;
+
+ if (output_mask & _O_XML)
+ op_type = NFCT_O_XML;
+ if (output_mask & _O_EXT)
+ op_flags = NFCT_OF_SHOW_LAYER3;
+ if (output_mask & _O_ID)
+ op_flags |= NFCT_OF_ID;
+
+ nfct_snprintf(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags);
+ printf("%s\n", buf);
+
+ return NFCT_CB_CONTINUE;
+}
+
+static int update_cb(enum nf_conntrack_msg_type type,
+ struct nf_conntrack *ct,
+ void *data)
+{
+ int res;
+ struct nf_conntrack *obj = data;
+ char __tmp[nfct_maxsize()];
+ struct nf_conntrack *tmp = (struct nf_conntrack *) (void *)__tmp;
+
+ memset(tmp, 0, sizeof(__tmp));
+
+ if (filter_nat(obj, ct))
+ return NFCT_CB_CONTINUE;
+
+ if (nfct_attr_is_set(obj, ATTR_ID) && nfct_attr_is_set(ct, ATTR_ID) &&
+ nfct_get_attr_u32(obj, ATTR_ID) != nfct_get_attr_u32(ct, ATTR_ID))
+ return NFCT_CB_CONTINUE;
+
+ if (options & CT_OPT_TUPLE_ORIG && !nfct_cmp(obj, ct, NFCT_CMP_ORIG))
+ return NFCT_CB_CONTINUE;
+ if (options & CT_OPT_TUPLE_REPL && !nfct_cmp(obj, ct, NFCT_CMP_REPL))
+ return NFCT_CB_CONTINUE;
+
+ nfct_copy(tmp, ct, NFCT_CP_ORIG);
+ nfct_copy(tmp, obj, NFCT_CP_META);
+
+ res = nfct_query(ith, NFCT_Q_UPDATE, tmp);
+ if (res < 0)
+ exit_error(OTHER_PROBLEM,
+ "Operation failed: %s",
+ err2str(errno, CT_UPDATE));
+
+ nfct_callback_register(ith, NFCT_T_ALL, print_cb, NULL);
+
+ res = nfct_query(ith, NFCT_Q_GET, tmp);
+ if (res < 0) {
+ /* the entry has vanish in middle of the update */
+ if (errno == ENOENT) {
+ nfct_callback_unregister(ith);
+ return NFCT_CB_CONTINUE;
+ }
+
+ exit_error(OTHER_PROBLEM,
+ "Operation failed: %s",
+ err2str(errno, CT_UPDATE));
+ }
+
+ nfct_callback_unregister(ith);
+
+ counter++;
+
+ return NFCT_CB_CONTINUE;
+}
+
+static int dump_exp_cb(enum nf_conntrack_msg_type type,
+ struct nf_expect *exp,
+ void *data)
+{
+ char buf[1024];
+
+ nfexp_snprintf(buf,sizeof(buf), exp, NFCT_T_UNKNOWN, NFCT_O_DEFAULT, 0);
+ printf("%s\n", buf);
+ counter++;
+
+ return NFCT_CB_CONTINUE;
+}
+
+static int count_exp_cb(enum nf_conntrack_msg_type type,
+ struct nf_expect *exp,
+ void *data)
+{
+ counter++;
+ return NFCT_CB_CONTINUE;
+}
+
+#ifndef CT_STATS_PROC
+#define CT_STATS_PROC "/proc/net/stat/nf_conntrack"
+#endif
+
+/* As of 2.6.29, we have 16 entries, this is enough */
+#ifndef CT_STATS_ENTRIES_MAX
+#define CT_STATS_ENTRIES_MAX 64
+#endif
+
+/* maximum string length currently is 13 characters */
+#ifndef CT_STATS_STRING_MAX
+#define CT_STATS_STRING_MAX 64
+#endif
+
+static int display_proc_conntrack_stats(void)
+{
+ int ret = 0;
+ FILE *fd;
+ char buf[4096], *token, *nl;
+ char output[CT_STATS_ENTRIES_MAX][CT_STATS_STRING_MAX];
+ unsigned int value[CT_STATS_ENTRIES_MAX], i, max;
+
+ fd = fopen(CT_STATS_PROC, "r");
+ if (fd == NULL)
+ return -1;
+
+ if (fgets(buf, sizeof(buf), fd) == NULL) {
+ ret = -1;
+ goto out_err;
+ }
+
+ /* trim off trailing \n */
+ nl = strchr(buf, '\n');
+ if (nl != NULL) {
+ *nl = '\0';
+ nl = strchr(buf, '\n');
+ }
+ token = strtok(buf, " ");
+ for (i=0; token != NULL && i<CT_STATS_ENTRIES_MAX; i++) {
+ strncpy(output[i], token, CT_STATS_STRING_MAX);
+ output[i][CT_STATS_STRING_MAX-1]='\0';
+ token = strtok(NULL, " ");
+ }
+ max = i;
+
+ if (fgets(buf, sizeof(buf), fd) == NULL) {
+ ret = -1;
+ goto out_err;
+ }
+
+ nl = strchr(buf, '\n');
+ while (nl != NULL) {
+ *nl = '\0';
+ nl = strchr(buf, '\n');
+ }
+ token = strtok(buf, " ");
+ for (i=0; token != NULL && i<CT_STATS_ENTRIES_MAX; i++) {
+ value[i] = (unsigned int) strtol(token, (char**) NULL, 16);
+ token = strtok(NULL, " ");
+ }
+
+ for (i=0; i<max; i++)
+ printf("%-10s\t\t%-8u\n", output[i], value[i]);
+
+out_err:
+ fclose(fd);
+ return ret;
+}
+
+static struct ctproto_handler *h;
+
+static const int cmd2type[][2] = {
+ ['L'] = { CT_LIST, EXP_LIST },
+ ['I'] = { CT_CREATE, EXP_CREATE },
+ ['D'] = { CT_DELETE, EXP_DELETE },
+ ['G'] = { CT_GET, EXP_GET },
+ ['F'] = { CT_FLUSH, EXP_FLUSH },
+ ['E'] = { CT_EVENT, EXP_EVENT },
+ ['V'] = { CT_VERSION, CT_VERSION },
+ ['h'] = { CT_HELP, CT_HELP },
+ ['C'] = { CT_COUNT, EXP_COUNT },
+};
+
+static const int opt2type[] = {
+ ['s'] = CT_OPT_ORIG_SRC,
+ ['d'] = CT_OPT_ORIG_DST,
+ ['r'] = CT_OPT_REPL_SRC,
+ ['q'] = CT_OPT_REPL_DST,
+ ['{'] = CT_OPT_MASK_SRC,
+ ['}'] = CT_OPT_MASK_DST,
+ ['['] = CT_OPT_EXP_SRC,
+ [']'] = CT_OPT_EXP_DST,
+ ['n'] = CT_OPT_SRC_NAT,
+ ['g'] = CT_OPT_DST_NAT,
+ ['m'] = CT_OPT_MARK,
+ ['c'] = CT_OPT_SECMARK,
+ ['i'] = CT_OPT_ID,
+};
+
+static const int opt2family_attr[][2] = {
+ ['s'] = { ATTR_ORIG_IPV4_SRC, ATTR_ORIG_IPV6_SRC },
+ ['d'] = { ATTR_ORIG_IPV4_DST, ATTR_ORIG_IPV6_DST },
+ ['r'] = { ATTR_REPL_IPV4_SRC, ATTR_REPL_IPV6_SRC },
+ ['q'] = { ATTR_REPL_IPV4_DST, ATTR_REPL_IPV6_DST },
+ ['{'] = { ATTR_ORIG_IPV4_SRC, ATTR_ORIG_IPV6_SRC },
+ ['}'] = { ATTR_ORIG_IPV4_DST, ATTR_ORIG_IPV6_DST },
+ ['['] = { ATTR_ORIG_IPV4_SRC, ATTR_ORIG_IPV6_SRC },
+ [']'] = { ATTR_ORIG_IPV4_DST, ATTR_ORIG_IPV6_DST },
+};
+
+static const int opt2attr[] = {
+ ['s'] = ATTR_ORIG_L3PROTO,
+ ['d'] = ATTR_ORIG_L3PROTO,
+ ['r'] = ATTR_REPL_L3PROTO,
+ ['q'] = ATTR_REPL_L3PROTO,
+ ['m'] = ATTR_MARK,
+ ['c'] = ATTR_SECMARK,
+ ['i'] = ATTR_ID,
+};
+
+static char exit_msg[NUMBER_OF_CMD][64] = {
+ [CT_LIST_BIT] = "%d flow entries have been shown.\n",
+ [CT_CREATE_BIT] = "%d flow entries have been created.\n",
+ [CT_UPDATE_BIT] = "%d flow entries have been updated.\n",
+ [CT_DELETE_BIT] = "%d flow entries have been deleted.\n",
+ [CT_GET_BIT] = "%d flow entries have been shown.\n",
+ [CT_EVENT_BIT] = "%d flow events have been shown.\n",
+ [EXP_LIST_BIT] = "%d expectations have been shown.\n",
+ [EXP_DELETE_BIT] = "%d expectations have been shown.\n",
+};
+
+int main(int argc, char *argv[])
+{
+ int c, cmd;
+ unsigned int type = 0, event_mask = 0, l4flags = 0, status = 0;
+ int res = 0, partial;
+ size_t socketbuffersize = 0;
+ int family = AF_UNSPEC;
+ char __obj[nfct_maxsize()];
+ char __exptuple[nfct_maxsize()];
+ char __mask[nfct_maxsize()];
+ struct nf_conntrack *obj = (struct nf_conntrack *)(void*) __obj;
+ struct nf_conntrack *exptuple =
+ (struct nf_conntrack *)(void*) __exptuple;
+ struct nf_conntrack *mask = (struct nf_conntrack *)(void*) __mask;
+ char __exp[nfexp_maxsize()];
+ struct nf_expect *exp = (struct nf_expect *)(void*) __exp;
+ int l3protonum, protonum = 0;
+ union ct_address ad;
+ unsigned int command = 0;
+
+ memset(__obj, 0, sizeof(__obj));
+ memset(__exptuple, 0, sizeof(__exptuple));
+ memset(__mask, 0, sizeof(__mask));
+ memset(__exp, 0, sizeof(__exp));
+
+ register_tcp();
+ register_udp();
+ register_udplite();
+ register_sctp();
+ register_dccp();
+ register_icmp();
+ register_icmpv6();
+ register_gre();
+ register_unknown();
+
+ /* disable explicit missing arguments error output from getopt_long */
+ opterr = 0;
+
+ while ((c = getopt_long(argc, argv, "L::I::U::D::G::E::F::hVs:d:r:q:"
+ "p:t:u:e:a:z[:]:{:}:m:i:f:o:n::"
+ "g::c:b:C::S",
+ opts, NULL)) != -1) {
+ switch(c) {
+ /* commands */
+ case 'L':
+ case 'I':
+ case 'D':
+ case 'G':
+ case 'F':
+ case 'E':
+ case 'V':
+ case 'h':
+ case 'C':
+ type = check_type(argc, argv);
+ add_command(&command, cmd2type[c][type]);
+ break;
+ case 'U':
+ type = check_type(argc, argv);
+ if (type == 0)
+ add_command(&command, CT_UPDATE);
+ else
+ exit_error(PARAMETER_PROBLEM,
+ "Can't update expectations");
+ break;
+ case 'S':
+ add_command(&command, X_STATS);
+ break;
+ /* options */
+ case 's':
+ case 'd':
+ case 'r':
+ case 'q':
+ options |= opt2type[c];
+
+ l3protonum = parse_addr(optarg, &ad);
+ set_family(&family, l3protonum);
+ if (l3protonum == AF_INET) {
+ nfct_set_attr_u32(obj,
+ opt2family_attr[c][0],
+ ad.v4);
+ } else if (l3protonum == AF_INET6) {
+ nfct_set_attr(obj,
+ opt2family_attr[c][1],
+ &ad.v6);
+ }
+ nfct_set_attr_u8(obj, opt2attr[c], l3protonum);
+ break;
+ case '{':
+ case '}':
+ case '[':
+ case ']':
+ options |= opt2type[c];
+ l3protonum = parse_addr(optarg, &ad);
+ set_family(&family, l3protonum);
+ if (l3protonum == AF_INET) {
+ nfct_set_attr_u32(mask,
+ opt2family_attr[c][0],
+ ad.v4);
+ } else if (l3protonum == AF_INET6) {
+ nfct_set_attr(mask,
+ opt2family_attr[c][1],
+ &ad.v6);
+ }
+ nfct_set_attr_u8(mask, ATTR_ORIG_L3PROTO, l3protonum);
+ break;
+ case 'p':
+ options |= CT_OPT_PROTO;
+ h = findproto(optarg, &protonum);
+ if (!h)
+ exit_error(PARAMETER_PROBLEM,
+ "`%s' unsupported protocol",
+ optarg);
+
+ opts = merge_options(opts, h->opts, &h->option_offset);
+ if (opts == NULL)
+ exit_error(OTHER_PROBLEM, "out of memory");
+
+ nfct_set_attr_u8(obj, ATTR_L4PROTO, protonum);
+ break;
+ case 't':
+ options |= CT_OPT_TIMEOUT;
+ nfct_set_attr_u32(obj, ATTR_TIMEOUT, atol(optarg));
+ nfexp_set_attr_u32(exp, ATTR_EXP_TIMEOUT, atol(optarg));
+ break;
+ case 'u':
+ options |= CT_OPT_STATUS;
+ parse_parameter(optarg, &status, PARSE_STATUS);
+ nfct_set_attr_u32(obj, ATTR_STATUS, status);
+ break;
+ case 'e':
+ options |= CT_OPT_EVENT_MASK;
+ parse_parameter(optarg, &event_mask, PARSE_EVENT);
+ break;
+ case 'o':
+ options |= CT_OPT_OUTPUT;
+ parse_parameter(optarg, &output_mask, PARSE_OUTPUT);
+ break;
+ case 'z':
+ options |= CT_OPT_ZERO;
+ break;
+ case 'n':
+ case 'g': {
+ char *tmp = NULL;
+
+ options |= opt2type[c];
+
+ if (optarg)
+ continue;
+ else if (optind < argc && argv[optind][0] != '-'
+ && argv[optind][0] != '!')
+ tmp = argv[optind++];
+
+ if (tmp == NULL)
+ continue;
+
+ set_family(&family, AF_INET);
+ nat_parse(tmp, 1, obj, opt2type[c]);
+ break;
+ }
+ case 'i':
+ case 'm':
+ case 'c':
+ options |= opt2type[c];
+ nfct_set_attr_u32(obj,
+ opt2attr[c],
+ strtoul(optarg, NULL, 0));
+ break;
+ case 'a':
+ fprintf(stderr, "WARNING: ignoring -%c, "
+ "deprecated option.\n", c);
+ break;
+ case 'f':
+ options |= CT_OPT_FAMILY;
+ if (strncmp(optarg, "ipv4", strlen("ipv4")) == 0)
+ set_family(&family, AF_INET);
+ else if (strncmp(optarg, "ipv6", strlen("ipv6")) == 0)
+ set_family(&family, AF_INET6);
+ else
+ exit_error(PARAMETER_PROBLEM,
+ "`%s' unsupported protocol",
+ optarg);
+ break;
+ case 'b':
+ socketbuffersize = atol(optarg);
+ options |= CT_OPT_BUFFERSIZE;
+ break;
+ case '?':
+ if (optopt)
+ exit_error(PARAMETER_PROBLEM,
+ "option `%s' requires an "
+ "argument", argv[optind-1]);
+ else
+ exit_error(PARAMETER_PROBLEM,
+ "unknown option `%s'",
+ argv[optind-1]);
+ break;
+ default:
+ if (h && h->parse_opts
+ &&!h->parse_opts(c - h->option_offset, obj,
+ exptuple, mask, &l4flags))
+ exit_error(PARAMETER_PROBLEM, "parse error");
+ break;
+ }
+ }
+
+ /* default family */
+ if (family == AF_UNSPEC)
+ family = AF_INET;
+
+ cmd = bit2cmd(command);
+ res = generic_opt_check(options, NUMBER_OF_OPT,
+ commands_v_options[cmd], optflags,
+ addr_valid_flags, ADDR_VALID_FLAGS_MAX,
+ &partial);
+ if (!res) {
+ switch(partial) {
+ case -1:
+ case 0:
+ exit_error(PARAMETER_PROBLEM, "you have to specify "
+ "`--src' and `--dst'");
+ break;
+ case 1:
+ exit_error(PARAMETER_PROBLEM, "you have to specify "
+ "`--reply-src' and "
+ "`--reply-dst'");
+ break;
+ }
+ }
+ if (!(command & CT_HELP) && h && h->final_check)
+ h->final_check(l4flags, cmd, obj);
+
+ switch(command) {
+
+ case CT_LIST:
+ cth = nfct_open(CONNTRACK, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+
+ if (options & CT_COMPARISON &&
+ options & CT_OPT_ZERO)
+ exit_error(PARAMETER_PROBLEM, "Can't use -z with "
+ "filtering parameters");
+
+ nfct_callback_register(cth, NFCT_T_ALL, dump_cb, obj);
+
+ if (options & CT_OPT_ZERO)
+ res = nfct_query(cth, NFCT_Q_DUMP_RESET, &family);
+ else
+ res = nfct_query(cth, NFCT_Q_DUMP, &family);
+
+ if (dump_xml_header_done == 0) {
+ printf("</conntrack>\n");
+ fflush(stdout);
+ }
+
+ nfct_close(cth);
+ break;
+
+ case EXP_LIST:
+ cth = nfct_open(EXPECT, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+
+ nfexp_callback_register(cth, NFCT_T_ALL, dump_exp_cb, NULL);
+ res = nfexp_query(cth, NFCT_Q_DUMP, &family);
+ nfct_close(cth);
+ break;
+
+ case CT_CREATE:
+ if ((options & CT_OPT_ORIG) && !(options & CT_OPT_REPL))
+ nfct_setobjopt(obj, NFCT_SOPT_SETUP_REPLY);
+ else if (!(options & CT_OPT_ORIG) && (options & CT_OPT_REPL))
+ nfct_setobjopt(obj, NFCT_SOPT_SETUP_ORIGINAL);
+
+ cth = nfct_open(CONNTRACK, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+
+ res = nfct_query(cth, NFCT_Q_CREATE, obj);
+ if (res != -1)
+ counter++;
+ nfct_close(cth);
+ break;
+
+ case EXP_CREATE:
+ nfexp_set_attr(exp, ATTR_EXP_MASTER, obj);
+ nfexp_set_attr(exp, ATTR_EXP_EXPECTED, exptuple);
+ nfexp_set_attr(exp, ATTR_EXP_MASK, mask);
+
+ cth = nfct_open(EXPECT, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+
+ res = nfexp_query(cth, NFCT_Q_CREATE, exp);
+ nfct_close(cth);
+ break;
+
+ case CT_UPDATE:
+ cth = nfct_open(CONNTRACK, 0);
+ /* internal handler for delete_cb, otherwise we hit EILSEQ */
+ ith = nfct_open(CONNTRACK, 0);
+ if (!cth || !ith)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+
+ nfct_callback_register(cth, NFCT_T_ALL, update_cb, obj);
+
+ 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 || !ith)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+
+ nfct_callback_register(cth, NFCT_T_ALL, delete_cb, obj);
+
+ res = nfct_query(cth, NFCT_Q_DUMP, &family);
+ nfct_close(ith);
+ nfct_close(cth);
+ break;
+
+ case EXP_DELETE:
+ nfexp_set_attr(exp, ATTR_EXP_EXPECTED, obj);
+
+ cth = nfct_open(EXPECT, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+
+ res = nfexp_query(cth, NFCT_Q_DESTROY, exp);
+ nfct_close(cth);
+ break;
+
+ case CT_GET:
+ cth = nfct_open(CONNTRACK, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+
+ nfct_callback_register(cth, NFCT_T_ALL, dump_cb, obj);
+ res = nfct_query(cth, NFCT_Q_GET, obj);
+ nfct_close(cth);
+ break;
+
+ case EXP_GET:
+ nfexp_set_attr(exp, ATTR_EXP_MASTER, obj);
+
+ cth = nfct_open(EXPECT, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+
+ nfexp_callback_register(cth, NFCT_T_ALL, dump_exp_cb, NULL);
+ res = nfexp_query(cth, NFCT_Q_GET, exp);
+ nfct_close(cth);
+ break;
+
+ case CT_FLUSH:
+ cth = nfct_open(CONNTRACK, 0);
+ 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");
+ break;
+
+ case EXP_FLUSH:
+ cth = nfct_open(EXPECT, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+ res = nfexp_query(cth, NFCT_Q_FLUSH, &family);
+ nfct_close(cth);
+ break;
+
+ case CT_EVENT:
+ if (options & CT_OPT_EVENT_MASK)
+ cth = nfct_open(CONNTRACK,
+ event_mask & NFCT_ALL_CT_GROUPS);
+ else
+ cth = nfct_open(CONNTRACK, NFCT_ALL_CT_GROUPS);
+
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+
+ if (options & CT_OPT_BUFFERSIZE) {
+ size_t ret;
+ ret = nfnl_rcvbufsiz(nfct_nfnlh(cth), socketbuffersize);
+ fprintf(stderr, "NOTICE: Netlink socket buffer size "
+ "has been set to %zu bytes.\n", ret);
+ }
+ signal(SIGINT, event_sighandler);
+ signal(SIGTERM, event_sighandler);
+ nfct_callback_register(cth, NFCT_T_ALL, event_cb, obj);
+ res = nfct_catch(cth);
+ if (res == -1) {
+ if (errno == ENOBUFS) {
+ fprintf(stderr,
+ "WARNING: We have hit ENOBUFS! We "
+ "are losing events.\nThis message "
+ "means that the current netlink "
+ "socket buffer size is too small.\n"
+ "Please, check --buffer-size in "
+ "conntrack(8) manpage.\n");
+ }
+ }
+ nfct_close(cth);
+ break;
+
+ case EXP_EVENT:
+ cth = nfct_open(EXPECT, NF_NETLINK_CONNTRACK_EXP_NEW);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+ signal(SIGINT, event_sighandler);
+ signal(SIGTERM, event_sighandler);
+ nfexp_callback_register(cth, NFCT_T_ALL, dump_exp_cb, NULL);
+ res = nfexp_catch(cth);
+ nfct_close(cth);
+ break;
+ case CT_COUNT: {
+#define NF_CONNTRACK_COUNT_PROC "/proc/sys/net/netfilter/nf_conntrack_count"
+ FILE *fd;
+ int count;
+ fd = fopen(NF_CONNTRACK_COUNT_PROC, "r");
+ if (fd == NULL) {
+ exit_error(OTHER_PROBLEM, "Can't open %s",
+ NF_CONNTRACK_COUNT_PROC);
+ }
+ if (fscanf(fd, "%d", &count) != 1) {
+ exit_error(OTHER_PROBLEM, "Can't read %s",
+ NF_CONNTRACK_COUNT_PROC);
+ }
+ fclose(fd);
+ printf("%d\n", count);
+ break;
+ }
+ case EXP_COUNT:
+ cth = nfct_open(EXPECT, 0);
+ if (!cth)
+ exit_error(OTHER_PROBLEM, "Can't open handler");
+
+ nfexp_callback_register(cth, NFCT_T_ALL, count_exp_cb, NULL);
+ res = nfexp_query(cth, NFCT_Q_DUMP, &family);
+ nfct_close(cth);
+ printf("%d\n", counter);
+ break;
+ case X_STATS:
+ if (display_proc_conntrack_stats() < 0)
+ exit_error(OTHER_PROBLEM, "Can't open /proc interface");
+ break;
+ case CT_VERSION:
+ printf("%s v%s (conntrack-tools)\n", PROGNAME, VERSION);
+ break;
+ case CT_HELP:
+ usage(argv[0]);
+ if (options & CT_OPT_PROTO)
+ extension_help(h, protonum);
+ break;
+ default:
+ usage(argv[0]);
+ break;
+ }
+
+ if (res < 0)
+ exit_error(OTHER_PROBLEM, "Operation failed: %s",
+ err2str(errno, command));
+
+ free_options();
+
+ if (command && exit_msg[cmd][0]) {
+ fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION);
+ fprintf(stderr, exit_msg[cmd], counter);
+ if (counter == 0 && !(command & (CT_LIST | EXP_LIST)))
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}