aboutsummaryrefslogtreecommitdiffstats
path: root/net/netfilter
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2011-12-24 08:11:39 -0500
committerPablo Neira Ayuso <pablo@netfilter.org>2011-12-24 08:35:59 -0500
commit80e60e67bc4bbfe61b61a344f542af23e16abdbf (patch)
treec5269a9db0efcfb64ea83190b4b83500773711af /net/netfilter
parentb9e61f0dff4b50e207ff4bb09472bda7881b21a9 (diff)
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>
Diffstat (limited to 'net/netfilter')
-rw-r--r--net/netfilter/nf_conntrack_netlink.c84
1 files changed, 39 insertions, 45 deletions
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 4f9c941335c9..85033344aed2 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -203,25 +203,18 @@ nla_put_failure:
203} 203}
204 204
205static int 205static int
206ctnetlink_dump_counters(struct sk_buff *skb, const struct nf_conn *ct, 206dump_counters(struct sk_buff *skb, u64 pkts, u64 bytes,
207 enum ip_conntrack_dir dir) 207 enum ip_conntrack_dir dir)
208{ 208{
209 enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG; 209 enum ctattr_type type = dir ? CTA_COUNTERS_REPLY: CTA_COUNTERS_ORIG;
210 struct nlattr *nest_count; 210 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;
216 211
217 nest_count = nla_nest_start(skb, type | NLA_F_NESTED); 212 nest_count = nla_nest_start(skb, type | NLA_F_NESTED);
218 if (!nest_count) 213 if (!nest_count)
219 goto nla_put_failure; 214 goto nla_put_failure;
220 215
221 NLA_PUT_BE64(skb, CTA_COUNTERS_PACKETS, 216 NLA_PUT_BE64(skb, CTA_COUNTERS_PACKETS, cpu_to_be64(pkts));
222 cpu_to_be64(atomic64_read(&acct[dir].packets))); 217 NLA_PUT_BE64(skb, CTA_COUNTERS_BYTES, cpu_to_be64(bytes));
223 NLA_PUT_BE64(skb, CTA_COUNTERS_BYTES,
224 cpu_to_be64(atomic64_read(&acct[dir].bytes)));
225 218
226 nla_nest_end(skb, nest_count); 219 nla_nest_end(skb, nest_count);
227 220
@@ -232,6 +225,27 @@ nla_put_failure:
232} 225}
233 226
234static int 227static int
228ctnetlink_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
248static int
235ctnetlink_dump_timestamp(struct sk_buff *skb, const struct nf_conn *ct) 249ctnetlink_dump_timestamp(struct sk_buff *skb, const struct nf_conn *ct)
236{ 250{
237 struct nlattr *nest_count; 251 struct nlattr *nest_count;
@@ -393,15 +407,15 @@ nla_put_failure:
393} 407}
394 408
395static int 409static int
396ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq, 410ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq, u32 type,
397 int event, struct nf_conn *ct) 411 struct nf_conn *ct)
398{ 412{
399 struct nlmsghdr *nlh; 413 struct nlmsghdr *nlh;
400 struct nfgenmsg *nfmsg; 414 struct nfgenmsg *nfmsg;
401 struct nlattr *nest_parms; 415 struct nlattr *nest_parms;
402 unsigned int flags = pid ? NLM_F_MULTI : 0; 416 unsigned int flags = pid ? NLM_F_MULTI : 0, event;
403 417
404 event |= NFNL_SUBSYS_CTNETLINK << 8; 418 event = (NFNL_SUBSYS_CTNETLINK << 8 | IPCTNL_MSG_CT_NEW);
405 nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags); 419 nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags);
406 if (nlh == NULL) 420 if (nlh == NULL)
407 goto nlmsg_failure; 421 goto nlmsg_failure;
@@ -430,8 +444,8 @@ ctnetlink_fill_info(struct sk_buff *skb, u32 pid, u32 seq,
430 444
431 if (ctnetlink_dump_status(skb, ct) < 0 || 445 if (ctnetlink_dump_status(skb, ct) < 0 ||
432 ctnetlink_dump_timeout(skb, ct) < 0 || 446 ctnetlink_dump_timeout(skb, ct) < 0 ||
433 ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || 447 ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL, type) < 0 ||
434 ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0 || 448 ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY, type) < 0 ||
435 ctnetlink_dump_timestamp(skb, ct) < 0 || 449 ctnetlink_dump_timestamp(skb, ct) < 0 ||
436 ctnetlink_dump_protoinfo(skb, ct) < 0 || 450 ctnetlink_dump_protoinfo(skb, ct) < 0 ||
437 ctnetlink_dump_helpinfo(skb, ct) < 0 || 451 ctnetlink_dump_helpinfo(skb, ct) < 0 ||
@@ -612,8 +626,10 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item)
612 goto nla_put_failure; 626 goto nla_put_failure;
613 627
614 if (events & (1 << IPCT_DESTROY)) { 628 if (events & (1 << IPCT_DESTROY)) {
615 if (ctnetlink_dump_counters(skb, ct, IP_CT_DIR_ORIGINAL) < 0 || 629 if (ctnetlink_dump_counters(skb, ct,
616 ctnetlink_dump_counters(skb, ct, IP_CT_DIR_REPLY) < 0 || 630 IP_CT_DIR_ORIGINAL, type) < 0 ||
631 ctnetlink_dump_counters(skb, ct,
632 IP_CT_DIR_REPLY, type) < 0 ||
617 ctnetlink_dump_timestamp(skb, ct) < 0) 633 ctnetlink_dump_timestamp(skb, ct) < 0)
618 goto nla_put_failure; 634 goto nla_put_failure;
619 } else { 635 } else {
@@ -709,24 +725,13 @@ restart:
709 } 725 }
710 if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid, 726 if (ctnetlink_fill_info(skb, NETLINK_CB(cb->skb).pid,
711 cb->nlh->nlmsg_seq, 727 cb->nlh->nlmsg_seq,
712 IPCTNL_MSG_CT_NEW, ct) < 0) { 728 NFNL_MSG_TYPE(
729 cb->nlh->nlmsg_type),
730 ct) < 0) {
713 nf_conntrack_get(&ct->ct_general); 731 nf_conntrack_get(&ct->ct_general);
714 cb->args[1] = (unsigned long)ct; 732 cb->args[1] = (unsigned long)ct;
715 goto out; 733 goto out;
716 } 734 }
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 }
730 } 735 }
731 if (cb->args[1]) { 736 if (cb->args[1]) {
732 cb->args[1] = 0; 737 cb->args[1] = 0;
@@ -1005,7 +1010,7 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
1005 1010
1006 rcu_read_lock(); 1011 rcu_read_lock();
1007 err = ctnetlink_fill_info(skb2, NETLINK_CB(skb).pid, nlh->nlmsg_seq, 1012 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);
1009 rcu_read_unlock(); 1014 rcu_read_unlock();
1010 nf_ct_put(ct); 1015 nf_ct_put(ct);
1011 if (err <= 0) 1016 if (err <= 0)
@@ -1015,17 +1020,6 @@ ctnetlink_get_conntrack(struct sock *ctnl, struct sk_buff *skb,
1015 if (err < 0) 1020 if (err < 0)
1016 goto out; 1021 goto out;
1017 1022
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 }
1029 return 0; 1023 return 0;
1030 1024
1031free: 1025free: