Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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