Skip to content

Commit 80e60e6

Browse files
committed
netfilter: ctnetlink: get and zero operations must be atomic
The get and zero operations have to be done in an atomic context, otherwise counters added between them will be lost. This problem was spotted by Changli Gao while discussing the nfacct infrastructure. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
1 parent b9e61f0 commit 80e60e6

File tree

1 file changed

+39
-45
lines changed

1 file changed

+39
-45
lines changed

net/netfilter/nf_conntrack_netlink.c

Lines changed: 39 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -203,25 +203,18 @@ ctnetlink_dump_helpinfo(struct sk_buff *skb, const struct nf_conn *ct)
203203
}
204204

205205
static int
206-
ctnetlink_dump_counters(struct sk_buff *skb, const struct nf_conn *ct,
207-
enum ip_conntrack_dir dir)
206+
dump_counters(struct sk_buff *skb, u64 pkts, u64 bytes,
207+
enum ip_conntrack_dir dir)
208208
{
209209
enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG;
210210
struct nlattr *nest_count;
211-
const struct nf_conn_counter *acct;
212-
213-
acct = nf_conn_acct_find(ct);
214-
if (!acct)
215-
return 0;
216211

217212
nest_count = nla_nest_start(skb, type | NLA_F_NESTED);
218213
if (!nest_count)
219214
goto nla_put_failure;
220215

221-
NLA_PUT_BE64(skb, CTA_COUNTERS_PACKETS,
222-
cpu_to_be64(atomic64_read(&acct[dir].packets)));
223-
NLA_PUT_BE64(skb, CTA_COUNTERS_BYTES,
224-
cpu_to_be64(atomic64_read(&acct[dir].bytes)));
216+
NLA_PUT_BE64(skb, CTA_COUNTERS_PACKETS, cpu_to_be64(pkts));
217+
NLA_PUT_BE64(skb, CTA_COUNTERS_BYTES, cpu_to_be64(bytes));
225218

226219
nla_nest_end(skb, nest_count);
227220

@@ -231,6 +224,27 @@ ctnetlink_dump_counters(struct sk_buff *skb, const struct nf_conn *ct,
231224
return -1;
232225
}
233226

227+
static int
228+
ctnetlink_dump_counters(struct sk_buff *skb, const struct nf_conn *ct,
229+
enum ip_conntrack_dir dir, int type)
230+
{
231+
struct nf_conn_counter *acct;
232+
u64 pkts, bytes;
233+
234+
acct = nf_conn_acct_find(ct);
235+
if (!acct)
236+
return 0;
237+
238+
if (type == IPCTNL_MSG_CT_GET_CTRZERO) {
239+
pkts = atomic64_xchg(&acct[dir].packets, 0);
240+
bytes = atomic64_xchg(&acct[dir].bytes, 0);
241+
} else {
242+
pkts = atomic64_read(&acct[dir].packets);
243+
bytes = atomic64_read(&acct[dir].bytes);
244+
}
245+
return dump_counters(skb, pkts, bytes, dir);
246+
}
247+
234248
static int
235249
ctnetlink_dump_timestamp(struct sk_buff *skb, const struct nf_conn *ct)
236250
{
@@ -393,15 +407,15 @@ ctnetlink_dump_use(struct sk_buff *skb, const struct nf_conn *ct)
393407
}
394408

395409
static int
396-
ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
397-
int event, struct nf_conn *ct)
410+
ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq, u32 type,
411+
struct nf_conn *ct)
398412
{
399413
struct nlmsghdr *nlh;
400414
struct nfgenmsg *nfmsg;
401415
struct nlattr *nest_parms;
402-
unsigned int flags = pid ? NLM_F_MULTI : 0;
416+
unsigned int flags = pid ? NLM_F_MULTI : 0, event;
403417

404-
event |= NFNL_SUBSYS_CTNETLINK << 8;
418+
event = (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_NEW);
405419
nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags);
406420
if (nlh == NULL)
407421
goto nlmsg_failure;
@@ -430,8 +444,8 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
430444

431445
if (ctnetlink_dump_status(skb, ct) < 0 ||
432446
ctnetlink_dump_timeout(skb, ct) < 0 ||
433-
ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 ||
434-
ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0 ||
447+
ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL, type) < 0 ||
448+
ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY, type) < 0 ||
435449
ctnetlink_dump_timestamp(skb, ct) < 0 ||
436450
ctnetlink_dump_protoinfo(skb, ct) < 0 ||
437451
ctnetlink_dump_helpinfo(skb, ct) < 0 ||
@@ -612,8 +626,10 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item)
612626
goto nla_put_failure;
613627

614628
if (events & (1 << IPCT_DESTROY)) {
615-
if (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 ||
616-
ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0 ||
629+
if (ctnetlink_dump_counters(skb, ct,
630+
IP_CT_DIR_ORIGINAL, type) < 0 ||
631+
ctnetlink_dump_counters(skb, ct,
632+
IP_CT_DIR_REPLY, type) < 0 ||
617633
ctnetlink_dump_timestamp(skb, ct) < 0)
618634
goto nla_put_failure;
619635
} else {
@@ -709,24 +725,13 @@ ctnetlink_dump_table(struct sk_buff *skb, struct netlink_callback *cb)
709725
}
710726
if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid,
711727
cb->nlh->nlmsg_seq,
712-
IPCTNL_MSG_CT_NEW, ct) < 0) {
728+
NFNL_MSG_TYPE(
729+
cb->nlh->nlmsg_type),
730+
ct) < 0) {
713731
nf_conntrack_get(&ct->ct_general);
714732
cb->args[1] = (unsigned long)ct;
715733
goto out;
716734
}
717-
718-
if (NFNL_MSG_TYPE(cb->nlh->nlmsg_type) ==
719-
IPCTNL_MSG_CT_GET_CTRZERO) {
720-
struct nf_conn_counter *acct;
721-
722-
acct = nf_conn_acct_find(ct);
723-
if (acct) {
724-
atomic64_set(&acct[IP_CT_DIR_ORIGINAL].bytes, 0);
725-
atomic64_set(&acct[IP_CT_DIR_ORIGINAL].packets, 0);
726-
atomic64_set(&acct[IP_CT_DIR_REPLY].bytes, 0);
727-
atomic64_set(&acct[IP_CT_DIR_REPLY].packets, 0);
728-
}
729-
}
730735
}
731736
if (cb->args[1]) {
732737
cb->args[1] = 0;
@@ -1005,7 +1010,7 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
10051010

10061011
rcu_read_lock();
10071012
err = ctnetlink_fill_info(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq,
1008-
IPCTNL_MSG_CT_NEW, ct);
1013+
NFNL_MSG_TYPE(nlh->nlmsg_type), ct);
10091014
rcu_read_unlock();
10101015
nf_ct_put(ct);
10111016
if (err <= 0)
@@ -1015,17 +1020,6 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
10151020
if (err < 0)
10161021
goto out;
10171022

1018-
if (NFNL_MSG_TYPE(nlh->nlmsg_type) == IPCTNL_MSG_CT_GET_CTRZERO) {
1019-
struct nf_conn_counter *acct;
1020-
1021-
acct = nf_conn_acct_find(ct);
1022-
if (acct) {
1023-
atomic64_set(&acct[IP_CT_DIR_ORIGINAL].bytes, 0);
1024-
atomic64_set(&acct[IP_CT_DIR_ORIGINAL].packets, 0);
1025-
atomic64_set(&acct[IP_CT_DIR_REPLY].bytes, 0);
1026-
atomic64_set(&acct[IP_CT_DIR_REPLY].packets, 0);
1027-
}
1028-
}
10291023
return 0;
10301024

10311025
free:

0 commit comments

Comments
 (0)