From 2217eb4c53a54eabbc09e043209181c483e2eace Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sat, 23 Oct 2010 17:35:57 +0200 Subject: conntrack: add timestamp support This patch adds the connection tracking extension that allows conntrack timestamping. This requires a Linux kernel >= 2.6.38. We have now 65 attributes, we need 96 bits to store what attributes are set in the objects. Signed-off-by: Pablo Neira Ayuso --- src/conntrack/api.c | 16 ++++- src/conntrack/copy.c | 14 ++++ src/conntrack/getter.c | 12 ++++ src/conntrack/parse.c | 23 ++++++ src/conntrack/setter.c | 2 + src/conntrack/snprintf_default.c | 56 +++++++++++++++ src/conntrack/snprintf_xml.c | 151 ++++++++++++++++++++++++++++++++------- 7 files changed, 249 insertions(+), 25 deletions(-) (limited to 'src/conntrack') diff --git a/src/conntrack/api.c b/src/conntrack/api.c index 6b73817..2262974 100644 --- a/src/conntrack/api.c +++ b/src/conntrack/api.c @@ -356,6 +356,7 @@ void nfct_callback_unregister2(struct nfct_handle *h) * - ATTR_ID * - ATTR_*_COUNTER_* * - ATTR_SECCTX + * - ATTR_TIMESTAMP_* * The call of this function for such attributes do nothing. */ void nfct_set_attr(struct nf_conntrack *ct, @@ -970,7 +971,20 @@ int nfct_catch(struct nfct_handle *h) * The output flags are: * - NFCT_OF_SHOW_LAYER3: include layer 3 information in the output, * this is *only* required by NFCT_O_DEFAULT. - * - NFCT_OF_TIME: display time. + * - NFCT_OF_TIME: display current time. + * - NFCT_OF_ID: display the ID number. + * - NFCT_OF_TIMESTAMP: display creation and (if exists) deletion time. + * + * To use NFCT_OF_TIMESTAMP, you have to: + * \verbatim + * $ echo 1 > /proc/sys/net/netfilter/nf_conntrack_timestamp +\endverbatim + * This requires a Linux kernel >= 2.6.38. + * + * Note that NFCT_OF_TIME displays the current time when nfct_snprintf() has + * been called. Thus, it can be used to know when a flow was destroy if you + * print the message just after you receive the destroy event. If you want + * more accurate timestamping, use NFCT_OF_TIMESTAMP. * * This function returns the size of the information that _would_ have been * written to the buffer, even if there was no room for it. Thus, the diff --git a/src/conntrack/copy.c b/src/conntrack/copy.c index 9148640..591dde1 100644 --- a/src/conntrack/copy.c +++ b/src/conntrack/copy.c @@ -423,6 +423,18 @@ static void copy_attr_secctx(struct nf_conntrack *dest, dest->secctx = strdup(orig->secctx); } +static void copy_attr_timestamp_start(struct nf_conntrack *dest, + const struct nf_conntrack *orig) +{ + dest->timestamp.start = orig->timestamp.start; +} + +static void copy_attr_timestamp_stop(struct nf_conntrack *dest, + const struct nf_conntrack *orig) +{ + dest->timestamp.stop = orig->timestamp.stop; +} + const copy_attr copy_attr_array[ATTR_MAX] = { [ATTR_ORIG_IPV4_SRC] = copy_attr_orig_ipv4_src, [ATTR_ORIG_IPV4_DST] = copy_attr_orig_ipv4_dst, @@ -487,4 +499,6 @@ const copy_attr copy_attr_array[ATTR_MAX] = { [ATTR_TCP_WSCALE_REPL] = copy_attr_tcp_wscale_repl, [ATTR_ZONE] = copy_attr_zone, [ATTR_SECCTX] = copy_attr_secctx, + [ATTR_TIMESTAMP_START] = copy_attr_timestamp_start, + [ATTR_TIMESTAMP_STOP] = copy_attr_timestamp_stop, }; diff --git a/src/conntrack/getter.c b/src/conntrack/getter.c index 8a093c6..0b56468 100644 --- a/src/conntrack/getter.c +++ b/src/conntrack/getter.c @@ -322,6 +322,16 @@ static const void *get_attr_secctx(const struct nf_conntrack *ct) return ct->secctx; } +static const void *get_attr_timestamp_start(const struct nf_conntrack *ct) +{ + return &ct->timestamp.start; +} + +static const void *get_attr_timestamp_stop(const struct nf_conntrack *ct) +{ + return &ct->timestamp.stop; +} + const get_attr get_attr_array[ATTR_MAX] = { [ATTR_ORIG_IPV4_SRC] = get_attr_orig_ipv4_src, [ATTR_ORIG_IPV4_DST] = get_attr_orig_ipv4_dst, @@ -386,4 +396,6 @@ const get_attr get_attr_array[ATTR_MAX] = { [ATTR_TCP_WSCALE_REPL] = get_attr_tcp_wscale_repl, [ATTR_ZONE] = get_attr_zone, [ATTR_SECCTX] = get_attr_secctx, + [ATTR_TIMESTAMP_START] = get_attr_timestamp_start, + [ATTR_TIMESTAMP_STOP] = get_attr_timestamp_stop, }; diff --git a/src/conntrack/parse.c b/src/conntrack/parse.c index 841693e..743e8d4 100644 --- a/src/conntrack/parse.c +++ b/src/conntrack/parse.c @@ -453,6 +453,26 @@ int __parse_message_type(const struct nlmsghdr *nlh) return ret; } +static void +__parse_timestamp(const struct nfattr *attr, struct nf_conntrack *ct) +{ + struct nfattr *tb[CTA_TIMESTAMP_MAX]; + + nfnl_parse_nested(tb, CTA_TIMESTAMP_MAX, attr); + if (tb[CTA_TIMESTAMP_START-1]) { + u_int64_t tmp; + memcpy(&tmp, NFA_DATA(tb[CTA_TIMESTAMP_START-1]), sizeof(tmp)); + ct->timestamp.start = __be64_to_cpu(tmp); + set_bit(ATTR_TIMESTAMP_START, ct->set); + } + if (tb[CTA_TIMESTAMP_STOP-1]) { + u_int64_t tmp; + memcpy(&tmp, NFA_DATA(tb[CTA_TIMESTAMP_STOP-1]), sizeof(tmp)); + ct->timestamp.stop = __be64_to_cpu(tmp); + set_bit(ATTR_TIMESTAMP_STOP, ct->set); + } +} + void __parse_conntrack(const struct nlmsghdr *nlh, struct nfattr *cda[], struct nf_conntrack *ct) @@ -538,4 +558,7 @@ void __parse_conntrack(const struct nlmsghdr *nlh, if (cda[CTA_SECCTX-1]) __parse_secctx(cda[CTA_SECCTX-1], ct); + + if (cda[CTA_TIMESTAMP-1]) + __parse_timestamp(cda[CTA_TIMESTAMP-1], ct); } diff --git a/src/conntrack/setter.c b/src/conntrack/setter.c index 99ac8d7..3282035 100644 --- a/src/conntrack/setter.c +++ b/src/conntrack/setter.c @@ -411,4 +411,6 @@ const set_attr set_attr_array[ATTR_MAX] = { [ATTR_TCP_WSCALE_REPL] = set_attr_tcp_wscale_repl, [ATTR_ZONE] = set_attr_zone, [ATTR_SECCTX] = set_attr_do_nothing, + [ATTR_TIMESTAMP_START] = set_attr_do_nothing, + [ATTR_TIMESTAMP_STOP] = set_attr_do_nothing, }; diff --git a/src/conntrack/snprintf_default.c b/src/conntrack/snprintf_default.c index abb9d9f..8523bd1 100644 --- a/src/conntrack/snprintf_default.c +++ b/src/conntrack/snprintf_default.c @@ -231,6 +231,47 @@ __snprintf_secctx(char *buf, unsigned int len, const struct nf_conntrack *ct) return (snprintf(buf, len, "secctx=%s ", ct->secctx)); } +static int +__snprintf_timestamp_start(char *buf, unsigned int len, + const struct nf_conntrack *ct) +{ + time_t start = (time_t)(ct->timestamp.start / NSEC_PER_SEC); + char *tmp = ctime(&start); + + /* overwrite \n in the ctime() output. */ + tmp[strlen(tmp)-1] = '\0'; + return (snprintf(buf, len, "[start=%s] ", tmp)); +} + +static int +__snprintf_timestamp_stop(char *buf, unsigned int len, + const struct nf_conntrack *ct) +{ + time_t stop = (time_t)(ct->timestamp.stop / NSEC_PER_SEC); + char *tmp = ctime(&stop); + + /* overwrite \n in the ctime() output. */ + tmp[strlen(tmp)-1] = '\0'; + return (snprintf(buf, len, "[stop=%s] ", tmp)); +} + +static int +__snprintf_timestamp_delta(char *buf, unsigned int len, + const struct nf_conntrack *ct) +{ + time_t delta_time, stop; + + if (ct->timestamp.stop == 0) + time(&stop); + else + stop = (time_t)(ct->timestamp.stop / NSEC_PER_SEC); + + delta_time = stop - (time_t)(ct->timestamp.start / NSEC_PER_SEC); + + return (snprintf(buf, len, "delta-time=%llu ", + (unsigned long long)delta_time)); +} + int __snprintf_conntrack_default(char *buf, unsigned int len, const struct nf_conntrack *ct, @@ -337,6 +378,21 @@ int __snprintf_conntrack_default(char *buf, BUFFER_SIZE(ret, size, len, offset); } + if (test_bit(ATTR_TIMESTAMP_START, ct->set)) { + ret = __snprintf_timestamp_delta(buf+offset, len, ct); + BUFFER_SIZE(ret, size, len, offset); + } + if (flags & NFCT_OF_TIMESTAMP) { + if (test_bit(ATTR_TIMESTAMP_START, ct->set)) { + ret = __snprintf_timestamp_start(buf+offset, len, ct); + BUFFER_SIZE(ret, size, len, offset); + } + if (test_bit(ATTR_TIMESTAMP_STOP, ct->set)) { + ret = __snprintf_timestamp_stop(buf+offset, len, ct); + BUFFER_SIZE(ret, size, len, offset); + } + } + if (test_bit(ATTR_USE, ct->set)) { ret = __snprintf_use(buf+offset, len, ct); BUFFER_SIZE(ret, size, len, offset); diff --git a/src/conntrack/snprintf_xml.c b/src/conntrack/snprintf_xml.c index 97f6650..12dd686 100644 --- a/src/conntrack/snprintf_xml.c +++ b/src/conntrack/snprintf_xml.c @@ -198,6 +198,97 @@ static int __snprintf_counters_xml(char *buf, return size; } +static int +__snprintf_timestamp_start(char *buf, unsigned int len, + const struct nf_conntrack *ct) +{ + int ret; + unsigned int size = 0, offset = 0; + + ret = snprintf(buf, len, "%llu", + (unsigned long long)ct->timestamp.start); + BUFFER_SIZE(ret, size, len, offset); + + return size; +} + +static int +__snprintf_timestamp_stop(char *buf, unsigned int len, + const struct nf_conntrack *ct) +{ + int ret; + unsigned int size = 0, offset = 0; + + ret = snprintf(buf, len, "%llu", + (unsigned long long)ct->timestamp.stop); + BUFFER_SIZE(ret, size, len, offset); + + return size; +} + +static int +__snprintf_deltatime_now(char *buf, unsigned int len, + const struct nf_conntrack *ct) +{ + int ret; + unsigned int size = 0, offset = 0; + time_t now, delta_time; + + time(&now); + delta_time = now - (time_t)(ct->timestamp.start / NSEC_PER_SEC); + + ret = snprintf(buf+offset, len, "%llu", + (unsigned long long)delta_time); + BUFFER_SIZE(ret, size, len, offset); + + return size; +} + +static int +__snprintf_deltatime(char *buf, unsigned int len, const struct nf_conntrack *ct) +{ + int ret; + unsigned int size = 0, offset = 0; + time_t delta_time = (time_t)((ct->timestamp.stop - + ct->timestamp.start) / NSEC_PER_SEC); + + ret = snprintf(buf+offset, len, "%llu", + (unsigned long long)delta_time); + BUFFER_SIZE(ret, size, len, offset); + + return size; +} + +static int +__snprintf_localtime_xml(char *buf, unsigned int len, const struct tm *tm) +{ + int ret = 0; + unsigned int size = 0, offset = 0; + + ret = snprintf(buf+offset, len, "%d", tm->tm_hour); + BUFFER_SIZE(ret, size, len, offset); + + ret = snprintf(buf+offset, len, "%02d", tm->tm_min); + BUFFER_SIZE(ret, size, len, offset); + + ret = snprintf(buf+offset, len, "%02d", tm->tm_sec); + BUFFER_SIZE(ret, size, len, offset); + + ret = snprintf(buf+offset, len, "%d", tm->tm_wday + 1); + BUFFER_SIZE(ret, size, len, offset); + + ret = snprintf(buf+offset, len, "%d", tm->tm_mday); + BUFFER_SIZE(ret, size, len, offset); + + ret = snprintf(buf+offset, len, "%d", tm->tm_mon + 1); + BUFFER_SIZE(ret, size, len, offset); + + ret = snprintf(buf+offset, len, "%d", 1900 + tm->tm_year); + BUFFER_SIZE(ret, size, len, offset); + + return size; +} + static int __snprintf_tuple_xml(char *buf, unsigned int len, const struct nf_conntrack *ct, @@ -298,7 +389,9 @@ int __snprintf_conntrack_xml(char *buf, test_bit(ATTR_ZONE, ct->set) || test_bit(ATTR_USE, ct->set) || test_bit(ATTR_STATUS, ct->set) || - test_bit(ATTR_ID, ct->set)) { + test_bit(ATTR_ID, ct->set) || + test_bit(ATTR_TIMESTAMP_START, ct->set) || + test_bit(ATTR_TIMESTAMP_STOP, ct->set)) { ret = snprintf(buf+offset, len, ""); BUFFER_SIZE(ret, size, len, offset); @@ -378,6 +471,35 @@ int __snprintf_conntrack_xml(char *buf, BUFFER_SIZE(ret, size, len, offset); } + if (flags & NFCT_OF_TIMESTAMP) { + if (test_bit(ATTR_TIMESTAMP_START, ct->set) || + test_bit(ATTR_TIMESTAMP_STOP, ct->set)) { + ret = snprintf(buf+offset, len, ""); + BUFFER_SIZE(ret, size, len, offset); + } + if (test_bit(ATTR_TIMESTAMP_START, ct->set)) { + ret = __snprintf_timestamp_start(buf+offset, len, ct); + BUFFER_SIZE(ret, size, len, offset); + } + if (test_bit(ATTR_TIMESTAMP_STOP, ct->set)) { + ret = __snprintf_timestamp_stop(buf+offset, len, ct); + BUFFER_SIZE(ret, size, len, offset); + } + if (test_bit(ATTR_TIMESTAMP_START, ct->set) || + test_bit(ATTR_TIMESTAMP_STOP, ct->set)) { + ret = snprintf(buf+offset, len, ""); + BUFFER_SIZE(ret, size, len, offset); + } + } + if (test_bit(ATTR_TIMESTAMP_START, ct->set) && + test_bit(ATTR_TIMESTAMP_STOP, ct->set)) { + ret = __snprintf_deltatime(buf+offset, len, ct); + BUFFER_SIZE(ret, size, len, offset); + } else if (test_bit(ATTR_TIMESTAMP_START, ct->set)) { + ret = __snprintf_deltatime_now(buf+offset, len, ct); + BUFFER_SIZE(ret, size, len, offset); + } + if (test_bit(ATTR_TCP_STATE, ct->set) || test_bit(ATTR_SCTP_STATE, ct->set) || test_bit(ATTR_DCCP_STATE, ct->set) || @@ -387,7 +509,9 @@ int __snprintf_conntrack_xml(char *buf, test_bit(ATTR_ZONE, ct->set) || test_bit(ATTR_USE, ct->set) || test_bit(ATTR_STATUS, ct->set) || - test_bit(ATTR_ID, ct->set)) { + test_bit(ATTR_ID, ct->set) || + test_bit(ATTR_TIMESTAMP_START, ct->set) || + test_bit(ATTR_TIMESTAMP_STOP, ct->set)) { ret = snprintf(buf+offset, len, ""); BUFFER_SIZE(ret, size, len, offset); } @@ -403,28 +527,7 @@ int __snprintf_conntrack_xml(char *buf, ret = snprintf(buf+offset, len, ""); BUFFER_SIZE(ret, size, len, offset); - ret = snprintf(buf+offset, len, "%d", tm.tm_hour); - BUFFER_SIZE(ret, size, len, offset); - - ret = snprintf(buf+offset, len, "%02d", tm.tm_min); - BUFFER_SIZE(ret, size, len, offset); - - ret = snprintf(buf+offset, len, "%02d", tm.tm_sec); - BUFFER_SIZE(ret, size, len, offset); - - ret = snprintf(buf+offset, len, "%d", - tm.tm_wday + 1); - BUFFER_SIZE(ret, size, len, offset); - - ret = snprintf(buf+offset, len, "%d", tm.tm_mday); - BUFFER_SIZE(ret, size, len, offset); - - ret = snprintf(buf+offset, len, "%d", - tm.tm_mon + 1); - BUFFER_SIZE(ret, size, len, offset); - - ret = snprintf(buf+offset, len, "%d", - 1900 + tm.tm_year); + ret = __snprintf_localtime_xml(buf+offset, len, &tm); BUFFER_SIZE(ret, size, len, offset); ret = snprintf(buf+offset, len, ""); -- cgit v1.2.3