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 --- include/internal/internal.h | 4 + include/internal/object.h | 8 +- .../libnetfilter_conntrack.h | 5 + .../linux_nfnetlink_conntrack.h | 9 ++ 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 +++++++++++++++++---- utils/conntrack_dump.c | 2 +- utils/conntrack_events.c | 2 +- 13 files changed, 276 insertions(+), 28 deletions(-) diff --git a/include/internal/internal.h b/include/internal/internal.h index c335afd..a984e6b 100644 --- a/include/internal/internal.h +++ b/include/internal/internal.h @@ -77,4 +77,8 @@ #define likely(x) __builtin_expect((x),1) #define unlikely(x) __builtin_expect((x),0) +#ifndef NSEC_PER_SEC +#define NSEC_PER_SEC 1000000000L +#endif + #endif diff --git a/include/internal/object.h b/include/internal/object.h index 76a0566..5dce9d0 100644 --- a/include/internal/object.h +++ b/include/internal/object.h @@ -175,7 +175,13 @@ struct nf_conntrack { struct __nfct_nat snat; struct __nfct_nat dnat; -#define __NFCT_BITSET 2 + struct { + u_int64_t start; + u_int64_t stop; + } timestamp; + +/* we've got more than 64 attributes now, we need 96 bits to store them. */ +#define __NFCT_BITSET 3 u_int32_t set[__NFCT_BITSET]; }; diff --git a/include/libnetfilter_conntrack/libnetfilter_conntrack.h b/include/libnetfilter_conntrack/libnetfilter_conntrack.h index 698b0ae..f09e03b 100644 --- a/include/libnetfilter_conntrack/libnetfilter_conntrack.h +++ b/include/libnetfilter_conntrack/libnetfilter_conntrack.h @@ -128,6 +128,8 @@ enum nf_conntrack_attr { ATTR_TCP_WSCALE_REPL = 60, /* u8 bits */ ATTR_ZONE, /* u16 bits */ ATTR_SECCTX, /* string */ + ATTR_TIMESTAMP_START, /* u64 bits, linux >= 2.6.38 */ + ATTR_TIMESTAMP_STOP = 64, /* u64 bits, linux >= 2.6.38 */ ATTR_MAX }; @@ -344,6 +346,9 @@ enum { NFCT_OF_ID_BIT = 2, NFCT_OF_ID = (1 << NFCT_OF_ID_BIT), + + NFCT_OF_TIMESTAMP_BIT = 3, + NFCT_OF_TIMESTAMP = (1 << NFCT_OF_TIMESTAMP_BIT), }; extern int nfct_snprintf(char *buf, diff --git a/include/libnetfilter_conntrack/linux_nfnetlink_conntrack.h b/include/libnetfilter_conntrack/linux_nfnetlink_conntrack.h index 3b0c009..abab4a0 100644 --- a/include/libnetfilter_conntrack/linux_nfnetlink_conntrack.h +++ b/include/libnetfilter_conntrack/linux_nfnetlink_conntrack.h @@ -46,6 +46,7 @@ enum ctattr_type { CTA_SECMARK, /* obsolete */ CTA_ZONE, CTA_SECCTX, + CTA_TIMESTAMP, __CTA_MAX }; #define CTA_MAX (__CTA_MAX - 1) @@ -131,6 +132,14 @@ enum ctattr_counters { }; #define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1) +enum ctattr_tstamp { + CTA_TIMESTAMP_UNSPEC, + CTA_TIMESTAMP_START, + CTA_TIMESTAMP_STOP, + __CTA_TIMESTAMP_MAX +}; +#define CTA_TIMESTAMP_MAX (__CTA_TIMESTAMP_MAX - 1) + enum ctattr_nat { CTA_NAT_UNSPEC, CTA_NAT_MINIP, 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, ""); diff --git a/utils/conntrack_dump.c b/utils/conntrack_dump.c index a36e753..f1d63bb 100644 --- a/utils/conntrack_dump.c +++ b/utils/conntrack_dump.c @@ -11,7 +11,7 @@ static int cb(enum nf_conntrack_msg_type type, { char buf[1024]; - nfct_snprintf(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, NFCT_O_DEFAULT, NFCT_OF_SHOW_LAYER3); + nfct_snprintf(buf, sizeof(buf), ct, NFCT_T_UNKNOWN, NFCT_O_DEFAULT, NFCT_OF_SHOW_LAYER3 | NFCT_OF_TIMESTAMP); printf("%s\n", buf); return NFCT_CB_CONTINUE; diff --git a/utils/conntrack_events.c b/utils/conntrack_events.c index f5e72c7..7675663 100644 --- a/utils/conntrack_events.c +++ b/utils/conntrack_events.c @@ -12,7 +12,7 @@ static int event_cb(enum nf_conntrack_msg_type type, static int n = 0; char buf[1024]; - nfct_snprintf(buf, sizeof(buf), ct, type, NFCT_O_XML, NFCT_OF_TIME); + nfct_snprintf(buf, sizeof(buf), ct, type, NFCT_O_XML, NFCT_OF_TIME | NFCT_OF_TIMESTAMP); printf("%s\n", buf); if (++n == 10) -- cgit v1.2.3