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