diff options
Diffstat (limited to 'src/conntrack.c')
-rw-r--r-- | src/conntrack.c | 897 |
1 files changed, 658 insertions, 239 deletions
diff --git a/src/conntrack.c b/src/conntrack.c index eec3868..9602e2e 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -58,10 +58,219 @@ #include <fcntl.h> #include <libnetfilter_conntrack/libnetfilter_conntrack.h> +struct u32_mask { + uint32_t value; + uint32_t mask; +}; + +/* These are the template objects that are used to send commands. */ +static struct { + struct nf_conntrack *ct; + struct nf_expect *exp; + /* Expectations require the expectation tuple and the mask. */ + struct nf_conntrack *exptuple, *mask; + + /* Allows filtering/setting specific bits in the ctmark */ + struct u32_mask mark; +} tmpl; + +static int alloc_tmpl_objects(void) +{ + tmpl.ct = nfct_new(); + tmpl.exptuple = nfct_new(); + tmpl.mask = nfct_new(); + tmpl.exp = nfexp_new(); + + memset(&tmpl.mark, 0, sizeof(tmpl.mark)); + + return tmpl.ct != NULL && tmpl.exptuple != NULL && + tmpl.mask != NULL && tmpl.exp != NULL; +} + +static void free_tmpl_objects(void) +{ + if (tmpl.ct) + nfct_destroy(tmpl.ct); + if (tmpl.exptuple) + nfct_destroy(tmpl.exptuple); + if (tmpl.mask) + nfct_destroy(tmpl.mask); + if (tmpl.exp) + nfexp_destroy(tmpl.exp); +} + +enum ct_command { + CT_NONE = 0, + + CT_LIST_BIT = 0, + CT_LIST = (1 << CT_LIST_BIT), + + CT_CREATE_BIT = 1, + CT_CREATE = (1 << CT_CREATE_BIT), + + CT_UPDATE_BIT = 2, + CT_UPDATE = (1 << CT_UPDATE_BIT), + + CT_DELETE_BIT = 3, + CT_DELETE = (1 << CT_DELETE_BIT), + + CT_GET_BIT = 4, + CT_GET = (1 << CT_GET_BIT), + + CT_FLUSH_BIT = 5, + CT_FLUSH = (1 << CT_FLUSH_BIT), + + CT_EVENT_BIT = 6, + CT_EVENT = (1 << CT_EVENT_BIT), + + CT_VERSION_BIT = 7, + CT_VERSION = (1 << CT_VERSION_BIT), + + CT_HELP_BIT = 8, + CT_HELP = (1 << CT_HELP_BIT), + + EXP_LIST_BIT = 9, + EXP_LIST = (1 << EXP_LIST_BIT), + + EXP_CREATE_BIT = 10, + EXP_CREATE = (1 << EXP_CREATE_BIT), + + EXP_DELETE_BIT = 11, + EXP_DELETE = (1 << EXP_DELETE_BIT), + + EXP_GET_BIT = 12, + EXP_GET = (1 << EXP_GET_BIT), + + EXP_FLUSH_BIT = 13, + EXP_FLUSH = (1 << EXP_FLUSH_BIT), + + EXP_EVENT_BIT = 14, + EXP_EVENT = (1 << EXP_EVENT_BIT), + + CT_COUNT_BIT = 15, + CT_COUNT = (1 << CT_COUNT_BIT), + + EXP_COUNT_BIT = 16, + EXP_COUNT = (1 << EXP_COUNT_BIT), + + X_STATS_BIT = 17, + X_STATS = (1 << X_STATS_BIT), +}; +/* If you add a new command, you have to update NUMBER_OF_CMD in conntrack.h */ + +enum ct_options { + CT_OPT_ORIG_SRC_BIT = 0, + CT_OPT_ORIG_SRC = (1 << CT_OPT_ORIG_SRC_BIT), + + CT_OPT_ORIG_DST_BIT = 1, + CT_OPT_ORIG_DST = (1 << CT_OPT_ORIG_DST_BIT), + + CT_OPT_ORIG = (CT_OPT_ORIG_SRC | CT_OPT_ORIG_DST), + + CT_OPT_REPL_SRC_BIT = 2, + CT_OPT_REPL_SRC = (1 << CT_OPT_REPL_SRC_BIT), + + CT_OPT_REPL_DST_BIT = 3, + CT_OPT_REPL_DST = (1 << CT_OPT_REPL_DST_BIT), + + CT_OPT_REPL = (CT_OPT_REPL_SRC | CT_OPT_REPL_DST), + + CT_OPT_PROTO_BIT = 4, + CT_OPT_PROTO = (1 << CT_OPT_PROTO_BIT), + + CT_OPT_TUPLE_ORIG = (CT_OPT_ORIG | CT_OPT_PROTO), + CT_OPT_TUPLE_REPL = (CT_OPT_REPL | CT_OPT_PROTO), + + CT_OPT_TIMEOUT_BIT = 5, + CT_OPT_TIMEOUT = (1 << CT_OPT_TIMEOUT_BIT), + + CT_OPT_STATUS_BIT = 6, + CT_OPT_STATUS = (1 << CT_OPT_STATUS_BIT), + + CT_OPT_ZERO_BIT = 7, + CT_OPT_ZERO = (1 << CT_OPT_ZERO_BIT), + + CT_OPT_EVENT_MASK_BIT = 8, + CT_OPT_EVENT_MASK = (1 << CT_OPT_EVENT_MASK_BIT), + + CT_OPT_EXP_SRC_BIT = 9, + CT_OPT_EXP_SRC = (1 << CT_OPT_EXP_SRC_BIT), + + CT_OPT_EXP_DST_BIT = 10, + CT_OPT_EXP_DST = (1 << CT_OPT_EXP_DST_BIT), + + CT_OPT_MASK_SRC_BIT = 11, + CT_OPT_MASK_SRC = (1 << CT_OPT_MASK_SRC_BIT), + + CT_OPT_MASK_DST_BIT = 12, + CT_OPT_MASK_DST = (1 << CT_OPT_MASK_DST_BIT), + + CT_OPT_NATRANGE_BIT = 13, + CT_OPT_NATRANGE = (1 << CT_OPT_NATRANGE_BIT), + + CT_OPT_MARK_BIT = 14, + CT_OPT_MARK = (1 << CT_OPT_MARK_BIT), + + CT_OPT_ID_BIT = 15, + CT_OPT_ID = (1 << CT_OPT_ID_BIT), + + CT_OPT_FAMILY_BIT = 16, + CT_OPT_FAMILY = (1 << CT_OPT_FAMILY_BIT), + + CT_OPT_SRC_NAT_BIT = 17, + CT_OPT_SRC_NAT = (1 << CT_OPT_SRC_NAT_BIT), + + CT_OPT_DST_NAT_BIT = 18, + CT_OPT_DST_NAT = (1 << CT_OPT_DST_NAT_BIT), + + CT_OPT_OUTPUT_BIT = 19, + CT_OPT_OUTPUT = (1 << CT_OPT_OUTPUT_BIT), + + CT_OPT_SECMARK_BIT = 20, + CT_OPT_SECMARK = (1 << CT_OPT_SECMARK_BIT), + + CT_OPT_BUFFERSIZE_BIT = 21, + CT_OPT_BUFFERSIZE = (1 << CT_OPT_BUFFERSIZE_BIT), + + CT_OPT_ANY_NAT_BIT = 22, + CT_OPT_ANY_NAT = (1 << CT_OPT_ANY_NAT_BIT), + + CT_OPT_ZONE_BIT = 23, + CT_OPT_ZONE = (1 << CT_OPT_ZONE_BIT), +}; +/* If you add a new option, you have to update NUMBER_OF_OPT in conntrack.h */ + +/* Update this mask to allow to filter based on new 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 | CT_OPT_ZONE) + 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"}; + [CT_OPT_ORIG_SRC_BIT] = "src", + [CT_OPT_ORIG_DST_BIT] = "dst", + [CT_OPT_REPL_SRC_BIT] = "reply-src", + [CT_OPT_REPL_DST_BIT] = "reply-dst", + [CT_OPT_PROTO_BIT] = "protonum", + [CT_OPT_TIMEOUT_BIT] = "timeout", + [CT_OPT_STATUS_BIT] = "status", + [CT_OPT_ZERO_BIT] = "zero", + [CT_OPT_EVENT_MASK_BIT] = "event-mask", + [CT_OPT_EXP_SRC_BIT] = "tuple-src", + [CT_OPT_EXP_DST_BIT] = "tuple-dst", + [CT_OPT_MASK_SRC_BIT] = "mask-src", + [CT_OPT_MASK_DST_BIT] = "mask-dst", + [CT_OPT_NATRANGE_BIT] = "nat-range", + [CT_OPT_MARK_BIT] = "mark", + [CT_OPT_ID_BIT] = "id", + [CT_OPT_FAMILY_BIT] = "family", + [CT_OPT_SRC_NAT_BIT] = "src-nat", + [CT_OPT_DST_NAT_BIT] = "dst-nat", + [CT_OPT_OUTPUT_BIT] = "output", + [CT_OPT_SECMARK_BIT] = "secmark", + [CT_OPT_BUFFERSIZE_BIT] = "buffer-size", + [CT_OPT_ANY_NAT_BIT] = "any-nat", + [CT_OPT_ZONE_BIT] = "zone", +}; static struct option original_opts[] = { {"dump", 2, 0, 'L'}, @@ -99,14 +308,14 @@ static struct option original_opts[] = { {"dst-nat", 2, 0, 'g'}, {"output", 1, 0, 'o'}, {"buffer-size", 1, 0, 'b'}, + {"any-nat", 2, 0, 'j'}, + {"zone", 1, 0, 'w'}, {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; +static const char *getopt_str = "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::Sj::w:"; /* Table of legal combinations of commands and options. If any of the * given commands make an option legal, that option is legal (applies to @@ -121,27 +330,143 @@ static unsigned int global_option_offset = 0; 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}, + /* s d r q p t u z e [ ] { } a m i f n g o c b j w*/ +/*CT_LIST*/ {2,2,2,2,2,0,2,2,0,0,0,0,0,0,2,0,2,2,2,2,2,0,2,2}, +/*CT_CREATE*/ {3,3,3,3,1,1,2,0,0,0,0,0,0,2,2,0,0,2,2,0,0,0,0,2}, +/*CT_UPDATE*/ {2,2,2,2,2,2,2,0,0,0,0,0,0,0,2,2,2,2,2,2,0,0,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,0,2}, +/*CT_GET*/ {3,3,3,3,1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,2,0,0,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,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,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,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,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,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,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,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,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,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,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,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,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,0,0}, +}; + +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, + ['j'] = CT_OPT_ANY_NAT, + ['w'] = CT_OPT_ZONE, }; +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, + ['w'] = ATTR_ZONE, +}; + +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", +}; + +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" + " -j, --any-nat ip\t\t\tsource or destination 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" + " -w, --zone value\t\tSet conntrack zone\n" + " -b, --buffer-size\t\tNetlink socket buffer size\n" + ; + +#define OPTION_OFFSET 256 + +static struct nfct_handle *cth, *ith; +static struct option *opts = original_opts; +static unsigned int global_option_offset = 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, @@ -152,9 +477,6 @@ 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) { @@ -348,11 +670,11 @@ merge_options(struct option *oldopts, const struct option *newopts, /* Translates errno numbers into more human-readable form than strerror. */ static const char * -err2str(int err, enum action command) +err2str(int err, enum ct_command command) { unsigned int i; struct table_struct { - enum action act; + enum ct_command act; int err; const char *message; } table [] = @@ -378,11 +700,32 @@ err2str(int err, enum action command) return strerror(err); } +static int mark_cmp(const struct u32_mask *m, const struct nf_conntrack *ct) +{ + return nfct_attr_is_set(ct, ATTR_MARK) && + (nfct_get_attr_u32(ct, ATTR_MARK) & m->mask) == m->value; +} + #define PARSE_STATUS 0 #define PARSE_EVENT 1 #define PARSE_OUTPUT 2 #define PARSE_MAX 3 +enum { + _O_XML = (1 << 0), + _O_EXT = (1 << 1), + _O_TMS = (1 << 2), + _O_ID = (1 << 3), + _O_KTMS = (1 << 4), +}; + +enum { + CT_EVENT_F_NEW = (1 << 0), + CT_EVENT_F_UPD = (1 << 1), + CT_EVENT_F_DEL = (1 << 2), + CT_EVENT_F_ALL = CT_EVENT_F_NEW | CT_EVENT_F_UPD | CT_EVENT_F_DEL, +}; + static struct parse_parameter { const char *parameter[6]; size_t size; @@ -391,10 +734,9 @@ static struct parse_parameter { { {"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 }, + { CT_EVENT_F_ALL, CT_EVENT_F_NEW, CT_EVENT_F_UPD, CT_EVENT_F_DEL } }, + { {"xml", "extended", "timestamp", "id", "ktimestamp"}, 5, + { _O_XML, _O_EXT, _O_TMS, _O_ID, _O_KTMS }, }, }; @@ -446,6 +788,19 @@ parse_parameter(const char *arg, unsigned int *status, int parse_type) } static void +parse_u32_mask(const char *arg, struct u32_mask *m) +{ + char *end; + + m->value = (uint32_t) strtoul(arg, &end, 0); + + if (*end == '/') + m->mask = (uint32_t) strtoul(end+1, NULL, 0); + else + m->mask = ~0; +} + +static void add_command(unsigned int *cmd, const int newcmd) { if (*cmd) @@ -503,8 +858,7 @@ parse_inetaddr(const char *cp, struct addr_parse *parse) else if (inet_pton(AF_INET6, cp, &parse->addr6) > 0) return AF_INET6; #endif - - exit_error(PARAMETER_PROBLEM, "Invalid IP address `%s'", cp); + return AF_UNSPEC; } union ct_address { @@ -526,9 +880,8 @@ parse_addr(const char *cp, union ct_address *address) 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) +nat_parse(char *arg, struct nf_conntrack *obj, int type) { char *colon, *error; union ct_address parse; @@ -538,14 +891,18 @@ nat_parse(char *arg, int portok, struct nf_conntrack *obj, int type) if (colon) { uint16_t port; - if (!portok) - exit_error(PARAMETER_PROBLEM, - "Need TCP or UDP with port specification"); + *colon = '\0'; port = (uint16_t)atoi(colon+1); - if (port == 0) - exit_error(PARAMETER_PROBLEM, - "Port `%s' not valid", colon+1); + if (port == 0) { + if (strlen(colon+1) == 0) { + exit_error(PARAMETER_PROBLEM, + "No port specified after `:'"); + } else { + exit_error(PARAMETER_PROBLEM, + "Port `%s' not valid", colon+1); + } + } error = strchr(colon+1, ':'); if (error) @@ -553,66 +910,30 @@ nat_parse(char *arg, int portok, struct nf_conntrack *obj, int type) "Invalid port:port syntax"); if (type == CT_OPT_SRC_NAT) - nfct_set_attr_u16(obj, ATTR_SNAT_PORT, port); + nfct_set_attr_u16(tmpl.ct, ATTR_SNAT_PORT, ntohs(port)); else if (type == CT_OPT_DST_NAT) - nfct_set_attr_u16(obj, ATTR_DNAT_PORT, port); + nfct_set_attr_u16(tmpl.ct, ATTR_DNAT_PORT, ntohs(port)); + else if (type == CT_OPT_ANY_NAT) { + nfct_set_attr_u16(tmpl.ct, ATTR_SNAT_PORT, ntohs(port)); + nfct_set_attr_u16(tmpl.ct, ATTR_DNAT_PORT, ntohs(port)); + } } - if (parse_addr(arg, &parse) != AF_INET) - return; + if (parse_addr(arg, &parse) == AF_UNSPEC) { + if (strlen(arg) == 0) { + exit_error(PARAMETER_PROBLEM, "No IP specified"); + } else { + exit_error(PARAMETER_PROBLEM, + "Invalid IP address `%s'", arg); + } + } - 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); + if (type == CT_OPT_SRC_NAT || type == CT_OPT_ANY_NAT) + nfct_set_attr_u32(tmpl.ct, ATTR_SNAT_IPV4, parse.v4); + else if (type == CT_OPT_DST_NAT || type == CT_OPT_ANY_NAT) + nfct_set_attr_u32(tmpl.ct, 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) { @@ -629,31 +950,97 @@ usage(char *prog) static unsigned int output_mask; + +static int +filter_mark(const struct nf_conntrack *ct) +{ + if ((options & CT_OPT_MARK) && + !mark_cmp(&tmpl.mark, ct)) + return 1; + return 0; +} + + static int filter_nat(const struct nf_conntrack *obj, const struct nf_conntrack *ct) { + int check_srcnat = options & CT_OPT_SRC_NAT ? 1 : 0; + int check_dstnat = options & CT_OPT_DST_NAT ? 1 : 0; + int has_srcnat = 0, has_dstnat = 0; uint32_t ip; + uint16_t port; + + if (options & CT_OPT_ANY_NAT) + check_srcnat = check_dstnat = 1; - if (options & CT_OPT_SRC_NAT) { - if (!nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT)) - return 1; + if (check_srcnat) { + int check_address = 0, check_port = 0; if (nfct_attr_is_set(obj, ATTR_SNAT_IPV4)) { + check_address = 1; ip = nfct_get_attr_u32(obj, ATTR_SNAT_IPV4); - if (ip != nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST)) - return 1; + if (nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT) && + ip == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_DST)) + has_srcnat = 1; + } + if (nfct_attr_is_set(obj, ATTR_SNAT_PORT)) { + int ret = 0; + + check_port = 1; + port = nfct_get_attr_u16(obj, ATTR_SNAT_PORT); + if (nfct_getobjopt(ct, NFCT_GOPT_IS_SPAT) && + port == nfct_get_attr_u16(ct, ATTR_REPL_PORT_DST)) + ret = 1; + + /* the address matches but the port does not. */ + if (check_address && has_srcnat && !ret) + has_srcnat = 0; + if (!check_address && ret) + has_srcnat = 1; } + if (!check_address && !check_port && + (nfct_getobjopt(ct, NFCT_GOPT_IS_SNAT) || + nfct_getobjopt(ct, NFCT_GOPT_IS_SPAT))) + has_srcnat = 1; } - if (options & CT_OPT_DST_NAT) { - if (!nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT)) - return 1; + if (check_dstnat) { + int check_address = 0, check_port = 0; if (nfct_attr_is_set(obj, ATTR_DNAT_IPV4)) { + check_address = 1; ip = nfct_get_attr_u32(obj, ATTR_DNAT_IPV4); - if (ip != nfct_get_attr_u32(ct, ATTR_REPL_IPV4_SRC)) - return 1; + if (nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT) && + ip == nfct_get_attr_u32(ct, ATTR_REPL_IPV4_SRC)) + has_dstnat = 1; } + if (nfct_attr_is_set(obj, ATTR_DNAT_PORT)) { + int ret = 0; + + check_port = 1; + port = nfct_get_attr_u16(obj, ATTR_DNAT_PORT); + if (nfct_getobjopt(ct, NFCT_GOPT_IS_DPAT) && + port == nfct_get_attr_u16(ct, ATTR_REPL_PORT_SRC)) + ret = 1; + + /* the address matches but the port does not. */ + if (check_address && has_dstnat && !ret) + has_dstnat = 0; + if (!check_address && ret) + has_dstnat = 1; + } + if (!check_address && !check_port && + (nfct_getobjopt(ct, NFCT_GOPT_IS_DNAT) || + nfct_getobjopt(ct, NFCT_GOPT_IS_DPAT))) + has_dstnat = 1; } + if (options & CT_OPT_ANY_NAT) + return !(has_srcnat || has_dstnat); + else if ((options & CT_OPT_SRC_NAT) && (options & CT_OPT_DST_NAT)) + return !(has_srcnat && has_dstnat); + else if (options & CT_OPT_SRC_NAT) + return !has_srcnat; + else if (options & CT_OPT_DST_NAT) + return !has_dstnat; return 0; } @@ -687,6 +1074,9 @@ static int event_cb(enum nf_conntrack_msg_type type, if (filter_nat(obj, ct)) return NFCT_CB_CONTINUE; + if (filter_mark(ct)) + return NFCT_CB_CONTINUE; + if (options & CT_COMPARISON && !nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK)) return NFCT_CB_CONTINUE; @@ -709,6 +1099,8 @@ static int event_cb(enum nf_conntrack_msg_type type, } else op_flags |= NFCT_OF_TIME; } + if (output_mask & _O_KTMS) + op_flags |= NFCT_OF_TIMESTAMP; if (output_mask & _O_ID) op_flags |= NFCT_OF_ID; @@ -734,6 +1126,9 @@ static int dump_cb(enum nf_conntrack_msg_type type, if (filter_nat(obj, ct)) return NFCT_CB_CONTINUE; + if (filter_mark(ct)) + return NFCT_CB_CONTINUE; + if (options & CT_COMPARISON && !nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK)) return NFCT_CB_CONTINUE; @@ -748,6 +1143,8 @@ static int dump_cb(enum nf_conntrack_msg_type type, } if (output_mask & _O_EXT) op_flags = NFCT_OF_SHOW_LAYER3; + if (output_mask & _O_KTMS) + op_flags |= NFCT_OF_TIMESTAMP; if (output_mask & _O_ID) op_flags |= NFCT_OF_ID; @@ -772,6 +1169,9 @@ static int delete_cb(enum nf_conntrack_msg_type type, if (filter_nat(obj, ct)) return NFCT_CB_CONTINUE; + if (filter_mark(ct)) + return NFCT_CB_CONTINUE; + if (options & CT_COMPARISON && !nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK)) return NFCT_CB_CONTINUE; @@ -818,16 +1218,23 @@ static int print_cb(enum nf_conntrack_msg_type type, return NFCT_CB_CONTINUE; } +static void copy_mark(struct nf_conntrack *tmp, + const struct nf_conntrack *ct, + const struct u32_mask *m) +{ + if (options & CT_OPT_MARK) { + uint32_t mark = nfct_get_attr_u32(ct, ATTR_MARK); + mark = (mark & ~m->mask) ^ m->value; + nfct_set_attr_u32(tmp, ATTR_MARK, mark); + } +} + 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)); + struct nf_conntrack *obj = data, *tmp; if (filter_nat(obj, ct)) return NFCT_CB_CONTINUE; @@ -841,30 +1248,42 @@ static int update_cb(enum nf_conntrack_msg_type type, if (options & CT_OPT_TUPLE_REPL && !nfct_cmp(obj, ct, NFCT_CMP_REPL)) return NFCT_CB_CONTINUE; + tmp = nfct_new(); + if (tmp == NULL) + exit_error(OTHER_PROBLEM, "out of memory"); + nfct_copy(tmp, ct, NFCT_CP_ORIG); nfct_copy(tmp, obj, NFCT_CP_META); + copy_mark(tmp, ct, &tmpl.mark); + + /* do not send NFCT_Q_UPDATE if ct appears unchanged */ + if (nfct_cmp(tmp, ct, NFCT_CMP_ALL | NFCT_CMP_MASK)) { + nfct_destroy(tmp); + return NFCT_CB_CONTINUE; + } res = nfct_query(ith, NFCT_Q_UPDATE, tmp); - if (res < 0) + if (res < 0) { + nfct_destroy(tmp); 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) { + nfct_destroy(tmp); /* 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_destroy(tmp); nfct_callback_unregister(ith); counter++; @@ -885,6 +1304,18 @@ static int dump_exp_cb(enum nf_conntrack_msg_type type, return NFCT_CB_CONTINUE; } +static int event_exp_cb(enum nf_conntrack_msg_type type, + struct nf_expect *exp, void *data) +{ + char buf[1024]; + + nfexp_snprintf(buf,sizeof(buf), exp, type, 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) @@ -926,10 +1357,9 @@ static int display_proc_conntrack_stats(void) /* trim off trailing \n */ nl = strchr(buf, '\n'); - if (nl != NULL) { + 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); @@ -964,66 +1394,6 @@ out_err: 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; @@ -1031,23 +1401,13 @@ int main(int argc, char *argv[]) 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)); + /* we release these objects in the exit_error() path. */ + if (!alloc_tmpl_objects()) + exit_error(OTHER_PROBLEM, "out of memory"); register_tcp(); register_udp(); @@ -1062,10 +1422,7 @@ int main(int argc, char *argv[]) /* 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) { + while ((c = getopt_long(argc, argv, getopt_str, opts, NULL)) != -1) { switch(c) { /* commands */ case 'L': @@ -1099,17 +1456,21 @@ int main(int argc, char *argv[]) options |= opt2type[c]; l3protonum = parse_addr(optarg, &ad); + if (l3protonum == AF_UNSPEC) { + exit_error(PARAMETER_PROBLEM, + "Invalid IP address `%s'", optarg); + } set_family(&family, l3protonum); if (l3protonum == AF_INET) { - nfct_set_attr_u32(obj, + nfct_set_attr_u32(tmpl.ct, opt2family_attr[c][0], ad.v4); } else if (l3protonum == AF_INET6) { - nfct_set_attr(obj, + nfct_set_attr(tmpl.ct, opt2family_attr[c][1], &ad.v6); } - nfct_set_attr_u8(obj, opt2attr[c], l3protonum); + nfct_set_attr_u8(tmpl.ct, opt2attr[c], l3protonum); break; case '{': case '}': @@ -1117,17 +1478,22 @@ int main(int argc, char *argv[]) case ']': options |= opt2type[c]; l3protonum = parse_addr(optarg, &ad); + if (l3protonum == AF_UNSPEC) { + exit_error(PARAMETER_PROBLEM, + "Invalid IP address `%s'", optarg); + } set_family(&family, l3protonum); if (l3protonum == AF_INET) { - nfct_set_attr_u32(mask, + nfct_set_attr_u32(tmpl.mask, opt2family_attr[c][0], ad.v4); } else if (l3protonum == AF_INET6) { - nfct_set_attr(mask, + nfct_set_attr(tmpl.mask, opt2family_attr[c][1], &ad.v6); } - nfct_set_attr_u8(mask, ATTR_ORIG_L3PROTO, l3protonum); + nfct_set_attr_u8(tmpl.mask, + ATTR_ORIG_L3PROTO, l3protonum); break; case 'p': options |= CT_OPT_PROTO; @@ -1141,17 +1507,18 @@ int main(int argc, char *argv[]) if (opts == NULL) exit_error(OTHER_PROBLEM, "out of memory"); - nfct_set_attr_u8(obj, ATTR_L4PROTO, protonum); + nfct_set_attr_u8(tmpl.ct, 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)); + nfct_set_attr_u32(tmpl.ct, ATTR_TIMEOUT, atol(optarg)); + nfexp_set_attr_u32(tmpl.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); + nfct_set_attr_u32(tmpl.ct, ATTR_STATUS, status); break; case 'e': options |= CT_OPT_EVENT_MASK; @@ -1165,7 +1532,8 @@ int main(int argc, char *argv[]) options |= CT_OPT_ZERO; break; case 'n': - case 'g': { + case 'g': + case 'j': { char *tmp = NULL; options |= opt2type[c]; @@ -1180,17 +1548,26 @@ int main(int argc, char *argv[]) continue; set_family(&family, AF_INET); - nat_parse(tmp, 1, obj, opt2type[c]); + nat_parse(tmp, tmpl.ct, opt2type[c]); break; } + case 'w': + options |= opt2type[c]; + nfct_set_attr_u16(tmpl.ct, + opt2attr[c], + strtoul(optarg, NULL, 0)); + break; case 'i': - case 'm': case 'c': options |= opt2type[c]; - nfct_set_attr_u32(obj, + nfct_set_attr_u32(tmpl.ct, opt2attr[c], strtoul(optarg, NULL, 0)); break; + case 'm': + options |= opt2type[c]; + parse_u32_mask(optarg, &tmpl.mark); + break; case 'a': fprintf(stderr, "WARNING: ignoring -%c, " "deprecated option.\n", c); @@ -1222,8 +1599,9 @@ int main(int argc, char *argv[]) break; default: if (h && h->parse_opts - &&!h->parse_opts(c - h->option_offset, obj, - exptuple, mask, &l4flags)) + &&!h->parse_opts(c - h->option_offset, tmpl.ct, + tmpl.exptuple, tmpl.mask, + &l4flags)) exit_error(PARAMETER_PROBLEM, "parse error"); break; } @@ -1233,6 +1611,12 @@ int main(int argc, char *argv[]) if (family == AF_UNSPEC) family = AF_INET; + /* we cannot check this combination with generic_opt_check. */ + if (options & CT_OPT_ANY_NAT && + ((options & CT_OPT_SRC_NAT) || (options & CT_OPT_DST_NAT))) { + exit_error(PARAMETER_PROBLEM, "cannot specify `--src-nat' or " + "`--dst-nat' with `--any-nat'"); + } cmd = bit2cmd(command); res = generic_opt_check(options, NUMBER_OF_OPT, commands_v_options[cmd], optflags, @@ -1253,7 +1637,7 @@ int main(int argc, char *argv[]) } } if (!(command & CT_HELP) && h && h->final_check) - h->final_check(l4flags, cmd, obj); + h->final_check(l4flags, cmd, tmpl.ct); switch(command) { @@ -1267,7 +1651,7 @@ int main(int argc, char *argv[]) exit_error(PARAMETER_PROBLEM, "Can't use -z with " "filtering parameters"); - nfct_callback_register(cth, NFCT_T_ALL, dump_cb, obj); + nfct_callback_register(cth, NFCT_T_ALL, dump_cb, tmpl.ct); if (options & CT_OPT_ZERO) res = nfct_query(cth, NFCT_Q_DUMP_RESET, &family); @@ -1294,30 +1678,33 @@ int main(int argc, char *argv[]) case CT_CREATE: if ((options & CT_OPT_ORIG) && !(options & CT_OPT_REPL)) - nfct_setobjopt(obj, NFCT_SOPT_SETUP_REPLY); + nfct_setobjopt(tmpl.ct, NFCT_SOPT_SETUP_REPLY); else if (!(options & CT_OPT_ORIG) && (options & CT_OPT_REPL)) - nfct_setobjopt(obj, NFCT_SOPT_SETUP_ORIGINAL); + nfct_setobjopt(tmpl.ct, NFCT_SOPT_SETUP_ORIGINAL); + + if (options & CT_OPT_MARK) + nfct_set_attr_u32(tmpl.ct, ATTR_MARK, tmpl.mark.value); cth = nfct_open(CONNTRACK, 0); if (!cth) exit_error(OTHER_PROBLEM, "Can't open handler"); - res = nfct_query(cth, NFCT_Q_CREATE, obj); + res = nfct_query(cth, NFCT_Q_CREATE, tmpl.ct); 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); + nfexp_set_attr(tmpl.exp, ATTR_EXP_MASTER, tmpl.ct); + nfexp_set_attr(tmpl.exp, ATTR_EXP_EXPECTED, tmpl.exptuple); + nfexp_set_attr(tmpl.exp, ATTR_EXP_MASK, tmpl.mask); cth = nfct_open(EXPECT, 0); if (!cth) exit_error(OTHER_PROBLEM, "Can't open handler"); - res = nfexp_query(cth, NFCT_Q_CREATE, exp); + res = nfexp_query(cth, NFCT_Q_CREATE, tmpl.exp); nfct_close(cth); break; @@ -1328,7 +1715,7 @@ int main(int argc, char *argv[]) if (!cth || !ith) exit_error(OTHER_PROBLEM, "Can't open handler"); - nfct_callback_register(cth, NFCT_T_ALL, update_cb, obj); + nfct_callback_register(cth, NFCT_T_ALL, update_cb, tmpl.ct); res = nfct_query(cth, NFCT_Q_DUMP, &family); nfct_close(ith); @@ -1341,7 +1728,7 @@ int main(int argc, char *argv[]) if (!cth || !ith) exit_error(OTHER_PROBLEM, "Can't open handler"); - nfct_callback_register(cth, NFCT_T_ALL, delete_cb, obj); + nfct_callback_register(cth, NFCT_T_ALL, delete_cb, tmpl.ct); res = nfct_query(cth, NFCT_Q_DUMP, &family); nfct_close(ith); @@ -1349,13 +1736,13 @@ int main(int argc, char *argv[]) break; case EXP_DELETE: - nfexp_set_attr(exp, ATTR_EXP_EXPECTED, obj); + nfexp_set_attr(tmpl.exp, ATTR_EXP_EXPECTED, tmpl.ct); cth = nfct_open(EXPECT, 0); if (!cth) exit_error(OTHER_PROBLEM, "Can't open handler"); - res = nfexp_query(cth, NFCT_Q_DESTROY, exp); + res = nfexp_query(cth, NFCT_Q_DESTROY, tmpl.exp); nfct_close(cth); break; @@ -1364,20 +1751,20 @@ int main(int argc, char *argv[]) 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_callback_register(cth, NFCT_T_ALL, dump_cb, tmpl.ct); + res = nfct_query(cth, NFCT_Q_GET, tmpl.ct); nfct_close(cth); break; case EXP_GET: - nfexp_set_attr(exp, ATTR_EXP_MASTER, obj); + nfexp_set_attr(tmpl.exp, ATTR_EXP_MASTER, tmpl.ct); 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); + res = nfexp_query(cth, NFCT_Q_GET, tmpl.exp); nfct_close(cth); break; @@ -1397,14 +1784,28 @@ int main(int argc, char *argv[]) exit_error(OTHER_PROBLEM, "Can't open handler"); res = nfexp_query(cth, NFCT_Q_FLUSH, &family); nfct_close(cth); + fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION); + fprintf(stderr,"expectation table has been emptied.\n"); break; case CT_EVENT: - if (options & CT_OPT_EVENT_MASK) + if (options & CT_OPT_EVENT_MASK) { + unsigned int nl_events = 0; + + if (event_mask & CT_EVENT_F_NEW) + nl_events |= NF_NETLINK_CONNTRACK_NEW; + if (event_mask & CT_EVENT_F_UPD) + nl_events |= NF_NETLINK_CONNTRACK_UPDATE; + if (event_mask & CT_EVENT_F_DEL) + nl_events |= NF_NETLINK_CONNTRACK_DESTROY; + + cth = nfct_open(CONNTRACK, nl_events); + } else { cth = nfct_open(CONNTRACK, - event_mask & NFCT_ALL_CT_GROUPS); - else - cth = nfct_open(CONNTRACK, NFCT_ALL_CT_GROUPS); + NF_NETLINK_CONNTRACK_NEW | + NF_NETLINK_CONNTRACK_UPDATE | + NF_NETLINK_CONNTRACK_DESTROY); + } if (!cth) exit_error(OTHER_PROBLEM, "Can't open handler"); @@ -1417,7 +1818,7 @@ int main(int argc, char *argv[]) } signal(SIGINT, event_sighandler); signal(SIGTERM, event_sighandler); - nfct_callback_register(cth, NFCT_T_ALL, event_cb, obj); + nfct_callback_register(cth, NFCT_T_ALL, event_cb, tmpl.ct); res = nfct_catch(cth); if (res == -1) { if (errno == ENOBUFS) { @@ -1434,12 +1835,29 @@ int main(int argc, char *argv[]) break; case EXP_EVENT: - cth = nfct_open(EXPECT, NF_NETLINK_CONNTRACK_EXP_NEW); + if (options & CT_OPT_EVENT_MASK) { + unsigned int nl_events = 0; + + if (event_mask & CT_EVENT_F_NEW) + nl_events |= NF_NETLINK_CONNTRACK_EXP_NEW; + if (event_mask & CT_EVENT_F_UPD) + nl_events |= NF_NETLINK_CONNTRACK_EXP_UPDATE; + if (event_mask & CT_EVENT_F_DEL) + nl_events |= NF_NETLINK_CONNTRACK_EXP_DESTROY; + + cth = nfct_open(CONNTRACK, nl_events); + } else { + cth = nfct_open(EXPECT, + NF_NETLINK_CONNTRACK_EXP_NEW | + NF_NETLINK_CONNTRACK_EXP_UPDATE | + NF_NETLINK_CONNTRACK_EXP_DESTROY); + } + 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); + nfexp_callback_register(cth, NFCT_T_ALL, event_exp_cb, NULL); res = nfexp_catch(cth); nfct_close(cth); break; @@ -1491,6 +1909,7 @@ int main(int argc, char *argv[]) exit_error(OTHER_PROBLEM, "Operation failed: %s", err2str(errno, command)); + free_tmpl_objects(); free_options(); if (command && exit_msg[cmd][0]) { |