diff options
Diffstat (limited to 'src/conntrack.c')
-rw-r--r-- | src/conntrack.c | 1131 |
1 files changed, 1131 insertions, 0 deletions
diff --git a/src/conntrack.c b/src/conntrack.c new file mode 100644 index 0000000..30fbf69 --- /dev/null +++ b/src/conntrack.c @@ -0,0 +1,1131 @@ +/* + * (C) 2005 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" + * + */ +#include <stdio.h> +#include <sys/wait.h> +#include <stdlib.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/stat.h> +#include <sys/socket.h> +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif +#include <fcntl.h> +#include <dlfcn.h> +#include <signal.h> +#include <string.h> +#include "linux_list.h" +#include "conntrack.h" +#include <libnetfilter_conntrack/libnetfilter_conntrack.h> +#include <libnetfilter_conntrack/libnetfilter_conntrack_ipv4.h> +#include <libnetfilter_conntrack/libnetfilter_conntrack_ipv6.h> + +static const char cmdflags[NUMBER_OF_CMD] += {'L','I','U','D','G','F','E','V','h','L','I','D','G','F','E'}; + +static const char cmd_need_param[NUMBER_OF_CMD] += { 2, 0, 0, 0, 0, 2, 2, 2, 2, 2, 0, 0, 0, 2, 2 }; + +static const char optflags[NUMBER_OF_OPT] += {'s','d','r','q','p','t','u','z','e','[',']','{','}','a','m','i','f'}; + +static struct option original_opts[] = { + {"dump", 2, 0, 'L'}, + {"create", 1, 0, 'I'}, + {"delete", 1, 0, 'D'}, + {"update", 1, 0, 'U'}, + {"get", 1, 0, 'G'}, + {"flush", 1, 0, 'F'}, + {"event", 1, 0, 'E'}, + {"version", 0, 0, 'V'}, + {"help", 0, 0, 'h'}, + {"orig-src", 1, 0, 's'}, + {"orig-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'}, + {"mark", 1, 0, 'm'}, + {"id", 2, 0, 'i'}, + {"family", 1, 0, 'f'}, + {0, 0, 0, 0} +}; + +#define OPTION_OFFSET 256 + +static struct nfct_handle *cth; +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 + */ + +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 x y k l a m i f*/ +/*CT_LIST*/ {2,2,2,2,2,0,0,2,0,0,0,0,0,0,2,2,2}, +/*CT_CREATE*/ {2,2,2,2,1,1,1,0,0,0,0,0,0,2,2,0,0}, +/*CT_UPDATE*/ {2,2,2,2,1,2,2,0,0,0,0,0,0,0,2,2,0}, +/*CT_DELETE*/ {2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,2,0}, +/*CT_GET*/ {2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,2,0}, +/*CT_FLUSH*/ {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}, +/*VERSION*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}, +/*HELP*/ {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,2,2}, +/*EXP_CREATE*/{1,1,2,2,1,1,2,0,0,1,1,1,1,0,0,0,0}, +/*EXP_DELETE*/{1,1,2,2,1,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}, +/*EXP_FLUSH*/ {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}, +}; + +static char *lib_dir = CONNTRACK_LIB_DIR; + +static LIST_HEAD(proto_list); + +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); +} + +static struct ctproto_handler *findproto(char *name) +{ + struct list_head *i; + struct ctproto_handler *cur = NULL, *handler = NULL; + + if (!name) + return handler; + + lib_dir = getenv("CONNTRACK_LIB_DIR"); + if (!lib_dir) + lib_dir = CONNTRACK_LIB_DIR; + + list_for_each(i, &proto_list) { + cur = (struct ctproto_handler *) i; + if (strcmp(cur->name, name) == 0) { + handler = cur; + break; + } + } + + if (!handler) { + char path[sizeof("ct_proto_.so") + + strlen(name) + strlen(lib_dir)]; + sprintf(path, "%s/ct_proto_%s.so", lib_dir, name); + if (dlopen(path, RTLD_NOW)) + handler = findproto(name); + else + fprintf(stderr, "%s\n", dlerror()); + } + + return handler; +} + +enum exittype { + OTHER_PROBLEM = 1, + PARAMETER_PROBLEM, + VERSION_PROBLEM +}; + +void extension_help(struct ctproto_handler *h) +{ + fprintf(stdout, "\n"); + fprintf(stdout, "Proto `%s' help:\n", h->name); + h->help(); +} + +void +exit_tryhelp(int status) +{ + fprintf(stderr, "Try `%s -h' or '%s --help' for more information.\n", + PROGNAME, PROGNAME); + exit(status); +} + +static void +exit_error(enum exittype status, char *msg, ...) +{ + va_list args; + + /* On error paths, make sure that we don't leak the memory + * reserved during options merging */ + if (opts != original_opts) { + free(opts); + opts = original_opts; + global_option_offset = 0; + } + va_start(args, msg); + fprintf(stderr,"%s v%s: ", PROGNAME, VERSION); + vfprintf(stderr, msg, args); + va_end(args); + fprintf(stderr, "\n"); + if (status == PARAMETER_PROBLEM) + exit_tryhelp(status); + exit(status); +} + +static void +generic_cmd_check(int command, int options) +{ + int i; + + for (i = 0; i < NUMBER_OF_CMD; i++) { + if (!(command & (1<<i))) + continue; + + if (cmd_need_param[i] == 0 && !options) + exit_error(PARAMETER_PROBLEM, + "You need to supply parameters to `-%c'\n", + cmdflags[i]); + } +} + +static void +generic_opt_check(int command, int options) +{ + int i, j, legal = 0; + + /* Check that commands are valid with options. Complicated by the + * fact that if an option is legal with *any* command given, it is + * legal overall (ie. -z and -l). + */ + for (i = 0; i < NUMBER_OF_OPT; i++) { + legal = 0; /* -1 => illegal, 1 => legal, 0 => undecided. */ + + for (j = 0; j < NUMBER_OF_CMD; j++) { + if (!(command & (1<<j))) + continue; + + if (!(options & (1<<i))) { + if (commands_v_options[j][i] == 1) + exit_error(PARAMETER_PROBLEM, + "You need to supply the " + "`-%c' option for this " + "command\n", optflags[i]); + } else { + if (commands_v_options[j][i] != 0) + legal = 1; + else if (legal == 0) + legal = -1; + } + } + if (legal == -1) + exit_error(PARAMETER_PROBLEM, "Illegal option `-%c' " + "with this command\n", optflags[i]); + } +} + +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)); + 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. */ +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" }, + { ~0UL, -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_MAX 2 + +static struct parse_parameter { + char *parameter[6]; + size_t size; + unsigned int value[6]; +} parse_array[PARSE_MAX] = { + { {"ASSURED", "SEEN_REPLY", "UNSET", "SRC_NAT", "DST_NAT","FIXED_TIMEOUT"}, 6, + { IPS_ASSURED, IPS_SEEN_REPLY, 0, + IPS_SRC_NAT_DONE, IPS_DST_NAT_DONE, IPS_FIXED_TIMEOUT} }, + { {"ALL", "NEW", "UPDATES", "DESTROY"}, 4, + {~0U, NF_NETLINK_CONNTRACK_NEW, NF_NETLINK_CONNTRACK_UPDATE, + NF_NETLINK_CONNTRACK_DESTROY} }, +}; + +static int +do_parse_parameter(const char *str, size_t strlen, unsigned int *value, + int parse_type) +{ + int i, ret = 0; + struct parse_parameter *p = &parse_array[parse_type]; + + for (i = 0; i < p->size; i++) + if (strncasecmp(str, p->parameter[i], strlen) == 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, const int othercmds) +{ + if (*cmd & (~othercmds)) + exit_error(PARAMETER_PROBLEM, "Invalid commands combination\n"); + *cmd |= newcmd; +} + +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, 6) == 0) + return 1; + else if (strncmp("conntrack", table, 9) == 0) + return 0; + else + exit_error(PARAMETER_PROBLEM, "unknown type `%s'\n", 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\n"); +} + +struct addr_parse { + struct in_addr addr; + struct in6_addr addr6; + unsigned int family; +}; + +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); +} + +int parse_inetaddr(const char *cp, union nfct_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 nfct_nat *range) +{ + char *colon, *dash, *error; + struct addr_parse parse; + + memset(range, 0, sizeof(range)); + colon = strchr(arg, ':'); + + if (colon) { + int port; + + if (!portok) + exit_error(PARAMETER_PROBLEM, + "Need TCP or UDP with port specification"); + + port = atoi(colon+1); + if (port == 0 || port > 65535) + exit_error(PARAMETER_PROBLEM, + "Port `%s' not valid\n", colon+1); + + error = strchr(colon+1, ':'); + if (error) + exit_error(PARAMETER_PROBLEM, + "Invalid port:port syntax - use dash\n"); + + dash = strchr(colon, '-'); + if (!dash) { + range->l4min.tcp.port + = range->l4max.tcp.port + = htons(port); + } else { + int maxport; + + maxport = atoi(dash + 1); + if (maxport == 0 || maxport > 65535) + exit_error(PARAMETER_PROBLEM, + "Port `%s' not valid\n", dash+1); + if (maxport < port) + /* People are stupid. */ + exit_error(PARAMETER_PROBLEM, + "Port range `%s' funky\n", colon+1); + range->l4min.tcp.port = htons(port); + range->l4max.tcp.port = htons(maxport); + } + /* Starts with a colon? No IP info... */ + if (colon == arg) + return; + *colon = '\0'; + } + + dash = strchr(arg, '-'); + if (colon && dash && dash > colon) + dash = NULL; + + if (dash) + *dash = '\0'; + + if (__parse_inetaddr(arg, &parse) != AF_INET) + return; + + range->min_ip = parse.addr.s_addr; + if (dash) { + if (__parse_inetaddr(dash+1, &parse) != AF_INET) + return; + range->max_ip = parse.addr.s_addr; + } else + range->max_ip = parse.addr.s_addr; +} + +static void event_sighandler(int s) +{ + fprintf(stdout, "Now closing conntrack event dumping...\n"); + nfct_close(cth); + exit(0); +} + +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"; + +static const char usage_tables[] = + "Tables: conntrack, expect\n"; + +static const char usage_conntrack_parameters[] = + "Conntrack parameters and options:\n" + " -a, --nat-range min_ip[-max_ip]\tNAT ip range\n" + " -m, --mark mark\t\t\tSet mark\n" + " -e, --event-mask eventmask\t\tEvent mask, eg. NEW,DESTROY\n" + " -z, --zero \t\t\t\tZero counters while listing\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" + " -i, --id [id]\t\t\tShow or set conntrack ID\n" + ; + + +void usage(char *prog) { + fprintf(stdout, "Tool to manipulate conntrack and expectations. 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", usage_parameters); +} + +#define CT_COMPARISON (CT_OPT_PROTO | CT_OPT_ORIG | CT_OPT_REPL | CT_OPT_MARK) + +static struct nfct_tuple orig, reply, mask; +static struct nfct_tuple exptuple; +static struct ctproto_handler *h; +static union nfct_protoinfo proto; +static struct nfct_nat range; +static struct nfct_conntrack *ct; +static struct nfct_expect *exp; +static unsigned long timeout; +static unsigned int status; +static unsigned int mark; +static unsigned int id = NFCT_ANY_ID; +static struct nfct_conntrack_compare cmp; + +int main(int argc, char *argv[]) +{ + int c; + unsigned int command = 0, options = 0; + unsigned int type = 0, event_mask = 0; + unsigned int l3flags = 0, l4flags = 0, metaflags = 0; + int res = 0; + int family = AF_UNSPEC; + struct nfct_conntrack_compare *pcmp; + + 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:", + opts, NULL)) != -1) { + switch(c) { + case 'L': + type = check_type(argc, argv); + if (type == 0) + add_command(&command, CT_LIST, CT_NONE); + else if (type == 1) + add_command(&command, EXP_LIST, CT_NONE); + break; + case 'I': + type = check_type(argc, argv); + if (type == 0) + add_command(&command, CT_CREATE, CT_NONE); + else if (type == 1) + add_command(&command, EXP_CREATE, CT_NONE); + break; + case 'U': + type = check_type(argc, argv); + if (type == 0) + add_command(&command, CT_UPDATE, CT_NONE); + else + exit_error(PARAMETER_PROBLEM, "Can't update " + "expectations"); + break; + case 'D': + type = check_type(argc, argv); + if (type == 0) + add_command(&command, CT_DELETE, CT_NONE); + else if (type == 1) + add_command(&command, EXP_DELETE, CT_NONE); + break; + case 'G': + type = check_type(argc, argv); + if (type == 0) + add_command(&command, CT_GET, CT_NONE); + else if (type == 1) + add_command(&command, EXP_GET, CT_NONE); + break; + case 'F': + type = check_type(argc, argv); + if (type == 0) + add_command(&command, CT_FLUSH, CT_NONE); + else if (type == 1) + add_command(&command, EXP_FLUSH, CT_NONE); + break; + case 'E': + type = check_type(argc, argv); + if (type == 0) + add_command(&command, CT_EVENT, CT_NONE); + else if (type == 1) + add_command(&command, EXP_EVENT, CT_NONE); + break; + case 'V': + add_command(&command, CT_VERSION, CT_NONE); + break; + case 'h': + add_command(&command, CT_HELP, CT_NONE); + break; + case 's': + options |= CT_OPT_ORIG_SRC; + if (optarg) { + orig.l3protonum = + parse_inetaddr(optarg, &orig.src); + set_family(&family, orig.l3protonum); + if (orig.l3protonum == AF_INET) + l3flags |= IPV4_ORIG_SRC; + else if (orig.l3protonum == AF_INET6) + l3flags |= IPV6_ORIG_SRC; + } + break; + case 'd': + options |= CT_OPT_ORIG_DST; + if (optarg) { + orig.l3protonum = + parse_inetaddr(optarg, &orig.dst); + set_family(&family, orig.l3protonum); + if (orig.l3protonum == AF_INET) + l3flags |= IPV4_ORIG_DST; + else if (orig.l3protonum == AF_INET6) + l3flags |= IPV6_ORIG_DST; + } + break; + case 'r': + options |= CT_OPT_REPL_SRC; + if (optarg) { + reply.l3protonum = + parse_inetaddr(optarg, &reply.src); + set_family(&family, reply.l3protonum); + if (orig.l3protonum == AF_INET) + l3flags |= IPV4_REPL_SRC; + else if (orig.l3protonum == AF_INET6) + l3flags |= IPV6_REPL_SRC; + } + break; + case 'q': + options |= CT_OPT_REPL_DST; + if (optarg) { + reply.l3protonum = + parse_inetaddr(optarg, &reply.dst); + set_family(&family, reply.l3protonum); + if (orig.l3protonum == AF_INET) + l3flags |= IPV4_REPL_DST; + else if (orig.l3protonum == AF_INET6) + l3flags |= IPV6_REPL_DST; + } + break; + case 'p': + options |= CT_OPT_PROTO; + h = findproto(optarg); + if (!h) + exit_error(PARAMETER_PROBLEM, "proto needed\n"); + orig.protonum = h->protonum; + reply.protonum = h->protonum; + exptuple.protonum = h->protonum; + mask.protonum = h->protonum; + opts = merge_options(opts, h->opts, + &h->option_offset); + break; + case 't': + options |= CT_OPT_TIMEOUT; + if (optarg) + timeout = atol(optarg); + break; + case 'u': { + if (!optarg) + continue; + + options |= CT_OPT_STATUS; + parse_parameter(optarg, &status, PARSE_STATUS); + break; + } + case 'e': + options |= CT_OPT_EVENT_MASK; + parse_parameter(optarg, &event_mask, PARSE_EVENT); + break; + case 'z': + options |= CT_OPT_ZERO; + break; + case '{': + options |= CT_OPT_MASK_SRC; + if (optarg) { + mask.l3protonum = + parse_inetaddr(optarg, &mask.src); + set_family(&family, mask.l3protonum); + } + break; + case '}': + options |= CT_OPT_MASK_DST; + if (optarg) { + mask.l3protonum = + parse_inetaddr(optarg, &mask.dst); + set_family(&family, mask.l3protonum); + } + break; + case '[': + options |= CT_OPT_EXP_SRC; + if (optarg) { + exptuple.l3protonum = + parse_inetaddr(optarg, &exptuple.src); + set_family(&family, exptuple.l3protonum); + } + break; + case ']': + options |= CT_OPT_EXP_DST; + if (optarg) { + exptuple.l3protonum = + parse_inetaddr(optarg, &exptuple.dst); + set_family(&family, exptuple.l3protonum); + } + break; + case 'a': + options |= CT_OPT_NATRANGE; + set_family(&family, AF_INET); + nat_parse(optarg, 1, &range); + break; + case 'm': + options |= CT_OPT_MARK; + mark = atol(optarg); + metaflags |= NFCT_MARK; + break; + case 'i': { + char *s = NULL; + options |= CT_OPT_ID; + if (optarg) + break; + else if (optind < argc && argv[optind][0] != '-' + && argv[optind][0] != '!') + s = argv[optind++]; + + if (s) + id = atol(s); + 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, "Unknown " + "protocol family\n"); + break; + default: + if (h && h->parse_opts + &&!h->parse_opts(c - h->option_offset, argv, &orig, + &reply, &exptuple, &mask, &proto, + &l4flags)) + exit_error(PARAMETER_PROBLEM, "parse error\n"); + + /* Unknown argument... */ + if (!h) { + usage(argv[0]); + exit_error(PARAMETER_PROBLEM, "Missing " + "arguments...\n"); + } + break; + } + } + + /* default family */ + if (family == AF_UNSPEC) + family = AF_INET; + + generic_cmd_check(command, options); + generic_opt_check(command, options); + + if (!(command & CT_HELP) + && h && h->final_check + && !h->final_check(l4flags, command, &orig, &reply)) { + usage(argv[0]); + extension_help(h); + exit_error(PARAMETER_PROBLEM, "Missing protocol arguments!\n"); + } + + switch(command) { + + case CT_LIST: + cth = nfct_open(CONNTRACK, 0); + if (!cth) + exit_error(OTHER_PROBLEM, "Can't open handler"); + + if (options & CT_COMPARISON) { + + if (options & CT_OPT_ZERO) + exit_error(PARAMETER_PROBLEM, "Can't use -z " + "with filtering parameters"); + + ct = nfct_conntrack_alloc(&orig, &reply, timeout, + &proto, status, mark, id, + NULL); + if (!ct) + exit_error(OTHER_PROBLEM, "Not enough memory"); + + cmp.ct = ct; + cmp.flags = metaflags; + cmp.l3flags = l3flags; + cmp.l4flags = l4flags; + pcmp = &cmp; + } + + if (options & CT_OPT_ID) + nfct_register_callback(cth, + nfct_default_conntrack_display_id, + (void *) pcmp); + else + nfct_register_callback(cth, + nfct_default_conntrack_display, + (void *) pcmp); + + if (options & CT_OPT_ZERO) + res = + nfct_dump_conntrack_table_reset_counters(cth, family); + else + res = nfct_dump_conntrack_table(cth, family); + nfct_close(cth); + break; + + case EXP_LIST: + cth = nfct_open(EXPECT, 0); + if (!cth) + exit_error(OTHER_PROBLEM, "Can't open handler"); + if (options & CT_OPT_ID) + nfct_register_callback(cth, + nfct_default_expect_display_id, + NULL); + else + nfct_register_callback(cth, + nfct_default_expect_display, + NULL); + res = nfct_dump_expect_list(cth, family); + nfct_close(cth); + break; + + case CT_CREATE: + if ((options & CT_OPT_ORIG) + && !(options & CT_OPT_REPL)) { + reply.l3protonum = orig.l3protonum; + memcpy(&reply.src, &orig.dst, sizeof(reply.src)); + memcpy(&reply.dst, &orig.src, sizeof(reply.dst)); + } else if (!(options & CT_OPT_ORIG) + && (options & CT_OPT_REPL)) { + orig.l3protonum = reply.l3protonum; + memcpy(&orig.src, &reply.dst, sizeof(orig.src)); + memcpy(&orig.dst, &reply.src, sizeof(orig.dst)); + } + if (options & CT_OPT_NATRANGE) + ct = nfct_conntrack_alloc(&orig, &reply, timeout, + &proto, status, mark, id, + &range); + else + ct = nfct_conntrack_alloc(&orig, &reply, timeout, + &proto, status, mark, id, + NULL); + if (!ct) + exit_error(OTHER_PROBLEM, "Not Enough memory"); + + cth = nfct_open(CONNTRACK, 0); + if (!cth) { + nfct_conntrack_free(ct); + exit_error(OTHER_PROBLEM, "Can't open handler"); + } + res = nfct_create_conntrack(cth, ct); + nfct_close(cth); + nfct_conntrack_free(ct); + break; + + case EXP_CREATE: + if (options & CT_OPT_ORIG) + exp = nfct_expect_alloc(&orig, &exptuple, + &mask, timeout, id); + else if (options & CT_OPT_REPL) + exp = nfct_expect_alloc(&reply, &exptuple, + &mask, timeout, id); + if (!exp) + exit_error(OTHER_PROBLEM, "Not enough memory"); + + cth = nfct_open(EXPECT, 0); + if (!cth) { + nfct_expect_free(exp); + exit_error(OTHER_PROBLEM, "Can't open handler"); + } + res = nfct_create_expectation(cth, exp); + nfct_expect_free(exp); + nfct_close(cth); + break; + + case CT_UPDATE: + if ((options & CT_OPT_ORIG) + && !(options & CT_OPT_REPL)) { + reply.l3protonum = orig.l3protonum; + memcpy(&reply.src, &orig.dst, sizeof(reply.src)); + memcpy(&reply.dst, &orig.src, sizeof(reply.dst)); + } else if (!(options & CT_OPT_ORIG) + && (options & CT_OPT_REPL)) { + orig.l3protonum = reply.l3protonum; + memcpy(&orig.src, &reply.dst, sizeof(orig.src)); + memcpy(&orig.dst, &reply.src, sizeof(orig.dst)); + } + ct = nfct_conntrack_alloc(&orig, &reply, timeout, + &proto, status, mark, id, + NULL); + if (!ct) + exit_error(OTHER_PROBLEM, "Not enough memory"); + + cth = nfct_open(CONNTRACK, 0); + if (!cth) { + nfct_conntrack_free(ct); + exit_error(OTHER_PROBLEM, "Can't open handler"); + } + res = nfct_update_conntrack(cth, ct); + nfct_conntrack_free(ct); + nfct_close(cth); + break; + + case CT_DELETE: + if (!(options & CT_OPT_ORIG) && !(options & CT_OPT_REPL)) + exit_error(PARAMETER_PROBLEM, "Can't kill conntracks " + "just by its ID"); + cth = nfct_open(CONNTRACK, 0); + if (!cth) + exit_error(OTHER_PROBLEM, "Can't open handler"); + if (options & CT_OPT_ORIG) + res = nfct_delete_conntrack(cth, &orig, + NFCT_DIR_ORIGINAL, + id); + else if (options & CT_OPT_REPL) + res = nfct_delete_conntrack(cth, &reply, + NFCT_DIR_REPLY, + id); + nfct_close(cth); + break; + + case EXP_DELETE: + cth = nfct_open(EXPECT, 0); + if (!cth) + exit_error(OTHER_PROBLEM, "Can't open handler"); + if (options & CT_OPT_ORIG) + res = nfct_delete_expectation(cth, &orig, id); + else if (options & CT_OPT_REPL) + res = nfct_delete_expectation(cth, &reply, id); + nfct_close(cth); + break; + + case CT_GET: + cth = nfct_open(CONNTRACK, 0); + if (!cth) + exit_error(OTHER_PROBLEM, "Can't open handler"); + nfct_register_callback(cth, nfct_default_conntrack_display, + NULL); + if (options & CT_OPT_ORIG) + res = nfct_get_conntrack(cth, &orig, + NFCT_DIR_ORIGINAL, id); + else if (options & CT_OPT_REPL) + res = nfct_get_conntrack(cth, &reply, + NFCT_DIR_REPLY, id); + nfct_close(cth); + break; + + case EXP_GET: + cth = nfct_open(EXPECT, 0); + if (!cth) + exit_error(OTHER_PROBLEM, "Can't open handler"); + nfct_register_callback(cth, nfct_default_expect_display, + NULL); + if (options & CT_OPT_ORIG) + res = nfct_get_expectation(cth, &orig, id); + else if (options & CT_OPT_REPL) + res = nfct_get_expectation(cth, &reply, id); + nfct_close(cth); + break; + + case CT_FLUSH: + cth = nfct_open(CONNTRACK, 0); + if (!cth) + exit_error(OTHER_PROBLEM, "Can't open handler"); + res = nfct_flush_conntrack_table(cth, AF_INET); + nfct_close(cth); + break; + + case EXP_FLUSH: + cth = nfct_open(EXPECT, 0); + if (!cth) + exit_error(OTHER_PROBLEM, "Can't open handler"); + res = nfct_flush_expectation_table(cth, AF_INET); + nfct_close(cth); + break; + + case CT_EVENT: + if (options & CT_OPT_EVENT_MASK) + cth = nfct_open(CONNTRACK, event_mask); + else + cth = nfct_open(CONNTRACK, NFCT_ALL_CT_GROUPS); + + if (!cth) + exit_error(OTHER_PROBLEM, "Can't open handler"); + signal(SIGINT, event_sighandler); + + if (options & CT_COMPARISON) { + ct = nfct_conntrack_alloc(&orig, &reply, timeout, + &proto, status, mark, id, + NULL); + if (!ct) + exit_error(OTHER_PROBLEM, "Not enough memory"); + + cmp.ct = ct; + cmp.flags = metaflags; + cmp.l3flags = l3flags; + cmp.l4flags = l4flags; + pcmp = &cmp; + } + + nfct_register_callback(cth, + nfct_default_conntrack_event_display, + (void *) pcmp); + res = nfct_event_conntrack(cth); + 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); + nfct_register_callback(cth, nfct_default_expect_display, + NULL); + res = nfct_event_expectation(cth); + nfct_close(cth); + break; + + case CT_VERSION: + fprintf(stdout, "%s v%s\n", PROGNAME, VERSION); + break; + case CT_HELP: + usage(argv[0]); + if (options & CT_OPT_PROTO) + extension_help(h); + break; + default: + usage(argv[0]); + break; + } + + if (opts != original_opts) { + free(opts); + opts = original_opts; + global_option_offset = 0; + } + + if (res < 0) { + fprintf(stderr, "Operation failed: %s\n", err2str(res, command)); + exit(OTHER_PROBLEM); + } + + return 0; +} |