/* * (C) 2005-2007 by Pablo Neira Ayuso * * 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 * for introducing me to advanced firewalling stuff. * * --pablo 13/04/2005 * * 2005-04-16 Harald Welte : * Add support for conntrack accounting and conntrack mark * 2005-06-23 Harald Welte : * Add support for expect creation * 2005-09-24 Harald Welte : * Remove remaints of "-A" * 2007-04-22 Pablo Neira Ayuso : * Ported to the new libnetfilter_conntrack API * */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_ARPA_INET_H #include #endif #include #include #include #include #include "linux_list.h" #include "conntrack.h" #include #include #include 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','n','g','x'}; 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'}, /* deprecated */ {"mark", 1, 0, 'm'}, {"id", 2, 0, 'i'}, /* deprecated */ {"family", 1, 0, 'f'}, {"src-nat", 1, 0, 'n'}, {"dst-nat", 1, 0, 'g'}, {"xml", 0, 0, 'x'}, {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 n g x */ /*CT_LIST*/ {2,2,2,2,2,0,0,2,0,0,0,0,0,0,2,2,2,0,0,2}, /*CT_CREATE*/ {2,2,2,2,1,1,1,0,0,0,0,0,0,2,2,0,0,2,2,0}, /*CT_UPDATE*/ {2,2,2,2,1,2,2,0,0,0,0,0,0,0,2,2,0,0,0,0}, /*CT_DELETE*/ {2,2,2,2,2,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0}, /*CT_GET*/ {2,2,2,2,1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2}, /*CT_FLUSH*/ {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,0,0,2}, /*VERSION*/ {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,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,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}, /*EXP_DELETE*/{1,1,2,2,1,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}, /*EXP_FLUSH*/ {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}, }; static char *lib_dir = CONNTRACK_LIB_DIR; static LIST_HEAD(proto_list); static unsigned int options; static unsigned int command; #define CT_COMPARISON (CT_OPT_PROTO | CT_OPT_ORIG | CT_OPT_REPL | CT_OPT_MARK) 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< illegal, 1 => legal, 0 => undecided. */ for (j = 0; j < NUMBER_OF_CMD; j++) { if (!(command & (1<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); } union ct_address { u_int32_t v4; u_int32_t v6[4]; }; 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, *dash, *error; union ct_address parse; colon = strchr(arg, ':'); if (colon) { u_int16_t port; if (!portok) exit_error(PARAMETER_PROBLEM, "Need TCP or UDP with port specification"); port = atoi(colon+1); if (port == 0) 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\n"); 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 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" " -n, --src-nat ip\tsource NAT ip\n" " -g, --dst-nat ip\tdestination NAT ip\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" " -x, --xml \t\t\t\tDisplay output in XML format\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" ; 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); } unsigned int output_flags = NFCT_O_DEFAULT; static int event_cb(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data) { char buf[1024]; struct nf_conntrack *obj = data; if (options & CT_COMPARISON && !nfct_compare(obj, ct)) return NFCT_CB_CONTINUE; nfct_snprintf(buf, 1024, ct, type, output_flags, 0); printf("%s\n", buf); 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; if (options & CT_COMPARISON && !nfct_compare(obj, ct)) return NFCT_CB_CONTINUE; nfct_snprintf(buf, 1024, ct, NFCT_T_UNKNOWN, output_flags, 0); printf("%s\n", buf); 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, 1024, exp, NFCT_T_UNKNOWN, NFCT_O_DEFAULT, 0); printf("%s\n", buf); return NFCT_CB_CONTINUE; } static struct ctproto_handler *h; int main(int argc, char *argv[]) { int c; unsigned int type = 0, event_mask = 0, l4flags = 0, status = 0; int res = 0; int family = AF_UNSPEC; char __obj[nfct_maxsize()]; char __exptuple[nfct_maxsize()]; char __mask[nfct_maxsize()]; struct nf_conntrack *obj = (struct nf_conntrack *) __obj; struct nf_conntrack *exptuple = (struct nf_conntrack *) __exptuple; struct nf_conntrack *mask = (struct nf_conntrack *) __mask; char __exp[nfexp_maxsize()]; struct nf_expect *exp = (struct nf_expect *) __exp; int l3protonum; union ct_address ad; memset(__obj, 0, sizeof(__obj)); memset(__exptuple, 0, sizeof(__exptuple)); memset(__mask, 0, sizeof(__mask)); memset(__exp, 0, sizeof(__exp)); 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:x", 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) break; l3protonum = parse_addr(optarg, &ad); set_family(&family, l3protonum); if (l3protonum == AF_INET) { nfct_set_attr_u32(obj, ATTR_ORIG_IPV4_SRC, ad.v4); } else if (l3protonum == AF_INET6) { nfct_set_attr(obj, ATTR_ORIG_IPV6_SRC, &ad.v6); } nfct_set_attr_u8(obj, ATTR_ORIG_L3PROTO, l3protonum); break; case 'd': options |= CT_OPT_ORIG_DST; if (!optarg) break; l3protonum = parse_addr(optarg, &ad); set_family(&family, l3protonum); if (l3protonum == AF_INET) { nfct_set_attr_u32(obj, ATTR_ORIG_IPV4_DST, ad.v4); } else if (l3protonum == AF_INET6) { nfct_set_attr(obj, ATTR_ORIG_IPV6_DST, &ad.v6); } nfct_set_attr_u8(obj, ATTR_ORIG_L3PROTO, l3protonum); break; case 'r': options |= CT_OPT_REPL_SRC; if (!optarg) break; l3protonum = parse_addr(optarg, &ad); set_family(&family, l3protonum); if (l3protonum == AF_INET) { nfct_set_attr_u32(obj, ATTR_REPL_IPV4_SRC, ad.v4); } else if (l3protonum == AF_INET6) { nfct_set_attr(obj, ATTR_REPL_IPV6_SRC, &ad.v6); } nfct_set_attr_u8(obj, ATTR_REPL_L3PROTO, l3protonum); break; case 'q': options |= CT_OPT_REPL_DST; if (!optarg) break; l3protonum = parse_addr(optarg, &ad); set_family(&family, l3protonum); if (l3protonum == AF_INET) { nfct_set_attr_u32(obj, ATTR_REPL_IPV4_DST, ad.v4); } else if (l3protonum == AF_INET6) { nfct_set_attr(obj, ATTR_REPL_IPV6_DST, &ad.v6); } nfct_set_attr_u8(obj, ATTR_REPL_L3PROTO, l3protonum); break; case 'p': options |= CT_OPT_PROTO; h = findproto(optarg); if (!h) exit_error(PARAMETER_PROBLEM, "proto needed\n"); nfct_set_attr_u8(obj, ATTR_ORIG_L4PROTO, h->protonum); nfct_set_attr_u8(obj, ATTR_REPL_L4PROTO, h->protonum); nfct_set_attr_u8(exptuple, ATTR_ORIG_L4PROTO, h->protonum); nfct_set_attr_u8(mask, ATTR_ORIG_L4PROTO, h->protonum); opts = merge_options(opts, h->opts, &h->option_offset); break; case 't': options |= CT_OPT_TIMEOUT; if (!optarg) continue; nfct_set_attr_u32(obj, ATTR_TIMEOUT, atol(optarg)); nfexp_set_attr_u32(exp, ATTR_EXP_TIMEOUT, atol(optarg)); break; case 'u': { if (!optarg) continue; 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 'z': options |= CT_OPT_ZERO; break; case '{': options |= CT_OPT_MASK_SRC; if (!optarg) break; l3protonum = parse_addr(optarg, &ad); set_family(&family, l3protonum); if (l3protonum == AF_INET) { nfct_set_attr_u32(mask, ATTR_ORIG_IPV4_SRC, ad.v4); } else if (l3protonum == AF_INET6) { nfct_set_attr(mask, ATTR_ORIG_IPV6_SRC, &ad.v6); } nfct_set_attr_u8(mask, ATTR_ORIG_L3PROTO, l3protonum); break; case '}': options |= CT_OPT_MASK_DST; if (!optarg) break; l3protonum = parse_addr(optarg, &ad); set_family(&family, l3protonum); if (l3protonum == AF_INET) { nfct_set_attr_u32(mask, ATTR_ORIG_IPV4_DST, ad.v4); } else if (l3protonum == AF_INET6) { nfct_set_attr(mask, ATTR_ORIG_IPV6_DST, &ad.v6); } nfct_set_attr_u8(mask, ATTR_ORIG_L3PROTO, l3protonum); break; case '[': options |= CT_OPT_EXP_SRC; if (!optarg) break; l3protonum = parse_addr(optarg, &ad); set_family(&family, l3protonum); if (l3protonum == AF_INET) { nfct_set_attr_u32(exptuple, ATTR_ORIG_IPV4_SRC, ad.v4); } else if (l3protonum == AF_INET6) { nfct_set_attr(exptuple, ATTR_ORIG_IPV6_SRC, &ad.v6); } nfct_set_attr_u8(exptuple, ATTR_ORIG_L3PROTO, l3protonum); break; case ']': options |= CT_OPT_EXP_DST; if (!optarg) break; l3protonum = parse_addr(optarg, &ad); set_family(&family, l3protonum); if (l3protonum == AF_INET) { nfct_set_attr_u32(exptuple, ATTR_ORIG_IPV4_DST, ad.v4); } else if (l3protonum == AF_INET6) { nfct_set_attr(exptuple, ATTR_ORIG_IPV6_DST, &ad.v6); } nfct_set_attr_u8(exptuple, ATTR_ORIG_L3PROTO, l3protonum); break; case 'a': printf("warning: ignoring --nat-range, " "use --src-nat or --dst-nat instead.\n"); break; case 'n': options |= CT_OPT_SRC_NAT; set_family(&family, AF_INET); nat_parse(optarg, 1, obj, CT_OPT_SRC_NAT); break; case 'g': options |= CT_OPT_DST_NAT; set_family(&family, AF_INET); nat_parse(optarg, 1, obj, CT_OPT_DST_NAT); case 'm': options |= CT_OPT_MARK; if (!optarg) continue; nfct_set_attr_u32(obj, ATTR_MARK, atol(optarg)); break; case 'i': printf("warning: ignoring --id. deprecated option.\n"); 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; case 'x': options |= CT_OPT_XML; output_flags = NFCT_O_XML; break; default: if (h && h->parse_opts &&!h->parse_opts(c - h->option_offset, argv, obj, exptuple, mask, &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, obj)) { 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 && 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); 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_set_attr_u8(obj, ATTR_REPL_L3PROTO, nfct_get_attr_u8(obj, ATTR_ORIG_L3PROTO)); if (family == AF_INET) { nfct_set_attr_u32(obj, ATTR_REPL_IPV4_SRC, nfct_get_attr_u32(obj, ATTR_ORIG_IPV4_DST)); nfct_set_attr_u32(obj, ATTR_REPL_IPV4_DST, nfct_get_attr_u32(obj, ATTR_ORIG_IPV4_SRC)); } else if (family == AF_INET6) { nfct_set_attr(obj, ATTR_REPL_IPV6_SRC, nfct_get_attr(obj, ATTR_ORIG_IPV6_DST)); nfct_set_attr(obj, ATTR_REPL_IPV6_DST, nfct_get_attr(obj, ATTR_ORIG_IPV6_SRC)); } } else if (!(options & CT_OPT_ORIG) && (options & CT_OPT_REPL)) { nfct_set_attr_u8(obj, ATTR_ORIG_L3PROTO, nfct_get_attr_u8(obj, ATTR_REPL_L3PROTO)); if (family == AF_INET) { nfct_set_attr_u32(obj, ATTR_ORIG_IPV4_SRC, nfct_get_attr_u32(obj, ATTR_REPL_IPV4_DST)); nfct_set_attr_u32(obj, ATTR_ORIG_IPV4_DST, nfct_get_attr_u32(obj, ATTR_REPL_IPV4_SRC)); } else if (family == AF_INET6) { nfct_set_attr(obj, ATTR_ORIG_IPV6_SRC, nfct_get_attr(obj, ATTR_REPL_IPV6_DST)); nfct_set_attr(obj, ATTR_ORIG_IPV6_DST, nfct_get_attr(obj, ATTR_REPL_IPV6_SRC)); } } cth = nfct_open(CONNTRACK, 0); if (!cth) exit_error(OTHER_PROBLEM, "Can't open handler"); res = nfct_query(cth, NFCT_Q_CREATE, obj); 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: if ((options & CT_OPT_ORIG) && !(options & CT_OPT_REPL)) { nfct_set_attr_u8(obj, ATTR_REPL_L3PROTO, nfct_get_attr_u8(obj, ATTR_ORIG_L3PROTO)); if (family == AF_INET) { nfct_set_attr_u32(obj, ATTR_REPL_IPV4_SRC, nfct_get_attr_u32(obj, ATTR_ORIG_IPV4_DST)); nfct_set_attr_u32(obj, ATTR_REPL_IPV4_DST, nfct_get_attr_u32(obj, ATTR_ORIG_IPV4_SRC)); } else if (family == AF_INET6) { nfct_set_attr(obj, ATTR_REPL_IPV6_SRC, nfct_get_attr(obj, ATTR_ORIG_IPV6_DST)); nfct_set_attr(obj, ATTR_REPL_IPV6_DST, nfct_get_attr(obj, ATTR_ORIG_IPV6_SRC)); } } else if (!(options & CT_OPT_ORIG) && (options & CT_OPT_REPL)) { nfct_set_attr_u8(obj, ATTR_ORIG_L3PROTO, nfct_get_attr_u8(obj, ATTR_REPL_L3PROTO)); if (family == AF_INET) { nfct_set_attr_u32(obj, ATTR_ORIG_IPV4_SRC, nfct_get_attr_u32(obj, ATTR_REPL_IPV4_DST)); nfct_set_attr_u32(obj, ATTR_ORIG_IPV4_DST, nfct_get_attr_u32(obj, ATTR_REPL_IPV4_SRC)); } else if (family == AF_INET6) { nfct_set_attr(obj, ATTR_ORIG_IPV6_SRC, nfct_get_attr(obj, ATTR_REPL_IPV6_DST)); nfct_set_attr(obj, ATTR_ORIG_IPV6_DST, nfct_get_attr(obj, ATTR_REPL_IPV6_SRC)); } } cth = nfct_open(CONNTRACK, 0); if (!cth) exit_error(OTHER_PROBLEM, "Can't open handler"); res = nfct_query(cth, NFCT_Q_UPDATE, obj); 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"); res = nfct_query(cth, NFCT_Q_DESTROY, obj); 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); 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); else cth = nfct_open(CONNTRACK, NFCT_ALL_CT_GROUPS); if (!cth) exit_error(OTHER_PROBLEM, "Can't open handler"); signal(SIGINT, event_sighandler); nfct_callback_register(cth, NFCT_T_ALL, event_cb, obj); res = nfct_catch(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); nfexp_callback_register(cth, NFCT_T_ALL, dump_exp_cb, NULL); res = nfexp_catch(cth); nfct_close(cth); 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); 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(errno, command)); exit(OTHER_PROBLEM); } return 0; }