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