Commit b6d04688040218d66edd8b221e43c67240b83119
Committed by
Pablo Neira Ayuso
1 parent
d24675cb1f
netfilter: nfnetlink_acct: avoid using NFACCT_F_OVERQUOTA with bit helper functions
Bit helper functions were used for manipulation with NFACCT_F_OVERQUOTA, but they are accepting pit position, but not a bit mask. As a result not a third bit for NFACCT_F_OVERQUOTA was set, but forth. Such behaviour was dangarous and could lead to unexpected overquota report result. Signed-off-by: Alexey Perevalov <a.perevalov@samsung.com> Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Showing 1 changed file with 5 additions and 3 deletions Inline Diff
net/netfilter/nfnetlink_acct.c
1 | /* | 1 | /* |
2 | * (C) 2011 Pablo Neira Ayuso <pablo@netfilter.org> | 2 | * (C) 2011 Pablo Neira Ayuso <pablo@netfilter.org> |
3 | * (C) 2011 Intra2net AG <http://www.intra2net.com> | 3 | * (C) 2011 Intra2net AG <http://www.intra2net.com> |
4 | * | 4 | * |
5 | * This program is free software; you can redistribute it and/or modify | 5 | * This program is free software; you can redistribute it and/or modify |
6 | * it under the terms of the GNU General Public License version 2 as | 6 | * it under the terms of the GNU General Public License version 2 as |
7 | * published by the Free Software Foundation (or any later at your option). | 7 | * published by the Free Software Foundation (or any later at your option). |
8 | */ | 8 | */ |
9 | #include <linux/init.h> | 9 | #include <linux/init.h> |
10 | #include <linux/module.h> | 10 | #include <linux/module.h> |
11 | #include <linux/kernel.h> | 11 | #include <linux/kernel.h> |
12 | #include <linux/skbuff.h> | 12 | #include <linux/skbuff.h> |
13 | #include <linux/atomic.h> | 13 | #include <linux/atomic.h> |
14 | #include <linux/netlink.h> | 14 | #include <linux/netlink.h> |
15 | #include <linux/rculist.h> | 15 | #include <linux/rculist.h> |
16 | #include <linux/slab.h> | 16 | #include <linux/slab.h> |
17 | #include <linux/types.h> | 17 | #include <linux/types.h> |
18 | #include <linux/errno.h> | 18 | #include <linux/errno.h> |
19 | #include <net/netlink.h> | 19 | #include <net/netlink.h> |
20 | #include <net/sock.h> | 20 | #include <net/sock.h> |
21 | 21 | ||
22 | #include <linux/netfilter.h> | 22 | #include <linux/netfilter.h> |
23 | #include <linux/netfilter/nfnetlink.h> | 23 | #include <linux/netfilter/nfnetlink.h> |
24 | #include <linux/netfilter/nfnetlink_acct.h> | 24 | #include <linux/netfilter/nfnetlink_acct.h> |
25 | 25 | ||
26 | MODULE_LICENSE("GPL"); | 26 | MODULE_LICENSE("GPL"); |
27 | MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); | 27 | MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); |
28 | MODULE_DESCRIPTION("nfacct: Extended Netfilter accounting infrastructure"); | 28 | MODULE_DESCRIPTION("nfacct: Extended Netfilter accounting infrastructure"); |
29 | 29 | ||
30 | static LIST_HEAD(nfnl_acct_list); | 30 | static LIST_HEAD(nfnl_acct_list); |
31 | 31 | ||
32 | struct nf_acct { | 32 | struct nf_acct { |
33 | atomic64_t pkts; | 33 | atomic64_t pkts; |
34 | atomic64_t bytes; | 34 | atomic64_t bytes; |
35 | unsigned long flags; | 35 | unsigned long flags; |
36 | struct list_head head; | 36 | struct list_head head; |
37 | atomic_t refcnt; | 37 | atomic_t refcnt; |
38 | char name[NFACCT_NAME_MAX]; | 38 | char name[NFACCT_NAME_MAX]; |
39 | struct rcu_head rcu_head; | 39 | struct rcu_head rcu_head; |
40 | char data[0]; | 40 | char data[0]; |
41 | }; | 41 | }; |
42 | 42 | ||
43 | #define NFACCT_F_QUOTA (NFACCT_F_QUOTA_PKTS | NFACCT_F_QUOTA_BYTES) | 43 | #define NFACCT_F_QUOTA (NFACCT_F_QUOTA_PKTS | NFACCT_F_QUOTA_BYTES) |
44 | #define NFACCT_OVERQUOTA_BIT 2 /* NFACCT_F_OVERQUOTA */ | ||
44 | 45 | ||
45 | static int | 46 | static int |
46 | nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb, | 47 | nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb, |
47 | const struct nlmsghdr *nlh, const struct nlattr * const tb[]) | 48 | const struct nlmsghdr *nlh, const struct nlattr * const tb[]) |
48 | { | 49 | { |
49 | struct nf_acct *nfacct, *matching = NULL; | 50 | struct nf_acct *nfacct, *matching = NULL; |
50 | char *acct_name; | 51 | char *acct_name; |
51 | unsigned int size = 0; | 52 | unsigned int size = 0; |
52 | u32 flags = 0; | 53 | u32 flags = 0; |
53 | 54 | ||
54 | if (!tb[NFACCT_NAME]) | 55 | if (!tb[NFACCT_NAME]) |
55 | return -EINVAL; | 56 | return -EINVAL; |
56 | 57 | ||
57 | acct_name = nla_data(tb[NFACCT_NAME]); | 58 | acct_name = nla_data(tb[NFACCT_NAME]); |
58 | if (strlen(acct_name) == 0) | 59 | if (strlen(acct_name) == 0) |
59 | return -EINVAL; | 60 | return -EINVAL; |
60 | 61 | ||
61 | list_for_each_entry(nfacct, &nfnl_acct_list, head) { | 62 | list_for_each_entry(nfacct, &nfnl_acct_list, head) { |
62 | if (strncmp(nfacct->name, acct_name, NFACCT_NAME_MAX) != 0) | 63 | if (strncmp(nfacct->name, acct_name, NFACCT_NAME_MAX) != 0) |
63 | continue; | 64 | continue; |
64 | 65 | ||
65 | if (nlh->nlmsg_flags & NLM_F_EXCL) | 66 | if (nlh->nlmsg_flags & NLM_F_EXCL) |
66 | return -EEXIST; | 67 | return -EEXIST; |
67 | 68 | ||
68 | matching = nfacct; | 69 | matching = nfacct; |
69 | break; | 70 | break; |
70 | } | 71 | } |
71 | 72 | ||
72 | if (matching) { | 73 | if (matching) { |
73 | if (nlh->nlmsg_flags & NLM_F_REPLACE) { | 74 | if (nlh->nlmsg_flags & NLM_F_REPLACE) { |
74 | /* reset counters if you request a replacement. */ | 75 | /* reset counters if you request a replacement. */ |
75 | atomic64_set(&matching->pkts, 0); | 76 | atomic64_set(&matching->pkts, 0); |
76 | atomic64_set(&matching->bytes, 0); | 77 | atomic64_set(&matching->bytes, 0); |
77 | smp_mb__before_atomic(); | 78 | smp_mb__before_atomic(); |
78 | /* reset overquota flag if quota is enabled. */ | 79 | /* reset overquota flag if quota is enabled. */ |
79 | if ((matching->flags & NFACCT_F_QUOTA)) | 80 | if ((matching->flags & NFACCT_F_QUOTA)) |
80 | clear_bit(NFACCT_F_OVERQUOTA, &matching->flags); | 81 | clear_bit(NFACCT_OVERQUOTA_BIT, |
82 | &matching->flags); | ||
81 | return 0; | 83 | return 0; |
82 | } | 84 | } |
83 | return -EBUSY; | 85 | return -EBUSY; |
84 | } | 86 | } |
85 | 87 | ||
86 | if (tb[NFACCT_FLAGS]) { | 88 | if (tb[NFACCT_FLAGS]) { |
87 | flags = ntohl(nla_get_be32(tb[NFACCT_FLAGS])); | 89 | flags = ntohl(nla_get_be32(tb[NFACCT_FLAGS])); |
88 | if (flags & ~NFACCT_F_QUOTA) | 90 | if (flags & ~NFACCT_F_QUOTA) |
89 | return -EOPNOTSUPP; | 91 | return -EOPNOTSUPP; |
90 | if ((flags & NFACCT_F_QUOTA) == NFACCT_F_QUOTA) | 92 | if ((flags & NFACCT_F_QUOTA) == NFACCT_F_QUOTA) |
91 | return -EINVAL; | 93 | return -EINVAL; |
92 | if (flags & NFACCT_F_OVERQUOTA) | 94 | if (flags & NFACCT_F_OVERQUOTA) |
93 | return -EINVAL; | 95 | return -EINVAL; |
94 | 96 | ||
95 | size += sizeof(u64); | 97 | size += sizeof(u64); |
96 | } | 98 | } |
97 | 99 | ||
98 | nfacct = kzalloc(sizeof(struct nf_acct) + size, GFP_KERNEL); | 100 | nfacct = kzalloc(sizeof(struct nf_acct) + size, GFP_KERNEL); |
99 | if (nfacct == NULL) | 101 | if (nfacct == NULL) |
100 | return -ENOMEM; | 102 | return -ENOMEM; |
101 | 103 | ||
102 | if (flags & NFACCT_F_QUOTA) { | 104 | if (flags & NFACCT_F_QUOTA) { |
103 | u64 *quota = (u64 *)nfacct->data; | 105 | u64 *quota = (u64 *)nfacct->data; |
104 | 106 | ||
105 | *quota = be64_to_cpu(nla_get_be64(tb[NFACCT_QUOTA])); | 107 | *quota = be64_to_cpu(nla_get_be64(tb[NFACCT_QUOTA])); |
106 | nfacct->flags = flags; | 108 | nfacct->flags = flags; |
107 | } | 109 | } |
108 | 110 | ||
109 | strncpy(nfacct->name, nla_data(tb[NFACCT_NAME]), NFACCT_NAME_MAX); | 111 | strncpy(nfacct->name, nla_data(tb[NFACCT_NAME]), NFACCT_NAME_MAX); |
110 | 112 | ||
111 | if (tb[NFACCT_BYTES]) { | 113 | if (tb[NFACCT_BYTES]) { |
112 | atomic64_set(&nfacct->bytes, | 114 | atomic64_set(&nfacct->bytes, |
113 | be64_to_cpu(nla_get_be64(tb[NFACCT_BYTES]))); | 115 | be64_to_cpu(nla_get_be64(tb[NFACCT_BYTES]))); |
114 | } | 116 | } |
115 | if (tb[NFACCT_PKTS]) { | 117 | if (tb[NFACCT_PKTS]) { |
116 | atomic64_set(&nfacct->pkts, | 118 | atomic64_set(&nfacct->pkts, |
117 | be64_to_cpu(nla_get_be64(tb[NFACCT_PKTS]))); | 119 | be64_to_cpu(nla_get_be64(tb[NFACCT_PKTS]))); |
118 | } | 120 | } |
119 | atomic_set(&nfacct->refcnt, 1); | 121 | atomic_set(&nfacct->refcnt, 1); |
120 | list_add_tail_rcu(&nfacct->head, &nfnl_acct_list); | 122 | list_add_tail_rcu(&nfacct->head, &nfnl_acct_list); |
121 | return 0; | 123 | return 0; |
122 | } | 124 | } |
123 | 125 | ||
124 | static int | 126 | static int |
125 | nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, | 127 | nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, |
126 | int event, struct nf_acct *acct) | 128 | int event, struct nf_acct *acct) |
127 | { | 129 | { |
128 | struct nlmsghdr *nlh; | 130 | struct nlmsghdr *nlh; |
129 | struct nfgenmsg *nfmsg; | 131 | struct nfgenmsg *nfmsg; |
130 | unsigned int flags = portid ? NLM_F_MULTI : 0; | 132 | unsigned int flags = portid ? NLM_F_MULTI : 0; |
131 | u64 pkts, bytes; | 133 | u64 pkts, bytes; |
132 | u32 old_flags; | 134 | u32 old_flags; |
133 | 135 | ||
134 | event |= NFNL_SUBSYS_ACCT << 8; | 136 | event |= NFNL_SUBSYS_ACCT << 8; |
135 | nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); | 137 | nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); |
136 | if (nlh == NULL) | 138 | if (nlh == NULL) |
137 | goto nlmsg_failure; | 139 | goto nlmsg_failure; |
138 | 140 | ||
139 | nfmsg = nlmsg_data(nlh); | 141 | nfmsg = nlmsg_data(nlh); |
140 | nfmsg->nfgen_family = AF_UNSPEC; | 142 | nfmsg->nfgen_family = AF_UNSPEC; |
141 | nfmsg->version = NFNETLINK_V0; | 143 | nfmsg->version = NFNETLINK_V0; |
142 | nfmsg->res_id = 0; | 144 | nfmsg->res_id = 0; |
143 | 145 | ||
144 | if (nla_put_string(skb, NFACCT_NAME, acct->name)) | 146 | if (nla_put_string(skb, NFACCT_NAME, acct->name)) |
145 | goto nla_put_failure; | 147 | goto nla_put_failure; |
146 | 148 | ||
147 | old_flags = acct->flags; | 149 | old_flags = acct->flags; |
148 | if (type == NFNL_MSG_ACCT_GET_CTRZERO) { | 150 | if (type == NFNL_MSG_ACCT_GET_CTRZERO) { |
149 | pkts = atomic64_xchg(&acct->pkts, 0); | 151 | pkts = atomic64_xchg(&acct->pkts, 0); |
150 | bytes = atomic64_xchg(&acct->bytes, 0); | 152 | bytes = atomic64_xchg(&acct->bytes, 0); |
151 | smp_mb__before_atomic(); | 153 | smp_mb__before_atomic(); |
152 | if (acct->flags & NFACCT_F_QUOTA) | 154 | if (acct->flags & NFACCT_F_QUOTA) |
153 | clear_bit(NFACCT_F_OVERQUOTA, &acct->flags); | 155 | clear_bit(NFACCT_OVERQUOTA_BIT, &acct->flags); |
154 | } else { | 156 | } else { |
155 | pkts = atomic64_read(&acct->pkts); | 157 | pkts = atomic64_read(&acct->pkts); |
156 | bytes = atomic64_read(&acct->bytes); | 158 | bytes = atomic64_read(&acct->bytes); |
157 | } | 159 | } |
158 | if (nla_put_be64(skb, NFACCT_PKTS, cpu_to_be64(pkts)) || | 160 | if (nla_put_be64(skb, NFACCT_PKTS, cpu_to_be64(pkts)) || |
159 | nla_put_be64(skb, NFACCT_BYTES, cpu_to_be64(bytes)) || | 161 | nla_put_be64(skb, NFACCT_BYTES, cpu_to_be64(bytes)) || |
160 | nla_put_be32(skb, NFACCT_USE, htonl(atomic_read(&acct->refcnt)))) | 162 | nla_put_be32(skb, NFACCT_USE, htonl(atomic_read(&acct->refcnt)))) |
161 | goto nla_put_failure; | 163 | goto nla_put_failure; |
162 | if (acct->flags & NFACCT_F_QUOTA) { | 164 | if (acct->flags & NFACCT_F_QUOTA) { |
163 | u64 *quota = (u64 *)acct->data; | 165 | u64 *quota = (u64 *)acct->data; |
164 | 166 | ||
165 | if (nla_put_be32(skb, NFACCT_FLAGS, htonl(old_flags)) || | 167 | if (nla_put_be32(skb, NFACCT_FLAGS, htonl(old_flags)) || |
166 | nla_put_be64(skb, NFACCT_QUOTA, cpu_to_be64(*quota))) | 168 | nla_put_be64(skb, NFACCT_QUOTA, cpu_to_be64(*quota))) |
167 | goto nla_put_failure; | 169 | goto nla_put_failure; |
168 | } | 170 | } |
169 | nlmsg_end(skb, nlh); | 171 | nlmsg_end(skb, nlh); |
170 | return skb->len; | 172 | return skb->len; |
171 | 173 | ||
172 | nlmsg_failure: | 174 | nlmsg_failure: |
173 | nla_put_failure: | 175 | nla_put_failure: |
174 | nlmsg_cancel(skb, nlh); | 176 | nlmsg_cancel(skb, nlh); |
175 | return -1; | 177 | return -1; |
176 | } | 178 | } |
177 | 179 | ||
178 | static int | 180 | static int |
179 | nfnl_acct_dump(struct sk_buff *skb, struct netlink_callback *cb) | 181 | nfnl_acct_dump(struct sk_buff *skb, struct netlink_callback *cb) |
180 | { | 182 | { |
181 | struct nf_acct *cur, *last; | 183 | struct nf_acct *cur, *last; |
182 | 184 | ||
183 | if (cb->args[2]) | 185 | if (cb->args[2]) |
184 | return 0; | 186 | return 0; |
185 | 187 | ||
186 | last = (struct nf_acct *)cb->args[1]; | 188 | last = (struct nf_acct *)cb->args[1]; |
187 | if (cb->args[1]) | 189 | if (cb->args[1]) |
188 | cb->args[1] = 0; | 190 | cb->args[1] = 0; |
189 | 191 | ||
190 | rcu_read_lock(); | 192 | rcu_read_lock(); |
191 | list_for_each_entry_rcu(cur, &nfnl_acct_list, head) { | 193 | list_for_each_entry_rcu(cur, &nfnl_acct_list, head) { |
192 | if (last) { | 194 | if (last) { |
193 | if (cur != last) | 195 | if (cur != last) |
194 | continue; | 196 | continue; |
195 | 197 | ||
196 | last = NULL; | 198 | last = NULL; |
197 | } | 199 | } |
198 | if (nfnl_acct_fill_info(skb, NETLINK_CB(cb->skb).portid, | 200 | if (nfnl_acct_fill_info(skb, NETLINK_CB(cb->skb).portid, |
199 | cb->nlh->nlmsg_seq, | 201 | cb->nlh->nlmsg_seq, |
200 | NFNL_MSG_TYPE(cb->nlh->nlmsg_type), | 202 | NFNL_MSG_TYPE(cb->nlh->nlmsg_type), |
201 | NFNL_MSG_ACCT_NEW, cur) < 0) { | 203 | NFNL_MSG_ACCT_NEW, cur) < 0) { |
202 | cb->args[1] = (unsigned long)cur; | 204 | cb->args[1] = (unsigned long)cur; |
203 | break; | 205 | break; |
204 | } | 206 | } |
205 | } | 207 | } |
206 | if (!cb->args[1]) | 208 | if (!cb->args[1]) |
207 | cb->args[2] = 1; | 209 | cb->args[2] = 1; |
208 | rcu_read_unlock(); | 210 | rcu_read_unlock(); |
209 | return skb->len; | 211 | return skb->len; |
210 | } | 212 | } |
211 | 213 | ||
212 | static int | 214 | static int |
213 | nfnl_acct_get(struct sock *nfnl, struct sk_buff *skb, | 215 | nfnl_acct_get(struct sock *nfnl, struct sk_buff *skb, |
214 | const struct nlmsghdr *nlh, const struct nlattr * const tb[]) | 216 | const struct nlmsghdr *nlh, const struct nlattr * const tb[]) |
215 | { | 217 | { |
216 | int ret = -ENOENT; | 218 | int ret = -ENOENT; |
217 | struct nf_acct *cur; | 219 | struct nf_acct *cur; |
218 | char *acct_name; | 220 | char *acct_name; |
219 | 221 | ||
220 | if (nlh->nlmsg_flags & NLM_F_DUMP) { | 222 | if (nlh->nlmsg_flags & NLM_F_DUMP) { |
221 | struct netlink_dump_control c = { | 223 | struct netlink_dump_control c = { |
222 | .dump = nfnl_acct_dump, | 224 | .dump = nfnl_acct_dump, |
223 | }; | 225 | }; |
224 | return netlink_dump_start(nfnl, skb, nlh, &c); | 226 | return netlink_dump_start(nfnl, skb, nlh, &c); |
225 | } | 227 | } |
226 | 228 | ||
227 | if (!tb[NFACCT_NAME]) | 229 | if (!tb[NFACCT_NAME]) |
228 | return -EINVAL; | 230 | return -EINVAL; |
229 | acct_name = nla_data(tb[NFACCT_NAME]); | 231 | acct_name = nla_data(tb[NFACCT_NAME]); |
230 | 232 | ||
231 | list_for_each_entry(cur, &nfnl_acct_list, head) { | 233 | list_for_each_entry(cur, &nfnl_acct_list, head) { |
232 | struct sk_buff *skb2; | 234 | struct sk_buff *skb2; |
233 | 235 | ||
234 | if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) | 236 | if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) |
235 | continue; | 237 | continue; |
236 | 238 | ||
237 | skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 239 | skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
238 | if (skb2 == NULL) { | 240 | if (skb2 == NULL) { |
239 | ret = -ENOMEM; | 241 | ret = -ENOMEM; |
240 | break; | 242 | break; |
241 | } | 243 | } |
242 | 244 | ||
243 | ret = nfnl_acct_fill_info(skb2, NETLINK_CB(skb).portid, | 245 | ret = nfnl_acct_fill_info(skb2, NETLINK_CB(skb).portid, |
244 | nlh->nlmsg_seq, | 246 | nlh->nlmsg_seq, |
245 | NFNL_MSG_TYPE(nlh->nlmsg_type), | 247 | NFNL_MSG_TYPE(nlh->nlmsg_type), |
246 | NFNL_MSG_ACCT_NEW, cur); | 248 | NFNL_MSG_ACCT_NEW, cur); |
247 | if (ret <= 0) { | 249 | if (ret <= 0) { |
248 | kfree_skb(skb2); | 250 | kfree_skb(skb2); |
249 | break; | 251 | break; |
250 | } | 252 | } |
251 | ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid, | 253 | ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid, |
252 | MSG_DONTWAIT); | 254 | MSG_DONTWAIT); |
253 | if (ret > 0) | 255 | if (ret > 0) |
254 | ret = 0; | 256 | ret = 0; |
255 | 257 | ||
256 | /* this avoids a loop in nfnetlink. */ | 258 | /* this avoids a loop in nfnetlink. */ |
257 | return ret == -EAGAIN ? -ENOBUFS : ret; | 259 | return ret == -EAGAIN ? -ENOBUFS : ret; |
258 | } | 260 | } |
259 | return ret; | 261 | return ret; |
260 | } | 262 | } |
261 | 263 | ||
262 | /* try to delete object, fail if it is still in use. */ | 264 | /* try to delete object, fail if it is still in use. */ |
263 | static int nfnl_acct_try_del(struct nf_acct *cur) | 265 | static int nfnl_acct_try_del(struct nf_acct *cur) |
264 | { | 266 | { |
265 | int ret = 0; | 267 | int ret = 0; |
266 | 268 | ||
267 | /* we want to avoid races with nfnl_acct_find_get. */ | 269 | /* we want to avoid races with nfnl_acct_find_get. */ |
268 | if (atomic_dec_and_test(&cur->refcnt)) { | 270 | if (atomic_dec_and_test(&cur->refcnt)) { |
269 | /* We are protected by nfnl mutex. */ | 271 | /* We are protected by nfnl mutex. */ |
270 | list_del_rcu(&cur->head); | 272 | list_del_rcu(&cur->head); |
271 | kfree_rcu(cur, rcu_head); | 273 | kfree_rcu(cur, rcu_head); |
272 | } else { | 274 | } else { |
273 | /* still in use, restore reference counter. */ | 275 | /* still in use, restore reference counter. */ |
274 | atomic_inc(&cur->refcnt); | 276 | atomic_inc(&cur->refcnt); |
275 | ret = -EBUSY; | 277 | ret = -EBUSY; |
276 | } | 278 | } |
277 | return ret; | 279 | return ret; |
278 | } | 280 | } |
279 | 281 | ||
280 | static int | 282 | static int |
281 | nfnl_acct_del(struct sock *nfnl, struct sk_buff *skb, | 283 | nfnl_acct_del(struct sock *nfnl, struct sk_buff *skb, |
282 | const struct nlmsghdr *nlh, const struct nlattr * const tb[]) | 284 | const struct nlmsghdr *nlh, const struct nlattr * const tb[]) |
283 | { | 285 | { |
284 | char *acct_name; | 286 | char *acct_name; |
285 | struct nf_acct *cur; | 287 | struct nf_acct *cur; |
286 | int ret = -ENOENT; | 288 | int ret = -ENOENT; |
287 | 289 | ||
288 | if (!tb[NFACCT_NAME]) { | 290 | if (!tb[NFACCT_NAME]) { |
289 | list_for_each_entry(cur, &nfnl_acct_list, head) | 291 | list_for_each_entry(cur, &nfnl_acct_list, head) |
290 | nfnl_acct_try_del(cur); | 292 | nfnl_acct_try_del(cur); |
291 | 293 | ||
292 | return 0; | 294 | return 0; |
293 | } | 295 | } |
294 | acct_name = nla_data(tb[NFACCT_NAME]); | 296 | acct_name = nla_data(tb[NFACCT_NAME]); |
295 | 297 | ||
296 | list_for_each_entry(cur, &nfnl_acct_list, head) { | 298 | list_for_each_entry(cur, &nfnl_acct_list, head) { |
297 | if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX) != 0) | 299 | if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX) != 0) |
298 | continue; | 300 | continue; |
299 | 301 | ||
300 | ret = nfnl_acct_try_del(cur); | 302 | ret = nfnl_acct_try_del(cur); |
301 | if (ret < 0) | 303 | if (ret < 0) |
302 | return ret; | 304 | return ret; |
303 | 305 | ||
304 | break; | 306 | break; |
305 | } | 307 | } |
306 | return ret; | 308 | return ret; |
307 | } | 309 | } |
308 | 310 | ||
309 | static const struct nla_policy nfnl_acct_policy[NFACCT_MAX+1] = { | 311 | static const struct nla_policy nfnl_acct_policy[NFACCT_MAX+1] = { |
310 | [NFACCT_NAME] = { .type = NLA_NUL_STRING, .len = NFACCT_NAME_MAX-1 }, | 312 | [NFACCT_NAME] = { .type = NLA_NUL_STRING, .len = NFACCT_NAME_MAX-1 }, |
311 | [NFACCT_BYTES] = { .type = NLA_U64 }, | 313 | [NFACCT_BYTES] = { .type = NLA_U64 }, |
312 | [NFACCT_PKTS] = { .type = NLA_U64 }, | 314 | [NFACCT_PKTS] = { .type = NLA_U64 }, |
313 | [NFACCT_FLAGS] = { .type = NLA_U32 }, | 315 | [NFACCT_FLAGS] = { .type = NLA_U32 }, |
314 | [NFACCT_QUOTA] = { .type = NLA_U64 }, | 316 | [NFACCT_QUOTA] = { .type = NLA_U64 }, |
315 | }; | 317 | }; |
316 | 318 | ||
317 | static const struct nfnl_callback nfnl_acct_cb[NFNL_MSG_ACCT_MAX] = { | 319 | static const struct nfnl_callback nfnl_acct_cb[NFNL_MSG_ACCT_MAX] = { |
318 | [NFNL_MSG_ACCT_NEW] = { .call = nfnl_acct_new, | 320 | [NFNL_MSG_ACCT_NEW] = { .call = nfnl_acct_new, |
319 | .attr_count = NFACCT_MAX, | 321 | .attr_count = NFACCT_MAX, |
320 | .policy = nfnl_acct_policy }, | 322 | .policy = nfnl_acct_policy }, |
321 | [NFNL_MSG_ACCT_GET] = { .call = nfnl_acct_get, | 323 | [NFNL_MSG_ACCT_GET] = { .call = nfnl_acct_get, |
322 | .attr_count = NFACCT_MAX, | 324 | .attr_count = NFACCT_MAX, |
323 | .policy = nfnl_acct_policy }, | 325 | .policy = nfnl_acct_policy }, |
324 | [NFNL_MSG_ACCT_GET_CTRZERO] = { .call = nfnl_acct_get, | 326 | [NFNL_MSG_ACCT_GET_CTRZERO] = { .call = nfnl_acct_get, |
325 | .attr_count = NFACCT_MAX, | 327 | .attr_count = NFACCT_MAX, |
326 | .policy = nfnl_acct_policy }, | 328 | .policy = nfnl_acct_policy }, |
327 | [NFNL_MSG_ACCT_DEL] = { .call = nfnl_acct_del, | 329 | [NFNL_MSG_ACCT_DEL] = { .call = nfnl_acct_del, |
328 | .attr_count = NFACCT_MAX, | 330 | .attr_count = NFACCT_MAX, |
329 | .policy = nfnl_acct_policy }, | 331 | .policy = nfnl_acct_policy }, |
330 | }; | 332 | }; |
331 | 333 | ||
332 | static const struct nfnetlink_subsystem nfnl_acct_subsys = { | 334 | static const struct nfnetlink_subsystem nfnl_acct_subsys = { |
333 | .name = "acct", | 335 | .name = "acct", |
334 | .subsys_id = NFNL_SUBSYS_ACCT, | 336 | .subsys_id = NFNL_SUBSYS_ACCT, |
335 | .cb_count = NFNL_MSG_ACCT_MAX, | 337 | .cb_count = NFNL_MSG_ACCT_MAX, |
336 | .cb = nfnl_acct_cb, | 338 | .cb = nfnl_acct_cb, |
337 | }; | 339 | }; |
338 | 340 | ||
339 | MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_ACCT); | 341 | MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_ACCT); |
340 | 342 | ||
341 | struct nf_acct *nfnl_acct_find_get(const char *acct_name) | 343 | struct nf_acct *nfnl_acct_find_get(const char *acct_name) |
342 | { | 344 | { |
343 | struct nf_acct *cur, *acct = NULL; | 345 | struct nf_acct *cur, *acct = NULL; |
344 | 346 | ||
345 | rcu_read_lock(); | 347 | rcu_read_lock(); |
346 | list_for_each_entry_rcu(cur, &nfnl_acct_list, head) { | 348 | list_for_each_entry_rcu(cur, &nfnl_acct_list, head) { |
347 | if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) | 349 | if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) |
348 | continue; | 350 | continue; |
349 | 351 | ||
350 | if (!try_module_get(THIS_MODULE)) | 352 | if (!try_module_get(THIS_MODULE)) |
351 | goto err; | 353 | goto err; |
352 | 354 | ||
353 | if (!atomic_inc_not_zero(&cur->refcnt)) { | 355 | if (!atomic_inc_not_zero(&cur->refcnt)) { |
354 | module_put(THIS_MODULE); | 356 | module_put(THIS_MODULE); |
355 | goto err; | 357 | goto err; |
356 | } | 358 | } |
357 | 359 | ||
358 | acct = cur; | 360 | acct = cur; |
359 | break; | 361 | break; |
360 | } | 362 | } |
361 | err: | 363 | err: |
362 | rcu_read_unlock(); | 364 | rcu_read_unlock(); |
363 | return acct; | 365 | return acct; |
364 | } | 366 | } |
365 | EXPORT_SYMBOL_GPL(nfnl_acct_find_get); | 367 | EXPORT_SYMBOL_GPL(nfnl_acct_find_get); |
366 | 368 | ||
367 | void nfnl_acct_put(struct nf_acct *acct) | 369 | void nfnl_acct_put(struct nf_acct *acct) |
368 | { | 370 | { |
369 | atomic_dec(&acct->refcnt); | 371 | atomic_dec(&acct->refcnt); |
370 | module_put(THIS_MODULE); | 372 | module_put(THIS_MODULE); |
371 | } | 373 | } |
372 | EXPORT_SYMBOL_GPL(nfnl_acct_put); | 374 | EXPORT_SYMBOL_GPL(nfnl_acct_put); |
373 | 375 | ||
374 | void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct) | 376 | void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct) |
375 | { | 377 | { |
376 | atomic64_inc(&nfacct->pkts); | 378 | atomic64_inc(&nfacct->pkts); |
377 | atomic64_add(skb->len, &nfacct->bytes); | 379 | atomic64_add(skb->len, &nfacct->bytes); |
378 | } | 380 | } |
379 | EXPORT_SYMBOL_GPL(nfnl_acct_update); | 381 | EXPORT_SYMBOL_GPL(nfnl_acct_update); |
380 | 382 | ||
381 | static void nfnl_overquota_report(struct nf_acct *nfacct) | 383 | static void nfnl_overquota_report(struct nf_acct *nfacct) |
382 | { | 384 | { |
383 | int ret; | 385 | int ret; |
384 | struct sk_buff *skb; | 386 | struct sk_buff *skb; |
385 | 387 | ||
386 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); | 388 | skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); |
387 | if (skb == NULL) | 389 | if (skb == NULL) |
388 | return; | 390 | return; |
389 | 391 | ||
390 | ret = nfnl_acct_fill_info(skb, 0, 0, NFNL_MSG_ACCT_OVERQUOTA, 0, | 392 | ret = nfnl_acct_fill_info(skb, 0, 0, NFNL_MSG_ACCT_OVERQUOTA, 0, |
391 | nfacct); | 393 | nfacct); |
392 | if (ret <= 0) { | 394 | if (ret <= 0) { |
393 | kfree_skb(skb); | 395 | kfree_skb(skb); |
394 | return; | 396 | return; |
395 | } | 397 | } |
396 | netlink_broadcast(init_net.nfnl, skb, 0, NFNLGRP_ACCT_QUOTA, | 398 | netlink_broadcast(init_net.nfnl, skb, 0, NFNLGRP_ACCT_QUOTA, |
397 | GFP_ATOMIC); | 399 | GFP_ATOMIC); |
398 | } | 400 | } |
399 | 401 | ||
400 | int nfnl_acct_overquota(const struct sk_buff *skb, struct nf_acct *nfacct) | 402 | int nfnl_acct_overquota(const struct sk_buff *skb, struct nf_acct *nfacct) |
401 | { | 403 | { |
402 | u64 now; | 404 | u64 now; |
403 | u64 *quota; | 405 | u64 *quota; |
404 | int ret = NFACCT_UNDERQUOTA; | 406 | int ret = NFACCT_UNDERQUOTA; |
405 | 407 | ||
406 | /* no place here if we don't have a quota */ | 408 | /* no place here if we don't have a quota */ |
407 | if (!(nfacct->flags & NFACCT_F_QUOTA)) | 409 | if (!(nfacct->flags & NFACCT_F_QUOTA)) |
408 | return NFACCT_NO_QUOTA; | 410 | return NFACCT_NO_QUOTA; |
409 | 411 | ||
410 | quota = (u64 *)nfacct->data; | 412 | quota = (u64 *)nfacct->data; |
411 | now = (nfacct->flags & NFACCT_F_QUOTA_PKTS) ? | 413 | now = (nfacct->flags & NFACCT_F_QUOTA_PKTS) ? |
412 | atomic64_read(&nfacct->pkts) : atomic64_read(&nfacct->bytes); | 414 | atomic64_read(&nfacct->pkts) : atomic64_read(&nfacct->bytes); |
413 | 415 | ||
414 | ret = now > *quota; | 416 | ret = now > *quota; |
415 | 417 | ||
416 | if (now >= *quota && | 418 | if (now >= *quota && |
417 | !test_and_set_bit(NFACCT_F_OVERQUOTA, &nfacct->flags)) { | 419 | !test_and_set_bit(NFACCT_OVERQUOTA_BIT, &nfacct->flags)) { |
418 | nfnl_overquota_report(nfacct); | 420 | nfnl_overquota_report(nfacct); |
419 | } | 421 | } |
420 | 422 | ||
421 | return ret; | 423 | return ret; |
422 | } | 424 | } |
423 | EXPORT_SYMBOL_GPL(nfnl_acct_overquota); | 425 | EXPORT_SYMBOL_GPL(nfnl_acct_overquota); |
424 | 426 | ||
425 | static int __init nfnl_acct_init(void) | 427 | static int __init nfnl_acct_init(void) |
426 | { | 428 | { |
427 | int ret; | 429 | int ret; |
428 | 430 | ||
429 | pr_info("nfnl_acct: registering with nfnetlink.\n"); | 431 | pr_info("nfnl_acct: registering with nfnetlink.\n"); |
430 | ret = nfnetlink_subsys_register(&nfnl_acct_subsys); | 432 | ret = nfnetlink_subsys_register(&nfnl_acct_subsys); |
431 | if (ret < 0) { | 433 | if (ret < 0) { |
432 | pr_err("nfnl_acct_init: cannot register with nfnetlink.\n"); | 434 | pr_err("nfnl_acct_init: cannot register with nfnetlink.\n"); |
433 | goto err_out; | 435 | goto err_out; |
434 | } | 436 | } |
435 | return 0; | 437 | return 0; |
436 | err_out: | 438 | err_out: |
437 | return ret; | 439 | return ret; |
438 | } | 440 | } |
439 | 441 | ||
440 | static void __exit nfnl_acct_exit(void) | 442 | static void __exit nfnl_acct_exit(void) |
441 | { | 443 | { |
442 | struct nf_acct *cur, *tmp; | 444 | struct nf_acct *cur, *tmp; |
443 | 445 | ||
444 | pr_info("nfnl_acct: unregistering from nfnetlink.\n"); | 446 | pr_info("nfnl_acct: unregistering from nfnetlink.\n"); |
445 | nfnetlink_subsys_unregister(&nfnl_acct_subsys); | 447 | nfnetlink_subsys_unregister(&nfnl_acct_subsys); |
446 | 448 | ||
447 | list_for_each_entry_safe(cur, tmp, &nfnl_acct_list, head) { | 449 | list_for_each_entry_safe(cur, tmp, &nfnl_acct_list, head) { |
448 | list_del_rcu(&cur->head); | 450 | list_del_rcu(&cur->head); |
449 | /* We are sure that our objects have no clients at this point, | 451 | /* We are sure that our objects have no clients at this point, |
450 | * it's safe to release them all without checking refcnt. */ | 452 | * it's safe to release them all without checking refcnt. */ |
451 | kfree_rcu(cur, rcu_head); | 453 | kfree_rcu(cur, rcu_head); |
452 | } | 454 | } |
453 | } | 455 | } |
454 | 456 | ||
455 | module_init(nfnl_acct_init); | 457 | module_init(nfnl_acct_init); |
456 | module_exit(nfnl_acct_exit); | 458 | module_exit(nfnl_acct_exit); |
457 | 459 |