Skip to content

Commit 601731f

Browse files
Florian Westphalummakynes
authored andcommitted
netfilter: conntrack: add conntrack event timestamp
Nadia Pinaeva writes: I am working on a tool that allows collecting network performance metrics by using conntrack events. Start time of a conntrack entry is used to evaluate seen_reply latency, therefore the sooner it is timestamped, the better the precision is. In particular, when using this tool to compare the performance of the same feature implemented using iptables/nftables/OVS it is crucial to have the entry timestamped earlier to see any difference. At this time, conntrack events can only get timestamped at recv time in userspace, so there can be some delay between the event being generated and the userspace process consuming the message. There is sys/net/netfilter/nf_conntrack_timestamp, which adds a 64bit timestamp (ns resolution) that records start and stop times, but its not suited for this either, start time is the 'hashtable insertion time', not 'conntrack allocation time'. There is concern that moving the start-time moment to conntrack allocation will add overhead in case of flooding, where conntrack entries are allocated and released right away without getting inserted into the hashtable. Also, even if this was changed it would not with events other than new (start time) and destroy (stop time). Pablo suggested to add new CTA_TIMESTAMP_EVENT, this adds this feature. The timestamp is recorded in case both events are requested and the sys/net/netfilter/nf_conntrack_timestamp toggle is enabled. Reported-by: Nadia Pinaeva <[email protected]> Suggested-by: Pablo Neira Ayuso <[email protected]> Signed-off-by: Florian Westphal <[email protected]> Signed-off-by: Pablo Neira Ayuso <[email protected]>
1 parent 95f1c1e commit 601731f

File tree

4 files changed

+61
-0
lines changed

4 files changed

+61
-0
lines changed

include/net/netfilter/nf_conntrack_ecache.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/netfilter/nf_conntrack_common.h>
1313
#include <linux/netfilter/nf_conntrack_tuple_common.h>
1414
#include <net/netfilter/nf_conntrack_extend.h>
15+
#include <asm/local64.h>
1516

1617
enum nf_ct_ecache_state {
1718
NFCT_ECACHE_DESTROY_FAIL, /* tried but failed to send destroy event */
@@ -20,6 +21,9 @@ enum nf_ct_ecache_state {
2021

2122
struct nf_conntrack_ecache {
2223
unsigned long cache; /* bitops want long */
24+
#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
25+
local64_t timestamp; /* event timestamp, in nanoseconds */
26+
#endif
2327
u16 ctmask; /* bitmask of ct events to be delivered */
2428
u16 expmask; /* bitmask of expect events to be delivered */
2529
u32 missed; /* missed events */
@@ -108,6 +112,14 @@ nf_conntrack_event_cache(enum ip_conntrack_events event, struct nf_conn *ct)
108112
if (e == NULL)
109113
return;
110114

115+
#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
116+
/* renew only if this is the first cached event, so that the
117+
* timestamp reflects the first, not the last, generated event.
118+
*/
119+
if (local64_read(&e->timestamp) && READ_ONCE(e->cache) == 0)
120+
local64_set(&e->timestamp, ktime_get_real_ns());
121+
#endif
122+
111123
set_bit(event, &e->cache);
112124
#endif
113125
}

include/uapi/linux/netfilter/nfnetlink_conntrack.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ enum ctattr_type {
5757
CTA_SYNPROXY,
5858
CTA_FILTER,
5959
CTA_STATUS_MASK,
60+
CTA_TIMESTAMP_EVENT,
6061
__CTA_MAX
6162
};
6263
#define CTA_MAX (__CTA_MAX - 1)

net/netfilter/nf_conntrack_ecache.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,14 @@ static int __nf_conntrack_eventmask_report(struct nf_conntrack_ecache *e,
162162
return ret;
163163
}
164164

165+
static void nf_ct_ecache_tstamp_refresh(struct nf_conntrack_ecache *e)
166+
{
167+
#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
168+
if (local64_read(&e->timestamp))
169+
local64_set(&e->timestamp, ktime_get_real_ns());
170+
#endif
171+
}
172+
165173
int nf_conntrack_eventmask_report(unsigned int events, struct nf_conn *ct,
166174
u32 portid, int report)
167175
{
@@ -186,6 +194,8 @@ int nf_conntrack_eventmask_report(unsigned int events, struct nf_conn *ct,
186194
/* This is a resent of a destroy event? If so, skip missed */
187195
missed = e->portid ? 0 : e->missed;
188196

197+
nf_ct_ecache_tstamp_refresh(e);
198+
189199
ret = __nf_conntrack_eventmask_report(e, events, missed, &item);
190200
if (unlikely(ret < 0 && (events & (1 << IPCT_DESTROY)))) {
191201
/* This is a destroy event that has been triggered by a process,
@@ -297,6 +307,18 @@ void nf_conntrack_ecache_work(struct net *net, enum nf_ct_ecache_state state)
297307
}
298308
}
299309

310+
static void nf_ct_ecache_tstamp_new(const struct nf_conn *ct, struct nf_conntrack_ecache *e)
311+
{
312+
#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
313+
u64 ts = 0;
314+
315+
if (nf_ct_ext_exist(ct, NF_CT_EXT_TSTAMP))
316+
ts = ktime_get_real_ns();
317+
318+
local64_set(&e->timestamp, ts);
319+
#endif
320+
}
321+
300322
bool nf_ct_ecache_ext_add(struct nf_conn *ct, u16 ctmask, u16 expmask, gfp_t gfp)
301323
{
302324
struct net *net = nf_ct_net(ct);
@@ -326,6 +348,7 @@ bool nf_ct_ecache_ext_add(struct nf_conn *ct, u16 ctmask, u16 expmask, gfp_t gfp
326348

327349
e = nf_ct_ext_add(ct, NF_CT_EXT_ECACHE, gfp);
328350
if (e) {
351+
nf_ct_ecache_tstamp_new(ct, e);
329352
e->ctmask = ctmask;
330353
e->expmask = expmask;
331354
}

net/netfilter/nf_conntrack_netlink.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,23 @@ static int ctnetlink_dump_secctx(struct sk_buff *skb, const struct nf_conn *ct)
383383
#endif
384384

385385
#ifdef CONFIG_NF_CONNTRACK_EVENTS
386+
static int
387+
ctnetlink_dump_event_timestamp(struct sk_buff *skb, const struct nf_conn *ct)
388+
{
389+
#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
390+
const struct nf_conntrack_ecache *e = nf_ct_ecache_find(ct);
391+
392+
if (e) {
393+
u64 ts = local64_read(&e->timestamp);
394+
395+
if (ts)
396+
return nla_put_be64(skb, CTA_TIMESTAMP_EVENT,
397+
cpu_to_be64(ts), CTA_TIMESTAMP_PAD);
398+
}
399+
#endif
400+
return 0;
401+
}
402+
386403
static inline int ctnetlink_label_size(const struct nf_conn *ct)
387404
{
388405
struct nf_conn_labels *labels = nf_ct_labels_find(ct);
@@ -717,6 +734,9 @@ static size_t ctnetlink_nlmsg_size(const struct nf_conn *ct)
717734
#endif
718735
+ ctnetlink_proto_size(ct)
719736
+ ctnetlink_label_size(ct)
737+
#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP
738+
+ nla_total_size(sizeof(u64)) /* CTA_TIMESTAMP_EVENT */
739+
#endif
720740
;
721741
}
722742

@@ -838,6 +858,10 @@ ctnetlink_conntrack_event(unsigned int events, const struct nf_ct_event *item)
838858
if (ctnetlink_dump_mark(skb, ct, events & (1 << IPCT_MARK)))
839859
goto nla_put_failure;
840860
#endif
861+
862+
if (ctnetlink_dump_event_timestamp(skb, ct))
863+
goto nla_put_failure;
864+
841865
nlmsg_end(skb, nlh);
842866
err = nfnetlink_send(skb, net, item->portid, group, item->report,
843867
GFP_ATOMIC);
@@ -1557,6 +1581,7 @@ static const struct nla_policy ct_nla_policy[CTA_MAX+1] = {
15571581
.len = NF_CT_LABELS_MAX_SIZE },
15581582
[CTA_FILTER] = { .type = NLA_NESTED },
15591583
[CTA_STATUS_MASK] = { .type = NLA_U32 },
1584+
[CTA_TIMESTAMP_EVENT] = { .type = NLA_REJECT },
15601585
};
15611586

15621587
static int ctnetlink_flush_iterate(struct nf_conn *ct, void *data)

0 commit comments

Comments
 (0)