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 000000000..2a9875311 --- /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 000000000..f145195cd --- /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 000000000..03e904946 --- /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 000000000..4231223e0 --- /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");  | 
