Commit 7c8011895330d3069b137233e2673f40ee6f4c91
1 parent
48f03bdad8
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
nfnetlink_acct: Stop using NLA_PUT*().
These macros contain a hidden goto, and are thus extremely error prone and make code hard to audit. Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 1 changed file with 6 additions and 4 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 | struct list_head head; | 35 | struct list_head head; |
36 | atomic_t refcnt; | 36 | atomic_t refcnt; |
37 | char name[NFACCT_NAME_MAX]; | 37 | char name[NFACCT_NAME_MAX]; |
38 | struct rcu_head rcu_head; | 38 | struct rcu_head rcu_head; |
39 | }; | 39 | }; |
40 | 40 | ||
41 | static int | 41 | static int |
42 | nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb, | 42 | nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb, |
43 | const struct nlmsghdr *nlh, const struct nlattr * const tb[]) | 43 | const struct nlmsghdr *nlh, const struct nlattr * const tb[]) |
44 | { | 44 | { |
45 | struct nf_acct *nfacct, *matching = NULL; | 45 | struct nf_acct *nfacct, *matching = NULL; |
46 | char *acct_name; | 46 | char *acct_name; |
47 | 47 | ||
48 | if (!tb[NFACCT_NAME]) | 48 | if (!tb[NFACCT_NAME]) |
49 | return -EINVAL; | 49 | return -EINVAL; |
50 | 50 | ||
51 | acct_name = nla_data(tb[NFACCT_NAME]); | 51 | acct_name = nla_data(tb[NFACCT_NAME]); |
52 | 52 | ||
53 | list_for_each_entry(nfacct, &nfnl_acct_list, head) { | 53 | list_for_each_entry(nfacct, &nfnl_acct_list, head) { |
54 | if (strncmp(nfacct->name, acct_name, NFACCT_NAME_MAX) != 0) | 54 | if (strncmp(nfacct->name, acct_name, NFACCT_NAME_MAX) != 0) |
55 | continue; | 55 | continue; |
56 | 56 | ||
57 | if (nlh->nlmsg_flags & NLM_F_EXCL) | 57 | if (nlh->nlmsg_flags & NLM_F_EXCL) |
58 | return -EEXIST; | 58 | return -EEXIST; |
59 | 59 | ||
60 | matching = nfacct; | 60 | matching = nfacct; |
61 | break; | 61 | break; |
62 | } | 62 | } |
63 | 63 | ||
64 | if (matching) { | 64 | if (matching) { |
65 | if (nlh->nlmsg_flags & NLM_F_REPLACE) { | 65 | if (nlh->nlmsg_flags & NLM_F_REPLACE) { |
66 | /* reset counters if you request a replacement. */ | 66 | /* reset counters if you request a replacement. */ |
67 | atomic64_set(&matching->pkts, 0); | 67 | atomic64_set(&matching->pkts, 0); |
68 | atomic64_set(&matching->bytes, 0); | 68 | atomic64_set(&matching->bytes, 0); |
69 | return 0; | 69 | return 0; |
70 | } | 70 | } |
71 | return -EBUSY; | 71 | return -EBUSY; |
72 | } | 72 | } |
73 | 73 | ||
74 | nfacct = kzalloc(sizeof(struct nf_acct), GFP_KERNEL); | 74 | nfacct = kzalloc(sizeof(struct nf_acct), GFP_KERNEL); |
75 | if (nfacct == NULL) | 75 | if (nfacct == NULL) |
76 | return -ENOMEM; | 76 | return -ENOMEM; |
77 | 77 | ||
78 | strncpy(nfacct->name, nla_data(tb[NFACCT_NAME]), NFACCT_NAME_MAX); | 78 | strncpy(nfacct->name, nla_data(tb[NFACCT_NAME]), NFACCT_NAME_MAX); |
79 | 79 | ||
80 | if (tb[NFACCT_BYTES]) { | 80 | if (tb[NFACCT_BYTES]) { |
81 | atomic64_set(&nfacct->bytes, | 81 | atomic64_set(&nfacct->bytes, |
82 | be64_to_cpu(nla_get_u64(tb[NFACCT_BYTES]))); | 82 | be64_to_cpu(nla_get_u64(tb[NFACCT_BYTES]))); |
83 | } | 83 | } |
84 | if (tb[NFACCT_PKTS]) { | 84 | if (tb[NFACCT_PKTS]) { |
85 | atomic64_set(&nfacct->pkts, | 85 | atomic64_set(&nfacct->pkts, |
86 | be64_to_cpu(nla_get_u64(tb[NFACCT_PKTS]))); | 86 | be64_to_cpu(nla_get_u64(tb[NFACCT_PKTS]))); |
87 | } | 87 | } |
88 | atomic_set(&nfacct->refcnt, 1); | 88 | atomic_set(&nfacct->refcnt, 1); |
89 | list_add_tail_rcu(&nfacct->head, &nfnl_acct_list); | 89 | list_add_tail_rcu(&nfacct->head, &nfnl_acct_list); |
90 | return 0; | 90 | return 0; |
91 | } | 91 | } |
92 | 92 | ||
93 | static int | 93 | static int |
94 | nfnl_acct_fill_info(struct sk_buff *skb, u32 pid, u32 seq, u32 type, | 94 | nfnl_acct_fill_info(struct sk_buff *skb, u32 pid, u32 seq, u32 type, |
95 | int event, struct nf_acct *acct) | 95 | int event, struct nf_acct *acct) |
96 | { | 96 | { |
97 | struct nlmsghdr *nlh; | 97 | struct nlmsghdr *nlh; |
98 | struct nfgenmsg *nfmsg; | 98 | struct nfgenmsg *nfmsg; |
99 | unsigned int flags = pid ? NLM_F_MULTI : 0; | 99 | unsigned int flags = pid ? NLM_F_MULTI : 0; |
100 | u64 pkts, bytes; | 100 | u64 pkts, bytes; |
101 | 101 | ||
102 | event |= NFNL_SUBSYS_ACCT << 8; | 102 | event |= NFNL_SUBSYS_ACCT << 8; |
103 | nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags); | 103 | nlh = nlmsg_put(skb, pid, seq, event, sizeof(*nfmsg), flags); |
104 | if (nlh == NULL) | 104 | if (nlh == NULL) |
105 | goto nlmsg_failure; | 105 | goto nlmsg_failure; |
106 | 106 | ||
107 | nfmsg = nlmsg_data(nlh); | 107 | nfmsg = nlmsg_data(nlh); |
108 | nfmsg->nfgen_family = AF_UNSPEC; | 108 | nfmsg->nfgen_family = AF_UNSPEC; |
109 | nfmsg->version = NFNETLINK_V0; | 109 | nfmsg->version = NFNETLINK_V0; |
110 | nfmsg->res_id = 0; | 110 | nfmsg->res_id = 0; |
111 | 111 | ||
112 | NLA_PUT_STRING(skb, NFACCT_NAME, acct->name); | 112 | if (nla_put_string(skb, NFACCT_NAME, acct->name)) |
113 | goto nla_put_failure; | ||
113 | 114 | ||
114 | if (type == NFNL_MSG_ACCT_GET_CTRZERO) { | 115 | if (type == NFNL_MSG_ACCT_GET_CTRZERO) { |
115 | pkts = atomic64_xchg(&acct->pkts, 0); | 116 | pkts = atomic64_xchg(&acct->pkts, 0); |
116 | bytes = atomic64_xchg(&acct->bytes, 0); | 117 | bytes = atomic64_xchg(&acct->bytes, 0); |
117 | } else { | 118 | } else { |
118 | pkts = atomic64_read(&acct->pkts); | 119 | pkts = atomic64_read(&acct->pkts); |
119 | bytes = atomic64_read(&acct->bytes); | 120 | bytes = atomic64_read(&acct->bytes); |
120 | } | 121 | } |
121 | NLA_PUT_BE64(skb, NFACCT_PKTS, cpu_to_be64(pkts)); | 122 | if (nla_put_be64(skb, NFACCT_PKTS, cpu_to_be64(pkts)) || |
122 | NLA_PUT_BE64(skb, NFACCT_BYTES, cpu_to_be64(bytes)); | 123 | nla_put_be64(skb, NFACCT_BYTES, cpu_to_be64(bytes)) || |
123 | NLA_PUT_BE32(skb, NFACCT_USE, htonl(atomic_read(&acct->refcnt))); | 124 | nla_put_be32(skb, NFACCT_USE, htonl(atomic_read(&acct->refcnt)))) |
125 | goto nla_put_failure; | ||
124 | 126 | ||
125 | nlmsg_end(skb, nlh); | 127 | nlmsg_end(skb, nlh); |
126 | return skb->len; | 128 | return skb->len; |
127 | 129 | ||
128 | nlmsg_failure: | 130 | nlmsg_failure: |
129 | nla_put_failure: | 131 | nla_put_failure: |
130 | nlmsg_cancel(skb, nlh); | 132 | nlmsg_cancel(skb, nlh); |
131 | return -1; | 133 | return -1; |
132 | } | 134 | } |
133 | 135 | ||
134 | static int | 136 | static int |
135 | nfnl_acct_dump(struct sk_buff *skb, struct netlink_callback *cb) | 137 | nfnl_acct_dump(struct sk_buff *skb, struct netlink_callback *cb) |
136 | { | 138 | { |
137 | struct nf_acct *cur, *last; | 139 | struct nf_acct *cur, *last; |
138 | 140 | ||
139 | if (cb->args[2]) | 141 | if (cb->args[2]) |
140 | return 0; | 142 | return 0; |
141 | 143 | ||
142 | last = (struct nf_acct *)cb->args[1]; | 144 | last = (struct nf_acct *)cb->args[1]; |
143 | if (cb->args[1]) | 145 | if (cb->args[1]) |
144 | cb->args[1] = 0; | 146 | cb->args[1] = 0; |
145 | 147 | ||
146 | rcu_read_lock(); | 148 | rcu_read_lock(); |
147 | list_for_each_entry_rcu(cur, &nfnl_acct_list, head) { | 149 | list_for_each_entry_rcu(cur, &nfnl_acct_list, head) { |
148 | if (last && cur != last) | 150 | if (last && cur != last) |
149 | continue; | 151 | continue; |
150 | 152 | ||
151 | if (nfnl_acct_fill_info(skb, NETLINK_CB(cb->skb).pid, | 153 | if (nfnl_acct_fill_info(skb, NETLINK_CB(cb->skb).pid, |
152 | cb->nlh->nlmsg_seq, | 154 | cb->nlh->nlmsg_seq, |
153 | NFNL_MSG_TYPE(cb->nlh->nlmsg_type), | 155 | NFNL_MSG_TYPE(cb->nlh->nlmsg_type), |
154 | NFNL_MSG_ACCT_NEW, cur) < 0) { | 156 | NFNL_MSG_ACCT_NEW, cur) < 0) { |
155 | cb->args[1] = (unsigned long)cur; | 157 | cb->args[1] = (unsigned long)cur; |
156 | break; | 158 | break; |
157 | } | 159 | } |
158 | } | 160 | } |
159 | if (!cb->args[1]) | 161 | if (!cb->args[1]) |
160 | cb->args[2] = 1; | 162 | cb->args[2] = 1; |
161 | rcu_read_unlock(); | 163 | rcu_read_unlock(); |
162 | return skb->len; | 164 | return skb->len; |
163 | } | 165 | } |
164 | 166 | ||
165 | static int | 167 | static int |
166 | nfnl_acct_get(struct sock *nfnl, struct sk_buff *skb, | 168 | nfnl_acct_get(struct sock *nfnl, struct sk_buff *skb, |
167 | const struct nlmsghdr *nlh, const struct nlattr * const tb[]) | 169 | const struct nlmsghdr *nlh, const struct nlattr * const tb[]) |
168 | { | 170 | { |
169 | int ret = -ENOENT; | 171 | int ret = -ENOENT; |
170 | struct nf_acct *cur; | 172 | struct nf_acct *cur; |
171 | char *acct_name; | 173 | char *acct_name; |
172 | 174 | ||
173 | if (nlh->nlmsg_flags & NLM_F_DUMP) { | 175 | if (nlh->nlmsg_flags & NLM_F_DUMP) { |
174 | struct netlink_dump_control c = { | 176 | struct netlink_dump_control c = { |
175 | .dump = nfnl_acct_dump, | 177 | .dump = nfnl_acct_dump, |
176 | }; | 178 | }; |
177 | return netlink_dump_start(nfnl, skb, nlh, &c); | 179 | return netlink_dump_start(nfnl, skb, nlh, &c); |
178 | } | 180 | } |
179 | 181 | ||
180 | if (!tb[NFACCT_NAME]) | 182 | if (!tb[NFACCT_NAME]) |
181 | return -EINVAL; | 183 | return -EINVAL; |
182 | acct_name = nla_data(tb[NFACCT_NAME]); | 184 | acct_name = nla_data(tb[NFACCT_NAME]); |
183 | 185 | ||
184 | list_for_each_entry(cur, &nfnl_acct_list, head) { | 186 | list_for_each_entry(cur, &nfnl_acct_list, head) { |
185 | struct sk_buff *skb2; | 187 | struct sk_buff *skb2; |
186 | 188 | ||
187 | if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) | 189 | if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) |
188 | continue; | 190 | continue; |
189 | 191 | ||
190 | skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | 192 | skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); |
191 | if (skb2 == NULL) { | 193 | if (skb2 == NULL) { |
192 | ret = -ENOMEM; | 194 | ret = -ENOMEM; |
193 | break; | 195 | break; |
194 | } | 196 | } |
195 | 197 | ||
196 | ret = nfnl_acct_fill_info(skb2, NETLINK_CB(skb).pid, | 198 | ret = nfnl_acct_fill_info(skb2, NETLINK_CB(skb).pid, |
197 | nlh->nlmsg_seq, | 199 | nlh->nlmsg_seq, |
198 | NFNL_MSG_TYPE(nlh->nlmsg_type), | 200 | NFNL_MSG_TYPE(nlh->nlmsg_type), |
199 | NFNL_MSG_ACCT_NEW, cur); | 201 | NFNL_MSG_ACCT_NEW, cur); |
200 | if (ret <= 0) { | 202 | if (ret <= 0) { |
201 | kfree_skb(skb2); | 203 | kfree_skb(skb2); |
202 | break; | 204 | break; |
203 | } | 205 | } |
204 | ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).pid, | 206 | ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).pid, |
205 | MSG_DONTWAIT); | 207 | MSG_DONTWAIT); |
206 | if (ret > 0) | 208 | if (ret > 0) |
207 | ret = 0; | 209 | ret = 0; |
208 | 210 | ||
209 | /* this avoids a loop in nfnetlink. */ | 211 | /* this avoids a loop in nfnetlink. */ |
210 | return ret == -EAGAIN ? -ENOBUFS : ret; | 212 | return ret == -EAGAIN ? -ENOBUFS : ret; |
211 | } | 213 | } |
212 | return ret; | 214 | return ret; |
213 | } | 215 | } |
214 | 216 | ||
215 | /* try to delete object, fail if it is still in use. */ | 217 | /* try to delete object, fail if it is still in use. */ |
216 | static int nfnl_acct_try_del(struct nf_acct *cur) | 218 | static int nfnl_acct_try_del(struct nf_acct *cur) |
217 | { | 219 | { |
218 | int ret = 0; | 220 | int ret = 0; |
219 | 221 | ||
220 | /* we want to avoid races with nfnl_acct_find_get. */ | 222 | /* we want to avoid races with nfnl_acct_find_get. */ |
221 | if (atomic_dec_and_test(&cur->refcnt)) { | 223 | if (atomic_dec_and_test(&cur->refcnt)) { |
222 | /* We are protected by nfnl mutex. */ | 224 | /* We are protected by nfnl mutex. */ |
223 | list_del_rcu(&cur->head); | 225 | list_del_rcu(&cur->head); |
224 | kfree_rcu(cur, rcu_head); | 226 | kfree_rcu(cur, rcu_head); |
225 | } else { | 227 | } else { |
226 | /* still in use, restore reference counter. */ | 228 | /* still in use, restore reference counter. */ |
227 | atomic_inc(&cur->refcnt); | 229 | atomic_inc(&cur->refcnt); |
228 | ret = -EBUSY; | 230 | ret = -EBUSY; |
229 | } | 231 | } |
230 | return ret; | 232 | return ret; |
231 | } | 233 | } |
232 | 234 | ||
233 | static int | 235 | static int |
234 | nfnl_acct_del(struct sock *nfnl, struct sk_buff *skb, | 236 | nfnl_acct_del(struct sock *nfnl, struct sk_buff *skb, |
235 | const struct nlmsghdr *nlh, const struct nlattr * const tb[]) | 237 | const struct nlmsghdr *nlh, const struct nlattr * const tb[]) |
236 | { | 238 | { |
237 | char *acct_name; | 239 | char *acct_name; |
238 | struct nf_acct *cur; | 240 | struct nf_acct *cur; |
239 | int ret = -ENOENT; | 241 | int ret = -ENOENT; |
240 | 242 | ||
241 | if (!tb[NFACCT_NAME]) { | 243 | if (!tb[NFACCT_NAME]) { |
242 | list_for_each_entry(cur, &nfnl_acct_list, head) | 244 | list_for_each_entry(cur, &nfnl_acct_list, head) |
243 | nfnl_acct_try_del(cur); | 245 | nfnl_acct_try_del(cur); |
244 | 246 | ||
245 | return 0; | 247 | return 0; |
246 | } | 248 | } |
247 | acct_name = nla_data(tb[NFACCT_NAME]); | 249 | acct_name = nla_data(tb[NFACCT_NAME]); |
248 | 250 | ||
249 | list_for_each_entry(cur, &nfnl_acct_list, head) { | 251 | list_for_each_entry(cur, &nfnl_acct_list, head) { |
250 | if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX) != 0) | 252 | if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX) != 0) |
251 | continue; | 253 | continue; |
252 | 254 | ||
253 | ret = nfnl_acct_try_del(cur); | 255 | ret = nfnl_acct_try_del(cur); |
254 | if (ret < 0) | 256 | if (ret < 0) |
255 | return ret; | 257 | return ret; |
256 | 258 | ||
257 | break; | 259 | break; |
258 | } | 260 | } |
259 | return ret; | 261 | return ret; |
260 | } | 262 | } |
261 | 263 | ||
262 | static const struct nla_policy nfnl_acct_policy[NFACCT_MAX+1] = { | 264 | static const struct nla_policy nfnl_acct_policy[NFACCT_MAX+1] = { |
263 | [NFACCT_NAME] = { .type = NLA_NUL_STRING, .len = NFACCT_NAME_MAX-1 }, | 265 | [NFACCT_NAME] = { .type = NLA_NUL_STRING, .len = NFACCT_NAME_MAX-1 }, |
264 | [NFACCT_BYTES] = { .type = NLA_U64 }, | 266 | [NFACCT_BYTES] = { .type = NLA_U64 }, |
265 | [NFACCT_PKTS] = { .type = NLA_U64 }, | 267 | [NFACCT_PKTS] = { .type = NLA_U64 }, |
266 | }; | 268 | }; |
267 | 269 | ||
268 | static const struct nfnl_callback nfnl_acct_cb[NFNL_MSG_ACCT_MAX] = { | 270 | static const struct nfnl_callback nfnl_acct_cb[NFNL_MSG_ACCT_MAX] = { |
269 | [NFNL_MSG_ACCT_NEW] = { .call = nfnl_acct_new, | 271 | [NFNL_MSG_ACCT_NEW] = { .call = nfnl_acct_new, |
270 | .attr_count = NFACCT_MAX, | 272 | .attr_count = NFACCT_MAX, |
271 | .policy = nfnl_acct_policy }, | 273 | .policy = nfnl_acct_policy }, |
272 | [NFNL_MSG_ACCT_GET] = { .call = nfnl_acct_get, | 274 | [NFNL_MSG_ACCT_GET] = { .call = nfnl_acct_get, |
273 | .attr_count = NFACCT_MAX, | 275 | .attr_count = NFACCT_MAX, |
274 | .policy = nfnl_acct_policy }, | 276 | .policy = nfnl_acct_policy }, |
275 | [NFNL_MSG_ACCT_GET_CTRZERO] = { .call = nfnl_acct_get, | 277 | [NFNL_MSG_ACCT_GET_CTRZERO] = { .call = nfnl_acct_get, |
276 | .attr_count = NFACCT_MAX, | 278 | .attr_count = NFACCT_MAX, |
277 | .policy = nfnl_acct_policy }, | 279 | .policy = nfnl_acct_policy }, |
278 | [NFNL_MSG_ACCT_DEL] = { .call = nfnl_acct_del, | 280 | [NFNL_MSG_ACCT_DEL] = { .call = nfnl_acct_del, |
279 | .attr_count = NFACCT_MAX, | 281 | .attr_count = NFACCT_MAX, |
280 | .policy = nfnl_acct_policy }, | 282 | .policy = nfnl_acct_policy }, |
281 | }; | 283 | }; |
282 | 284 | ||
283 | static const struct nfnetlink_subsystem nfnl_acct_subsys = { | 285 | static const struct nfnetlink_subsystem nfnl_acct_subsys = { |
284 | .name = "acct", | 286 | .name = "acct", |
285 | .subsys_id = NFNL_SUBSYS_ACCT, | 287 | .subsys_id = NFNL_SUBSYS_ACCT, |
286 | .cb_count = NFNL_MSG_ACCT_MAX, | 288 | .cb_count = NFNL_MSG_ACCT_MAX, |
287 | .cb = nfnl_acct_cb, | 289 | .cb = nfnl_acct_cb, |
288 | }; | 290 | }; |
289 | 291 | ||
290 | MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_ACCT); | 292 | MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_ACCT); |
291 | 293 | ||
292 | struct nf_acct *nfnl_acct_find_get(const char *acct_name) | 294 | struct nf_acct *nfnl_acct_find_get(const char *acct_name) |
293 | { | 295 | { |
294 | struct nf_acct *cur, *acct = NULL; | 296 | struct nf_acct *cur, *acct = NULL; |
295 | 297 | ||
296 | rcu_read_lock(); | 298 | rcu_read_lock(); |
297 | list_for_each_entry_rcu(cur, &nfnl_acct_list, head) { | 299 | list_for_each_entry_rcu(cur, &nfnl_acct_list, head) { |
298 | if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) | 300 | if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) |
299 | continue; | 301 | continue; |
300 | 302 | ||
301 | if (!try_module_get(THIS_MODULE)) | 303 | if (!try_module_get(THIS_MODULE)) |
302 | goto err; | 304 | goto err; |
303 | 305 | ||
304 | if (!atomic_inc_not_zero(&cur->refcnt)) { | 306 | if (!atomic_inc_not_zero(&cur->refcnt)) { |
305 | module_put(THIS_MODULE); | 307 | module_put(THIS_MODULE); |
306 | goto err; | 308 | goto err; |
307 | } | 309 | } |
308 | 310 | ||
309 | acct = cur; | 311 | acct = cur; |
310 | break; | 312 | break; |
311 | } | 313 | } |
312 | err: | 314 | err: |
313 | rcu_read_unlock(); | 315 | rcu_read_unlock(); |
314 | return acct; | 316 | return acct; |
315 | } | 317 | } |
316 | EXPORT_SYMBOL_GPL(nfnl_acct_find_get); | 318 | EXPORT_SYMBOL_GPL(nfnl_acct_find_get); |
317 | 319 | ||
318 | void nfnl_acct_put(struct nf_acct *acct) | 320 | void nfnl_acct_put(struct nf_acct *acct) |
319 | { | 321 | { |
320 | atomic_dec(&acct->refcnt); | 322 | atomic_dec(&acct->refcnt); |
321 | module_put(THIS_MODULE); | 323 | module_put(THIS_MODULE); |
322 | } | 324 | } |
323 | EXPORT_SYMBOL_GPL(nfnl_acct_put); | 325 | EXPORT_SYMBOL_GPL(nfnl_acct_put); |
324 | 326 | ||
325 | void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct) | 327 | void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct) |
326 | { | 328 | { |
327 | atomic64_inc(&nfacct->pkts); | 329 | atomic64_inc(&nfacct->pkts); |
328 | atomic64_add(skb->len, &nfacct->bytes); | 330 | atomic64_add(skb->len, &nfacct->bytes); |
329 | } | 331 | } |
330 | EXPORT_SYMBOL_GPL(nfnl_acct_update); | 332 | EXPORT_SYMBOL_GPL(nfnl_acct_update); |
331 | 333 | ||
332 | static int __init nfnl_acct_init(void) | 334 | static int __init nfnl_acct_init(void) |
333 | { | 335 | { |
334 | int ret; | 336 | int ret; |
335 | 337 | ||
336 | pr_info("nfnl_acct: registering with nfnetlink.\n"); | 338 | pr_info("nfnl_acct: registering with nfnetlink.\n"); |
337 | ret = nfnetlink_subsys_register(&nfnl_acct_subsys); | 339 | ret = nfnetlink_subsys_register(&nfnl_acct_subsys); |
338 | if (ret < 0) { | 340 | if (ret < 0) { |
339 | pr_err("nfnl_acct_init: cannot register with nfnetlink.\n"); | 341 | pr_err("nfnl_acct_init: cannot register with nfnetlink.\n"); |
340 | goto err_out; | 342 | goto err_out; |
341 | } | 343 | } |
342 | return 0; | 344 | return 0; |
343 | err_out: | 345 | err_out: |
344 | return ret; | 346 | return ret; |
345 | } | 347 | } |
346 | 348 | ||
347 | static void __exit nfnl_acct_exit(void) | 349 | static void __exit nfnl_acct_exit(void) |
348 | { | 350 | { |
349 | struct nf_acct *cur, *tmp; | 351 | struct nf_acct *cur, *tmp; |
350 | 352 | ||
351 | pr_info("nfnl_acct: unregistering from nfnetlink.\n"); | 353 | pr_info("nfnl_acct: unregistering from nfnetlink.\n"); |
352 | nfnetlink_subsys_unregister(&nfnl_acct_subsys); | 354 | nfnetlink_subsys_unregister(&nfnl_acct_subsys); |
353 | 355 | ||
354 | list_for_each_entry_safe(cur, tmp, &nfnl_acct_list, head) { | 356 | list_for_each_entry_safe(cur, tmp, &nfnl_acct_list, head) { |
355 | list_del_rcu(&cur->head); | 357 | list_del_rcu(&cur->head); |
356 | /* We are sure that our objects have no clients at this point, | 358 | /* We are sure that our objects have no clients at this point, |
357 | * it's safe to release them all without checking refcnt. */ | 359 | * it's safe to release them all without checking refcnt. */ |
358 | kfree_rcu(cur, rcu_head); | 360 | kfree_rcu(cur, rcu_head); |
359 | } | 361 | } |
360 | } | 362 | } |
361 | 363 | ||
362 | module_init(nfnl_acct_init); | 364 | module_init(nfnl_acct_init); |
363 | module_exit(nfnl_acct_exit); | 365 | module_exit(nfnl_acct_exit); |
364 | 366 |