From 02fd8b1fef9caf1da74e4e0d5ef3c16b2e4d37d7 Mon Sep 17 00:00:00 2001 From: James Guthrie Date: Wed, 20 Mar 2013 15:41:56 +0100 Subject: conntrackd: fix parsing of non-abbreviated IPv6 address in config file Both representations of this example IPv6 address should be accepted: fe80::1 fe80:0:0:0:0:0:0:1 This patch fixes the lexical parser for non-abbreviated version, which was not working. Signed-off-by: James Guthrie Signed-off-by: Roman Hoog Antink Signed-off-by: Pablo Neira Ayuso --- src/read_config_lex.l | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/read_config_lex.l b/src/read_config_lex.l index bec2d81..b4d11d4 100644 --- a/src/read_config_lex.l +++ b/src/read_config_lex.l @@ -44,8 +44,8 @@ ip4 {ip4_part}\.{ip4_part}\.{ip4_part}\.{ip4_part}{ip4_cidr}? hex_255 [0-9a-fA-F]{1,4} ip6_cidr \/[0-1]*[0-9]*[0-9]+ ip6_part {hex_255}":"? -ip6_form1 {ip6_part}{0,16}"::"{ip6_part}{0,16} -ip6_form2 ({hex_255}":"){16}{hex_255} +ip6_form1 {ip6_part}{0,7}"::"{ip6_part}{0,7} +ip6_form2 ({hex_255}":"){0,7}{hex_255} ip6 {ip6_form1}{ip6_cidr}?|{ip6_form2}{ip6_cidr}? string [a-zA-Z][a-zA-Z0-9\.\-]* persistent [P|p][E|e][R|r][S|s][I|i][S|s][T|t][E|e][N|n][T|T] -- cgit v1.2.3 From ca99976f450afa0f7ec2f4320e863860990af61d Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Wed, 15 May 2013 16:38:30 +0200 Subject: conntrack: fix timestamps when microseconds are less than 100000 The fractional portion of timestamps reported by conntrack is printed as a left-justified integer instead of fixed-width and zero-padded. Closes netfilter's bugzilla 817: https://bugzilla.netfilter.org/show_bug.cgi?id=817 Reported-by: hoffman@stanford.edu Signed-off-by: Pablo Neira Ayuso --- src/conntrack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/conntrack.c b/src/conntrack.c index 227c355..d4e79de 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -1141,7 +1141,7 @@ static int event_cb(enum nf_conntrack_msg_type type, if (!(output_mask & _O_XML)) { struct timeval tv; gettimeofday(&tv, NULL); - printf("[%-8ld.%-6ld]\t", tv.tv_sec, tv.tv_usec); + printf("[%-.8ld.%-.6ld]\t", tv.tv_sec, tv.tv_usec); } else op_flags |= NFCT_OF_TIME; } -- cgit v1.2.3 From 6869ca4032411ac79e6f2cda3cbd401c444364ef Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 2 Jun 2013 23:18:24 +0000 Subject: conntrackd: fix compiler warnings main.c:359:6: warning: ignoring return value of 'nice' [..] main.c:395:7: warning: ignoring return value of 'chdir' [..] run.c:43:17: warning: declaration of 'signal' shadows a global declaration Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- src/main.c | 22 ++++++++++++++++++++-- src/run.c | 4 ++-- 2 files changed, 22 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/main.c b/src/main.c index 831a3c2..dafeaee 100644 --- a/src/main.c +++ b/src/main.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -112,6 +113,23 @@ set_action_by_table(int i, int argc, char *argv[], return i; } +static void +set_nice_value(int nv) +{ + errno = 0; + if (nice(nv) == -1 && errno) /* warn only */ + fprintf(stderr, "Cannot set nice level %d: %s\n", + nv, strerror(errno)); +} + +static void +do_chdir(const char *d) +{ + if (chdir(d)) + fprintf(stderr, "Cannot change current directory to %s: %s\n", + d, strerror(errno)); +} + int main(int argc, char *argv[]) { int ret, i, action = -1; @@ -356,7 +374,7 @@ int main(int argc, char *argv[]) /* * Setting process priority and scheduler */ - nice(CONFIG(nice)); + set_nice_value(CONFIG(nice)); if (CONFIG(sched).type != SCHED_OTHER) { struct sched_param schedparam = { @@ -382,7 +400,7 @@ int main(int argc, char *argv[]) exit(EXIT_FAILURE); } - chdir("/"); + do_chdir("/"); close(STDIN_FILENO); /* Daemonize conntrackd */ diff --git a/src/run.c b/src/run.c index 44a179f..7fa6889 100644 --- a/src/run.c +++ b/src/run.c @@ -40,14 +40,14 @@ #include #include -void killer(int signal) +void killer(int signo) { /* Signals are re-entrant, disable signal handling to avoid problems * in case we receive SIGINT and SIGTERM in a row. This function is * also called via -k from the unix socket context, we already disabled * signals in that path, so don't do it. */ - if (signal) + if (signo) sigprocmask(SIG_BLOCK, &STATE(block), NULL); local_server_destroy(&STATE(local)); -- cgit v1.2.3 From 140bde424c1381353f37ebc3395305f2acfcf546 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 7 Jun 2013 21:34:35 +0200 Subject: cthelper: add IPv6 support Signed-off-by: Pablo Neira Ayuso --- src/cthelper.c | 70 ++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/cthelper.c b/src/cthelper.c index 307be96..f333625 100644 --- a/src/cthelper.c +++ b/src/cthelper.c @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -77,6 +78,8 @@ nfq_hdr_put(char *buf, int type, uint32_t queue_num) static int pkt_get(void *pkt, uint32_t pktlen, uint16_t proto, uint32_t *protoff) { + uint8_t protocol; + switch(proto) { case ETHERTYPE_IP: { struct iphdr *ip = (struct iphdr *) pkt; @@ -94,41 +97,60 @@ pkt_get(void *pkt, uint32_t pktlen, uint16_t proto, uint32_t *protoff) } *protoff = 4 * ip->ihl; + protocol = ip->protocol; + break; + } + case ETHERTYPE_IPV6: { + struct iphdr *ip = (struct iphdr *) pkt; + struct ip6_hdr *ip6 = (struct ip6_hdr *) pkt; - switch (ip->protocol) { - case IPPROTO_TCP: { - struct tcphdr *tcph = - (struct tcphdr *) ((char *)pkt + *protoff); - - /* No room for IPv4 header plus TCP header. */ - if (pktlen < *protoff + sizeof(struct tcphdr) - || pktlen < *protoff + tcph->doff * 4) { - dlog(LOG_ERR, "no room for IPv4 + TCP header, skip"); - return -1; - } - return 0; + /* No room for IPv6 header. */ + if (pktlen < sizeof(struct ip6_hdr)) { + dlog(LOG_ERR, "no room for IPv6 header"); + return -1; } - case IPPROTO_UDP: - /* No room for IPv4 header plus UDP header. */ - if (pktlen < *protoff + sizeof(struct udphdr)) { - dlog(LOG_ERR, "no room for IPv4 + UDP header, skip"); - return -1; - } - return 0; - default: - dlog(LOG_ERR, "not TCP/UDP, skipping"); + + /* this is not IPv6, skip. */ + if (ip->version != 6) { + dlog(LOG_ERR, "not IPv6, skipping"); return -1; } + + *protoff = sizeof(struct ip6_hdr); + protocol = ip6->ip6_nxt; break; } - case ETHERTYPE_IPV6: - dlog(LOG_ERR, "no IPv6 support sorry"); - return 0; default: /* Unknown layer 3 protocol. */ dlog(LOG_ERR, "unknown layer 3 protocol (%d), skipping", proto); return -1; } + + switch (protocol) { + case IPPROTO_TCP: { + struct tcphdr *tcph = + (struct tcphdr *) ((char *)pkt + *protoff); + + /* No room for IPv4 header plus TCP header. */ + if (pktlen < *protoff + sizeof(struct tcphdr) || + pktlen < *protoff + tcph->doff * 4) { + dlog(LOG_ERR, "no room for IPv4 + TCP header, skip"); + return -1; + } + return 0; + } + case IPPROTO_UDP: + /* No room for IPv4 header plus UDP header. */ + if (pktlen < *protoff + sizeof(struct udphdr)) { + dlog(LOG_ERR, "no room for IPv4 + UDP header, skip"); + return -1; + } + return 0; + default: + dlog(LOG_ERR, "not TCP/UDP, skipping"); + return -1; + } + return 0; } -- cgit v1.2.3 From 01a4c0925c455723cbb7f026614c91a9bc8bf62a Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Fri, 7 Jun 2013 21:36:39 +0200 Subject: cthelper: helpers may not use private information area Signed-off-by: Pablo Neira Ayuso --- src/cthelper.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/cthelper.c b/src/cthelper.c index f333625..5a8a92a 100644 --- a/src/cthelper.c +++ b/src/cthelper.c @@ -165,9 +165,11 @@ pkt_verdict_issue(struct ctd_helper_instance *cur, struct myct *myct, nlh = nfq_hdr_put(buf, NFQNL_MSG_VERDICT, queue_num); - /* save private data and send it back to kernel-space. */ - nfct_set_attr_l(myct->ct, ATTR_HELPER_INFO, myct->priv_data, - cur->helper->priv_data_len); + /* save private data and send it back to kernel-space, if any. */ + if (myct->priv_data) { + nfct_set_attr_l(myct->ct, ATTR_HELPER_INFO, myct->priv_data, + cur->helper->priv_data_len); + } nfq_nlmsg_verdict_put(nlh, id, verdict); if (pktb_mangled(pktb)) -- cgit v1.2.3 From d343b8c554b6a04f6c477841dc4cbb89b5cd1bd9 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 4 Jul 2013 16:04:39 +0200 Subject: conntrack: add connlabel format attribute Signed-off-by: Florian Westphal --- conntrack.8 | 4 +++- src/conntrack.c | 17 +++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/conntrack.8 b/conntrack.8 index a411fd4..d80a778 100644 --- a/conntrack.8 +++ b/conntrack.8 @@ -88,11 +88,13 @@ Show the in-kernel connection tracking system statistics. Atomically zero counters after reading them. This option is only valid in combination with the "-L, --dump" command options. .TP -.BI "-o, --output [extended,xml,timestamp,id,ktimestamp] " +.BI "-o, --output [extended,xml,timestamp,id,ktimestamp,labels] " Display output in a certain format. With the extended output option, this tool displays the layer 3 information. With ktimestamp, it displays the in-kernel timestamp available since 2.6.38 (you can enable it via echo 1 > /proc/sys/net/netfilter/nf_conntrack_timestamp). +The labels output option tells conntrack to show the names of connection +tracking labels that might be present. .TP .BI "-e, --event-mask " "[ALL|NEW|UPDATES|DESTROY][,...]" Set the bitmask of events that are to be generated by the in-kernel ctnetlink diff --git a/src/conntrack.c b/src/conntrack.c index d4e79de..61e2fce 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -488,6 +488,7 @@ static unsigned int addr_valid_flags[ADDR_VALID_FLAGS_MAX] = { static LIST_HEAD(proto_list); static unsigned int options; +static struct nfct_labelmap *labelmap; void register_proto(struct ctproto_handler *h) { @@ -731,6 +732,7 @@ enum { _O_TMS = (1 << 2), _O_ID = (1 << 3), _O_KTMS = (1 << 4), + _O_CL = (1 << 5), }; enum { @@ -749,8 +751,8 @@ static struct parse_parameter { { IPS_ASSURED, IPS_SEEN_REPLY, 0, IPS_FIXED_TIMEOUT, IPS_EXPECTED} }, { {"ALL", "NEW", "UPDATES", "DESTROY"}, 4, { 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 }, + { {"xml", "extended", "timestamp", "id", "ktimestamp", "labels", }, 6, + { _O_XML, _O_EXT, _O_TMS, _O_ID, _O_KTMS, _O_CL }, }, }; @@ -1150,7 +1152,7 @@ static int event_cb(enum nf_conntrack_msg_type type, if (output_mask & _O_ID) op_flags |= NFCT_OF_ID; - nfct_snprintf(buf, sizeof(buf), ct, type, op_type, op_flags); + nfct_snprintf_labels(buf, sizeof(buf), ct, type, op_type, op_flags, labelmap); printf("%s\n", buf); fflush(stdout); @@ -1194,7 +1196,7 @@ static int dump_cb(enum nf_conntrack_msg_type type, if (output_mask & _O_ID) op_flags |= NFCT_OF_ID; - nfct_snprintf(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags); + nfct_snprintf_labels(buf, sizeof(buf), ct, type, op_type, op_flags, labelmap); printf("%s\n", buf); counter++; @@ -1879,6 +1881,11 @@ int main(int argc, char *argv[]) case 'o': options |= CT_OPT_OUTPUT; parse_parameter(optarg, &output_mask, PARSE_OUTPUT); + if (output_mask & _O_CL) { + labelmap = nfct_labelmap_new(NULL); + if (!labelmap) + perror("nfct_labelmap_new"); + } break; case 'z': options |= CT_OPT_ZERO; @@ -2372,6 +2379,8 @@ try_proc: free_tmpl_objects(); free_options(); + if (labelmap) + nfct_labelmap_destroy(labelmap); if (command && exit_msg[cmd][0]) { fprintf(stderr, "%s v%s (conntrack-tools): ",PROGNAME,VERSION); -- cgit v1.2.3 From c4ce3ffb9a9f0288376312206529eb1428f3aeca Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 6 Jul 2013 12:09:36 +0200 Subject: conntrackd: cache: fix hashing based on IPv6 address Use source and destination address, not only source address for hashing. Signed-off-by: Pablo Neira Ayuso --- src/cache-ct.c | 2 +- src/cache-exp.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/cache-ct.c b/src/cache-ct.c index 0ad8d2a..a538215 100644 --- a/src/cache-ct.c +++ b/src/cache-ct.c @@ -59,7 +59,7 @@ cache_hash6_ct(const struct nf_conntrack *ct, const struct hashtable *table) uint32_t a[10]; memcpy(&a[0], nfct_get_attr(ct, ATTR_IPV6_SRC), sizeof(uint32_t)*4); - memcpy(&a[4], nfct_get_attr(ct, ATTR_IPV6_SRC), sizeof(uint32_t)*4); + memcpy(&a[4], nfct_get_attr(ct, ATTR_IPV6_DST), sizeof(uint32_t)*4); a[8] = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO) << 16 | nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO); a[9] = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) << 16 | diff --git a/src/cache-exp.c b/src/cache-exp.c index e88877a..9183b2c 100644 --- a/src/cache-exp.c +++ b/src/cache-exp.c @@ -59,7 +59,7 @@ cache_hash6_exp(const struct nf_conntrack *ct, const struct hashtable *table) uint32_t a[10]; memcpy(&a[0], nfct_get_attr(ct, ATTR_IPV6_SRC), sizeof(uint32_t)*4); - memcpy(&a[4], nfct_get_attr(ct, ATTR_IPV6_SRC), sizeof(uint32_t)*4); + memcpy(&a[4], nfct_get_attr(ct, ATTR_IPV6_DST), sizeof(uint32_t)*4); a[8] = nfct_get_attr_u8(ct, ATTR_ORIG_L3PROTO) << 16 | nfct_get_attr_u8(ct, ATTR_ORIG_L4PROTO); a[9] = nfct_get_attr_u16(ct, ATTR_ORIG_PORT_SRC) << 16 | -- cgit v1.2.3 From e2c6576e775652c35d336afa0551676339c6a793 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 6 Jul 2013 14:48:04 +0200 Subject: conntrackd: deprecate `Family' in configuration file This patch deprecates the `Family' tweak in the configuration file. Several reasons for this: * If not specified, this was default to IPv4 only in table dumps from the kernel. However, non-IPv4 events were still received. This is inconsistent. * It's an early tweak that was not documented (not included in any of the example files). If we want to support any sort of consistent filtering based on the family, this should happen in the filtering code. After this patch, conntrackd uses AF_UNSPEC to dump the conntrack and expectation tables from the kernel. Reported-by: Bill Fink Signed-off-by: Pablo Neira Ayuso --- include/conntrackd.h | 1 - src/netlink.c | 12 ++++++------ src/read_config_yy.y | 9 +-------- 3 files changed, 7 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/include/conntrackd.h b/include/conntrackd.h index 19e613c..d338fc4 100644 --- a/include/conntrackd.h +++ b/include/conntrackd.h @@ -104,7 +104,6 @@ struct ct_conf { unsigned int netlink_buffer_size_max_grown; int nl_overrun_resync; unsigned int flags; - int family; /* protocol family */ unsigned int resend_queue_size; /* FTFW protocol */ unsigned int window_size; int poll_kernel_secs; diff --git a/src/netlink.c b/src/netlink.c index bd38d99..5be102e 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -146,9 +146,11 @@ void nl_resize_socket_buffer(struct nfct_handle *h) "to %u bytes", CONFIG(netlink_buffer_size)); } +static const int family = AF_UNSPEC; + int nl_dump_conntrack_table(struct nfct_handle *h) { - return nfct_query(h, NFCT_Q_DUMP, &CONFIG(family)); + return nfct_query(h, NFCT_Q_DUMP, &family); } static int @@ -182,7 +184,7 @@ int nl_flush_conntrack_table_selective(void) } nfct_callback_register(h, NFCT_T_ALL, nl_flush_selective_cb, NULL); - ret = nfct_query(h, NFCT_Q_DUMP, &CONFIG(family)); + ret = nfct_query(h, NFCT_Q_DUMP, &family); nfct_close(h); @@ -191,7 +193,6 @@ int nl_flush_conntrack_table_selective(void) int nl_send_resync(struct nfct_handle *h) { - int family = CONFIG(family); return nfct_send(h, NFCT_Q_DUMP, &family); } @@ -380,16 +381,15 @@ int nl_get_expect(struct nfct_handle *h, const struct nf_expect *exp) int nl_dump_expect_table(struct nfct_handle *h) { - return nfexp_query(h, NFCT_Q_DUMP, &CONFIG(family)); + return nfexp_query(h, NFCT_Q_DUMP, &family); } int nl_flush_expect_table(struct nfct_handle *h) { - return nfexp_query(h, NFCT_Q_FLUSH, &CONFIG(family)); + return nfexp_query(h, NFCT_Q_FLUSH, &family); } int nl_send_expect_resync(struct nfct_handle *h) { - int family = CONFIG(family); return nfexp_send(h, NFCT_Q_DUMP, &family); } diff --git a/src/read_config_yy.y b/src/read_config_yy.y index 72a9654..b824150 100644 --- a/src/read_config_yy.y +++ b/src/read_config_yy.y @@ -1193,10 +1193,7 @@ scheduler_line : T_PRIO T_NUMBER family : T_FAMILY T_STRING { - if (strncmp($2, "IPv6", strlen("IPv6")) == 0) - conf.family = AF_INET6; - else - conf.family = AF_INET; + print_err(CTD_CFG_WARN, "`Family' is deprecated, ignoring"); }; event_iterations_limit : T_EVENT_ITER_LIMIT T_NUMBER @@ -1863,10 +1860,6 @@ init_config(char *filename) yyparse(); fclose(fp); - /* default to IPv4 */ - if (CONFIG(family) == 0) - CONFIG(family) = AF_INET; - /* set to default is not specified */ if (strcmp(CONFIG(lockfile), "") == 0) strncpy(CONFIG(lockfile), DEFAULT_LOCKFILE, FILENAME_MAXLEN); -- cgit v1.2.3 From 479a37a549abf197ce59a4ae1666d8cba80fe977 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 6 Jul 2013 14:04:24 +0200 Subject: conntrackd: fix crash with IPv6 expectation in the filtering code Jul 5 00:41:06 sen-fw1 kernel: [274422.060695] conntrackd[4821]: segfault at 0 ip 000000000040c660 sp 00007fffebb098a8 error 4 in conntrackd[400000+3d000] > #0 0x000000000040f217 in jhash2 (k=0x0, length=4, initval=0) at ../include/jhash.h:99 > a = 2654435769 b = 2654435769 c = 0 len = 4 > #1 0x000000000040f564 in ct_filter_hash6 (data=0x0, table=0x16ef630) at filter.c:57 > #2 0x000000000040ad34 in hashtable_hash (table=0x16ef630, data=0x0) at hash.c:63 > #3 0x000000000040fd19 in __ct_filter_test_ipv6 (f=0x16eeba0, ct=0x1703760) at filter.c:265 > id_src = 51 id_dst = 24051376 src = 0x1703760 dst = 0x0 The master conntrack of the expectation has no reply tuple. However, the filtering routine needs it. To avoid this issue, emulate the source address in the reply tuple. While at it, fix incorrect sanity checking that should have caught this issue. Thanks to Florian Westphal for initial diagnosing of this bug. Reported-by: Bill Fink Signed-off-by: Pablo Neira Ayuso --- Make_global.am | 2 +- src/ctnl.c | 31 +++++++++++++++++++++++++------ src/filter.c | 8 ++++---- 3 files changed, 30 insertions(+), 11 deletions(-) (limited to 'src') diff --git a/Make_global.am b/Make_global.am index 23c7dd0..8084249 100644 --- a/Make_global.am +++ b/Make_global.am @@ -1,7 +1,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/include AM_CFLAGS = -std=gnu99 -W -Wall \ - -Wmissing-prototypes -Wwrite-strings -Wcast-qual -Wfloat-equal -Wshadow -Wpointer-arith -Wbad-function-cast -Wsign-compare -Waggregate-return -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wstrict-prototypes -Wundef \ + -Wmissing-prototypes -Wwrite-strings -Wfloat-equal -Wshadow -Wpointer-arith -Wbad-function-cast -Wsign-compare -Waggregate-return -Wmissing-declarations -Wredundant-decls -Wnested-externs -Winline -Wstrict-prototypes -Wundef \ -Wno-unused-parameter ${LIBNFNETLINK_CFLAGS} ${LIBMNL_CFLAGS} \ ${LIBNETFILTER_CONNTRACK_CFLAGS} \ ${LIBNETFILTER_CTTIMEOUT_CFLAGS} \ diff --git a/src/ctnl.c b/src/ctnl.c index bb54727..9e1cfa1 100644 --- a/src/ctnl.c +++ b/src/ctnl.c @@ -211,14 +211,35 @@ out: return NFCT_CB_CONTINUE; } +static const struct nf_conntrack *exp_get_master_ct(struct nf_expect *exp) +{ + struct nf_conntrack *master = + (struct nf_conntrack *)nfexp_get_attr(exp, ATTR_EXP_MASTER); + + /* The function ct_filter_conntrack needs the source address of the + * reply tuple, emulate it. + */ + switch (nfct_get_attr_u8(master, ATTR_L3PROTO)) { + case AF_INET: + nfct_set_attr_u32(master, ATTR_REPL_IPV4_SRC, + nfct_get_attr_u32(master, ATTR_IPV4_DST)); + break; + case AF_INET6: + nfct_set_attr(master, ATTR_REPL_IPV6_SRC, + nfct_get_attr(master, ATTR_IPV6_DST)); + break; + } + + return master; +} + static int exp_event_handler(const struct nlmsghdr *nlh, enum nf_conntrack_msg_type type, struct nf_expect *exp, void *data) { int origin_type; - const struct nf_conntrack *master = - nfexp_get_attr(exp, ATTR_EXP_MASTER); + const struct nf_conntrack *master = exp_get_master_ct(exp); STATE(stats).nl_events_received++; @@ -275,8 +296,7 @@ static int dump_handler(enum nf_conntrack_msg_type type, static int exp_dump_handler(enum nf_conntrack_msg_type type, struct nf_expect *exp, void *data) { - const struct nf_conntrack *master = - nfexp_get_attr(exp, ATTR_EXP_MASTER); + const struct nf_conntrack *master = exp_get_master_ct(exp); if (!exp_filter_find(STATE(exp_filter), exp)) return NFCT_CB_CONTINUE; @@ -309,8 +329,7 @@ static int get_handler(enum nf_conntrack_msg_type type, static int exp_get_handler(enum nf_conntrack_msg_type type, struct nf_expect *exp, void *data) { - const struct nf_conntrack *master = - nfexp_get_attr(exp, ATTR_EXP_MASTER); + const struct nf_conntrack *master = exp_get_master_ct(exp); if (!exp_filter_find(STATE(exp_filter), exp)) return NFCT_CB_CONTINUE; diff --git a/src/filter.c b/src/filter.c index 02a8078..e21cfde 100644 --- a/src/filter.c +++ b/src/filter.c @@ -373,8 +373,8 @@ static inline int ct_filter_sanity_check(const struct nf_conntrack *ct) switch(nfct_get_attr_u8(ct, ATTR_L3PROTO)) { case AF_INET: - if (!nfct_attr_is_set(ct, ATTR_IPV4_SRC) || - !nfct_attr_is_set(ct, ATTR_IPV4_DST)) { + if (!nfct_attr_is_set(ct, ATTR_ORIG_IPV4_SRC) || + !nfct_attr_is_set(ct, ATTR_REPL_IPV4_SRC)) { dlog(LOG_ERR, "missing IPv4 address. " "You forgot to load " "nf_conntrack_ipv4?"); @@ -382,8 +382,8 @@ static inline int ct_filter_sanity_check(const struct nf_conntrack *ct) } break; case AF_INET6: - if (!nfct_attr_is_set(ct, ATTR_IPV6_SRC) || - !nfct_attr_is_set(ct, ATTR_IPV6_DST)) { + if (!nfct_attr_is_set(ct, ATTR_ORIG_IPV6_SRC) || + !nfct_attr_is_set(ct, ATTR_REPL_IPV6_SRC)) { dlog(LOG_ERR, "missing IPv6 address. " "You forgot to load " "nf_conntrack_ipv6?"); -- cgit v1.2.3 From 586382d9a8389ee553db019fd9be14a8a7c0b8ec Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Thu, 11 Jul 2013 00:43:20 +0200 Subject: conntrackd: simplify expectation filtering This patch simplifies the expectation filtering by looking up for the master conntrack. If it does not exists, then we assume that we don't want this expectation either. This simplification also fixes the current broken expectation filtering, since the master conntrack from expectations has neither reply tuple nor state, however, the filtering code assumes the opposite. This partially reverts (479a37a conntrackd: fix crash with IPv6 expectation in the filtering code) since it was incorrectly setting the reply tuple of the master conntrack. Thanks to Bill Fink for providing feedback to resolve this issue. Signed-off-by: Pablo Neira Ayuso --- include/filter.h | 1 + include/internal.h | 1 + src/cache-ct.c | 11 +++++++++-- src/ctnl.c | 37 +++++++++---------------------------- src/filter.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ src/internal_bypass.c | 6 ++++++ src/internal_cache.c | 11 +++++++++++ 7 files changed, 82 insertions(+), 30 deletions(-) (limited to 'src') diff --git a/include/filter.h b/include/filter.h index 3c7c8cc..d0acd96 100644 --- a/include/filter.h +++ b/include/filter.h @@ -51,6 +51,7 @@ void ct_filter_set_logic(struct ct_filter *f, enum ct_filter_type type, enum ct_filter_logic logic); int ct_filter_conntrack(const struct nf_conntrack *ct, int userspace); +int ct_filter_master(const struct nf_conntrack *master); struct exp_filter; struct nf_expect; diff --git a/include/internal.h b/include/internal.h index 2ba9714..1a796a7 100644 --- a/include/internal.h +++ b/include/internal.h @@ -40,6 +40,7 @@ struct internal_handler { void (*new)(struct nf_expect *exp, int origin_type); void (*upd)(struct nf_expect *exp, int origin_type); int (*del)(struct nf_expect *exp, int origin_type); + int (*find)(const struct nf_conntrack *master); void (*dump)(int fd, int type); void (*populate)(struct nf_expect *exp); diff --git a/src/cache-ct.c b/src/cache-ct.c index a538215..f86d143 100644 --- a/src/cache-ct.c +++ b/src/cache-ct.c @@ -88,14 +88,21 @@ cache_ct_hash(const void *data, const struct hashtable *table) return ret; } +/* master conntrack of expectations have no ID */ +static inline int +cache_ct_cmp_id(const struct nf_conntrack *ct1, const struct nf_conntrack *ct2) +{ + return nfct_attr_is_set(ct2, ATTR_ID) ? + nfct_get_attr_u32(ct1, ATTR_ID) == nfct_get_attr_u32(ct2, ATTR_ID) : 1; +} + static int cache_ct_cmp(const void *data1, const void *data2) { const struct cache_object *obj = data1; const struct nf_conntrack *ct = data2; return nfct_cmp(obj->ptr, ct, NFCT_CMP_ORIG) && - nfct_get_attr_u32(obj->ptr, ATTR_ID) == - nfct_get_attr_u32(ct, ATTR_ID); + cache_ct_cmp_id(obj->ptr, ct); } static void *cache_ct_alloc(void) diff --git a/src/ctnl.c b/src/ctnl.c index 9e1cfa1..10b5f4c 100644 --- a/src/ctnl.c +++ b/src/ctnl.c @@ -211,35 +211,14 @@ out: return NFCT_CB_CONTINUE; } -static const struct nf_conntrack *exp_get_master_ct(struct nf_expect *exp) -{ - struct nf_conntrack *master = - (struct nf_conntrack *)nfexp_get_attr(exp, ATTR_EXP_MASTER); - - /* The function ct_filter_conntrack needs the source address of the - * reply tuple, emulate it. - */ - switch (nfct_get_attr_u8(master, ATTR_L3PROTO)) { - case AF_INET: - nfct_set_attr_u32(master, ATTR_REPL_IPV4_SRC, - nfct_get_attr_u32(master, ATTR_IPV4_DST)); - break; - case AF_INET6: - nfct_set_attr(master, ATTR_REPL_IPV6_SRC, - nfct_get_attr(master, ATTR_IPV6_DST)); - break; - } - - return master; -} - static int exp_event_handler(const struct nlmsghdr *nlh, enum nf_conntrack_msg_type type, struct nf_expect *exp, void *data) { int origin_type; - const struct nf_conntrack *master = exp_get_master_ct(exp); + const struct nf_conntrack *master = + nfexp_get_attr(exp, ATTR_EXP_MASTER); STATE(stats).nl_events_received++; @@ -247,7 +226,7 @@ static int exp_event_handler(const struct nlmsghdr *nlh, STATE(stats).nl_events_filtered++; goto out; } - if (ct_filter_conntrack(master, 1)) + if (ct_filter_master(master)) return NFCT_CB_CONTINUE; origin_type = origin_find(nlh); @@ -296,12 +275,13 @@ static int dump_handler(enum nf_conntrack_msg_type type, static int exp_dump_handler(enum nf_conntrack_msg_type type, struct nf_expect *exp, void *data) { - const struct nf_conntrack *master = exp_get_master_ct(exp); + const struct nf_conntrack *master = + nfexp_get_attr(exp, ATTR_EXP_MASTER); if (!exp_filter_find(STATE(exp_filter), exp)) return NFCT_CB_CONTINUE; - if (ct_filter_conntrack(master, 1)) + if (ct_filter_master(master)) return NFCT_CB_CONTINUE; switch(type) { @@ -329,12 +309,13 @@ static int get_handler(enum nf_conntrack_msg_type type, static int exp_get_handler(enum nf_conntrack_msg_type type, struct nf_expect *exp, void *data) { - const struct nf_conntrack *master = exp_get_master_ct(exp); + const struct nf_conntrack *master = + nfexp_get_attr(exp, ATTR_EXP_MASTER); if (!exp_filter_find(STATE(exp_filter), exp)) return NFCT_CB_CONTINUE; - if (ct_filter_conntrack(master, 1)) + if (ct_filter_master(master)) return NFCT_CB_CONTINUE; STATE(get_retval) = 1; diff --git a/src/filter.c b/src/filter.c index e21cfde..8fac71b 100644 --- a/src/filter.c +++ b/src/filter.c @@ -407,6 +407,51 @@ int ct_filter_conntrack(const struct nf_conntrack *ct, int userspace) return 0; } +static inline int +ct_filter_master_sanity_check(const struct nf_conntrack *master) +{ + if (master == NULL) { + dlog(LOG_ERR, "no master tuple in expectation"); + return 0; + } + + if (!nfct_attr_is_set(master, ATTR_L3PROTO)) { + dlog(LOG_ERR, "missing layer 3 protocol"); + return 0; + } + + switch (nfct_get_attr_u8(master, ATTR_L3PROTO)) { + case AF_INET: + if (!nfct_attr_is_set(master, ATTR_IPV4_SRC) || + !nfct_attr_is_set(master, ATTR_IPV4_DST)) { + dlog(LOG_ERR, "missing IPv4 address. " + "You forgot to load nf_conntrack_ipv4?"); + return 0; + } + break; + case AF_INET6: + if (!nfct_attr_is_set(master, ATTR_IPV6_SRC) || + !nfct_attr_is_set(master, ATTR_IPV6_DST)) { + dlog(LOG_ERR, "missing IPv6 address. " + "You forgot to load nf_conntrack_ipv6?"); + return 0; + } + break; + } + return 1; +} + +int ct_filter_master(const struct nf_conntrack *master) +{ + if (!ct_filter_master_sanity_check(master)) + return 1; + + /* Check if we've got a master conntrack for this expectation in our + * caches. If there is not, we don't want this expectation either. + */ + return STATE(mode)->internal->exp.find(master) ? 0 : 1; +} + struct exp_filter { struct list_head list; }; diff --git a/src/internal_bypass.c b/src/internal_bypass.c index 1194339..ce2ae46 100644 --- a/src/internal_bypass.c +++ b/src/internal_bypass.c @@ -283,6 +283,11 @@ static int internal_bypass_exp_event_del(struct nf_expect *exp, int origin) return 1; } +static int internal_bypass_exp_master_find(const struct nf_conntrack *master) +{ + return nl_get_conntrack(STATE(get), master) == 0; +} + struct internal_handler internal_bypass = { .init = internal_bypass_init, .close = internal_bypass_close, @@ -309,5 +314,6 @@ struct internal_handler internal_bypass = { .new = internal_bypass_exp_event_new, .upd = internal_bypass_exp_event_upd, .del = internal_bypass_exp_event_del, + .find = internal_bypass_exp_master_find, }, }; diff --git a/src/internal_cache.c b/src/internal_cache.c index ba2d74b..bad31f3 100644 --- a/src/internal_cache.c +++ b/src/internal_cache.c @@ -364,6 +364,16 @@ static int internal_cache_exp_event_del(struct nf_expect *exp, int origin) return 1; } +static int internal_cache_exp_master_find(const struct nf_conntrack *master) +{ + struct cache_object *obj; + int id; + + obj = cache_find(STATE(mode)->internal->ct.data, + (struct nf_conntrack *)master, &id); + return obj ? 1 : 0; +} + struct internal_handler internal_cache = { .flags = INTERNAL_F_POPULATE | INTERNAL_F_RESYNC, .init = internal_cache_init, @@ -391,5 +401,6 @@ struct internal_handler internal_cache = { .new = internal_cache_exp_event_new, .upd = internal_cache_exp_event_upd, .del = internal_cache_exp_event_del, + .find = internal_cache_exp_master_find, }, }; -- cgit v1.2.3 From 1239b83da27545e3275127ac339cdca29c872304 Mon Sep 17 00:00:00 2001 From: Clemence Faure Date: Tue, 9 Jul 2013 10:37:02 +0200 Subject: conntrack: introduce -l option to filter by labels Signed-off-by: Clemence Faure Signed-off-by: Florian Westphal --- conntrack.8 | 5 ++ include/conntrack.h | 2 +- src/conntrack.c | 156 +++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 137 insertions(+), 26 deletions(-) (limited to 'src') diff --git a/conntrack.8 b/conntrack.8 index d80a778..f273434 100644 --- a/conntrack.8 +++ b/conntrack.8 @@ -144,6 +144,11 @@ the MARK value into the ctmark. Otherwise, the mask is logically ANDed with the existing mark before the comparision. In "--create" mode, the mask is ignored. .TP +.BI "-l, --label " "LABEL,..." +Specify the conntrack labels. +This option is only available in conjunction with "-L, --dump" or "-E, --event". +Match entries whose labels matches at least those specified as arguments. +.TP .BI "-c, --secmark " "SECMARK" Specify the conntrack selinux security mark. .TP diff --git a/include/conntrack.h b/include/conntrack.h index fd6126b..6cd9962 100644 --- a/include/conntrack.h +++ b/include/conntrack.h @@ -10,7 +10,7 @@ #include #define NUMBER_OF_CMD 19 -#define NUMBER_OF_OPT 24 +#define NUMBER_OF_OPT 25 struct ctproto_handler { struct list_head head; diff --git a/src/conntrack.c b/src/conntrack.c index 61e2fce..353ff61 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -79,6 +79,9 @@ static struct { /* Allow to filter by mark from kernel-space. */ struct nfct_filter_dump_mark filter_mark_kernel; + + /* Allows filtering by ctlabels */ + struct nfct_bitmask *label; } tmpl; static int alloc_tmpl_objects(void) @@ -104,6 +107,8 @@ static void free_tmpl_objects(void) nfct_destroy(tmpl.mask); if (tmpl.exp) nfexp_destroy(tmpl.exp); + if (tmpl.label) + nfct_bitmask_destroy(tmpl.label); } enum ct_command { @@ -247,13 +252,16 @@ enum ct_options { CT_OPT_ZONE_BIT = 23, CT_OPT_ZONE = (1 << CT_OPT_ZONE_BIT), + + CT_OPT_LABEL_BIT = 24, + CT_OPT_LABEL = (1 << CT_OPT_LABEL_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) + CT_OPT_ID | CT_OPT_ZONE | CT_OPT_LABEL) static const char *optflags[NUMBER_OF_OPT] = { [CT_OPT_ORIG_SRC_BIT] = "src", @@ -280,6 +288,7 @@ static const char *optflags[NUMBER_OF_OPT] = { [CT_OPT_BUFFERSIZE_BIT] = "buffer-size", [CT_OPT_ANY_NAT_BIT] = "any-nat", [CT_OPT_ZONE_BIT] = "zone", + [CT_OPT_LABEL_BIT] = "label", }; static struct option original_opts[] = { @@ -320,12 +329,13 @@ static struct option original_opts[] = { {"buffer-size", 1, 0, 'b'}, {"any-nat", 2, 0, 'j'}, {"zone", 1, 0, 'w'}, + {"label", 1, 0, 'l'}, {0, 0, 0, 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:"; + "g::c:b:C::Sj::w:l:"; /* Table of legal combinations of commands and options. If any of the * given commands make an option legal, that option is legal (applies to @@ -340,26 +350,26 @@ static const char *getopt_str = "L::I::U::D::G::E::F::hVs:d:r:q:" 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 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,2,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,2,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}, -/*CT_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}, -/*EXP_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}, + /* s d r q p t u z e [ ] { } a m i f n g o c b j w l*/ +/*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,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,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,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,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,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,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,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,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,0}, +/*EXP_LIST*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,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,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,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,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,0}, +/*EXP_EVENT*/ {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,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,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,0}, +/*CT_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,0}, +/*EXP_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,0}, }; static const int cmd2type[][2] = { @@ -391,6 +401,7 @@ static const int opt2type[] = { ['i'] = CT_OPT_ID, ['j'] = CT_OPT_ANY_NAT, ['w'] = CT_OPT_ZONE, + ['l'] = CT_OPT_LABEL, }; static const int opt2family_attr[][2] = { @@ -413,6 +424,7 @@ static const int opt2attr[] = { ['c'] = ATTR_SECMARK, ['i'] = ATTR_ID, ['w'] = ATTR_ZONE, + ['l'] = ATTR_CONNLABELS, }; static char exit_msg[NUMBER_OF_CMD][64] = { @@ -450,7 +462,8 @@ static const char usage_conntrack_parameters[] = " -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"; + " -o, --output type[,...]\t\tOutput format, eg. xml\n" + " -l, --label label[,...]\t\tconntrack labels\n"; static const char usage_expectation_parameters[] = "Expectation parameters and options:\n" @@ -816,6 +829,59 @@ parse_u32_mask(const char *arg, struct u32_mask *m) m->mask = ~0; } +static int +get_label(char *name) +{ + int bit = nfct_labelmap_get_bit(labelmap, name); + if (bit < 0) + exit_error(PARAMETER_PROBLEM, "unknown label '%s'", name); + return bit; +} + +static void +set_label(struct nfct_bitmask *b, char *name) +{ + int bit = get_label(name); + nfct_bitmask_set_bit(b, bit); +} + +static unsigned int +set_max_label(char *name, unsigned int current_max) +{ + int bit = get_label(name); + if ((unsigned int) bit > current_max) + return (unsigned int) bit; + return current_max; +} + +static unsigned int +parse_label_get_max(char *arg) +{ + unsigned int max = 0; + char *parse; + + while ((parse = strchr(arg, ',')) != NULL) { + parse[0] = '\0'; + max = set_max_label(arg, max); + arg = &parse[1]; + } + + max = set_max_label(arg, max); + return max; +} + +static void +parse_label(struct nfct_bitmask *b, char *arg) +{ + char * parse; + while ((parse = strchr(arg, ',')) != NULL) { + parse[0] = '\0'; + set_label(b, arg); + arg = &parse[1]; + } + set_label(b, arg); +} + static void add_command(unsigned int *cmd, const int newcmd) { @@ -984,6 +1050,24 @@ usage(char *prog) static unsigned int output_mask; +static int +filter_label(const struct nf_conntrack *ct) +{ + if (tmpl.label == NULL) + return 0; + + const struct nfct_bitmask *ctb = nfct_get_attr(ct, ATTR_CONNLABELS); + if (ctb == NULL) + return 1; + + for (unsigned int i = 0; i <= nfct_bitmask_maxbit(tmpl.label); i++) { + if (nfct_bitmask_test_bit(tmpl.label, i) && + !nfct_bitmask_test_bit(ctb, i)) + return 1; + } + + return 0; +} static int filter_mark(const struct nf_conntrack *ct) @@ -994,7 +1078,6 @@ filter_mark(const struct nf_conntrack *ct) return 0; } - static int filter_nat(const struct nf_conntrack *obj, const struct nf_conntrack *ct) { @@ -1125,6 +1208,9 @@ static int event_cb(enum nf_conntrack_msg_type type, if (filter_mark(ct)) return NFCT_CB_CONTINUE; + if (filter_label(ct)) + return NFCT_CB_CONTINUE; + if (options & CT_COMPARISON && !nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK)) return NFCT_CB_CONTINUE; @@ -1177,6 +1263,9 @@ static int dump_cb(enum nf_conntrack_msg_type type, if (filter_mark(ct)) return NFCT_CB_CONTINUE; + if (filter_label(ct)) + return NFCT_CB_CONTINUE; + if (options & CT_COMPARISON && !nfct_cmp(obj, ct, NFCT_CMP_ALL | NFCT_CMP_MASK)) return NFCT_CB_CONTINUE; @@ -1882,7 +1971,8 @@ int main(int argc, char *argv[]) options |= CT_OPT_OUTPUT; parse_parameter(optarg, &output_mask, PARSE_OUTPUT); if (output_mask & _O_CL) { - labelmap = nfct_labelmap_new(NULL); + if (!labelmap) + labelmap = nfct_labelmap_new(NULL); if (!labelmap) perror("nfct_labelmap_new"); } @@ -1929,6 +2019,22 @@ int main(int argc, char *argv[]) tmpl.filter_mark_kernel.val = tmpl.mark.value; tmpl.filter_mark_kernel.mask = tmpl.mark.mask; break; + case 'l': + options |= opt2type[c]; + char *optarg2 = strdup(optarg); + + if (!labelmap) + labelmap = nfct_labelmap_new(NULL); + if (!labelmap) + exit_error(OTHER_PROBLEM, "unable to open labelmap file"); + + unsigned int max = parse_label_get_max(optarg); + struct nfct_bitmask * b = nfct_bitmask_new(max); + + parse_label(b, optarg2); + tmpl.label = b; + free(optarg2); + break; case 'a': fprintf(stderr, "WARNING: ignoring -%c, " "deprecated option.\n", c); -- cgit v1.2.3 From c9cba32f4820a9febee116bbc268ec8b1ae9a04c Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 30 Jun 2013 23:10:47 +0200 Subject: conntrackd: support replication of connlabels - check if ct has label attribute, and at least one label (bit) is set - serialize bitmap into array-of-u32, in network byte order - add code to build new nfct_bitmask object from array-of-u32 Current parse functions don't have length information, this adds optional parse2() which gets struct netattr pointer. Attributes that want to use parse2 need to set .maxsize to nonzero value. Signed-off-by: Florian Westphal --- include/network.h | 4 +++ src/build.c | 39 ++++++++++++++++++++++++++++ src/parse.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 110 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/include/network.h b/include/network.h index 79745f3..cc312cb 100644 --- a/include/network.h +++ b/include/network.h @@ -228,9 +228,13 @@ enum nta_attr { NTA_TCP_WSCALE_ORIG, /* uint8_t */ NTA_TCP_WSCALE_REPL, /* uint8_t */ NTA_HELPER_NAME, /* string (variable length) */ + NTA_LABELS, /* array of uint32_t (variable length) */ NTA_MAX }; +/* allow to serialize/replicate up to 4k labels per flow */ +#define NTA_LABELS_MAX_SIZE (4096/sizeof(uint32_t)) + struct nta_attr_natseqadj { uint32_t orig_seq_correction_pos; uint32_t orig_seq_offset_before; diff --git a/src/build.c b/src/build.c index e15eb4f..5799b51 100644 --- a/src/build.c +++ b/src/build.c @@ -158,6 +158,42 @@ static void build_l4proto_udp(const struct nf_conntrack *ct, struct nethdr *n) sizeof(struct nfct_attr_grp_port)); } +static void ct_build_clabel(const struct nf_conntrack *ct, struct nethdr *n) +{ + const struct nfct_bitmask *b; + uint32_t *words; + unsigned int wordcount, i, maxbit; + + if (!nfct_attr_is_set(ct, ATTR_CONNLABELS)) + return; + + b = nfct_get_attr(ct, ATTR_CONNLABELS); + + maxbit = nfct_bitmask_maxbit(b); + for (i=0; i <= maxbit; i++) { + if (nfct_bitmask_test_bit(b, i)) + break; + } + + if (i > maxbit) + return; + + wordcount = (nfct_bitmask_maxbit(b) / 32) + 1; + words = put_header(n, NTA_LABELS, wordcount * sizeof(*words)); + + for (i=0; i < wordcount; i++) { + int bit = 31; + uint32_t tmp = 0; + + do { + if (nfct_bitmask_test_bit(b, (32 * i) + bit)) + tmp |= (1 << bit); + } while (--bit >= 0); + + words[i] = htonl(tmp); + } +} + #ifndef IPPROTO_DCCP #define IPPROTO_DCCP 33 #endif @@ -233,6 +269,9 @@ void ct2msg(const struct nf_conntrack *ct, struct nethdr *n) if (nfct_attr_is_set(ct, ATTR_HELPER_NAME)) ct_build_str(ct, ATTR_HELPER_NAME, n, NTA_HELPER_NAME); + + if (nfct_attr_is_set(ct, ATTR_CONNLABELS)) + ct_build_clabel(ct, n); } static void diff --git a/src/parse.c b/src/parse.c index 8ce4495..f3ec6ac 100644 --- a/src/parse.c +++ b/src/parse.c @@ -29,15 +29,19 @@ static void ct_parse_u8(struct nf_conntrack *ct, int attr, void *data); static void ct_parse_u16(struct nf_conntrack *ct, int attr, void *data); static void ct_parse_u32(struct nf_conntrack *ct, int attr, void *data); -static void ct_parse_str(struct nf_conntrack *ct, int attr, void *data); +static void ct_parse_str(struct nf_conntrack *ct, + const struct netattr *, void *data); static void ct_parse_group(struct nf_conntrack *ct, int attr, void *data); static void ct_parse_nat_seq_adj(struct nf_conntrack *ct, int attr, void *data); +static void ct_parse_clabel(struct nf_conntrack *ct, + const struct netattr *, void *data); struct ct_parser { void (*parse)(struct nf_conntrack *ct, int attr, void *data); - int attr; - int size; - int max_size; + void (*parse2)(struct nf_conntrack *ct, const struct netattr *, void *); + uint16_t attr; + uint16_t size; + uint16_t max_size; }; static struct ct_parser h[NTA_MAX] = { @@ -176,10 +180,15 @@ static struct ct_parser h[NTA_MAX] = { .size = NTA_SIZE(sizeof(uint8_t)), }, [NTA_HELPER_NAME] = { - .parse = ct_parse_str, + .parse2 = ct_parse_str, .attr = ATTR_HELPER_NAME, .max_size = NFCT_HELPER_NAME_MAX, }, + [NTA_LABELS] = { + .parse2 = ct_parse_clabel, + .attr = ATTR_CONNLABELS, + .max_size = NTA_SIZE(NTA_LABELS_MAX_SIZE), + }, }; static void @@ -204,9 +213,9 @@ ct_parse_u32(struct nf_conntrack *ct, int attr, void *data) } static void -ct_parse_str(struct nf_conntrack *ct, int attr, void *data) +ct_parse_str(struct nf_conntrack *ct, const struct netattr *attr, void *data) { - nfct_set_attr(ct, h[attr].attr, data); + nfct_set_attr(ct, h[attr->nta_attr].attr, data); } static void @@ -215,6 +224,44 @@ ct_parse_group(struct nf_conntrack *ct, int attr, void *data) nfct_set_attr_grp(ct, h[attr].attr, data); } +static void +ct_parse_clabel(struct nf_conntrack *ct, const struct netattr *attr, void *data) +{ + struct nfct_bitmask *bitm; + unsigned int i, wordcount; + const uint32_t *words; + unsigned int len; + + len = attr->nta_len - NTA_LENGTH(0); + wordcount = len / sizeof(*words); + if (!wordcount) + return; + + if (len & (sizeof(*words) - 1)) + return; + + bitm = nfct_bitmask_new((len * 8) - 1); + if (!bitm) + return; + + words = data; + for (i=0; i < wordcount; i++) { + uint32_t word; + int bit; + + if (words[i] == 0) + continue; + + word = htonl(words[i]); + bit = 31; + do { + if (word & (1 << bit)) + nfct_bitmask_set_bit(bitm, (32 * i) + bit); + } while (--bit >= 0); + } + nfct_set_attr(ct, ATTR_CONNLABELS, bitm); +} + static void ct_parse_nat_seq_adj(struct nf_conntrack *ct, int attr, void *data) { @@ -248,14 +295,22 @@ int msg2ct(struct nf_conntrack *ct, struct nethdr *net, size_t remain) ATTR_NETWORK2HOST(attr); if (attr->nta_len > len) return -1; + if (attr->nta_len < NTA_LENGTH(0)) + return -1; if (attr->nta_attr > NTA_MAX) return -1; if (h[attr->nta_attr].size && attr->nta_len != h[attr->nta_attr].size) return -1; - if (h[attr->nta_attr].max_size && - attr->nta_len > h[attr->nta_attr].max_size) - return -1; + + if (h[attr->nta_attr].max_size) { + if (attr->nta_len > h[attr->nta_attr].max_size) + return -1; + h[attr->nta_attr].parse2(ct, attr, NTA_DATA(attr)); + attr = NTA_NEXT(attr, len); + continue; + } + if (h[attr->nta_attr].parse == NULL) { attr = NTA_NEXT(attr, len); continue; @@ -457,6 +512,8 @@ int msg2exp(struct nf_expect *exp, struct nethdr *net, size_t remain) goto err; if (attr->nta_attr > NTA_MAX) goto err; + if (attr->nta_len < NTA_LENGTH(0)) + goto err; if (exp_h[attr->nta_attr].size && attr->nta_len != exp_h[attr->nta_attr].size) goto err; -- cgit v1.2.3 From 89d016bb2cd72013cec1ae1c5cb2dfff15563d51 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 24 Jul 2013 12:40:50 +0200 Subject: conntrack: fix -L format output commit d343b8c (conntrack: add connlabel format attribute) erronously removed _UNKNOWN format, i.e. conntrack -L displayed [UPDATE] tcp 6 114 TIME_WAIT src=.. ^^^^^ Reported-by: Pablo Neira Ayuso Signed-off-by: Florian Westphal --- src/conntrack.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/conntrack.c b/src/conntrack.c index 353ff61..af36f78 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -1285,7 +1285,7 @@ static int dump_cb(enum nf_conntrack_msg_type type, if (output_mask & _O_ID) op_flags |= NFCT_OF_ID; - nfct_snprintf_labels(buf, sizeof(buf), ct, type, op_type, op_flags, labelmap); + nfct_snprintf_labels(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, op_type, op_flags, labelmap); printf("%s\n", buf); counter++; -- cgit v1.2.3 From 8e9906b69b03eb12fc75e857a347c4505332b820 Mon Sep 17 00:00:00 2001 From: Clemence Faure Date: Tue, 23 Jul 2013 15:00:18 +0200 Subject: conntrack: fix reporting of unknown arguments short options were always reported as "unknown argument". getopt(3) says: if [it] finds an option character in argv that was not included in optstring, or if it detects a missing option argument, it returns '?' and sets the external variable optopt to the actual option character. If the first character [...] of optstring is a colon (':'), then getopt() returns ':' instead of '?' to indicate a missing option argument. Signed-off-by: Clemence Faure Signed-off-by: Florian Westphal --- src/conntrack.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/conntrack.c b/src/conntrack.c index af36f78..7d2a365 100644 --- a/src/conntrack.c +++ b/src/conntrack.c @@ -333,7 +333,7 @@ static struct option original_opts[] = { {0, 0, 0, 0} }; -static const char *getopt_str = "L::I::U::D::G::E::F::hVs:d:r:q:" +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:l:"; @@ -2054,15 +2054,13 @@ int main(int argc, char *argv[]) socketbuffersize = atol(optarg); options |= CT_OPT_BUFFERSIZE; break; + case ':': + exit_error(PARAMETER_PROBLEM, + "option `%s' requires an " + "argument", argv[optind-1]); case '?': - if (optopt) - exit_error(PARAMETER_PROBLEM, - "option `%s' requires an " - "argument", argv[optind-1]); - else - exit_error(PARAMETER_PROBLEM, - "unknown option `%s'", - argv[optind-1]); + exit_error(PARAMETER_PROBLEM, + "unknown option `%s'", argv[optind-1]); break; default: if (h && h->parse_opts -- cgit v1.2.3 From e38ff8f127b61f879f57439e37c1ad9b9bc10a04 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 6 Aug 2013 15:11:27 +0200 Subject: cthelper: fix IPv6 address and mask in newly created expectations Set to zero the entire address if needed, not just 4 bytes. Signed-off-by: Pablo Neira Ayuso --- src/expect.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/expect.c b/src/expect.c index 6069770..470b9ae 100644 --- a/src/expect.c +++ b/src/expect.c @@ -54,7 +54,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, nfct_set_attr(expected, ATTR_IPV6_SRC, saddr->ip6); for (i=0; i<4; i++) - memset(addr, 0xffffffff, sizeof(uint32_t)); + memset(&addr[i], 0xffffffff, sizeof(uint32_t)); nfct_set_attr_u8(mask, ATTR_L3PROTO, AF_INET6); nfct_set_attr(mask, ATTR_IPV6_SRC, addr); @@ -76,7 +76,7 @@ cthelper_expect_init(struct nf_expect *exp, struct nf_conntrack *master, break; case AF_INET6: for (i=0; i<4; i++) - memset(addr, 0x00000000, sizeof(uint32_t)); + memset(&addr[i], 0x00000000, sizeof(uint32_t)); nfct_set_attr_u8(expected, ATTR_L3PROTO, AF_INET6); nfct_set_attr(expected, ATTR_IPV6_SRC, addr); -- cgit v1.2.3