aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2011-01-19 10:00:07 -0500
committerPatrick McHardy <kaber@trash.net>2011-01-19 10:00:07 -0500
commita992ca2a0498edd22a88ac8c41570f536de29c9e (patch)
tree4574d4da3f44c7dd3879cb4f209a8bd3a37c0ca9
parent93557f53e1fbd9e2b6574ab0a9b5852628fde9e3 (diff)
netfilter: nf_conntrack_tstamp: add flow-based timestamp extension
This patch adds flow-based timestamping for conntracks. This conntrack extension is disabled by default. Basically, we use two 64-bits variables to store the creation timestamp once the conntrack has been confirmed and the other to store the deletion time. This extension is disabled by default, to enable it, you have to: echo 1 > /proc/sys/net/netfilter/nf_conntrack_timestamp This patch allows to save memory for user-space flow-based loogers such as ulogd2. In short, ulogd2 does not need to keep a hashtable with the conntrack in user-space to know when they were created and destroyed, instead we use the kernel timestamp. If we want to have a sane IPFIX implementation in user-space, this nanosecs resolution timestamps are also useful. Other custom user-space applications can benefit from this via libnetfilter_conntrack. This patch modifies the /proc output to display the delta time in seconds since the flow start. You can also obtain the flow-start date by means of the conntrack-tools. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Signed-off-by: Patrick McHardy <kaber@trash.net>
-rw-r--r--include/linux/netfilter/nfnetlink_conntrack.h9
-rw-r--r--include/net/netfilter/nf_conntrack_extend.h4
-rw-r--r--include/net/netfilter/nf_conntrack_timestamp.h53
-rw-r--r--include/net/netns/conntrack.h2
-rw-r--r--net/netfilter/Kconfig11
-rw-r--r--net/netfilter/Makefile1
-rw-r--r--net/netfilter/nf_conntrack_core.c26
-rw-r--r--net/netfilter/nf_conntrack_netlink.c46
-rw-r--r--net/netfilter/nf_conntrack_standalone.c41
-rw-r--r--net/netfilter/nf_conntrack_timestamp.c120
10 files changed, 312 insertions, 1 deletions
diff --git a/include/linux/netfilter/nfnetlink_conntrack.h b/include/linux/netfilter/nfnetlink_conntrack.h
index 19711e3ffd42..debf1aefd753 100644
--- a/include/linux/netfilter/nfnetlink_conntrack.h
+++ b/include/linux/netfilter/nfnetlink_conntrack.h
@@ -42,6 +42,7 @@ enum ctattr_type {
42 CTA_SECMARK, /* obsolete */ 42 CTA_SECMARK, /* obsolete */
43 CTA_ZONE, 43 CTA_ZONE,
44 CTA_SECCTX, 44 CTA_SECCTX,
45 CTA_TIMESTAMP,
45 __CTA_MAX 46 __CTA_MAX
46}; 47};
47#define CTA_MAX (__CTA_MAX - 1) 48#define CTA_MAX (__CTA_MAX - 1)
@@ -127,6 +128,14 @@ enum ctattr_counters {
127}; 128};
128#define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1) 129#define CTA_COUNTERS_MAX (__CTA_COUNTERS_MAX - 1)
129 130
131enum ctattr_tstamp {
132 CTA_TIMESTAMP_UNSPEC,
133 CTA_TIMESTAMP_START,
134 CTA_TIMESTAMP_STOP,
135 __CTA_TIMESTAMP_MAX
136};
137#define CTA_TIMESTAMP_MAX (__CTA_TIMESTAMP_MAX - 1)
138
130enum ctattr_nat { 139enum ctattr_nat {
131 CTA_NAT_UNSPEC, 140 CTA_NAT_UNSPEC,
132 CTA_NAT_MINIP, 141 CTA_NAT_MINIP,
diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h
index 1a9f96db3798..2dcf31703acb 100644
--- a/include/net/netfilter/nf_conntrack_extend.h
+++ b/include/net/netfilter/nf_conntrack_extend.h
@@ -17,6 +17,9 @@ enum nf_ct_ext_id {
17#ifdef CONFIG_NF_CONNTRACK_ZONES 17#ifdef CONFIG_NF_CONNTRACK_ZONES
18 NF_CT_EXT_ZONE, 18 NF_CT_EXT_ZONE,
19#endif 19#endif
20#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
21 NF_CT_EXT_TSTAMP,
22#endif
20 NF_CT_EXT_NUM, 23 NF_CT_EXT_NUM,
21}; 24};
22 25
@@ -25,6 +28,7 @@ enum nf_ct_ext_id {
25#define NF_CT_EXT_ACCT_TYPE struct nf_conn_counter 28#define NF_CT_EXT_ACCT_TYPE struct nf_conn_counter
26#define NF_CT_EXT_ECACHE_TYPE struct nf_conntrack_ecache 29#define NF_CT_EXT_ECACHE_TYPE struct nf_conntrack_ecache
27#define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone 30#define NF_CT_EXT_ZONE_TYPE struct nf_conntrack_zone
31#define NF_CT_EXT_TSTAMP_TYPE struct nf_conn_tstamp
28 32
29/* Extensions: optional stuff which isn't permanently in struct. */ 33/* Extensions: optional stuff which isn't permanently in struct. */
30struct nf_ct_ext { 34struct nf_ct_ext {
diff --git a/include/net/netfilter/nf_conntrack_timestamp.h b/include/net/netfilter/nf_conntrack_timestamp.h
new file mode 100644
index 000000000000..f17dcb664e29
--- /dev/null
+++ b/include/net/netfilter/nf_conntrack_timestamp.h
@@ -0,0 +1,53 @@
1#ifndef _NF_CONNTRACK_TSTAMP_H
2#define _NF_CONNTRACK_TSTAMP_H
3
4#include <net/net_namespace.h>
5#include <linux/netfilter/nf_conntrack_common.h>
6#include <linux/netfilter/nf_conntrack_tuple_common.h>
7#include <net/netfilter/nf_conntrack.h>
8#include <net/netfilter/nf_conntrack_extend.h>
9
10struct nf_conn_tstamp {
11 u_int64_t start;
12 u_int64_t stop;
13};
14
15static inline
16struct nf_conn_tstamp *nf_conn_tstamp_find(const struct nf_conn *ct)
17{
18#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
19 return nf_ct_ext_find(ct, NF_CT_EXT_TSTAMP);
20#else
21 return NULL;
22#endif
23}
24
25static inline
26struct nf_conn_tstamp *nf_ct_tstamp_ext_add(struct nf_conn *ct, gfp_t gfp)
27{
28#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
29 struct net *net = nf_ct_net(ct);
30
31 if (!net->ct.sysctl_tstamp)
32 return NULL;
33
34 return nf_ct_ext_add(ct, NF_CT_EXT_TSTAMP, gfp);
35#else
36 return NULL;
37#endif
38};
39
40static inline bool nf_ct_tstamp_enabled(struct net *net)
41{
42 return net->ct.sysctl_tstamp != 0;
43}
44
45static inline void nf_ct_set_tstamp(struct net *net, bool enable)
46{
47 net->ct.sysctl_tstamp = enable;
48}
49
50extern int nf_conntrack_tstamp_init(struct net *net);
51extern void nf_conntrack_tstamp_fini(struct net *net);
52
53#endif /* _NF_CONNTRACK_TSTAMP_H */
diff --git a/include/net/netns/conntrack.h b/include/net/netns/conntrack.h
index 5cf8a8c141aa..341eb089349e 100644
--- a/include/net/netns/conntrack.h
+++ b/include/net/netns/conntrack.h
@@ -21,11 +21,13 @@ struct netns_ct {
21 int sysctl_events; 21 int sysctl_events;
22 unsigned int sysctl_events_retry_timeout; 22 unsigned int sysctl_events_retry_timeout;
23 int sysctl_acct; 23 int sysctl_acct;
24 int sysctl_tstamp;
24 int sysctl_checksum; 25 int sysctl_checksum;
25 unsigned int sysctl_log_invalid; /* Log invalid packets */ 26 unsigned int sysctl_log_invalid; /* Log invalid packets */
26#ifdef CONFIG_SYSCTL 27#ifdef CONFIG_SYSCTL
27 struct ctl_table_header *sysctl_header; 28 struct ctl_table_header *sysctl_header;
28 struct ctl_table_header *acct_sysctl_header; 29 struct ctl_table_header *acct_sysctl_header;
30 struct ctl_table_header *tstamp_sysctl_header;
29 struct ctl_table_header *event_sysctl_header; 31 struct ctl_table_header *event_sysctl_header;
30#endif 32#endif
31 char *slabname; 33 char *slabname;
diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index 939b504604c2..faf7412ea453 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -85,6 +85,17 @@ config NF_CONNTRACK_EVENTS
85 85
86 If unsure, say `N'. 86 If unsure, say `N'.
87 87
88config NF_CONNTRACK_TIMESTAMP
89 bool 'Connection tracking timestamping'
90 depends on NETFILTER_ADVANCED
91 help
92 This option enables support for connection tracking timestamping.
93 This allows you to store the flow start-time and to obtain
94 the flow-stop time (once it has been destroyed) via Connection
95 tracking events.
96
97 If unsure, say `N'.
98
88config NF_CT_PROTO_DCCP 99config NF_CT_PROTO_DCCP
89 tristate 'DCCP protocol connection tracking support (EXPERIMENTAL)' 100 tristate 'DCCP protocol connection tracking support (EXPERIMENTAL)'
90 depends on EXPERIMENTAL 101 depends on EXPERIMENTAL
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 2c2628de9d3f..9ae6878a85b1 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -1,6 +1,7 @@
1netfilter-objs := core.o nf_log.o nf_queue.o nf_sockopt.o 1netfilter-objs := core.o nf_log.o nf_queue.o nf_sockopt.o
2 2
3nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o nf_conntrack_extend.o nf_conntrack_acct.o 3nf_conntrack-y := nf_conntrack_core.o nf_conntrack_standalone.o nf_conntrack_expect.o nf_conntrack_helper.o nf_conntrack_proto.o nf_conntrack_l3proto_generic.o nf_conntrack_proto_generic.o nf_conntrack_proto_tcp.o nf_conntrack_proto_udp.o nf_conntrack_extend.o nf_conntrack_acct.o
4nf_conntrack-$(CONFIG_NF_CONNTRACK_TIMESTAMP) += nf_conntrack_timestamp.o
4nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o 5nf_conntrack-$(CONFIG_NF_CONNTRACK_EVENTS) += nf_conntrack_ecache.o
5 6
6obj-$(CONFIG_NETFILTER) = netfilter.o 7obj-$(CONFIG_NETFILTER) = netfilter.o
diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c
index f47ac67e1bfe..1909311c392a 100644
--- a/net/netfilter/nf_conntrack_core.c
+++ b/net/netfilter/nf_conntrack_core.c
@@ -43,6 +43,7 @@
43#include <net/netfilter/nf_conntrack_acct.h> 43#include <net/netfilter/nf_conntrack_acct.h>
44#include <net/netfilter/nf_conntrack_ecache.h> 44#include <net/netfilter/nf_conntrack_ecache.h>
45#include <net/netfilter/nf_conntrack_zones.h> 45#include <net/netfilter/nf_conntrack_zones.h>
46#include <net/netfilter/nf_conntrack_timestamp.h>
46#include <net/netfilter/nf_nat.h> 47#include <net/netfilter/nf_nat.h>
47#include <net/netfilter/nf_nat_core.h> 48#include <net/netfilter/nf_nat_core.h>
48 49
@@ -282,6 +283,11 @@ EXPORT_SYMBOL_GPL(nf_ct_insert_dying_list);
282static void death_by_timeout(unsigned long ul_conntrack) 283static void death_by_timeout(unsigned long ul_conntrack)
283{ 284{
284 struct nf_conn *ct = (void *)ul_conntrack; 285 struct nf_conn *ct = (void *)ul_conntrack;
286 struct nf_conn_tstamp *tstamp;
287
288 tstamp = nf_conn_tstamp_find(ct);
289 if (tstamp && tstamp->stop == 0)
290 tstamp->stop = ktime_to_ns(ktime_get_real());
285 291
286 if (!test_bit(IPS_DYING_BIT, &ct->status) && 292 if (!test_bit(IPS_DYING_BIT, &ct->status) &&
287 unlikely(nf_conntrack_event(IPCT_DESTROY, ct) < 0)) { 293 unlikely(nf_conntrack_event(IPCT_DESTROY, ct) < 0)) {
@@ -419,6 +425,7 @@ __nf_conntrack_confirm(struct sk_buff *skb)
419 struct nf_conntrack_tuple_hash *h; 425 struct nf_conntrack_tuple_hash *h;
420 struct nf_conn *ct; 426 struct nf_conn *ct;
421 struct nf_conn_help *help; 427 struct nf_conn_help *help;
428 struct nf_conn_tstamp *tstamp;
422 struct hlist_nulls_node *n; 429 struct hlist_nulls_node *n;
423 enum ip_conntrack_info ctinfo; 430 enum ip_conntrack_info ctinfo;
424 struct net *net; 431 struct net *net;
@@ -488,6 +495,14 @@ __nf_conntrack_confirm(struct sk_buff *skb)
488 atomic_inc(&ct->ct_general.use); 495 atomic_inc(&ct->ct_general.use);
489 ct->status |= IPS_CONFIRMED; 496 ct->status |= IPS_CONFIRMED;
490 497
498 /* set conntrack timestamp, if enabled. */
499 tstamp = nf_conn_tstamp_find(ct);
500 if (tstamp) {
501 if (skb->tstamp.tv64 == 0)
502 __net_timestamp((struct sk_buff *)skb);
503
504 tstamp->start = ktime_to_ns(skb->tstamp);
505 }
491 /* Since the lookup is lockless, hash insertion must be done after 506 /* Since the lookup is lockless, hash insertion must be done after
492 * starting the timer and setting the CONFIRMED bit. The RCU barriers 507 * starting the timer and setting the CONFIRMED bit. The RCU barriers
493 * guarantee that no other CPU can find the conntrack before the above 508 * guarantee that no other CPU can find the conntrack before the above
@@ -746,6 +761,7 @@ init_conntrack(struct net *net, struct nf_conn *tmpl,
746 } 761 }
747 762
748 nf_ct_acct_ext_add(ct, GFP_ATOMIC); 763 nf_ct_acct_ext_add(ct, GFP_ATOMIC);
764 nf_ct_tstamp_ext_add(ct, GFP_ATOMIC);
749 765
750 ecache = tmpl ? nf_ct_ecache_find(tmpl) : NULL; 766 ecache = tmpl ? nf_ct_ecache_find(tmpl) : NULL;
751 nf_ct_ecache_ext_add(ct, ecache ? ecache->ctmask : 0, 767 nf_ct_ecache_ext_add(ct, ecache ? ecache->ctmask : 0,
@@ -1186,6 +1202,11 @@ struct __nf_ct_flush_report {
1186static int kill_report(struct nf_conn *i, void *data) 1202static int kill_report(struct nf_conn *i, void *data)
1187{ 1203{
1188 struct __nf_ct_flush_report *fr = (struct __nf_ct_flush_report *)data; 1204 struct __nf_ct_flush_report *fr = (struct __nf_ct_flush_report *)data;
1205 struct nf_conn_tstamp *tstamp;
1206
1207 tstamp = nf_conn_tstamp_find(i);
1208 if (tstamp && tstamp->stop == 0)
1209 tstamp->stop = ktime_to_ns(ktime_get_real());
1189 1210
1190 /* If we fail to deliver the event, death_by_timeout() will retry */ 1211 /* If we fail to deliver the event, death_by_timeout() will retry */
1191 if (nf_conntrack_event_report(IPCT_DESTROY, i, 1212 if (nf_conntrack_event_report(IPCT_DESTROY, i,
@@ -1497,6 +1518,9 @@ static int nf_conntrack_init_net(struct net *net)
1497 ret = nf_conntrack_acct_init(net); 1518 ret = nf_conntrack_acct_init(net);
1498 if (ret < 0) 1519 if (ret < 0)
1499 goto err_acct; 1520 goto err_acct;
1521 ret = nf_conntrack_tstamp_init(net);
1522 if (ret < 0)
1523 goto err_tstamp;
1500 ret = nf_conntrack_ecache_init(net); 1524 ret = nf_conntrack_ecache_init(net);
1501 if (ret < 0) 1525 if (ret < 0)
1502 goto err_ecache; 1526 goto err_ecache;
@@ -1504,6 +1528,8 @@ static int nf_conntrack_init_net(struct net *net)
1504 return 0; 1528 return 0;
1505 1529
1506err_ecache: 1530err_ecache:
1531 nf_conntrack_tstamp_fini(net);
1532err_tstamp:
1507 nf_conntrack_acct_fini(net); 1533 nf_conntrack_acct_fini(net);
1508err_acct: 1534err_acct:
1509 nf_conntrack_expect_fini(net); 1535 nf_conntrack_expect_fini(net);
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 9eabaa6f28a8..715d56c85475 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -42,6 +42,7 @@
42#include <net/netfilter/nf_conntrack_tuple.h> 42#include <net/netfilter/nf_conntrack_tuple.h>
43#include <net/netfilter/nf_conntrack_acct.h> 43#include <net/netfilter/nf_conntrack_acct.h>
44#include <net/netfilter/nf_conntrack_zones.h> 44#include <net/netfilter/nf_conntrack_zones.h>
45#include <net/netfilter/nf_conntrack_timestamp.h>
45#ifdef CONFIG_NF_NAT_NEEDED 46#ifdef CONFIG_NF_NAT_NEEDED
46#include <net/netfilter/nf_nat_core.h> 47#include <net/netfilter/nf_nat_core.h>
47#include <net/netfilter/nf_nat_protocol.h> 48#include <net/netfilter/nf_nat_protocol.h>
@@ -230,6 +231,33 @@ nla_put_failure:
230 return -1; 231 return -1;
231} 232}
232 233
234static int
235ctnetlink_dump_timestamp(struct sk_buff *skb, const struct nf_conn *ct)
236{
237 struct nlattr *nest_count;
238 const struct nf_conn_tstamp *tstamp;
239
240 tstamp = nf_conn_tstamp_find(ct);
241 if (!tstamp)
242 return 0;
243
244 nest_count = nla_nest_start(skb, CTA_TIMESTAMP | NLA_F_NESTED);
245 if (!nest_count)
246 goto nla_put_failure;
247
248 NLA_PUT_BE64(skb, CTA_TIMESTAMP_START, cpu_to_be64(tstamp->start));
249 if (tstamp->stop != 0) {
250 NLA_PUT_BE64(skb, CTA_TIMESTAMP_STOP,
251 cpu_to_be64(tstamp->stop));
252 }
253 nla_nest_end(skb, nest_count);
254
255 return 0;
256
257nla_put_failure:
258 return -1;
259}
260
233#ifdef CONFIG_NF_CONNTRACK_MARK 261#ifdef CONFIG_NF_CONNTRACK_MARK
234static inline int 262static inline int
235ctnetlink_dump_mark(struct sk_buff *skb, const struct nf_conn *ct) 263ctnetlink_dump_mark(struct sk_buff *skb, const struct nf_conn *ct)
@@ -404,6 +432,7 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
404 ctnetlink_dump_timeout(skb, ct) < 0 || 432 ctnetlink_dump_timeout(skb, ct) < 0 ||
405 ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || 433 ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 ||
406 ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0 || 434 ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0 ||
435 ctnetlink_dump_timestamp(skb, ct) < 0 ||
407 ctnetlink_dump_protoinfo(skb, ct) < 0 || 436 ctnetlink_dump_protoinfo(skb, ct) < 0 ||
408 ctnetlink_dump_helpinfo(skb, ct) < 0 || 437 ctnetlink_dump_helpinfo(skb, ct) < 0 ||
409 ctnetlink_dump_mark(skb, ct) < 0 || 438 ctnetlink_dump_mark(skb, ct) < 0 ||
@@ -471,6 +500,18 @@ ctnetlink_secctx_size(const struct nf_conn *ct)
471} 500}
472 501
473static inline size_t 502static inline size_t
503ctnetlink_timestamp_size(const struct nf_conn *ct)
504{
505#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
506 if (!nf_ct_ext_exist(ct, NF_CT_EXT_TSTAMP))
507 return 0;
508 return nla_total_size(0) + 2 * nla_total_size(sizeof(uint64_t));
509#else
510 return 0;
511#endif
512}
513
514static inline size_t
474ctnetlink_nlmsg_size(const struct nf_conn *ct) 515ctnetlink_nlmsg_size(const struct nf_conn *ct)
475{ 516{
476 return NLMSG_ALIGN(sizeof(struct nfgenmsg)) 517 return NLMSG_ALIGN(sizeof(struct nfgenmsg))
@@ -481,6 +522,7 @@ ctnetlink_nlmsg_size(const struct nf_conn *ct)
481 + nla_total_size(sizeof(u_int32_t)) /* CTA_ID */ 522 + nla_total_size(sizeof(u_int32_t)) /* CTA_ID */
482 + nla_total_size(sizeof(u_int32_t)) /* CTA_STATUS */ 523 + nla_total_size(sizeof(u_int32_t)) /* CTA_STATUS */
483 + ctnetlink_counters_size(ct) 524 + ctnetlink_counters_size(ct)
525 + ctnetlink_timestamp_size(ct)
484 + nla_total_size(sizeof(u_int32_t)) /* CTA_TIMEOUT */ 526 + nla_total_size(sizeof(u_int32_t)) /* CTA_TIMEOUT */
485 + nla_total_size(0) /* CTA_PROTOINFO */ 527 + nla_total_size(0) /* CTA_PROTOINFO */
486 + nla_total_size(0) /* CTA_HELP */ 528 + nla_total_size(0) /* CTA_HELP */
@@ -571,7 +613,8 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item)
571 613
572 if (events & (1 << IPCT_DESTROY)) { 614 if (events & (1 << IPCT_DESTROY)) {
573 if (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || 615 if (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 ||
574 ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0) 616 ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0 ||
617 ctnetlink_dump_timestamp(skb, ct) < 0)
575 goto nla_put_failure; 618 goto nla_put_failure;
576 } else { 619 } else {
577 if (ctnetlink_dump_timeout(skb, ct) < 0) 620 if (ctnetlink_dump_timeout(skb, ct) < 0)
@@ -1360,6 +1403,7 @@ ctnetlink_create_conntrack(struct net *net, u16 zone,
1360 } 1403 }
1361 1404
1362 nf_ct_acct_ext_add(ct, GFP_ATOMIC); 1405 nf_ct_acct_ext_add(ct, GFP_ATOMIC);
1406 nf_ct_tstamp_ext_add(ct, GFP_ATOMIC);
1363 nf_ct_ecache_ext_add(ct, 0, 0, GFP_ATOMIC); 1407 nf_ct_ecache_ext_add(ct, 0, 0, GFP_ATOMIC);
1364 /* we must add conntrack extensions before confirmation. */ 1408 /* we must add conntrack extensions before confirmation. */
1365 ct->status |= IPS_CONFIRMED; 1409 ct->status |= IPS_CONFIRMED;
diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c
index 8257bf643593..69107fd78d3e 100644
--- a/net/netfilter/nf_conntrack_standalone.c
+++ b/net/netfilter/nf_conntrack_standalone.c
@@ -29,6 +29,7 @@
29#include <net/netfilter/nf_conntrack_helper.h> 29#include <net/netfilter/nf_conntrack_helper.h>
30#include <net/netfilter/nf_conntrack_acct.h> 30#include <net/netfilter/nf_conntrack_acct.h>
31#include <net/netfilter/nf_conntrack_zones.h> 31#include <net/netfilter/nf_conntrack_zones.h>
32#include <net/netfilter/nf_conntrack_timestamp.h>
32#include <linux/rculist_nulls.h> 33#include <linux/rculist_nulls.h>
33 34
34MODULE_LICENSE("GPL"); 35MODULE_LICENSE("GPL");
@@ -46,6 +47,7 @@ EXPORT_SYMBOL_GPL(print_tuple);
46struct ct_iter_state { 47struct ct_iter_state {
47 struct seq_net_private p; 48 struct seq_net_private p;
48 unsigned int bucket; 49 unsigned int bucket;
50 u_int64_t time_now;
49}; 51};
50 52
51static struct hlist_nulls_node *ct_get_first(struct seq_file *seq) 53static struct hlist_nulls_node *ct_get_first(struct seq_file *seq)
@@ -96,6 +98,9 @@ static struct hlist_nulls_node *ct_get_idx(struct seq_file *seq, loff_t pos)
96static void *ct_seq_start(struct seq_file *seq, loff_t *pos) 98static void *ct_seq_start(struct seq_file *seq, loff_t *pos)
97 __acquires(RCU) 99 __acquires(RCU)
98{ 100{
101 struct ct_iter_state *st = seq->private;
102
103 st->time_now = ktime_to_ns(ktime_get_real());
99 rcu_read_lock(); 104 rcu_read_lock();
100 return ct_get_idx(seq, *pos); 105 return ct_get_idx(seq, *pos);
101} 106}
@@ -135,6 +140,39 @@ static inline int ct_show_secctx(struct seq_file *s, const struct nf_conn *ct)
135} 140}
136#endif 141#endif
137 142
143#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
144static u_int64_t ct_delta_time(u_int64_t time_now, const struct nf_conn *ct)
145{
146 struct nf_conn_tstamp *tstamp;
147
148 tstamp = nf_conn_tstamp_find(ct);
149 if (tstamp) {
150 u_int64_t delta_time = time_now - tstamp->start;
151 return delta_time > 0 ? div_s64(delta_time, NSEC_PER_SEC) : 0;
152 }
153 return -1;
154}
155
156static int ct_show_delta_time(struct seq_file *s, const struct nf_conn *ct)
157{
158 struct ct_iter_state *st = s->private;
159 u_int64_t delta_time;
160
161 delta_time = ct_delta_time(st->time_now, ct);
162 if (delta_time < 0)
163 return 0;
164
165 return seq_printf(s, "delta-time=%llu ",
166 (unsigned long long)delta_time);
167}
168#else
169static inline int
170ct_show_delta_time(struct seq_file *s, const struct nf_conn *ct)
171{
172 return 0;
173}
174#endif
175
138/* return 0 on success, 1 in case of error */ 176/* return 0 on success, 1 in case of error */
139static int ct_seq_show(struct seq_file *s, void *v) 177static int ct_seq_show(struct seq_file *s, void *v)
140{ 178{
@@ -203,6 +241,9 @@ static int ct_seq_show(struct seq_file *s, void *v)
203 goto release; 241 goto release;
204#endif 242#endif
205 243
244 if (ct_show_delta_time(s, ct))
245 goto release;
246
206 if (seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use))) 247 if (seq_printf(s, "use=%u\n", atomic_read(&ct->ct_general.use)))
207 goto release; 248 goto release;
208 249
diff --git a/net/netfilter/nf_conntrack_timestamp.c b/net/netfilter/nf_conntrack_timestamp.c
new file mode 100644
index 000000000000..af7dd31af0a1
--- /dev/null
+++ b/net/netfilter/nf_conntrack_timestamp.c
@@ -0,0 +1,120 @@
1/*
2 * (C) 2010 Pablo Neira Ayuso <pablo@netfilter.org>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation (or any later at your option).
7 */
8
9#include <linux/netfilter.h>
10#include <linux/slab.h>
11#include <linux/kernel.h>
12#include <linux/moduleparam.h>
13
14#include <net/netfilter/nf_conntrack.h>
15#include <net/netfilter/nf_conntrack_extend.h>
16#include <net/netfilter/nf_conntrack_timestamp.h>
17
18static int nf_ct_tstamp __read_mostly;
19
20module_param_named(tstamp, nf_ct_tstamp, bool, 0644);
21MODULE_PARM_DESC(tstamp, "Enable connection tracking flow timestamping.");
22
23#ifdef CONFIG_SYSCTL
24static struct ctl_table tstamp_sysctl_table[] = {
25 {
26 .procname = "nf_conntrack_timestamp",
27 .data = &init_net.ct.sysctl_tstamp,
28 .maxlen = sizeof(unsigned int),
29 .mode = 0644,
30 .proc_handler = proc_dointvec,
31 },
32 {}
33};
34#endif /* CONFIG_SYSCTL */
35
36static struct nf_ct_ext_type tstamp_extend __read_mostly = {
37 .len = sizeof(struct nf_conn_tstamp),
38 .align = __alignof__(struct nf_conn_tstamp),
39 .id = NF_CT_EXT_TSTAMP,
40};
41
42#ifdef CONFIG_SYSCTL
43static int nf_conntrack_tstamp_init_sysctl(struct net *net)
44{
45 struct ctl_table *table;
46
47 table = kmemdup(tstamp_sysctl_table, sizeof(tstamp_sysctl_table),
48 GFP_KERNEL);
49 if (!table)
50 goto out;
51
52 table[0].data = &net->ct.sysctl_tstamp;
53
54 net->ct.tstamp_sysctl_header = register_net_sysctl_table(net,
55 nf_net_netfilter_sysctl_path, table);
56 if (!net->ct.tstamp_sysctl_header) {
57 printk(KERN_ERR "nf_ct_tstamp: can't register to sysctl.\n");
58 goto out_register;
59 }
60 return 0;
61
62out_register:
63 kfree(table);
64out:
65 return -ENOMEM;
66}
67
68static void nf_conntrack_tstamp_fini_sysctl(struct net *net)
69{
70 struct ctl_table *table;
71
72 table = net->ct.tstamp_sysctl_header->ctl_table_arg;
73 unregister_net_sysctl_table(net->ct.tstamp_sysctl_header);
74 kfree(table);
75}
76#else
77static int nf_conntrack_tstamp_init_sysctl(struct net *net)
78{
79 return 0;
80}
81
82static void nf_conntrack_tstamp_fini_sysctl(struct net *net)
83{
84}
85#endif
86
87int nf_conntrack_tstamp_init(struct net *net)
88{
89 int ret;
90
91 net->ct.sysctl_tstamp = nf_ct_tstamp;
92
93 if (net_eq(net, &init_net)) {
94 ret = nf_ct_extend_register(&tstamp_extend);
95 if (ret < 0) {
96 printk(KERN_ERR "nf_ct_tstamp: Unable to register "
97 "extension\n");
98 goto out_extend_register;
99 }
100 }
101
102 ret = nf_conntrack_tstamp_init_sysctl(net);
103 if (ret < 0)
104 goto out_sysctl;
105
106 return 0;
107
108out_sysctl:
109 if (net_eq(net, &init_net))
110 nf_ct_extend_unregister(&tstamp_extend);
111out_extend_register:
112 return ret;
113}
114
115void nf_conntrack_tstamp_fini(struct net *net)
116{
117 nf_conntrack_tstamp_fini_sysctl(net);
118 if (net_eq(net, &init_net))
119 nf_ct_extend_unregister(&tstamp_extend);
120}