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.7-rc1 365 lines 8.8 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_be64(tb[NFACCT_BYTES]))); 83 } 84 if (tb[NFACCT_PKTS]) { 85 atomic64_set(&nfacct->pkts, 86 be64_to_cpu(nla_get_be64(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 portid, u32 seq, u32 type, 95 int event, struct nf_acct *acct) 96{ 97 struct nlmsghdr *nlh; 98 struct nfgenmsg *nfmsg; 99 unsigned int flags = portid ? NLM_F_MULTI : 0; 100 u64 pkts, bytes; 101 102 event |= NFNL_SUBSYS_ACCT << 8; 103 nlh = nlmsg_put(skb, portid, 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 if (nla_put_string(skb, NFACCT_NAME, acct->name)) 113 goto nla_put_failure; 114 115 if (type == NFNL_MSG_ACCT_GET_CTRZERO) { 116 pkts = atomic64_xchg(&acct->pkts, 0); 117 bytes = atomic64_xchg(&acct->bytes, 0); 118 } else { 119 pkts = atomic64_read(&acct->pkts); 120 bytes = atomic64_read(&acct->bytes); 121 } 122 if (nla_put_be64(skb, NFACCT_PKTS, cpu_to_be64(pkts)) || 123 nla_put_be64(skb, NFACCT_BYTES, cpu_to_be64(bytes)) || 124 nla_put_be32(skb, NFACCT_USE, htonl(atomic_read(&acct->refcnt)))) 125 goto nla_put_failure; 126 127 nlmsg_end(skb, nlh); 128 return skb->len; 129 130nlmsg_failure: 131nla_put_failure: 132 nlmsg_cancel(skb, nlh); 133 return -1; 134} 135 136static int 137nfnl_acct_dump(struct sk_buff *skb, struct netlink_callback *cb) 138{ 139 struct nf_acct *cur, *last; 140 141 if (cb->args[2]) 142 return 0; 143 144 last = (struct nf_acct *)cb->args[1]; 145 if (cb->args[1]) 146 cb->args[1] = 0; 147 148 rcu_read_lock(); 149 list_for_each_entry_rcu(cur, &nfnl_acct_list, head) { 150 if (last && cur != last) 151 continue; 152 153 if (nfnl_acct_fill_info(skb, NETLINK_CB(cb->skb).portid, 154 cb->nlh->nlmsg_seq, 155 NFNL_MSG_TYPE(cb->nlh->nlmsg_type), 156 NFNL_MSG_ACCT_NEW, cur) < 0) { 157 cb->args[1] = (unsigned long)cur; 158 break; 159 } 160 } 161 if (!cb->args[1]) 162 cb->args[2] = 1; 163 rcu_read_unlock(); 164 return skb->len; 165} 166 167static int 168nfnl_acct_get(struct sock *nfnl, struct sk_buff *skb, 169 const struct nlmsghdr *nlh, const struct nlattr * const tb[]) 170{ 171 int ret = -ENOENT; 172 struct nf_acct *cur; 173 char *acct_name; 174 175 if (nlh->nlmsg_flags & NLM_F_DUMP) { 176 struct netlink_dump_control c = { 177 .dump = nfnl_acct_dump, 178 }; 179 return netlink_dump_start(nfnl, skb, nlh, &c); 180 } 181 182 if (!tb[NFACCT_NAME]) 183 return -EINVAL; 184 acct_name = nla_data(tb[NFACCT_NAME]); 185 186 list_for_each_entry(cur, &nfnl_acct_list, head) { 187 struct sk_buff *skb2; 188 189 if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) 190 continue; 191 192 skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 193 if (skb2 == NULL) { 194 ret = -ENOMEM; 195 break; 196 } 197 198 ret = nfnl_acct_fill_info(skb2, NETLINK_CB(skb).portid, 199 nlh->nlmsg_seq, 200 NFNL_MSG_TYPE(nlh->nlmsg_type), 201 NFNL_MSG_ACCT_NEW, cur); 202 if (ret <= 0) { 203 kfree_skb(skb2); 204 break; 205 } 206 ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid, 207 MSG_DONTWAIT); 208 if (ret > 0) 209 ret = 0; 210 211 /* this avoids a loop in nfnetlink. */ 212 return ret == -EAGAIN ? -ENOBUFS : ret; 213 } 214 return ret; 215} 216 217/* try to delete object, fail if it is still in use. */ 218static int nfnl_acct_try_del(struct nf_acct *cur) 219{ 220 int ret = 0; 221 222 /* we want to avoid races with nfnl_acct_find_get. */ 223 if (atomic_dec_and_test(&cur->refcnt)) { 224 /* We are protected by nfnl mutex. */ 225 list_del_rcu(&cur->head); 226 kfree_rcu(cur, rcu_head); 227 } else { 228 /* still in use, restore reference counter. */ 229 atomic_inc(&cur->refcnt); 230 ret = -EBUSY; 231 } 232 return ret; 233} 234 235static int 236nfnl_acct_del(struct sock *nfnl, struct sk_buff *skb, 237 const struct nlmsghdr *nlh, const struct nlattr * const tb[]) 238{ 239 char *acct_name; 240 struct nf_acct *cur; 241 int ret = -ENOENT; 242 243 if (!tb[NFACCT_NAME]) { 244 list_for_each_entry(cur, &nfnl_acct_list, head) 245 nfnl_acct_try_del(cur); 246 247 return 0; 248 } 249 acct_name = nla_data(tb[NFACCT_NAME]); 250 251 list_for_each_entry(cur, &nfnl_acct_list, head) { 252 if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX) != 0) 253 continue; 254 255 ret = nfnl_acct_try_del(cur); 256 if (ret < 0) 257 return ret; 258 259 break; 260 } 261 return ret; 262} 263 264static const struct nla_policy nfnl_acct_policy[NFACCT_MAX+1] = { 265 [NFACCT_NAME] = { .type = NLA_NUL_STRING, .len = NFACCT_NAME_MAX-1 }, 266 [NFACCT_BYTES] = { .type = NLA_U64 }, 267 [NFACCT_PKTS] = { .type = NLA_U64 }, 268}; 269 270static const struct nfnl_callback nfnl_acct_cb[NFNL_MSG_ACCT_MAX] = { 271 [NFNL_MSG_ACCT_NEW] = { .call = nfnl_acct_new, 272 .attr_count = NFACCT_MAX, 273 .policy = nfnl_acct_policy }, 274 [NFNL_MSG_ACCT_GET] = { .call = nfnl_acct_get, 275 .attr_count = NFACCT_MAX, 276 .policy = nfnl_acct_policy }, 277 [NFNL_MSG_ACCT_GET_CTRZERO] = { .call = nfnl_acct_get, 278 .attr_count = NFACCT_MAX, 279 .policy = nfnl_acct_policy }, 280 [NFNL_MSG_ACCT_DEL] = { .call = nfnl_acct_del, 281 .attr_count = NFACCT_MAX, 282 .policy = nfnl_acct_policy }, 283}; 284 285static const struct nfnetlink_subsystem nfnl_acct_subsys = { 286 .name = "acct", 287 .subsys_id = NFNL_SUBSYS_ACCT, 288 .cb_count = NFNL_MSG_ACCT_MAX, 289 .cb = nfnl_acct_cb, 290}; 291 292MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_ACCT); 293 294struct nf_acct *nfnl_acct_find_get(const char *acct_name) 295{ 296 struct nf_acct *cur, *acct = NULL; 297 298 rcu_read_lock(); 299 list_for_each_entry_rcu(cur, &nfnl_acct_list, head) { 300 if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) 301 continue; 302 303 if (!try_module_get(THIS_MODULE)) 304 goto err; 305 306 if (!atomic_inc_not_zero(&cur->refcnt)) { 307 module_put(THIS_MODULE); 308 goto err; 309 } 310 311 acct = cur; 312 break; 313 } 314err: 315 rcu_read_unlock(); 316 return acct; 317} 318EXPORT_SYMBOL_GPL(nfnl_acct_find_get); 319 320void nfnl_acct_put(struct nf_acct *acct) 321{ 322 atomic_dec(&acct->refcnt); 323 module_put(THIS_MODULE); 324} 325EXPORT_SYMBOL_GPL(nfnl_acct_put); 326 327void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct) 328{ 329 atomic64_inc(&nfacct->pkts); 330 atomic64_add(skb->len, &nfacct->bytes); 331} 332EXPORT_SYMBOL_GPL(nfnl_acct_update); 333 334static int __init nfnl_acct_init(void) 335{ 336 int ret; 337 338 pr_info("nfnl_acct: registering with nfnetlink.\n"); 339 ret = nfnetlink_subsys_register(&nfnl_acct_subsys); 340 if (ret < 0) { 341 pr_err("nfnl_acct_init: cannot register with nfnetlink.\n"); 342 goto err_out; 343 } 344 return 0; 345err_out: 346 return ret; 347} 348 349static void __exit nfnl_acct_exit(void) 350{ 351 struct nf_acct *cur, *tmp; 352 353 pr_info("nfnl_acct: unregistering from nfnetlink.\n"); 354 nfnetlink_subsys_unregister(&nfnl_acct_subsys); 355 356 list_for_each_entry_safe(cur, tmp, &nfnl_acct_list, head) { 357 list_del_rcu(&cur->head); 358 /* We are sure that our objects have no clients at this point, 359 * it's safe to release them all without checking refcnt. */ 360 kfree_rcu(cur, rcu_head); 361 } 362} 363 364module_init(nfnl_acct_init); 365module_exit(nfnl_acct_exit);