diff options
Diffstat (limited to 'package/wprobe/src/kernel')
-rw-r--r-- | package/wprobe/src/kernel/Makefile | 5 | ||||
-rw-r--r-- | package/wprobe/src/kernel/linux/wprobe.h | 298 | ||||
-rw-r--r-- | package/wprobe/src/kernel/wprobe-core.c | 669 | ||||
-rw-r--r-- | package/wprobe/src/kernel/wprobe-dummy.c | 96 |
4 files changed, 1068 insertions, 0 deletions
diff --git a/package/wprobe/src/kernel/Makefile b/package/wprobe/src/kernel/Makefile new file mode 100644 index 0000000000..2a9875311e --- /dev/null +++ b/package/wprobe/src/kernel/Makefile @@ -0,0 +1,5 @@ +EXTRA_CFLAGS += -I. + +obj-m := wprobe.o wprobe-dummy.o + +wprobe-objs := wprobe-core.o diff --git a/package/wprobe/src/kernel/linux/wprobe.h b/package/wprobe/src/kernel/linux/wprobe.h new file mode 100644 index 0000000000..f145195cd4 --- /dev/null +++ b/package/wprobe/src/kernel/linux/wprobe.h @@ -0,0 +1,298 @@ +/* + * wprobe.h: API for the wireless probe interface + * Copyright (C) 2008-2009 Felix Fietkau <nbd@openwrt.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __WPROBE_H +#define __WPROBE_H + +#ifdef __KERNEL__ +#include <linux/types.h> +#include <linux/if_ether.h> +#include <linux/spinlock.h> +#include <linux/module.h> +#include <linux/list.h> +#include <net/genetlink.h> +#endif + +/** + * enum wprobe_attr: netlink attribute list + * + * @WPROBE_ATTR_UNSPEC: unused + * + * @WPROBE_ATTR_INTERFACE: interface name to process query on (NLA_STRING) + * @WPROBE_ATTR_MAC: mac address (used for wireless links) (NLA_STRING) + * @WPROBE_ATTR_FLAGS: interface/link/attribute flags (see enum wprobe_flags) (NLA_U32) + * @WPROBE_ATTR_DURATION: sampling duration (in milliseconds) (NLA_MSECS) + * + * @WPROBE_ATTR_ID: attribute id (NLA_U32) + * @WPROBE_ATTR_NAME: attribute name (NLA_STRING) + * @WPROBE_ATTR_TYPE: attribute type (NLA_U8) + * @WPROBE_ATTR_SCALE: attribute scale factor (NLA_U32) + * + * attribute values: + * + * @WPROBE_VAL_STRING: string value (NLA_STRING) + * @WPROBE_VAL_S8: signed 8-bit integer (NLA_U8) + * @WPROBE_VAL_S16: signed 16-bit integer (NLA_U16) + * @WPROBE_VAL_S32: signed 32-bit integer (NLA_U32) + * @WPROBE_VAL_S64: signed 64-bit integer (NLA_U64) + * @WPROBE_VAL_U8: unsigned 8-bit integer (NLA_U8) + * @WPROBE_VAL_U16: unsigned 16-bit integer (NLA_U16) + * @WPROBE_VAL_U32: unsigned 32-bit integer (NLA_U32) + * @WPROBE_VAL_U64: unsigned 64-bit integer (NLA_U64) + * + * statistics: + * @WPROBE_VAL_SUM: sum of all samples + * @WPROBE_VAL_SUM_SQ: sum of all samples^2 + * @WPROBE_VAL_SAMPLES: number of samples + * + * @WPROBE_ATTR_LAST: unused + */ +enum wprobe_attr { + WPROBE_ATTR_UNSPEC, + WPROBE_ATTR_INTERFACE, + WPROBE_ATTR_MAC, + WPROBE_ATTR_FLAGS, + WPROBE_ATTR_DURATION, + WPROBE_ATTR_SCALE, + /* end of query attributes */ + + /* response data */ + WPROBE_ATTR_ID, + WPROBE_ATTR_NAME, + WPROBE_ATTR_TYPE, + + /* value type attributes */ + WPROBE_VAL_STRING, + WPROBE_VAL_S8, + WPROBE_VAL_S16, + WPROBE_VAL_S32, + WPROBE_VAL_S64, + WPROBE_VAL_U8, + WPROBE_VAL_U16, + WPROBE_VAL_U32, + WPROBE_VAL_U64, + + /* aggregates for statistics */ + WPROBE_VAL_SUM, + WPROBE_VAL_SUM_SQ, + WPROBE_VAL_SAMPLES, + + WPROBE_ATTR_LAST +}; + + +/** + * enum wprobe_cmd: netlink commands for interacting with wprobe + * + * @WPROBE_CMD_UNSPEC: unused + * + * @WPROBE_CMD_GET_LIST: get global/link property list + * @WPROBE_CMD_GET_INFO: get global/link properties + * @WPROBE_CMD_SET_FLAGS: set global/link flags + * @WPROBE_CMD_MEASURE: take a snapshot of the current data + * @WPROBE_CMD_GET_LINKS: get a list of links + * + * @WPROBE_CMD_LAST: unused + * + * options for GET_INFO and SET_FLAGS: + * - mac address set: per-link + * - mac address unset: globalsa + */ +enum wprobe_cmd { + WPROBE_CMD_UNSPEC, + WPROBE_CMD_GET_LIST, + WPROBE_CMD_GET_INFO, + WPROBE_CMD_SET_FLAGS, + WPROBE_CMD_MEASURE, + WPROBE_CMD_GET_LINKS, + WPROBE_CMD_LAST +}; + +/** + * enum wprobe_flags: flags for wprobe links and items + * @WPROBE_F_KEEPSTAT: keep statistics for this link/device + * @WPROBE_F_RESET: reset statistics now (used only in WPROBE_CMD_SET_LINK) + * @WPROBE_F_NEWDATA: used to indicate that a value has been updated + */ +enum wprobe_flags { + WPROBE_F_KEEPSTAT = (1 << 0), + WPROBE_F_RESET = (1 << 1), + WPROBE_F_NEWDATA = (1 << 2), +}; + +#ifdef __KERNEL__ + +struct wprobe_link; +struct wprobe_item; +struct wprobe_source; + +/** + * struct wprobe_link - data structure describing a wireless link + * @iface: pointer to the wprobe_iface that this link belongs to + * @addr: BSSID of the remote link partner + * @flags: link flags (see wprobe_flags) + * @priv: user pointer + * + * @list: for internal use + * @val: for internal use + */ +struct wprobe_link { + struct list_head list; + struct wprobe_iface *iface; + char addr[ETH_ALEN]; + u32 flags; + void *priv; + void *val; +}; + +/** + * struct wprobe_item - data structure describing the format of wprobe_link::data or wprobe_iface::data + * @name: name of the field + * @type: data type of this field + * @flags: measurement item flags (see wprobe_flags) + */ +struct wprobe_item { + const char *name; + enum wprobe_attr type; + u32 flags; +}; + +struct wprobe_value { + bool pending; + union { + /* + * the following are kept uppercase to allow + * for automated checking against WPROBE_VAL_* + * via BUG_ON() + */ + const char *STRING; + u8 U8; + u16 U16; + u32 U32; + u64 U64; + s8 S8; + s16 S16; + s32 S32; + s64 S64; + }; + s64 s, ss; + unsigned int n; + + /* timestamps */ + u64 first, last; +}; + +/** + * struct wprobe_source - data structure describing a wireless interface + * + * @name: name of the interface + * @addr: local mac address of the interface + * @links: list of wireless links to poll + * @link_items: description of the per-link data structure + * @n_link_items: number of link description items + * @global_items: description of the per-interface data structure + * @n_global_items: number of per-interface description items + * @sync_data: callback allowing the driver to prepare data for the wprobe poll + * + * @list: head for the list of interfaces + * @priv: user pointer + * @lock: spinlock protecting value data access + * @val: internal use + * @query_val: internal use + * + * if sync_data is NULL, wprobe assumes that it can access the data structure + * at any time (in atomic context). if sync_data returns a negative error code, + * the poll request will not be handled for the given link + */ +struct wprobe_iface { + /* to be filled in by wprobe source drivers */ + const char *name; + const char *addr; + const struct wprobe_item *link_items; + int n_link_items; + const struct wprobe_item *global_items; + int n_global_items; + + int (*sync_data)(struct wprobe_iface *dev, struct wprobe_link *l, struct wprobe_value *val, bool measure); + void *priv; + + /* handled by the wprobe core */ + struct list_head list; + struct list_head links; + spinlock_t lock; + void *val; + void *query_val; +}; + +#define WPROBE_FILL_BEGIN(_ptr, _list) do { \ + struct wprobe_value *__val = (_ptr); \ + const struct wprobe_item *__item = _list; \ + u64 __msecs = jiffies_to_msecs(jiffies) + +#define WPROBE_SET(_idx, _type, _value) \ + if (__item[_idx].type != WPROBE_VAL_##_type) { \ + printk("ERROR: invalid data type at %s:%d\n", __FILE__, __LINE__); \ + break; \ + } \ + __val[_idx].pending = true; \ + __val[_idx]._type = _value; \ + if (!__val[_idx].first) \ + __val[_idx].first = __msecs; \ + __val[_idx].first = __msecs + +#define WPROBE_FILL_END() \ +} while(0) + +/** + * wprobe_add_iface: register an interface with the wireless probe subsystem + * @dev: wprobe_iface structure describing the interface + */ +extern int __weak wprobe_add_iface(struct wprobe_iface *dev); + +/** + * wprobe_remove_iface: deregister an interface from the wireless probe subsystem + * @dev: wprobe_iface structure describing the interface + */ +extern void __weak wprobe_remove_iface(struct wprobe_iface *dev); + +/** + * wprobe_add_link: register a new wireless link + * @dev: wprobe_iface structure describing the interface + * @l: storage space for the wprobe_link structure + * @addr: mac address of the new link + * + * the entire wprobe_link structure is overwritten by this function call + */ +extern int __weak wprobe_add_link(struct wprobe_iface *dev, struct wprobe_link *l, const char *addr); + +/** + * wprobe_remove_link: deregister a previously registered wireless link + * @dev: wprobe_iface structure describing the interface + * @l: wprobe_link data structure + */ +extern void __weak wprobe_remove_link(struct wprobe_iface *dev, struct wprobe_link *l); + +/** + * wprobe_update_stats: update statistics after sampling values + * @dev: wprobe_iface structure describing the interface + * @l: wprobe_link data structure + * + * if l == NULL, then the stats for globals are updated + */ +extern void __weak wprobe_update_stats(struct wprobe_iface *dev, struct wprobe_link *l); + +#endif /* __KERNEL__ */ + +#endif diff --git a/package/wprobe/src/kernel/wprobe-core.c b/package/wprobe/src/kernel/wprobe-core.c new file mode 100644 index 0000000000..03e9049464 --- /dev/null +++ b/package/wprobe/src/kernel/wprobe-core.c @@ -0,0 +1,669 @@ +/* + * wprobe-core.c: Wireless probe interface core + * Copyright (C) 2008-2009 Felix Fietkau <nbd@openwrt.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/version.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/spinlock.h> +#include <linux/rcupdate.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26) +#include <linux/rculist.h> +#else +#include <linux/list.h> +#endif +#include <linux/skbuff.h> +#include <linux/wprobe.h> +#include <linux/math64.h> + +#define static + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28) +#define list_for_each_rcu __list_for_each_rcu +#endif + +static struct list_head wprobe_if; +static spinlock_t wprobe_lock; + +static struct genl_family wprobe_fam = { + .id = GENL_ID_GENERATE, + .name = "wprobe", + .hdrsize = 0, + .version = 1, + /* only the first set of attributes is used for queries */ + .maxattr = WPROBE_ATTR_ID, +}; + +static void wprobe_update_stats(struct wprobe_iface *dev, struct wprobe_link *l); + +int +wprobe_add_link(struct wprobe_iface *s, struct wprobe_link *l, const char *addr) +{ + unsigned long flags; + + INIT_LIST_HEAD(&l->list); + l->val = kzalloc(sizeof(struct wprobe_value) * s->n_link_items, GFP_ATOMIC); + if (!l->val) + return -ENOMEM; + + l->iface = s; + memcpy(&l->addr, addr, ETH_ALEN); + spin_lock_irqsave(&wprobe_lock, flags); + list_add_tail_rcu(&l->list, &s->links); + spin_unlock_irqrestore(&wprobe_lock, flags); + + return 0; +} +EXPORT_SYMBOL(wprobe_add_link); + +void +wprobe_remove_link(struct wprobe_iface *s, struct wprobe_link *l) +{ + unsigned long flags; + + spin_lock_irqsave(&wprobe_lock, flags); + list_del_rcu(&l->list); + spin_unlock_irqrestore(&wprobe_lock, flags); + synchronize_rcu(); + kfree(l->val); +} +EXPORT_SYMBOL(wprobe_remove_link); + +int +wprobe_add_iface(struct wprobe_iface *s) +{ + unsigned long flags; + int vsize; + + /* reset only wprobe private area */ + memset(&s->list, 0, sizeof(struct wprobe_iface) - offsetof(struct wprobe_iface, list)); + + BUG_ON(!s->name); + INIT_LIST_HEAD(&s->list); + INIT_LIST_HEAD(&s->links); + + vsize = max(s->n_link_items, s->n_global_items); + s->val = kzalloc(sizeof(struct wprobe_value) * vsize, GFP_ATOMIC); + if (!s->val) + goto error; + + s->query_val = kzalloc(sizeof(struct wprobe_value) * vsize, GFP_ATOMIC); + if (!s->query_val) + goto error; + + spin_lock_irqsave(&wprobe_lock, flags); + list_add_rcu(&s->list, &wprobe_if); + spin_unlock_irqrestore(&wprobe_lock, flags); + + return 0; + +error: + if (s->val) + kfree(s->val); + return -ENOMEM; +} +EXPORT_SYMBOL(wprobe_add_iface); + +void +wprobe_remove_iface(struct wprobe_iface *s) +{ + unsigned long flags; + + BUG_ON(!list_empty(&s->links)); + + spin_lock_irqsave(&wprobe_lock, flags); + list_del_rcu(&s->list); + spin_unlock_irqrestore(&wprobe_lock, flags); + + /* wait for all queries to finish before freeing the + * temporary value storage buffer */ + synchronize_rcu(); + + kfree(s->val); + kfree(s->query_val); +} +EXPORT_SYMBOL(wprobe_remove_iface); + +static struct wprobe_iface * +wprobe_get_dev(struct nlattr *attr) +{ + struct wprobe_iface *dev = NULL; + struct wprobe_iface *p; + const char *name; + int i = 0; + + if (!attr) + return NULL; + + name = nla_data(attr); + list_for_each_entry_rcu(p, &wprobe_if, list) { + i++; + if (strcmp(name, p->name) != 0) + continue; + + dev = p; + break; + } + + return dev; +} + +int +wprobe_sync_data(struct wprobe_iface *dev, struct wprobe_link *l, bool query) +{ + struct wprobe_value *val; + unsigned long flags; + int n, err; + + if (l) { + n = dev->n_link_items; + val = l->val; + } else { + n = dev->n_global_items; + val = dev->val; + } + + spin_lock_irqsave(&dev->lock, flags); + err = dev->sync_data(dev, l, val, !query); + if (err) + goto done; + + if (query) + memcpy(dev->query_val, val, sizeof(struct wprobe_value) * n); + + wprobe_update_stats(dev, l); +done: + spin_unlock_irqrestore(&dev->lock, flags); + return 0; +} +EXPORT_SYMBOL(wprobe_sync_data); + +void +wprobe_update_stats(struct wprobe_iface *dev, struct wprobe_link *l) +{ + const struct wprobe_item *item; + struct wprobe_value *val; + int i, n; + + if (l) { + n = dev->n_link_items; + item = dev->link_items; + val = l->val; + } else { + n = dev->n_global_items; + item = dev->global_items; + val = dev->val; + } + + /* process statistics */ + for (i = 0; i < n; i++) { + s64 v; + + if (!val[i].pending) + continue; + + val[i].n++; + + switch(item[i].type) { + case WPROBE_VAL_S8: + v = val[i].S8; + break; + case WPROBE_VAL_S16: + v = val[i].S16; + break; + case WPROBE_VAL_S32: + v = val[i].S32; + break; + case WPROBE_VAL_S64: + v = val[i].S64; + break; + case WPROBE_VAL_U8: + v = val[i].U8; + break; + case WPROBE_VAL_U16: + v = val[i].U16; + break; + case WPROBE_VAL_U32: + v = val[i].U32; + break; + case WPROBE_VAL_U64: + v = val[i].U64; + break; + default: + continue; + } + + val[i].s += v; + val[i].ss += v * v; + val[i].pending = false; + } +} +EXPORT_SYMBOL(wprobe_update_stats); + +static const struct nla_policy wprobe_policy[WPROBE_ATTR_ID+1] = { + [WPROBE_ATTR_INTERFACE] = { .type = NLA_NUL_STRING }, + [WPROBE_ATTR_MAC] = { .type = NLA_STRING }, + [WPROBE_ATTR_DURATION] = { .type = NLA_MSECS }, + [WPROBE_ATTR_FLAGS] = { .type = NLA_U32 }, + [WPROBE_ATTR_SCALE] = { .type = NLA_U32 }, +}; + +static bool +wprobe_check_ptr(struct list_head *list, struct list_head *ptr) +{ + struct list_head *p; + + list_for_each_rcu(p, list) { + if (ptr == p) + return true; + } + return false; +} + +static bool +wprobe_send_item_value(struct sk_buff *msg, struct netlink_callback *cb, + struct wprobe_iface *dev, struct wprobe_link *l, + const struct wprobe_item *item, + int i, u32 flags) +{ + struct genlmsghdr *hdr; + struct wprobe_value *val = dev->query_val; + u64 time = val[i].last - val[i].first; + + hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, + &wprobe_fam, NLM_F_MULTI, WPROBE_CMD_GET_INFO); + + NLA_PUT_U32(msg, WPROBE_ATTR_ID, i); + NLA_PUT_U32(msg, WPROBE_ATTR_FLAGS, flags); + NLA_PUT_U8(msg, WPROBE_ATTR_TYPE, item[i].type); + NLA_PUT_U64(msg, WPROBE_ATTR_DURATION, time); + + switch(item[i].type) { + case WPROBE_VAL_S8: + case WPROBE_VAL_U8: + NLA_PUT_U8(msg, item[i].type, val[i].U8); + break; + case WPROBE_VAL_S16: + case WPROBE_VAL_U16: + NLA_PUT_U16(msg, item[i].type, val[i].U16); + break; + case WPROBE_VAL_S32: + case WPROBE_VAL_U32: + NLA_PUT_U32(msg, item[i].type, val[i].U32); + break; + case WPROBE_VAL_S64: + case WPROBE_VAL_U64: + NLA_PUT_U64(msg, item[i].type, val[i].U64); + break; + case WPROBE_VAL_STRING: + if (val[i].STRING) + NLA_PUT_STRING(msg, item[i].type, val[i].STRING); + else + NLA_PUT_STRING(msg, item[i].type, ""); + /* bypass avg/stdev */ + goto done; + default: + /* skip unknown values */ + goto done; + } + if (item[i].flags & WPROBE_F_KEEPSTAT) { + NLA_PUT_U64(msg, WPROBE_VAL_SUM, val[i].s); + NLA_PUT_U64(msg, WPROBE_VAL_SUM_SQ, val[i].ss); + NLA_PUT_U32(msg, WPROBE_VAL_SAMPLES, (u32) val[i].n); + } +done: + genlmsg_end(msg, hdr); + return true; + +nla_put_failure: + genlmsg_cancel(msg, hdr); + return false; +} + +static bool +wprobe_send_item_info(struct sk_buff *msg, struct netlink_callback *cb, + struct wprobe_iface *dev, + const struct wprobe_item *item, int i) +{ + struct genlmsghdr *hdr; + + hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, + &wprobe_fam, NLM_F_MULTI, WPROBE_CMD_GET_LIST); + + if ((i == 0) && (dev->addr != NULL)) + NLA_PUT(msg, WPROBE_ATTR_MAC, 6, dev->addr); + NLA_PUT_U32(msg, WPROBE_ATTR_ID, (u32) i); + NLA_PUT_STRING(msg, WPROBE_ATTR_NAME, item[i].name); + NLA_PUT_U8(msg, WPROBE_ATTR_TYPE, item[i].type); + NLA_PUT_U32(msg, WPROBE_ATTR_FLAGS, item[i].flags); + genlmsg_end(msg, hdr); + return true; + +nla_put_failure: + genlmsg_cancel(msg, hdr); + return false; +} + + +static struct wprobe_link * +wprobe_find_link(struct wprobe_iface *dev, const char *mac) +{ + struct wprobe_link *l; + + list_for_each_entry_rcu(l, &dev->links, list) { + if (!memcmp(l->addr, mac, 6)) + return l; + } + return NULL; +} + +static bool +wprobe_dump_link(struct sk_buff *msg, struct wprobe_link *l, struct netlink_callback *cb) +{ + struct genlmsghdr *hdr; + + hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, + &wprobe_fam, NLM_F_MULTI, WPROBE_CMD_GET_LINKS); + if (!hdr) + return false; + + NLA_PUT(msg, WPROBE_ATTR_MAC, 6, l->addr); + genlmsg_end(msg, hdr); + return true; + +nla_put_failure: + genlmsg_cancel(msg, hdr); + return false; +} + +static int +wprobe_dump_links(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct wprobe_iface *dev = (struct wprobe_iface *)cb->args[0]; + struct wprobe_link *l; + int err = 0; + int i = 0; + + if (!dev) { + err = nlmsg_parse(cb->nlh, GENL_HDRLEN + wprobe_fam.hdrsize, + wprobe_fam.attrbuf, wprobe_fam.maxattr, wprobe_policy); + if (err) + goto done; + + dev = wprobe_get_dev(wprobe_fam.attrbuf[WPROBE_ATTR_INTERFACE]); + if (!dev) { + err = -ENODEV; + goto done; + } + + cb->args[0] = (long) dev; + } else { + if (!wprobe_check_ptr(&wprobe_if, &dev->list)) { + err = -ENODEV; + goto done; + } + } + + rcu_read_lock(); + list_for_each_entry_rcu(l, &dev->links, list) { + if (i < cb->args[1]) + continue; + + if (unlikely(!wprobe_dump_link(skb, l, cb))) + break; + + i++; + } + cb->args[1] = i; + rcu_read_unlock(); + err = skb->len; +done: + return err; +} +static void +wprobe_scale_stats(const struct wprobe_item *item, struct wprobe_value *val, int n, u32 flags) +{ + u32 scale = 0; + int i; + + for (i = 0; i < n; i++) { + if (!(item[i].flags & WPROBE_F_KEEPSTAT)) + continue; + + /* reset statistics, if requested */ + if (flags & WPROBE_F_RESET) + scale = val[i].n; + else if (wprobe_fam.attrbuf[WPROBE_ATTR_SCALE]) + scale = nla_get_u32(wprobe_fam.attrbuf[WPROBE_ATTR_SCALE]); + + if ((scale > 0) && (val[i].n >= scale)) { + val[i].s = div_s64(val[i].s, scale); + val[i].ss = div_s64(val[i].ss, scale); + val[i].n /= scale; + } + } +} + +#define WPROBE_F_LINK (1 << 31) /* for internal use */ +static int +wprobe_dump_info(struct sk_buff *skb, struct netlink_callback *cb) +{ + struct wprobe_iface *dev = (struct wprobe_iface *)cb->args[0]; + struct wprobe_link *l = (struct wprobe_link *)cb->args[1]; + struct wprobe_value *val; + const struct wprobe_item *item; + struct genlmsghdr *hdr; + unsigned long flags; + int cmd, n, i = cb->args[3]; + u32 vflags = cb->args[2]; + int err = 0; + + hdr = (struct genlmsghdr *)nlmsg_data(cb->nlh); + cmd = hdr->cmd; + + /* since the attribute value list might be too big for a single netlink + * message, the device, link and offset get stored in the netlink callback. + * if this is the first request, we need to do the full lookup for the device. + * + * access to the device and link structure is synchronized through rcu. + */ + rcu_read_lock(); + if (!dev) { + err = nlmsg_parse(cb->nlh, GENL_HDRLEN + wprobe_fam.hdrsize, + wprobe_fam.attrbuf, wprobe_fam.maxattr, wprobe_policy); + if (err) + goto done; + + err = -ENOENT; + dev = wprobe_get_dev(wprobe_fam.attrbuf[WPROBE_ATTR_INTERFACE]); + if (!dev) + goto done; + + if (cmd == WPROBE_CMD_GET_INFO) { + if (wprobe_fam.attrbuf[WPROBE_ATTR_MAC]) { + l = wprobe_find_link(dev, nla_data(wprobe_fam.attrbuf[WPROBE_ATTR_MAC])); + if (!l) + goto done; + + vflags = l->flags; + } + + if (l) { + item = dev->link_items; + n = dev->n_link_items; + val = l->val; + } else { + item = dev->global_items; + n = dev->n_global_items; + val = dev->val; + } + + /* sync data and move to temp storage for the query */ + spin_lock_irqsave(&dev->lock, flags); + err = wprobe_sync_data(dev, l, true); + if (!err) + memcpy(dev->query_val, val, n * sizeof(struct wprobe_value)); + wprobe_scale_stats(item, val, n, flags); + spin_unlock_irqrestore(&dev->lock, flags); + + if (err) + goto done; + } + + if (wprobe_fam.attrbuf[WPROBE_ATTR_FLAGS]) + vflags |= nla_get_u32(wprobe_fam.attrbuf[WPROBE_ATTR_FLAGS]); + + if (wprobe_fam.attrbuf[WPROBE_ATTR_MAC]) + vflags |= WPROBE_F_LINK; + + cb->args[0] = (long) dev; + cb->args[1] = (long) l; + cb->args[2] = vflags; + cb->args[3] = 0; + } else { + /* when pulling pointers from the callback, validate them + * against the list using rcu to make sure that we won't + * dereference pointers to free'd memory after the last + * grace period */ + err = -ENOENT; + if (!wprobe_check_ptr(&wprobe_if, &dev->list)) + goto done; + + if (l && !wprobe_check_ptr(&dev->links, &l->list)) + goto done; + } + + if (vflags & WPROBE_F_LINK) { + item = dev->link_items; + n = dev->n_link_items; + } else { + item = dev->global_items; + n = dev->n_global_items; + } + + err = 0; + switch(cmd) { + case WPROBE_CMD_GET_INFO: + while (i < n) { + if (!wprobe_send_item_value(skb, cb, dev, l, item, i, vflags)) + break; + i++; + } + break; + case WPROBE_CMD_GET_LIST: + while (i < n) { + if (!wprobe_send_item_info(skb, cb, dev, item, i)) + break; + i++; + } + break; + default: + err = -EINVAL; + goto done; + } + cb->args[3] = i; + err = skb->len; + +done: + rcu_read_unlock(); + return err; +} +#undef WPROBE_F_LINK + +static int +wprobe_measure(struct sk_buff *skb, struct genl_info *info) +{ + struct wprobe_iface *dev; + struct wprobe_link *l = NULL; + int err = -ENOENT; + + rcu_read_lock(); + dev = wprobe_get_dev(info->attrs[WPROBE_ATTR_INTERFACE]); + if (!dev) + goto done; + + if (info->attrs[WPROBE_ATTR_MAC]) { + l = wprobe_find_link(dev, nla_data(wprobe_fam.attrbuf[WPROBE_ATTR_MAC])); + if (!l) + goto done; + } + + err = wprobe_sync_data(dev, l, false); + +done: + rcu_read_unlock(); + return err; +} + +static struct genl_ops wprobe_ops[] = { + { + .cmd = WPROBE_CMD_GET_INFO, + .dumpit = wprobe_dump_info, + .policy = wprobe_policy, + }, + { + .cmd = WPROBE_CMD_GET_LIST, + .dumpit = wprobe_dump_info, + .policy = wprobe_policy, + }, + { + .cmd = WPROBE_CMD_MEASURE, + .doit = wprobe_measure, + .policy = wprobe_policy, + }, + { + .cmd = WPROBE_CMD_GET_LINKS, + .dumpit = wprobe_dump_links, + .policy = wprobe_policy, + } +}; + +static void __exit +wprobe_exit(void) +{ + BUG_ON(!list_empty(&wprobe_if)); + genl_unregister_family(&wprobe_fam); +} + + +static int __init +wprobe_init(void) +{ + int i, err; + + spin_lock_init(&wprobe_lock); + INIT_LIST_HEAD(&wprobe_if); + + err = genl_register_family(&wprobe_fam); + if (err) + return err; + + for (i = 0; i < ARRAY_SIZE(wprobe_ops); i++) { + err = genl_register_ops(&wprobe_fam, &wprobe_ops[i]); + if (err) + goto error; + } + + return 0; + +error: + genl_unregister_family(&wprobe_fam); + return err; +} + +module_init(wprobe_init); +module_exit(wprobe_exit); +MODULE_LICENSE("GPL"); + diff --git a/package/wprobe/src/kernel/wprobe-dummy.c b/package/wprobe/src/kernel/wprobe-dummy.c new file mode 100644 index 0000000000..4231223e0e --- /dev/null +++ b/package/wprobe/src/kernel/wprobe-dummy.c @@ -0,0 +1,96 @@ +/* + * wprobe-core.c: Wireless probe interface dummy driver + * Copyright (C) 2008-2009 Felix Fietkau <nbd@openwrt.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/version.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/spinlock.h> +#include <linux/random.h> +#include <linux/wprobe.h> + +static const char local_addr[] = "\x00\x13\x37\xba\xbe\x00"; + +enum dummy_global_values { + DUMMY_GLOBAL_MEDIUM_BUSY +}; +enum dummy_link_values { + DUMMY_LINK_SNR +}; + +struct wprobe_item dummy_perlink[] = { + [DUMMY_LINK_SNR] = { + .name = "snr", + .type = WPROBE_VAL_U8, + .flags = WPROBE_F_KEEPSTAT, + }, +}; + +struct wprobe_item dummy_globals[] = { + [DUMMY_GLOBAL_MEDIUM_BUSY] = { + .name = "medium_busy", + .type = WPROBE_VAL_U8, + .flags = WPROBE_F_KEEPSTAT, + } +}; + +int dummy_sync(struct wprobe_iface *dev, struct wprobe_link *l, struct wprobe_value *val, bool measure) +{ + u8 intval = 0; + + get_random_bytes(&intval, 1); + if (l) { + WPROBE_FILL_BEGIN(val, dummy_perlink); + WPROBE_SET(DUMMY_LINK_SNR, U8, (intval % 40)); + WPROBE_FILL_END(); + } else { + WPROBE_FILL_BEGIN(val, dummy_globals); + WPROBE_SET(DUMMY_GLOBAL_MEDIUM_BUSY, U8, (intval % 100)); + WPROBE_FILL_END(); + } + return 0; +} + +static struct wprobe_iface dummy_dev = { + .name = "dummy", + .addr = local_addr, + .link_items = dummy_perlink, + .n_link_items = ARRAY_SIZE(dummy_perlink), + .global_items = dummy_globals, + .n_global_items = ARRAY_SIZE(dummy_globals), + .sync_data = dummy_sync, +}; + +static struct wprobe_link dummy_link; + +static int __init +wprobe_dummy_init(void) +{ + wprobe_add_iface(&dummy_dev); + wprobe_add_link(&dummy_dev, &dummy_link, "\x00\x13\x37\xda\xda\x00"); + return 0; +} + +static void __exit +wprobe_dummy_exit(void) +{ + wprobe_remove_link(&dummy_dev, &dummy_link); + wprobe_remove_iface(&dummy_dev); +} + +module_init(wprobe_dummy_init); +module_exit(wprobe_dummy_exit); + +MODULE_LICENSE("GPL"); |