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