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 26 MODULE_LICENSE("GPL"); 27 MODULE_AUTHOR("Pablo Neira Ayuso <pablo@netfilter.org>"); 28 MODULE_DESCRIPTION("nfacct: Extended Netfilter accounting infrastructure"); 29 30 struct nf_acct { 31 atomic64_t pkts; 32 atomic64_t bytes; 33 unsigned long flags; 34 struct list_head head; 35 atomic_t refcnt; 36 char name[NFACCT_NAME_MAX]; 37 struct rcu_head rcu_head; 38 char data[0]; 39 }; 40 41 struct nfacct_filter { 42 u32 value; 43 u32 mask; 44 }; 45 46 #define NFACCT_F_QUOTA (NFACCT_F_QUOTA_PKTS | NFACCT_F_QUOTA_BYTES) 47 #define NFACCT_OVERQUOTA_BIT 2 /* NFACCT_F_OVERQUOTA */ 48 49 static int 50 nfnl_acct_new(struct sock *nfnl, struct sk_buff *skb, 51 const struct nlmsghdr *nlh, const struct nlattr * const tb[]) 52 { 53 struct nf_acct *nfacct, *matching = NULL; 54 struct net *net = sock_net(nfnl); 55 char *acct_name; 56 unsigned int size = 0; 57 u32 flags = 0; 58 59 if (!tb[NFACCT_NAME]) 60 return -EINVAL; 61 62 acct_name = nla_data(tb[NFACCT_NAME]); 63 if (strlen(acct_name) == 0) 64 return -EINVAL; 65 66 list_for_each_entry(nfacct, &net->nfnl_acct_list, head) { 67 if (strncmp(nfacct->name, acct_name, NFACCT_NAME_MAX) != 0) 68 continue; 69 70 if (nlh->nlmsg_flags & NLM_F_EXCL) 71 return -EEXIST; 72 73 matching = nfacct; 74 break; 75 } 76 77 if (matching) { 78 if (nlh->nlmsg_flags & NLM_F_REPLACE) { 79 /* reset counters if you request a replacement. */ 80 atomic64_set(&matching->pkts, 0); 81 atomic64_set(&matching->bytes, 0); 82 smp_mb__before_atomic(); 83 /* reset overquota flag if quota is enabled. */ 84 if ((matching->flags & NFACCT_F_QUOTA)) 85 clear_bit(NFACCT_OVERQUOTA_BIT, 86 &matching->flags); 87 return 0; 88 } 89 return -EBUSY; 90 } 91 92 if (tb[NFACCT_FLAGS]) { 93 flags = ntohl(nla_get_be32(tb[NFACCT_FLAGS])); 94 if (flags & ~NFACCT_F_QUOTA) 95 return -EOPNOTSUPP; 96 if ((flags & NFACCT_F_QUOTA) == NFACCT_F_QUOTA) 97 return -EINVAL; 98 if (flags & NFACCT_F_OVERQUOTA) 99 return -EINVAL; 100 if ((flags & NFACCT_F_QUOTA) && !tb[NFACCT_QUOTA]) 101 return -EINVAL; 102 103 size += sizeof(u64); 104 } 105 106 nfacct = kzalloc(sizeof(struct nf_acct) + size, GFP_KERNEL); 107 if (nfacct == NULL) 108 return -ENOMEM; 109 110 if (flags & NFACCT_F_QUOTA) { 111 u64 *quota = (u64 *)nfacct->data; 112 113 *quota = be64_to_cpu(nla_get_be64(tb[NFACCT_QUOTA])); 114 nfacct->flags = flags; 115 } 116 117 strncpy(nfacct->name, nla_data(tb[NFACCT_NAME]), NFACCT_NAME_MAX); 118 119 if (tb[NFACCT_BYTES]) { 120 atomic64_set(&nfacct->bytes, 121 be64_to_cpu(nla_get_be64(tb[NFACCT_BYTES]))); 122 } 123 if (tb[NFACCT_PKTS]) { 124 atomic64_set(&nfacct->pkts, 125 be64_to_cpu(nla_get_be64(tb[NFACCT_PKTS]))); 126 } 127 atomic_set(&nfacct->refcnt, 1); 128 list_add_tail_rcu(&nfacct->head, &net->nfnl_acct_list); 129 return 0; 130 } 131 132 static int 133 nfnl_acct_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, 134 int event, struct nf_acct *acct) 135 { 136 struct nlmsghdr *nlh; 137 struct nfgenmsg *nfmsg; 138 unsigned int flags = portid ? NLM_F_MULTI : 0; 139 u64 pkts, bytes; 140 u32 old_flags; 141 142 event |= NFNL_SUBSYS_ACCT << 8; 143 nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); 144 if (nlh == NULL) 145 goto nlmsg_failure; 146 147 nfmsg = nlmsg_data(nlh); 148 nfmsg->nfgen_family = AF_UNSPEC; 149 nfmsg->version = NFNETLINK_V0; 150 nfmsg->res_id = 0; 151 152 if (nla_put_string(skb, NFACCT_NAME, acct->name)) 153 goto nla_put_failure; 154 155 old_flags = acct->flags; 156 if (type == NFNL_MSG_ACCT_GET_CTRZERO) { 157 pkts = atomic64_xchg(&acct->pkts, 0); 158 bytes = atomic64_xchg(&acct->bytes, 0); 159 smp_mb__before_atomic(); 160 if (acct->flags & NFACCT_F_QUOTA) 161 clear_bit(NFACCT_OVERQUOTA_BIT, &acct->flags); 162 } else { 163 pkts = atomic64_read(&acct->pkts); 164 bytes = atomic64_read(&acct->bytes); 165 } 166 if (nla_put_be64(skb, NFACCT_PKTS, cpu_to_be64(pkts)) || 167 nla_put_be64(skb, NFACCT_BYTES, cpu_to_be64(bytes)) || 168 nla_put_be32(skb, NFACCT_USE, htonl(atomic_read(&acct->refcnt)))) 169 goto nla_put_failure; 170 if (acct->flags & NFACCT_F_QUOTA) { 171 u64 *quota = (u64 *)acct->data; 172 173 if (nla_put_be32(skb, NFACCT_FLAGS, htonl(old_flags)) || 174 nla_put_be64(skb, NFACCT_QUOTA, cpu_to_be64(*quota))) 175 goto nla_put_failure; 176 } 177 nlmsg_end(skb, nlh); 178 return skb->len; 179 180 nlmsg_failure: 181 nla_put_failure: 182 nlmsg_cancel(skb, nlh); 183 return -1; 184 } 185 186 static int 187 nfnl_acct_dump(struct sk_buff *skb, struct netlink_callback *cb) 188 { 189 struct net *net = sock_net(skb->sk); 190 struct nf_acct *cur, *last; 191 const struct nfacct_filter *filter = cb->data; 192 193 if (cb->args[2]) 194 return 0; 195 196 last = (struct nf_acct *)cb->args[1]; 197 if (cb->args[1]) 198 cb->args[1] = 0; 199 200 rcu_read_lock(); 201 list_for_each_entry_rcu(cur, &net->nfnl_acct_list, head) { 202 if (last) { 203 if (cur != last) 204 continue; 205 206 last = NULL; 207 } 208 209 if (filter && (cur->flags & filter->mask) != filter->value) 210 continue; 211 212 if (nfnl_acct_fill_info(skb, NETLINK_CB(cb->skb).portid, 213 cb->nlh->nlmsg_seq, 214 NFNL_MSG_TYPE(cb->nlh->nlmsg_type), 215 NFNL_MSG_ACCT_NEW, cur) < 0) { 216 cb->args[1] = (unsigned long)cur; 217 break; 218 } 219 } 220 if (!cb->args[1]) 221 cb->args[2] = 1; 222 rcu_read_unlock(); 223 return skb->len; 224 } 225 226 static int nfnl_acct_done(struct netlink_callback *cb) 227 { 228 kfree(cb->data); 229 return 0; 230 } 231 232 static const struct nla_policy filter_policy[NFACCT_FILTER_MAX + 1] = { 233 [NFACCT_FILTER_MASK] = { .type = NLA_U32 }, 234 [NFACCT_FILTER_VALUE] = { .type = NLA_U32 }, 235 }; 236 237 static struct nfacct_filter * 238 nfacct_filter_alloc(const struct nlattr * const attr) 239 { 240 struct nfacct_filter *filter; 241 struct nlattr *tb[NFACCT_FILTER_MAX + 1]; 242 int err; 243 244 err = nla_parse_nested(tb, NFACCT_FILTER_MAX, attr, filter_policy); 245 if (err < 0) 246 return ERR_PTR(err); 247 248 if (!tb[NFACCT_FILTER_MASK] || !tb[NFACCT_FILTER_VALUE]) 249 return ERR_PTR(-EINVAL); 250 251 filter = kzalloc(sizeof(struct nfacct_filter), GFP_KERNEL); 252 if (!filter) 253 return ERR_PTR(-ENOMEM); 254 255 filter->mask = ntohl(nla_get_be32(tb[NFACCT_FILTER_MASK])); 256 filter->value = ntohl(nla_get_be32(tb[NFACCT_FILTER_VALUE])); 257 258 return filter; 259 } 260 261 static int 262 nfnl_acct_get(struct sock *nfnl, struct sk_buff *skb, 263 const struct nlmsghdr *nlh, const struct nlattr * const tb[]) 264 { 265 struct net *net = sock_net(nfnl); 266 int ret = -ENOENT; 267 struct nf_acct *cur; 268 char *acct_name; 269 270 if (nlh->nlmsg_flags & NLM_F_DUMP) { 271 struct netlink_dump_control c = { 272 .dump = nfnl_acct_dump, 273 .done = nfnl_acct_done, 274 }; 275 276 if (tb[NFACCT_FILTER]) { 277 struct nfacct_filter *filter; 278 279 filter = nfacct_filter_alloc(tb[NFACCT_FILTER]); 280 if (IS_ERR(filter)) 281 return PTR_ERR(filter); 282 283 c.data = filter; 284 } 285 return netlink_dump_start(nfnl, skb, nlh, &c); 286 } 287 288 if (!tb[NFACCT_NAME]) 289 return -EINVAL; 290 acct_name = nla_data(tb[NFACCT_NAME]); 291 292 list_for_each_entry(cur, &net->nfnl_acct_list, head) { 293 struct sk_buff *skb2; 294 295 if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) 296 continue; 297 298 skb2 = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); 299 if (skb2 == NULL) { 300 ret = -ENOMEM; 301 break; 302 } 303 304 ret = nfnl_acct_fill_info(skb2, NETLINK_CB(skb).portid, 305 nlh->nlmsg_seq, 306 NFNL_MSG_TYPE(nlh->nlmsg_type), 307 NFNL_MSG_ACCT_NEW, cur); 308 if (ret <= 0) { 309 kfree_skb(skb2); 310 break; 311 } 312 ret = netlink_unicast(nfnl, skb2, NETLINK_CB(skb).portid, 313 MSG_DONTWAIT); 314 if (ret > 0) 315 ret = 0; 316 317 /* this avoids a loop in nfnetlink. */ 318 return ret == -EAGAIN ? -ENOBUFS : ret; 319 } 320 return ret; 321 } 322 323 /* try to delete object, fail if it is still in use. */ 324 static int nfnl_acct_try_del(struct nf_acct *cur) 325 { 326 int ret = 0; 327 328 /* we want to avoid races with nfnl_acct_find_get. */ 329 if (atomic_dec_and_test(&cur->refcnt)) { 330 /* We are protected by nfnl mutex. */ 331 list_del_rcu(&cur->head); 332 kfree_rcu(cur, rcu_head); 333 } else { 334 /* still in use, restore reference counter. */ 335 atomic_inc(&cur->refcnt); 336 ret = -EBUSY; 337 } 338 return ret; 339 } 340 341 static int 342 nfnl_acct_del(struct sock *nfnl, struct sk_buff *skb, 343 const struct nlmsghdr *nlh, const struct nlattr * const tb[]) 344 { 345 struct net *net = sock_net(nfnl); 346 char *acct_name; 347 struct nf_acct *cur; 348 int ret = -ENOENT; 349 350 if (!tb[NFACCT_NAME]) { 351 list_for_each_entry(cur, &net->nfnl_acct_list, head) 352 nfnl_acct_try_del(cur); 353 354 return 0; 355 } 356 acct_name = nla_data(tb[NFACCT_NAME]); 357 358 list_for_each_entry(cur, &net->nfnl_acct_list, head) { 359 if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX) != 0) 360 continue; 361 362 ret = nfnl_acct_try_del(cur); 363 if (ret < 0) 364 return ret; 365 366 break; 367 } 368 return ret; 369 } 370 371 static const struct nla_policy nfnl_acct_policy[NFACCT_MAX+1] = { 372 [NFACCT_NAME] = { .type = NLA_NUL_STRING, .len = NFACCT_NAME_MAX-1 }, 373 [NFACCT_BYTES] = { .type = NLA_U64 }, 374 [NFACCT_PKTS] = { .type = NLA_U64 }, 375 [NFACCT_FLAGS] = { .type = NLA_U32 }, 376 [NFACCT_QUOTA] = { .type = NLA_U64 }, 377 [NFACCT_FILTER] = {.type = NLA_NESTED }, 378 }; 379 380 static const struct nfnl_callback nfnl_acct_cb[NFNL_MSG_ACCT_MAX] = { 381 [NFNL_MSG_ACCT_NEW] = { .call = nfnl_acct_new, 382 .attr_count = NFACCT_MAX, 383 .policy = nfnl_acct_policy }, 384 [NFNL_MSG_ACCT_GET] = { .call = nfnl_acct_get, 385 .attr_count = NFACCT_MAX, 386 .policy = nfnl_acct_policy }, 387 [NFNL_MSG_ACCT_GET_CTRZERO] = { .call = nfnl_acct_get, 388 .attr_count = NFACCT_MAX, 389 .policy = nfnl_acct_policy }, 390 [NFNL_MSG_ACCT_DEL] = { .call = nfnl_acct_del, 391 .attr_count = NFACCT_MAX, 392 .policy = nfnl_acct_policy }, 393 }; 394 395 static const struct nfnetlink_subsystem nfnl_acct_subsys = { 396 .name = "acct", 397 .subsys_id = NFNL_SUBSYS_ACCT, 398 .cb_count = NFNL_MSG_ACCT_MAX, 399 .cb = nfnl_acct_cb, 400 }; 401 402 MODULE_ALIAS_NFNL_SUBSYS(NFNL_SUBSYS_ACCT); 403 404 struct nf_acct *nfnl_acct_find_get(struct net *net, const char *acct_name) 405 { 406 struct nf_acct *cur, *acct = NULL; 407 408 rcu_read_lock(); 409 list_for_each_entry_rcu(cur, &net->nfnl_acct_list, head) { 410 if (strncmp(cur->name, acct_name, NFACCT_NAME_MAX)!= 0) 411 continue; 412 413 if (!try_module_get(THIS_MODULE)) 414 goto err; 415 416 if (!atomic_inc_not_zero(&cur->refcnt)) { 417 module_put(THIS_MODULE); 418 goto err; 419 } 420 421 acct = cur; 422 break; 423 } 424 err: 425 rcu_read_unlock(); 426 return acct; 427 } 428 EXPORT_SYMBOL_GPL(nfnl_acct_find_get); 429 430 void nfnl_acct_put(struct nf_acct *acct) 431 { 432 if (atomic_dec_and_test(&acct->refcnt)) 433 kfree_rcu(acct, rcu_head); 434 435 module_put(THIS_MODULE); 436 } 437 EXPORT_SYMBOL_GPL(nfnl_acct_put); 438 439 void nfnl_acct_update(const struct sk_buff *skb, struct nf_acct *nfacct) 440 { 441 atomic64_inc(&nfacct->pkts); 442 atomic64_add(skb->len, &nfacct->bytes); 443 } 444 EXPORT_SYMBOL_GPL(nfnl_acct_update); 445 446 static void nfnl_overquota_report(struct nf_acct *nfacct) 447 { 448 int ret; 449 struct sk_buff *skb; 450 451 skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC); 452 if (skb == NULL) 453 return; 454 455 ret = nfnl_acct_fill_info(skb, 0, 0, NFNL_MSG_ACCT_OVERQUOTA, 0, 456 nfacct); 457 if (ret <= 0) { 458 kfree_skb(skb); 459 return; 460 } 461 netlink_broadcast(init_net.nfnl, skb, 0, NFNLGRP_ACCT_QUOTA, 462 GFP_ATOMIC); 463 } 464 465 int nfnl_acct_overquota(const struct sk_buff *skb, struct nf_acct *nfacct) 466 { 467 u64 now; 468 u64 *quota; 469 int ret = NFACCT_UNDERQUOTA; 470 471 /* no place here if we don't have a quota */ 472 if (!(nfacct->flags & NFACCT_F_QUOTA)) 473 return NFACCT_NO_QUOTA; 474 475 quota = (u64 *)nfacct->data; 476 now = (nfacct->flags & NFACCT_F_QUOTA_PKTS) ? 477 atomic64_read(&nfacct->pkts) : atomic64_read(&nfacct->bytes); 478 479 ret = now > *quota; 480 481 if (now >= *quota && 482 !test_and_set_bit(NFACCT_OVERQUOTA_BIT, &nfacct->flags)) { 483 nfnl_overquota_report(nfacct); 484 } 485 486 return ret; 487 } 488 EXPORT_SYMBOL_GPL(nfnl_acct_overquota); 489 490 static int __net_init nfnl_acct_net_init(struct net *net) 491 { 492 INIT_LIST_HEAD(&net->nfnl_acct_list); 493 494 return 0; 495 } 496 497 static void __net_exit nfnl_acct_net_exit(struct net *net) 498 { 499 struct nf_acct *cur, *tmp; 500 501 list_for_each_entry_safe(cur, tmp, &net->nfnl_acct_list, head) { 502 list_del_rcu(&cur->head); 503 504 if (atomic_dec_and_test(&cur->refcnt)) 505 kfree_rcu(cur, rcu_head); 506 } 507 } 508 509 static struct pernet_operations nfnl_acct_ops = { 510 .init = nfnl_acct_net_init, 511 .exit = nfnl_acct_net_exit, 512 }; 513 514 static int __init nfnl_acct_init(void) 515 { 516 int ret; 517 518 ret = register_pernet_subsys(&nfnl_acct_ops); 519 if (ret < 0) { 520 pr_err("nfnl_acct_init: failed to register pernet ops\n"); 521 goto err_out; 522 } 523 524 pr_info("nfnl_acct: registering with nfnetlink.\n"); 525 ret = nfnetlink_subsys_register(&nfnl_acct_subsys); 526 if (ret < 0) { 527 pr_err("nfnl_acct_init: cannot register with nfnetlink.\n"); 528 goto cleanup_pernet; 529 } 530 return 0; 531 532 cleanup_pernet: 533 unregister_pernet_subsys(&nfnl_acct_ops); 534 err_out: 535 return ret; 536 } 537 538 static void __exit nfnl_acct_exit(void) 539 { 540 pr_info("nfnl_acct: unregistering from nfnetlink.\n"); 541 nfnetlink_subsys_unregister(&nfnl_acct_subsys); 542 unregister_pernet_subsys(&nfnl_acct_ops); 543 } 544 545 module_init(nfnl_acct_init); 546 module_exit(nfnl_acct_exit); 547
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.