diff options
Diffstat (limited to 'package/wprobe')
| -rw-r--r-- | package/wprobe/Makefile | 15 | ||||
| -rw-r--r-- | package/wprobe/src/Makefile.inc | 12 | ||||
| -rw-r--r-- | package/wprobe/src/exporter/Makefile | 3 | ||||
| -rw-r--r-- | package/wprobe/src/exporter/wprobe-export.c | 2 | ||||
| -rw-r--r-- | package/wprobe/src/filter/README.txt | 1 | ||||
| -rwxr-xr-x | package/wprobe/src/filter/gen_filter.pl | 63 | ||||
| -rw-r--r-- | package/wprobe/src/filter/pfc.c | 58 | ||||
| -rw-r--r-- | package/wprobe/src/kernel/linux/wprobe.h | 81 | ||||
| -rw-r--r-- | package/wprobe/src/kernel/wprobe-core.c | 353 | ||||
| -rw-r--r-- | package/wprobe/src/user/Makefile | 31 | ||||
| -rw-r--r-- | package/wprobe/src/user/wprobe-info.c | 210 | ||||
| -rw-r--r-- | package/wprobe/src/user/wprobe-lib.c | 1203 | ||||
| -rw-r--r-- | package/wprobe/src/user/wprobe-util.c | 441 | ||||
| -rw-r--r-- | package/wprobe/src/user/wprobe.c | 571 | ||||
| -rw-r--r-- | package/wprobe/src/user/wprobe.h | 60 | 
15 files changed, 2295 insertions, 809 deletions
| diff --git a/package/wprobe/Makefile b/package/wprobe/Makefile index a28ad930d..1e32eee0c 100644 --- a/package/wprobe/Makefile +++ b/package/wprobe/Makefile @@ -30,22 +30,22 @@ define KernelPackage/wprobe/description    A module that exports measurement data from wireless driver to user space  endef -define Package/wprobe-info +define Package/wprobe-util    SECTION:=net    CATEGORY:=Network    DEPENDS:=+kmod-wprobe +libnl-tiny    TITLE:=Wireless measurement utility  endef -define Package/wprobe-info/description -  wprobe-info uses the wprobe kernel module to query +define Package/wprobe-util/description +  wprobe-util uses the wprobe kernel module to query    wireless driver measurement data from an interface  endef  define Package/wprobe-export    SECTION:=net    CATEGORY:=Network -  DEPENDS:=+wprobe-info +  DEPENDS:=+wprobe-util    TITLE:=Wireless measurement data exporter  endef @@ -82,6 +82,7 @@ define Build/Compile/lib  		CFLAGS="$(TARGET_CFLAGS)" \  		CPPFLAGS="$(TARGET_CPPFLAGS) -I$(PKG_BUILD_DIR)/kernel" \  		LDFLAGS="$(TARGET_LDFLAGS)" \ +		HOST_OS=Linux \  		LIBNL="-lnl-tiny"  endef @@ -107,9 +108,9 @@ define Build/InstallDev  	$(CP) $(PKG_BUILD_DIR)/kernel/linux $(1)/usr/include/wprobe  endef -define Package/wprobe-info/install +define Package/wprobe-util/install  	$(INSTALL_DIR) $(1)/sbin -	$(INSTALL_BIN) $(PKG_BUILD_DIR)/user/wprobe-info $(1)/sbin/ +	$(INSTALL_BIN) $(PKG_BUILD_DIR)/user/wprobe-util $(1)/sbin/  endef  define Package/wprobe-export/install @@ -120,5 +121,5 @@ define Package/wprobe-export/install  endef  $(eval $(call KernelPackage,wprobe)) -$(eval $(call BuildPackage,wprobe-info)) +$(eval $(call BuildPackage,wprobe-util))  $(eval $(call BuildPackage,wprobe-export)) diff --git a/package/wprobe/src/Makefile.inc b/package/wprobe/src/Makefile.inc new file mode 100644 index 000000000..05a2dd979 --- /dev/null +++ b/package/wprobe/src/Makefile.inc @@ -0,0 +1,12 @@ +HOST_OS=$(shell uname) + +CC=gcc +AR=ar +RANLIB=ranlib + +WFLAGS = -Wall -Werror +CFLAGS?=-O2 +CPPFLAGS= +LDFLAGS= +LIBS= + diff --git a/package/wprobe/src/exporter/Makefile b/package/wprobe/src/exporter/Makefile index c8e489a6b..9f8150729 100644 --- a/package/wprobe/src/exporter/Makefile +++ b/package/wprobe/src/exporter/Makefile @@ -1,2 +1,5 @@ +include ../Makefile.inc +CPPFLAGS += -I../kernel -I../user +  wprobe-export: wprobe-export.c  	$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS) diff --git a/package/wprobe/src/exporter/wprobe-export.c b/package/wprobe/src/exporter/wprobe-export.c index fa32e8b28..b09260692 100644 --- a/package/wprobe/src/exporter/wprobe-export.c +++ b/package/wprobe/src/exporter/wprobe-export.c @@ -252,7 +252,7 @@ int main ( int argc, char **argv )  		return -1;  	} -	dev = wprobe_get_dev(ifname); +	dev = wprobe_get_auto(ifname);  	if (!dev || (list_empty(&dev->global_attr) && list_empty(&dev->link_attr))) {  		fprintf(stderr, "Cannot connect to wprobe on interface '%s'\n", ifname);  		return -1; diff --git a/package/wprobe/src/filter/README.txt b/package/wprobe/src/filter/README.txt new file mode 100644 index 000000000..6fa265e4f --- /dev/null +++ b/package/wprobe/src/filter/README.txt @@ -0,0 +1 @@ +To compile pfc you need at least libpcap version 1.0, as it requires proper radiotap header support diff --git a/package/wprobe/src/filter/gen_filter.pl b/package/wprobe/src/filter/gen_filter.pl new file mode 100755 index 000000000..f03f477a4 --- /dev/null +++ b/package/wprobe/src/filter/gen_filter.pl @@ -0,0 +1,63 @@ +#!/usr/bin/perl +use strict; + +# helpers for custom packet format +# bytes 0-7 are used by a dummy radiotap header +my $WLAN_LEN = "radio[8:2]"; +my $SNR = "radio[10:1]"; +my $DEFAULT = undef; + +my $MAGIC = "WPFF"; +my $VERSION = 1; # filter binary format version +my $HDRLEN = 3; # assumed storage space for custom fields + +my $output = "filter.bin"; +my $config = { +	"packetsize" => [ +		[ "small", "$WLAN_LEN < 250" ], +		[ "medium", "$WLAN_LEN < 800" ], +		[ "big", $DEFAULT ], +	], +	"snr" => [ +		[ "low", "$SNR < 10" ], +		[ "medium", "$SNR < 20" ], +		[ "high", $DEFAULT ], +	], +	"type" => [ +		[ "beacon", "type mgt subtype beacon" ], +		[ "data", "type data subtype data" ], +		[ "qosdata", "type data subtype qos-data" ], +		[ "other", "$DEFAULT" ] +	] +}; + +sub escape_q($) { +	my $str = shift; +	$str =~ s/'/'\\''/g; +	return $str; +} + +my $GROUPS = scalar(keys %$config); +open OUTPUT, ">$output" or die "Cannot open output file: $!\n"; +print OUTPUT pack("a4CCn", $MAGIC, $VERSION, $HDRLEN, $GROUPS); + +foreach my $groupname (keys %$config) { +	my $default = 0; +	my $group = $config->{$groupname}; +	print OUTPUT pack("a32N", $groupname, scalar(@$group)); +	foreach my $filter (@$group) { +		if (!$filter->[1]) { +			$default > 0 and print "Cannot add more than one default filter per group: $groupname -> ".$filter->[0]."\n"; +			print OUTPUT pack("a32N", $filter->[0], 0); +			$default++; +		} else { +			open FILTER, "./pfc '".escape_q($filter->[0])."' '".escape_q($filter->[1])."' |" +				or die "Failed to run filter command for '".$filter->[0]."': $!\n"; +			while (<FILTER>) { +				print OUTPUT $_; +			} +			close FILTER; +			$? and die "Filter '".$filter->[0]."' did not compile.\n"; +		} +	} +} diff --git a/package/wprobe/src/filter/pfc.c b/package/wprobe/src/filter/pfc.c new file mode 100644 index 000000000..76fb1412a --- /dev/null +++ b/package/wprobe/src/filter/pfc.c @@ -0,0 +1,58 @@ +#include <arpa/inet.h> +#include <sys/types.h> +#include <sys/time.h> +#include <string.h> +#include <stdint.h> +#include <stdlib.h> + +#include <pcap.h> +#include <pcap-bpf.h> + +struct wprobe_filter_hdr { +	char name[32]; +	uint32_t len; +} hdr; + +int main (int argc, char ** argv) +{ +    struct  bpf_program filter; +    pcap_t  *pc; +	int i; + +    if (argc != 3) +    { +		fprintf(stderr, "Usage: %s <name> <expression>\n", argv[0]); +		return 1; +    } + +    pc = pcap_open_dead(DLT_IEEE802_11_RADIO, 256); +    if (pcap_compile(pc, &filter, argv[2], 1, 0) != 0) +	{ +		pcap_perror(pc, argv[0]); +		exit(1); +	} + +	/* fix up for linux */ +	for (i = 0; i < filter.bf_len; i++) { +		struct bpf_insn *bi = &filter.bf_insns[i]; +		switch(BPF_CLASS(bi->code)) { +		case BPF_RET: +			if (BPF_MODE(bi->code) == BPF_K) { +				if (bi->k != 0) +					bi->k = 65535; +			} +			break; +		} +		bi->code = ntohs(bi->code); +		bi->k = ntohl(bi->k); +	} + +	memset(&hdr, 0, sizeof(hdr)); +	strncpy(hdr.name, argv[1], sizeof(hdr.name)); +	hdr.len = htonl(filter.bf_len); +	fwrite(&hdr, sizeof(hdr), 1, stdout); +	fwrite(filter.bf_insns, 8, filter.bf_len, stdout); +	fflush(stdout); + +    return 0; +} diff --git a/package/wprobe/src/kernel/linux/wprobe.h b/package/wprobe/src/kernel/linux/wprobe.h index 9536a929a..901daf3d1 100644 --- a/package/wprobe/src/kernel/linux/wprobe.h +++ b/package/wprobe/src/kernel/linux/wprobe.h @@ -23,6 +23,7 @@  #include <linux/module.h>  #include <linux/list.h>  #include <linux/timer.h> +#include <linux/filter.h>  #include <net/genetlink.h>  #endif @@ -103,6 +104,11 @@ enum wprobe_attr {  	WPROBE_ATTR_SAMPLES_MAX,  	WPROBE_ATTR_SAMPLES_SCALE_M,  	WPROBE_ATTR_SAMPLES_SCALE_D, +	WPROBE_ATTR_FILTER, + +	WPROBE_ATTR_FILTER_GROUP, +	WPROBE_ATTR_RXCOUNT, +	WPROBE_ATTR_TXCOUNT,  	WPROBE_ATTR_LAST  }; @@ -118,6 +124,8 @@ enum wprobe_attr {   * @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_CONFIG: set config options + * @WPROBE_CMD_GET_FILTER: get counters for active filters   *   * @WPROBE_CMD_LAST: unused   *  @@ -133,13 +141,14 @@ enum wprobe_cmd {  	WPROBE_CMD_MEASURE,  	WPROBE_CMD_GET_LINKS,  	WPROBE_CMD_CONFIG, +	WPROBE_CMD_GET_FILTER,  	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_RESET: reset statistics now   * @WPROBE_F_NEWDATA: used to indicate that a value has been updated   */  enum wprobe_flags { @@ -153,6 +162,7 @@ enum wprobe_flags {  struct wprobe_link;  struct wprobe_item;  struct wprobe_source; +struct wprobe_value;  /**   * struct wprobe_link - data structure describing a wireless link @@ -170,7 +180,7 @@ struct wprobe_link {  	char addr[ETH_ALEN];  	u32 flags;  	void *priv; -	void *val; +	struct wprobe_value *val;  };  /**  @@ -211,6 +221,58 @@ struct wprobe_value {  	u64 scale_timestamp;  }; +struct wprobe_filter_item_hdr { +	char name[32]; +	__be32 n_items; +} __attribute__((packed)); + +struct wprobe_filter_item { +	struct wprobe_filter_item_hdr hdr; +	struct sock_filter filter[]; +} __attribute__((packed)); + +struct wprobe_filter_counter { +	u64 tx; +	u64 rx; +}; + +struct wprobe_filter_group { +	const char *name; +	int n_items; +	struct wprobe_filter_item **items; +	struct wprobe_filter_counter *counters; +}; + +struct wprobe_filter_hdr { +	__u8 magic[4]; +	__u8 version; +	__u8 hdrlen; +	__u16 n_groups; +} __attribute__((packed)); + +struct wprobe_filter { +	spinlock_t lock; +	struct sk_buff *skb; +	void *data; +	int n_groups; +	int hdrlen; +	struct wprobe_filter_item **items; +	struct wprobe_filter_counter *counters; +	struct wprobe_filter_group groups[]; +}; + +enum { +	WPROBE_PKT_RX = 0x00, +	WPROBE_PKT_TX = 0x10, +}; + +struct wprobe_wlan_hdr { +	u16 len; +	u8 snr; +	u8 type; +} __attribute__((packed)); + +  /**   * struct wprobe_source - data structure describing a wireless interface   * @@ -250,8 +312,9 @@ struct wprobe_iface {  	struct list_head list;  	struct list_head links;  	spinlock_t lock; -	void *val; -	void *query_val; +	struct wprobe_value *val; +	struct wprobe_value *query_val; +	struct wprobe_filter *active_filter;  	u32 measure_interval;  	struct timer_list measure_timer; @@ -262,6 +325,7 @@ struct wprobe_iface {  	u32 scale_d;  }; +  #define WPROBE_FILL_BEGIN(_ptr, _list) do {			\  	struct wprobe_value *__val = (_ptr);			\  	const struct wprobe_item *__item = _list;		\ @@ -319,6 +383,15 @@ extern void __weak wprobe_remove_link(struct wprobe_iface *dev, struct wprobe_li   */  extern void __weak wprobe_update_stats(struct wprobe_iface *dev, struct wprobe_link *l); +/** + * wprobe_add_frame: add frame for layer 2 analysis + * @dev: wprobe_iface structure describing the interface + * @hdr: metadata for the frame + * @data: 802.11 header pointer + * @len: length of the 802.11 header + */ +extern int __weak wprobe_add_frame(struct wprobe_iface *dev, const struct wprobe_wlan_hdr *hdr, void *data, int len); +  #endif /* __KERNEL__ */  #endif diff --git a/package/wprobe/src/kernel/wprobe-core.c b/package/wprobe/src/kernel/wprobe-core.c index 51ee7bc1d..d8f5a16f6 100644 --- a/package/wprobe/src/kernel/wprobe-core.c +++ b/package/wprobe/src/kernel/wprobe-core.c @@ -35,6 +35,8 @@  #endif  #define WPROBE_MIN_INTERVAL		100 /* minimum measurement interval in msecs */ +#define WPROBE_MAX_FILTER_SIZE	1024 +#define WPROBE_MAX_FRAME_SIZE	1900  static struct list_head wprobe_if;  static spinlock_t wprobe_lock; @@ -48,8 +50,17 @@ static struct genl_family wprobe_fam = {  	.maxattr = WPROBE_ATTR_LAST,  }; +/* fake radiotap header */ +struct wprobe_rtap_hdr { +	__u8 version; +	__u8 padding; +	__le16 len; +	__le32 present; +}; +  static void wprobe_update_stats(struct wprobe_iface *dev, struct wprobe_link *l);  static int wprobe_sync_data(struct wprobe_iface *dev, struct wprobe_link *l, bool query); +static void wprobe_free_filter(struct wprobe_filter *f);  int  wprobe_add_link(struct wprobe_iface *s, struct wprobe_link *l, const char *addr) @@ -111,11 +122,11 @@ wprobe_add_iface(struct wprobe_iface *s)  	INIT_LIST_HEAD(&s->links);  	setup_timer(&s->measure_timer, wprobe_measure_timer, (unsigned long) s); -	vsize = max(s->n_link_items, s->n_global_items); -	s->val = kzalloc(sizeof(struct wprobe_value) * vsize, GFP_ATOMIC); +	s->val = kzalloc(sizeof(struct wprobe_value) * s->n_global_items, GFP_ATOMIC);  	if (!s->val)  		goto error; +	vsize = max(s->n_link_items, s->n_global_items);  	s->query_val = kzalloc(sizeof(struct wprobe_value) * vsize, GFP_ATOMIC);  	if (!s->query_val)  		goto error; @@ -160,6 +171,8 @@ wprobe_remove_iface(struct wprobe_iface *s)  	kfree(s->val);  	kfree(s->query_val); +	if (s->active_filter) +		wprobe_free_filter(s->active_filter);  }  EXPORT_SYMBOL(wprobe_remove_iface); @@ -187,6 +200,69 @@ wprobe_get_dev(struct nlattr *attr)  	return dev;  } +int +wprobe_add_frame(struct wprobe_iface *dev, const struct wprobe_wlan_hdr *hdr, void *data, int len) +{ +	struct wprobe_filter *f; +	struct sk_buff *skb; +	unsigned long flags; +	int i, j; + +	rcu_read_lock(); +	f = rcu_dereference(dev->active_filter); +	if (!f) +		goto out; + +	spin_lock_irqsave(&f->lock, flags); + +	skb = f->skb; +	skb->len = sizeof(struct wprobe_rtap_hdr); +	skb->tail = skb->data + skb->len; +	if (len + skb->len > WPROBE_MAX_FRAME_SIZE) +		len = WPROBE_MAX_FRAME_SIZE - skb->len; + +	memcpy(skb_put(skb, f->hdrlen), hdr, sizeof(struct wprobe_wlan_hdr)); +	memcpy(skb_put(skb, len), data, len); + +	for(i = 0; i < f->n_groups; i++) { +		struct wprobe_filter_group *fg = &f->groups[i]; +		bool found = false; +		int def = -1; + +		for (j = 0; j < fg->n_items; j++) { +			struct wprobe_filter_item *fi = fg->items[j]; + +			if (!fi->hdr.n_items) { +				def = j; +				continue; +			} +			if (sk_run_filter(skb, fi->filter, fi->hdr.n_items) == 0) +				continue; + +			found = true; +			break; +		} +		if (!found && def >= 0) { +			j = def; +			found = true; +		} +		if (found) { +			struct wprobe_filter_counter *c = &fg->counters[j]; + +			if (hdr->type >= WPROBE_PKT_TX) +				c->tx++; +			else +				c->rx++; +		} +	} + +	spin_unlock_irqrestore(&f->lock, flags); +out: +	rcu_read_unlock(); +	return 0; +} +EXPORT_SYMBOL(wprobe_add_frame); +  static int  wprobe_sync_data(struct wprobe_iface *dev, struct wprobe_link *l, bool query)  { @@ -325,6 +401,7 @@ static const struct nla_policy wprobe_policy[WPROBE_ATTR_LAST+1] = {  	[WPROBE_ATTR_SAMPLES_MAX] = { .type = NLA_U32 },  	[WPROBE_ATTR_SAMPLES_SCALE_M] = { .type = NLA_U32 },  	[WPROBE_ATTR_SAMPLES_SCALE_D] = { .type = NLA_U32 }, +	[WPROBE_ATTR_FILTER] = { .type = NLA_BINARY, .len = 32768 },  };  static bool @@ -438,6 +515,86 @@ wprobe_find_link(struct wprobe_iface *dev, const char *mac)  }  static bool +wprobe_dump_filter_group(struct sk_buff *msg, struct wprobe_filter_group *fg, struct netlink_callback *cb) +{ +	struct genlmsghdr *hdr; +	struct nlattr *group, *item; +	int i; + +	hdr = genlmsg_put(msg, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, +			&wprobe_fam, NLM_F_MULTI, WPROBE_CMD_GET_FILTER); +	if (!hdr) +		return false; + +	NLA_PUT_STRING(msg, WPROBE_ATTR_NAME, fg->name); +	group = nla_nest_start(msg, WPROBE_ATTR_FILTER_GROUP); +	for (i = 0; i < fg->n_items; i++) { +		struct wprobe_filter_item *fi = fg->items[i]; +		struct wprobe_filter_counter *fc = &fg->counters[i]; + +		item = nla_nest_start(msg, WPROBE_ATTR_FILTER_GROUP); +		NLA_PUT_STRING(msg, WPROBE_ATTR_NAME, fi->hdr.name); +		NLA_PUT_U64(msg, WPROBE_ATTR_RXCOUNT, fc->rx); +		NLA_PUT_U64(msg, WPROBE_ATTR_TXCOUNT, fc->tx); +		nla_nest_end(msg, item); +	} + +	nla_nest_end(msg, group); +	genlmsg_end(msg, hdr); +	return true; + +nla_put_failure: +	genlmsg_cancel(msg, hdr); +	return false; +} + +static int +wprobe_dump_filters(struct sk_buff *skb, struct netlink_callback *cb) +{ +	struct wprobe_iface *dev = (struct wprobe_iface *)cb->args[0]; +	struct wprobe_filter *f; +	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; +		cb->args[1] = 0; +	} else { +		if (!wprobe_check_ptr(&wprobe_if, &dev->list)) { +			err = -ENODEV; +			goto done; +		} +	} + +	rcu_read_lock(); +	f = rcu_dereference(dev->active_filter); +	if (!f) +		goto abort; + +	for (i = cb->args[1]; i < f->n_groups; i++) { +		if (unlikely(!wprobe_dump_filter_group(skb, &f->groups[i], cb))) +			break; +	} +	cb->args[1] = i; +abort: +	rcu_read_unlock(); +	err = skb->len; +done: +	return err; +} + +static bool  wprobe_dump_link(struct sk_buff *msg, struct wprobe_link *l, struct netlink_callback *cb)  {  	struct genlmsghdr *hdr; @@ -671,6 +828,158 @@ done:  }  static int +wprobe_check_filter(void *data, int datalen, int gs) +{ +	struct wprobe_filter_item_hdr *hdr; +	void *orig_data = data; +	void *end = data + datalen; +	int i, j, k, is, cur_is; + +	for (i = j = is = 0; i < gs; i++) { +		hdr = data; +		data += sizeof(*hdr); + +		if (data > end) +			goto overrun; + +		hdr->name[31] = 0; +		cur_is = be32_to_cpu(hdr->n_items); +		is += cur_is; +		for (j = 0; j < cur_is; j++) { +			struct sock_filter *sf; +			int n_items; + +			hdr = data; +			data += sizeof(*hdr); +			if (data > end) +				goto overrun; + +			if (hdr->n_items > 1024) +				goto overrun; + +			hdr->name[31] = 0; +			hdr->n_items = n_items = be32_to_cpu(hdr->n_items); +			sf = data; +			if (n_items > 0) { +				for (k = 0; k < n_items; k++) { +					sf->code = be16_to_cpu(sf->code); +					sf->k = be32_to_cpu(sf->k); +					sf++; +				} +				if (sk_chk_filter(data, n_items) != 0) { +					printk("%s: filter check failed at group %d, item %d\n", __func__, i, j); +					return 0; +				} +			} +			data += n_items * sizeof(struct sock_filter); +		} +	} +	return is; + +overrun: +	printk(KERN_ERR "%s: overrun during filter check at group %d, item %d, offset=%d, len=%d\n", __func__, i, j, (data - orig_data), datalen); +	return 0; +} + +static void +wprobe_free_filter(struct wprobe_filter *f) +{ +	if (f->skb) +		kfree_skb(f->skb); +	if (f->data) +		kfree(f->data); +	if (f->items) +		kfree(f->items); +	if (f->counters) +		kfree(f->counters); +	kfree(f); +} + + +static int +wprobe_set_filter(struct wprobe_iface *dev, void *data, int len) +{ +	struct wprobe_filter_hdr *fhdr; +	struct wprobe_rtap_hdr *rtap; +	struct wprobe_filter *f; +	int i, j, cur_is, is, gs; + +	if (len < sizeof(*fhdr)) +		return -EINVAL; + +	fhdr = data; +	data += sizeof(*fhdr); +	len -= sizeof(*fhdr); + +	if (memcmp(fhdr->magic, "WPFF", 4) != 0) { +		printk(KERN_ERR "%s: filter rejected (invalid magic)\n", __func__); +		return -EINVAL; +	} + +	gs = be16_to_cpu(fhdr->n_groups); +	is = wprobe_check_filter(data, len, gs); +	if (is == 0) +		return -EINVAL; + +	f = kzalloc(sizeof(struct wprobe_filter) + +		gs * sizeof(struct wprobe_filter_group), GFP_ATOMIC); +	if (!f) +		return -ENOMEM; + +	f->skb = alloc_skb(WPROBE_MAX_FRAME_SIZE, GFP_ATOMIC); +	if (!f->skb) +		goto error; + +	f->data = kmalloc(len, GFP_ATOMIC); +	if (!f->data) +		goto error; + +	f->items = kzalloc(sizeof(struct wprobe_filter_item *) * is, GFP_ATOMIC); +	if (!f->items) +		goto error; + +	f->counters = kzalloc(sizeof(struct wprobe_filter_counter) * is, GFP_ATOMIC); +	if (!f->counters) +		goto error; + +	spin_lock_init(&f->lock); +	memcpy(f->data, data, len); +	f->n_groups = gs; + +	if (f->hdrlen < sizeof(struct wprobe_wlan_hdr)) +		f->hdrlen = sizeof(struct wprobe_wlan_hdr); + +	rtap = (struct wprobe_rtap_hdr *)skb_put(f->skb, sizeof(*rtap)); +	memset(rtap, 0, sizeof(*rtap)); +	rtap->len = cpu_to_le16(sizeof(struct wprobe_rtap_hdr) + f->hdrlen); +	data = f->data; + +	cur_is = 0; +	for (i = 0; i < gs; i++) { +		struct wprobe_filter_item_hdr *hdr = data; +		struct wprobe_filter_group *g = &f->groups[i]; + +		data += sizeof(*hdr); +		g->name = hdr->name; +		g->items = &f->items[cur_is]; +		g->counters = &f->counters[cur_is]; +		g->n_items = hdr->n_items; + +		for (j = 0; j < g->n_items; j++) { +			hdr = data; +			f->items[cur_is++] = data; +			data += sizeof(*hdr) + be32_to_cpu(hdr->n_items) * sizeof(struct sock_filter); +		} +	} +	rcu_assign_pointer(dev->active_filter, f); +	return 0; + +error: +	wprobe_free_filter(f); +	return -ENOMEM; +} + +static int  wprobe_set_config(struct sk_buff *skb, struct genl_info *info)  {  	struct wprobe_iface *dev; @@ -678,6 +987,8 @@ wprobe_set_config(struct sk_buff *skb, struct genl_info *info)  	int err = -ENOENT;  	u32 scale_min, scale_max;  	u32 scale_m, scale_d; +	struct nlattr *attr; +	struct wprobe_filter *filter_free = NULL;  	rcu_read_lock();  	dev = wprobe_get_dev(info->attrs[WPROBE_ATTR_INTERFACE]); @@ -691,15 +1002,28 @@ wprobe_set_config(struct sk_buff *skb, struct genl_info *info)  		goto done;  	} +	if (info->attrs[WPROBE_ATTR_FLAGS]) { +		u32 flags = nla_get_u32(info->attrs[WPROBE_ATTR_FLAGS]); + +		if (flags & BIT(WPROBE_F_RESET)) { +			struct wprobe_link *l; + +			memset(dev->val, 0, sizeof(struct wprobe_value) * dev->n_global_items); +			list_for_each_entry_rcu(l, &dev->links, list) { +				memset(l->val, 0, sizeof(struct wprobe_value) * dev->n_link_items); +			} +		} +	} +  	if (info->attrs[WPROBE_ATTR_SAMPLES_MIN] ||  		info->attrs[WPROBE_ATTR_SAMPLES_MAX]) { -		if (info->attrs[WPROBE_ATTR_SAMPLES_MIN]) -			scale_min = nla_get_u32(info->attrs[WPROBE_ATTR_SAMPLES_MIN]); +		if ((attr = info->attrs[WPROBE_ATTR_SAMPLES_MIN])) +			scale_min = nla_get_u32(attr);  		else  			scale_min = dev->scale_min; -		if (info->attrs[WPROBE_ATTR_SAMPLES_MAX]) -			scale_max = nla_get_u32(info->attrs[WPROBE_ATTR_SAMPLES_MAX]); +		if ((attr = info->attrs[WPROBE_ATTR_SAMPLES_MAX])) +			scale_max = nla_get_u32(attr);  		else  			scale_max = dev->scale_max; @@ -725,6 +1049,13 @@ wprobe_set_config(struct sk_buff *skb, struct genl_info *info)  		dev->scale_d = scale_d;  	} +	if ((attr = info->attrs[WPROBE_ATTR_FILTER])) { +		filter_free = rcu_dereference(dev->active_filter); +		rcu_assign_pointer(dev->active_filter, NULL); +		if (nla_len(attr) > 0) +			wprobe_set_filter(dev, nla_data(attr), nla_len(attr)); +	} +  	err = 0;  	if (info->attrs[WPROBE_ATTR_INTERVAL]) {  		/* change of measurement interval requested */ @@ -736,6 +1067,10 @@ done:  	spin_unlock_irqrestore(&dev->lock, flags);  done_unlocked:  	rcu_read_unlock(); +	if (filter_free) { +		synchronize_rcu(); +		wprobe_free_filter(filter_free); +	}  	return err;  } @@ -763,6 +1098,12 @@ static struct genl_ops wprobe_ops[] = {  	{  		.cmd = WPROBE_CMD_CONFIG,  		.doit = wprobe_set_config, +		.policy = wprobe_policy, +	}, +	{ +		.cmd = WPROBE_CMD_GET_FILTER, +		.dumpit = wprobe_dump_filters, +		.policy = wprobe_policy,  	},  }; diff --git a/package/wprobe/src/user/Makefile b/package/wprobe/src/user/Makefile index e10dfc0d2..01e83da3e 100644 --- a/package/wprobe/src/user/Makefile +++ b/package/wprobe/src/user/Makefile @@ -1,21 +1,38 @@ -CFLAGS = -O2 -CPPFLAGS ?= -I../kernel -WFLAGS = -Wall -Werror +include ../Makefile.inc + +CPPFLAGS += -I../kernel  LDFLAGS = +ifneq ($(HOST_OS),Linux) +USE_LIBNL_MICRO=1 +else +USE_LIBNL_MICRO= +endif + +ifeq ($(USE_LIBNL_MICRO),1) +LIBNL_PREFIX = /usr/local +LIBNL = $(LIBNL_PREFIX)/lib/libnl-micro.a +CPPFLAGS += -I$(LIBNL_PREFIX)/include/libnl-micro +EXTRA_CFLAGS += -DNO_LOCAL_ACCESS +else  LIBNL = -lnl +endif +  LIBM = -lm  LIBS = $(LIBNL) $(LIBM) -all: libwprobe.a wprobe-info +all: libwprobe.a wprobe-util -libwprobe.a: wprobe.o +libwprobe.a: wprobe-lib.o  	rm -f $@  	$(AR) rcu $@ $^  	$(RANLIB) $@  %.o: %.c -	$(CC) $(WFLAGS) -c -o $@ $(CPPFLAGS) $(CFLAGS) $< +	$(CC) $(WFLAGS) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(EXTRA_CFLAGS) $< -wprobe-info: wprobe-info.o wprobe.o +wprobe-util: wprobe-util.o wprobe-lib.o  	$(CC) -o $@ $^ $(LDFLAGS) $(LIBS) + +clean: +	rm -f *.o *.a wprobe-util diff --git a/package/wprobe/src/user/wprobe-info.c b/package/wprobe/src/user/wprobe-info.c deleted file mode 100644 index 8361c0275..000000000 --- a/package/wprobe/src/user/wprobe-info.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * wprobe-test.c: Wireless probe user space test code - * 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 <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <inttypes.h> -#include <errno.h> -#include <stdint.h> -#include <getopt.h> -#include <stdbool.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <linux/wprobe.h> -#include "wprobe.h" - -static const char * -wprobe_dump_value(struct wprobe_attribute *attr) -{ -	static char buf[128]; - -#define HANDLE_TYPE(_type, _format) \ -	case WPROBE_VAL_##_type: \ -		snprintf(buf, sizeof(buf), _format, attr->val._type); \ -		break - -	switch(attr->type) { -		HANDLE_TYPE(S8, "%d"); -		HANDLE_TYPE(S16, "%d"); -		HANDLE_TYPE(S32, "%d"); -		HANDLE_TYPE(S64, "%lld"); -		HANDLE_TYPE(U8, "%d"); -		HANDLE_TYPE(U16, "%d"); -		HANDLE_TYPE(U32, "%d"); -		HANDLE_TYPE(U64, "%lld"); -		case WPROBE_VAL_STRING: -			/* FIXME: implement this */ -		default: -			strncpy(buf, "<unknown>", sizeof(buf)); -			break; -	} -	if ((attr->flags & WPROBE_F_KEEPSTAT) && -		(attr->val.n > 0)) { -		int len = strlen(buf); -		snprintf(buf + len, sizeof(buf) - len, " (avg: %.02f; stdev: %.02f, n=%d)", attr->val.avg, attr->val.stdev, attr->val.n); -	} -#undef HANDLE_TYPE - -	return buf; -} - - -static void -wprobe_dump_data(struct wprobe_iface *dev) -{ -	struct wprobe_attribute *attr; -	struct wprobe_link *link; -	bool first = true; - -	fprintf(stderr, "\n"); -	wprobe_request_data(dev, NULL); -	list_for_each_entry(attr, &dev->global_attr, list) { -		fprintf(stderr, (first ? -			"Global:            %s=%s\n" : -			"                   %s=%s\n"), -			attr->name, -			wprobe_dump_value(attr) -		); -		first = false; -	} - -	list_for_each_entry(link, &dev->links, list) { -		first = true; -		wprobe_request_data(dev, link->addr); -		list_for_each_entry(attr, &dev->link_attr, list) { -			if (first) { -				fprintf(stderr, -					"%02x:%02x:%02x:%02x:%02x:%02x: %s=%s\n", -					link->addr[0], link->addr[1], link->addr[2], -					link->addr[3], link->addr[4], link->addr[5], -					attr->name, -					wprobe_dump_value(attr)); -				first = false; -			} else { -				fprintf(stderr, -					"                   %s=%s\n", -					attr->name, -					wprobe_dump_value(attr)); -			} -		} -	} -} - -static const char *attr_typestr[] = { -	[0] = "Unknown", -	[WPROBE_VAL_STRING] = "String", -	[WPROBE_VAL_U8] = "Unsigned 8 bit", -	[WPROBE_VAL_U16] = "Unsigned 16 bit", -	[WPROBE_VAL_U32] = "Unsigned 32 bit", -	[WPROBE_VAL_U64] = "Unsigned 64 bit", -	[WPROBE_VAL_S8] = "Signed 8 bit", -	[WPROBE_VAL_S16] = "Signed 16 bit", -	[WPROBE_VAL_S32] = "Signed 32 bit", -	[WPROBE_VAL_S64] = "Signed 64 bit", -}; - -static int usage(const char *prog) -{ -	fprintf(stderr, -		"Usage: %s <interface> [options]\n" -		"\n" -		"Options:\n" -		"  -c:            Only apply configuration\n" -		"  -h:            This help text\n" -		"  -i <interval>: Set measurement interval\n" -		"  -m:            Run measurement loop\n" -		"\n" -		, prog); -	exit(1); -} - -static void show_attributes(struct wprobe_iface *dev) -{ -	struct wprobe_attribute *attr; -	list_for_each_entry(attr, &dev->global_attr, list) { -		fprintf(stderr, "Global attribute: '%s' (%s)\n", -			attr->name, attr_typestr[attr->type]); -	} -	list_for_each_entry(attr, &dev->link_attr, list) { -		fprintf(stderr, "Link attribute: '%s' (%s)\n", -			attr->name, attr_typestr[attr->type]); -	} -} - -static void loop_measurement(struct wprobe_iface *dev) -{ -	while (1) { -		sleep(1); -		wprobe_update_links(dev); -		wprobe_dump_data(dev); -	} -} - -int main(int argc, char **argv) -{ -	struct wprobe_iface *dev; -	const char *ifname; -	const char *prog = argv[0]; -	enum { -		CMD_NONE, -		CMD_CONFIG, -		CMD_MEASURE, -	} cmd = CMD_NONE; -	int ch; - -	if ((argc < 2) || (argv[1][0] == '-')) -		return usage(prog); - -	ifname = argv[1]; -	dev = wprobe_get_dev(ifname); -	argv++; -	argc--; - -	if (!dev || (list_empty(&dev->global_attr) && -		list_empty(&dev->link_attr))) { -		fprintf(stderr, "Interface '%s' not found\n", ifname); -		return -1; -	} - -	while ((ch = getopt(argc, argv, "chi:m")) != -1) { -		switch(ch) { -		case 'c': -			cmd = CMD_CONFIG; -			break; -		case 'm': -			cmd = CMD_MEASURE; -			break; -		case 'i': -			dev->interval = strtoul(optarg, NULL, 10); -			break; -		case 'h': -		default: -			usage(prog); -			break; -		} -	} - -	wprobe_apply_config(dev); -	if (cmd != CMD_CONFIG) -		show_attributes(dev); -	if (cmd == CMD_MEASURE) -		loop_measurement(dev); - -	wprobe_free_dev(dev); - -	return 0; -} diff --git a/package/wprobe/src/user/wprobe-lib.c b/package/wprobe/src/user/wprobe-lib.c new file mode 100644 index 000000000..a3b0fb5e5 --- /dev/null +++ b/package/wprobe/src/user/wprobe-lib.c @@ -0,0 +1,1203 @@ +/* + * wprobe.c: Wireless probe user space library + * 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 <sys/types.h> +#include <sys/socket.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <getopt.h> +#include <unistd.h> +#include <stdbool.h> +#include <math.h> +#include <linux/wprobe.h> +#include <netlink/netlink.h> +#include <netlink/attr.h> +#include <netlink/genl/genl.h> +#ifndef NO_LOCAL_ACCESS  +#include <netlink/genl/ctrl.h> +#include <netlink/genl/family.h> +#include <endian.h> +#endif +#include "wprobe.h" + +#define DEBUG 1 +#ifdef DEBUG +#define DPRINTF(fmt, ...) fprintf(stderr, "%s(%d): " fmt, __func__, __LINE__, ##__VA_ARGS__) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#if defined(BYTE_ORDER) && !defined(__BYTE_ORDER) +#define __LITTLE_ENDIAN LITTLE_ENDIAN +#define __BIG_ENDIAN BIG_ENDIAN +#define __BYTE_ORDER BYTE_ORDER +#endif + +#ifndef __BYTE_ORDER +#error Unknown endian type +#endif + +#define WPROBE_MAX_MSGLEN	65536 + +static inline __u16 __swab16(__u16 x) +{ +	return x<<8 | x>>8; +} + +static inline __u32 __swab32(__u32 x) +{ +	return x<<24 | x>>24 | +		(x & (__u32)0x0000ff00UL)<<8 | +		(x & (__u32)0x00ff0000UL)>>8; +} + +static inline __u64 __swab64(__u64 x) +{ +	return x<<56 | x>>56 | +		(x & (__u64)0x000000000000ff00ULL)<<40 | +		(x & (__u64)0x0000000000ff0000ULL)<<24 | +		(x & (__u64)0x00000000ff000000ULL)<< 8 | +		(x & (__u64)0x000000ff00000000ULL)>> 8 | +		(x & (__u64)0x0000ff0000000000ULL)>>24 | +		(x & (__u64)0x00ff000000000000ULL)>>40; +} + + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define SWAP16(var) var = __swab16(var) +#define SWAP32(var) var = __swab32(var) +#define SWAP64(var) var = __swab64(var) +#else +#define SWAP16(var) do {} while(0) +#define SWAP32(var) do {} while(0) +#define SWAP64(var) do {} while(0) +#endif + +int wprobe_port = 17990; +static struct nlattr *tb[WPROBE_ATTR_LAST+1]; +static struct nla_policy attribute_policy[WPROBE_ATTR_LAST+1] = { +	[WPROBE_ATTR_ID] = { .type = NLA_U32 }, +	[WPROBE_ATTR_MAC] = { .type = NLA_UNSPEC, .minlen = 6, .maxlen = 6 }, +	[WPROBE_ATTR_NAME] = { .type = NLA_STRING }, +	[WPROBE_ATTR_FLAGS] = { .type = NLA_U32 }, +	[WPROBE_ATTR_TYPE] = { .type = NLA_U8 }, +	[WPROBE_ATTR_FLAGS] = { .type = NLA_U32 }, +	[WPROBE_VAL_S8] = { .type = NLA_U8 }, +	[WPROBE_VAL_S16] = { .type = NLA_U16 }, +	[WPROBE_VAL_S32] = { .type = NLA_U32 }, +	[WPROBE_VAL_S64] = { .type = NLA_U64 }, +	[WPROBE_VAL_U8] = { .type = NLA_U8 }, +	[WPROBE_VAL_U16] = { .type = NLA_U16 }, +	[WPROBE_VAL_U32] = { .type = NLA_U32 }, +	[WPROBE_VAL_U64] = { .type = NLA_U64 }, +	[WPROBE_VAL_SUM] = { .type = NLA_U64 }, +	[WPROBE_VAL_SUM_SQ] = { .type = NLA_U64 }, +	[WPROBE_VAL_SAMPLES] = { .type = NLA_U32 }, +	[WPROBE_VAL_SCALE_TIME] = { .type = NLA_U64 }, +	[WPROBE_ATTR_INTERVAL] = { .type = NLA_U64 }, +	[WPROBE_ATTR_SAMPLES_MIN] = { .type = NLA_U32 }, +	[WPROBE_ATTR_SAMPLES_MAX] = { .type = NLA_U32 }, +	[WPROBE_ATTR_SAMPLES_SCALE_M] = { .type = NLA_U32 }, +	[WPROBE_ATTR_SAMPLES_SCALE_D] = { .type = NLA_U32 }, +	[WPROBE_ATTR_FILTER_GROUP] = { .type = NLA_NESTED }, +	[WPROBE_ATTR_RXCOUNT] = { .type = NLA_U64 }, +	[WPROBE_ATTR_TXCOUNT] = { .type = NLA_U64 }, +}; + +typedef int (*wprobe_cb_t)(struct nl_msg *, void *); + +struct wprobe_iface_ops { +	int (*send_msg)(struct wprobe_iface *dev, struct nl_msg *msg, wprobe_cb_t cb, void *arg); +	void (*free)(struct wprobe_iface *dev); +}; + +struct wprobe_attr_cb { +	struct list_head *list; +	char *addr; +}; + +#define WPROBE_MAGIC_STR "WPROBE" +struct wprobe_init_hdr { +	struct { +		char magic[sizeof(WPROBE_MAGIC_STR)]; + +		/* protocol version */ +		uint8_t version; + +		/* extra header length (unused for now) */ +		uint16_t extra; +	} pre __attribute__((packed)); +	union { +		struct { +			uint16_t genl_family; +		} v0 __attribute__((packed)); +	}; +} __attribute__((packed)); + +struct wprobe_msg_hdr { +	__u16 status; +	__u16 error; +	__u32 len; +}; + +enum wprobe_resp_status { +	WPROBE_MSG_DONE = 0, +	WPROBE_MSG_DATA = 1, +}; + +static inline void +wprobe_swap_msg_hdr(struct wprobe_msg_hdr *mhdr) +{ +	SWAP16(mhdr->status); +	SWAP16(mhdr->error); +	SWAP32(mhdr->len); +} + +static int +save_attribute_handler(struct nl_msg *msg, void *arg) +{ +	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); +	const char *name = "N/A"; +	struct wprobe_attribute *attr; +	int type = 0; +	struct wprobe_attr_cb *cb = arg; + +	nla_parse(tb, WPROBE_ATTR_LAST, genlmsg_attrdata(gnlh, 0), +			genlmsg_attrlen(gnlh, 0), attribute_policy); + +	if (tb[WPROBE_ATTR_NAME]) +		name = nla_data(tb[WPROBE_ATTR_NAME]); + +	attr = malloc(sizeof(struct wprobe_attribute) + strlen(name) + 1); +	if (!attr) +		return -1; + +	memset(attr, 0, sizeof(struct wprobe_attribute)); + +	if (tb[WPROBE_ATTR_ID]) +		attr->id = nla_get_u32(tb[WPROBE_ATTR_ID]); + +	if (tb[WPROBE_ATTR_MAC] && cb->addr) +		memcpy(cb->addr, nla_data(tb[WPROBE_ATTR_MAC]), 6); + +	if (tb[WPROBE_ATTR_FLAGS]) +		attr->flags = nla_get_u32(tb[WPROBE_ATTR_FLAGS]); + +	if (tb[WPROBE_ATTR_TYPE]) +		type = nla_get_u8(tb[WPROBE_ATTR_TYPE]); + +	if ((type < WPROBE_VAL_STRING) || +		(type > WPROBE_VAL_U64)) +		type = 0; + +	attr->type = type; +	strcpy(attr->name, name); +	INIT_LIST_HEAD(&attr->list); +	list_add(&attr->list, cb->list); +	return 0; +} + +static struct nl_msg * +wprobe_new_msg(struct wprobe_iface *dev, int cmd, bool dump) +{ +	struct nl_msg *msg; +	uint32_t flags = 0; + +	msg = nlmsg_alloc_size(65536); +	if (!msg) +		return NULL; + +	if (dump) +		flags |= NLM_F_DUMP; + +	genlmsg_put(msg, 0, 0, dev->genl_family, +			0, flags, cmd, 0); + +	NLA_PUT_STRING(msg, WPROBE_ATTR_INTERFACE, dev->ifname); +nla_put_failure: +	return msg; +} + + +static int +dump_attributes(struct wprobe_iface *dev, bool link, struct list_head *list, char *addr) +{ +	struct nl_msg *msg; +	struct wprobe_attr_cb cb; + +	cb.list = list; +	cb.addr = addr; +	msg = wprobe_new_msg(dev, WPROBE_CMD_GET_LIST, true); +	if (!msg) +		return -ENOMEM; + +	if (link) +		NLA_PUT(msg, WPROBE_ATTR_MAC, 6, "\x00\x00\x00\x00\x00\x00"); + +	return dev->ops->send_msg(dev, msg, save_attribute_handler, &cb); + +nla_put_failure: +	nlmsg_free(msg); +	return -EINVAL; +} + +static struct wprobe_iface * +wprobe_alloc_dev(void) +{ +	struct wprobe_iface *dev; + +	dev = malloc(sizeof(struct wprobe_iface)); +	if (!dev) +		return NULL; + +	memset(dev, 0, sizeof(struct wprobe_iface)); + +	dev->interval = -1; +	dev->scale_min = -1; +	dev->scale_max = -1; +	dev->scale_m = -1; +	dev->scale_d = -1; +	dev->sockfd = -1; + +	INIT_LIST_HEAD(&dev->global_attr); +	INIT_LIST_HEAD(&dev->link_attr); +	INIT_LIST_HEAD(&dev->links); +	return dev; +} + +static int +wprobe_init_dev(struct wprobe_iface *dev) +{ +	dump_attributes(dev, false, &dev->global_attr, NULL); +	dump_attributes(dev, true, &dev->link_attr, NULL); +	return 0; +} + +#ifndef NO_LOCAL_ACCESS  +static int n_devs = 0; +static struct nl_sock *handle = NULL; +static struct nl_cache *cache = NULL; +static struct genl_family *family = NULL; + +static int +error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) +{ +	int *ret = arg; +	*ret = err->error; +	return NL_STOP; +} + +static int +finish_handler(struct nl_msg *msg, void *arg) +{ +	int *ret = arg; +	*ret = 0; +	return NL_SKIP; +} + +static int +ack_handler(struct nl_msg *msg, void *arg) +{ +	int *ret = arg; +	*ret = 0; +	return NL_STOP; +} + +static void +wprobe_local_free(struct wprobe_iface *dev) +{ +	/* should not happen */ +	if (n_devs == 0) +		return; + +	if (--n_devs != 0) +		return; + +	if (cache) +		nl_cache_free(cache); +	if (handle) +		nl_socket_free(handle); +	handle = NULL; +	cache = NULL; +} + +static int +wprobe_local_init(void) +{ +	int ret; + +	if (n_devs++ > 0) +		return 0; + +	handle = nl_socket_alloc(); +	if (!handle) { +		DPRINTF("Failed to create handle\n"); +		goto err; +	} + +	if (genl_connect(handle)) { +		DPRINTF("Failed to connect to generic netlink\n"); +		goto err; +	} + +	ret = genl_ctrl_alloc_cache(handle, &cache); +	if (ret < 0) { +		DPRINTF("Failed to allocate netlink cache\n"); +		goto err; +	} + +	family = genl_ctrl_search_by_name(cache, "wprobe"); +	if (!family) { +		DPRINTF("wprobe API not present\n"); +		goto err; +	} +	return 0; + +err: +	wprobe_local_free(NULL); +	return -EINVAL; +} + + +static int +wprobe_local_send_msg(struct wprobe_iface *dev, struct nl_msg *msg, wprobe_cb_t callback, void *arg) +{ +	struct nl_cb *cb; +	int err = 0; + +	cb = nl_cb_alloc(NL_CB_DEFAULT); +	if (!cb) +		goto out_no_cb; + +	if (callback) +		nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, callback, arg); + +	err = nl_send_auto_complete(handle, msg); +	if (err < 0) +		goto out; + +	err = 1; + +	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); +	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); +	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); + +	while (err > 0) +		nl_recvmsgs(handle, cb); + +out: +	nl_cb_put(cb); +out_no_cb: +	nlmsg_free(msg); +	return err; +} + +static const struct wprobe_iface_ops wprobe_local_ops = { +	.send_msg = wprobe_local_send_msg, +	.free = wprobe_local_free, +}; + +struct wprobe_iface * +wprobe_get_dev(const char *ifname) +{ +	struct wprobe_iface *dev; + +	if (wprobe_local_init() != 0) +		return NULL; + +	dev = wprobe_alloc_dev(); +	if (!dev) +		goto error_alloc; + +	dev->ifname = strdup(ifname); +	dev->ops = &wprobe_local_ops; +	dev->genl_family = genl_family_get_id(family); + +	if (wprobe_init_dev(dev) < 0) +		goto error; + +	return dev; + +error: +	free(dev); +error_alloc: +	wprobe_local_free(NULL); +	return NULL; +} + +#endif + +static void swap_nlmsghdr(struct nlmsghdr *nlh) +{ +	SWAP32(nlh->nlmsg_len); +	SWAP16(nlh->nlmsg_type); +	SWAP16(nlh->nlmsg_flags); +	SWAP32(nlh->nlmsg_seq); +	SWAP32(nlh->nlmsg_pid); +} + +static void swap_genlmsghdr(struct genlmsghdr *gnlh) +{ +#if 0 /* probably unnecessary */ +	SWAP16(gnlh->reserved); +#endif +} + +static void +wprobe_swap_nested(void *data, int len, bool outgoing) +{ +	void *end = data + len; + +	while (data < end) { +		struct nlattr *nla = data; +		unsigned int type, len; + +		if (!outgoing) { +			SWAP16(nla->nla_len); +			SWAP16(nla->nla_type); + +			/* required for further sanity checks */ +			if (data + nla->nla_len > end) +				nla->nla_len = end - data; +		} + +		len = NLA_ALIGN(nla->nla_len); +		type = nla->nla_type & NLA_TYPE_MASK; + +		if (type <= WPROBE_ATTR_LAST) { +#if __BYTE_ORDER == __LITTLE_ENDIAN +			switch(attribute_policy[type].type) { +			case NLA_U16: +				SWAP16(*(__u16 *)nla_data(nla)); +				break; +			case NLA_U32: +				SWAP32(*(__u32 *)nla_data(nla)); +				break; +			case NLA_U64: +				SWAP64(*(__u64 *)nla_data(nla)); +				break; +			case NLA_NESTED: +				wprobe_swap_nested(nla_data(nla), nla_len(nla), outgoing); +				break; +			} +#endif +		} +		data += len; + +		if (outgoing) { +			SWAP16(nla->nla_len); +			SWAP16(nla->nla_type); +		} +		if (!nla->nla_len) +			break; +	} +} + +static struct nl_msg * +wprobe_msg_from_network(int socket, int len) +{ +	struct genlmsghdr *gnlh; +	struct nlmsghdr *nlh; +	struct nl_msg *msg; +	void *data; + +	msg = nlmsg_alloc_size(len + 32); +	if (!msg) +		return NULL; + +	nlh = nlmsg_hdr(msg); +	if (read(socket, nlh, len) != len) +		goto free; + +	swap_nlmsghdr(nlh); +	if (nlh->nlmsg_len > len) +		goto free; + +	gnlh = nlmsg_data(nlh); +	swap_genlmsghdr(gnlh); + +	data = genlmsg_data(gnlh); +	wprobe_swap_nested(data, genlmsg_len(gnlh), false); + +	return msg; +free: +	nlmsg_free(msg); +	return NULL; +} + +static int +wprobe_msg_to_network(int socket, struct nl_msg *msg) +{ +	struct nlmsghdr *nlh = nlmsg_hdr(msg); +	struct wprobe_msg_hdr mhdr; +	struct genlmsghdr *gnlh; +	void *buf, *data; +	int buflen, datalen; +	int ret; + +	buflen = nlh->nlmsg_len; +	buf = malloc(buflen); +	if (!buf) +		return -ENOMEM; + +	memset(&mhdr, 0, sizeof(mhdr)); +	mhdr.status = WPROBE_MSG_DATA; +	mhdr.len = buflen; +	wprobe_swap_msg_hdr(&mhdr); +	write(socket, &mhdr, sizeof(mhdr)); + +	memcpy(buf, nlh, buflen); +	nlh = buf; +	gnlh = nlmsg_data(nlh); +	data = genlmsg_data(gnlh); +	datalen = genlmsg_len(gnlh); + +	wprobe_swap_nested(data, datalen, true); +	swap_genlmsghdr(gnlh); +	swap_nlmsghdr(nlh); +	ret = write(socket, buf, buflen); +	free(buf); + +	return ret; +} + +static int +wprobe_remote_send_msg(struct wprobe_iface *dev, struct nl_msg *msg, wprobe_cb_t callback, void *arg) +{ +	struct wprobe_msg_hdr mhdr; +	int msgs = 0; + +	wprobe_msg_to_network(dev->sockfd, msg); +	nlmsg_free(msg); +	do { +		if (read(dev->sockfd, &mhdr, sizeof(mhdr)) != sizeof(mhdr)) { +			DPRINTF("Failed to read response header\n"); +			return -1; +		} +		wprobe_swap_msg_hdr(&mhdr); + +		switch(mhdr.status) { +		case WPROBE_MSG_DATA: +			if (mhdr.len > WPROBE_MAX_MSGLEN) { +				fprintf(stderr, "Invalid length in received response message.\n"); +				exit(1); +			} + +			msg = wprobe_msg_from_network(dev->sockfd, mhdr.len); +			if (!msg) +				return -EINVAL; + +			msgs++; +			callback(msg, arg); +			nlmsg_free(msg); +			break; +		} +	} while (mhdr.status != WPROBE_MSG_DONE); + +	if (mhdr.error) +		return -mhdr.error; +	else +		return msgs; +} + + +static void +wprobe_socket_dev_free(struct wprobe_iface *dev) +{ +	if (dev->sockfd >= 0) +		close(dev->sockfd); +} + +static const struct wprobe_iface_ops wprobe_remote_ops = { +	.send_msg = wprobe_remote_send_msg, +	.free = wprobe_socket_dev_free, +}; + + +#ifndef NO_LOCAL_ACCESS  +int +wprobe_server_init(int socket) +{ +	struct wprobe_init_hdr hdr; +	int ret; + +	ret = wprobe_local_init(); +	if (ret != 0) +		return ret; + +	memset(&hdr, 0, sizeof(hdr)); +	memcpy(hdr.pre.magic, WPROBE_MAGIC_STR, sizeof(WPROBE_MAGIC_STR)); +	hdr.pre.version = 0; +	hdr.v0.genl_family = genl_family_get_id(family); +	SWAP16(hdr.v0.genl_family); +	write(socket, (unsigned char *)&hdr, sizeof(hdr)); + +	return 0; +} + +static int +wprobe_server_cb(struct nl_msg *msg, void *arg) +{ +	int *socket = arg; +	int ret; + +	ret = wprobe_msg_to_network(*socket, msg); +	if (ret > 0) +		ret = 0; + +	return ret; +} + + +int +wprobe_server_handle(int socket) +{ +	struct wprobe_msg_hdr mhdr; +	struct nl_msg *msg; +	int ret; + +	ret = read(socket, &mhdr, sizeof(mhdr)); +	if (ret != sizeof(mhdr)) { +		if (ret <= 0) +			return -1; + +		DPRINTF("Failed to read request header\n"); +		return -EINVAL; +	} +	wprobe_swap_msg_hdr(&mhdr); + +	switch(mhdr.status) { +	case WPROBE_MSG_DATA: +		if (mhdr.len > WPROBE_MAX_MSGLEN) { +			DPRINTF("Invalid length in received response message.\n"); +			return -EINVAL; +		} +		msg = wprobe_msg_from_network(socket, mhdr.len); +		break; +	default: +		DPRINTF("Invalid request header type\n"); +		return -ENOENT; +	} + +	if (!msg) { +		DPRINTF("Failed to get message\n"); +		return -EINVAL; +	} + +	ret = wprobe_local_send_msg(NULL, msg, wprobe_server_cb, &socket); + +	memset(&mhdr, 0, sizeof(mhdr)); +	mhdr.status = WPROBE_MSG_DONE; +	if (ret < 0) +		mhdr.error = (uint16_t) -ret; + +	ret = write(socket, (unsigned char *)&mhdr, sizeof(mhdr)); +	if (ret > 0) +		ret = 0; + +	return ret; +} + +void +wprobe_server_done(void) +{ +	wprobe_local_free(NULL); +} +#endif + +struct wprobe_iface * +wprobe_get_from_socket(int socket, const char *name) +{ +	struct wprobe_iface *dev; +	struct wprobe_init_hdr hdr; + +	dev = wprobe_alloc_dev(); +	if (!dev) +		goto out; + +	dev->ops = &wprobe_remote_ops; +	dev->sockfd = socket; +	dev->ifname = strdup(name); + +	/* read version and header length */ +	if (read(socket, &hdr.pre, sizeof(hdr.pre)) != sizeof(hdr.pre)) { +		DPRINTF("Could not read header\n"); +		goto error; +	} + +	/* magic not found */ +	if (memcmp(hdr.pre.magic, WPROBE_MAGIC_STR, sizeof(hdr.pre.magic)) != 0) { +		DPRINTF("Magic does not match\n"); +		goto error; +	} + +	/* unsupported version */ +	if (hdr.pre.version != 0) { +		DPRINTF("Protocol version does not match\n"); +		goto error; +	} + +	if (read(socket, &hdr.v0, sizeof(hdr.v0)) != sizeof(hdr.v0)) { +		DPRINTF("Could not read header data\n"); +		goto error; +	} + +	SWAP16(hdr.pre.extra); +	SWAP16(hdr.v0.genl_family); +	dev->genl_family = hdr.v0.genl_family; + +	if (wprobe_init_dev(dev) < 0) { +		DPRINTF("Could not initialize device\n"); +		goto error; +	} + +out: +	return dev; + +error: +	wprobe_free_dev(dev); +	return NULL; +} + +struct wprobe_iface * +wprobe_get_auto(const char *arg, char **err) +{ +	static struct sockaddr_in sa; +	static char errbuf[512]; + +	struct wprobe_iface *dev = NULL; +	struct hostent *h; +	char *devstr = strdup(arg); +	char *sep = NULL; +	int sock = -1; +	int len; + +	if (err) +		*err = NULL; + +	sep = strchr(devstr, ':'); +	if (!sep) { +#ifndef NO_LOCAL_ACCESS  +		free(devstr); +		return wprobe_get_dev(arg); +#else +		*err = "Invalid argument"; +		goto out; +#endif +	} + +	*sep = 0; +	sep++; + +	sock = socket(AF_INET, SOCK_STREAM, 0); +	if (sock < 0) +		goto syserr; + +	h = gethostbyname(devstr); +	if (!h) { +		sprintf(errbuf, "Host not found"); +		goto out_err; +	} + +	memcpy(&sa.sin_addr, h->h_addr, h->h_length); +	sa.sin_family = AF_INET; +	sa.sin_port = htons(wprobe_port); +	if (connect(sock, (struct sockaddr *)&sa, sizeof(sa)) < 0) +		goto syserr; + +	dev = wprobe_get_from_socket(sock, sep); +	if (!dev) { +		sprintf(errbuf, "wprobe connection initialization failed"); +		goto out_err; +	} +	goto out; + +syserr: +	if (err) { +		strcpy(errbuf, "Connection failed: "); +		len = strlen(errbuf); +		strerror_r(errno, errbuf + len, sizeof(errbuf) - len - 1); +	} +out_err: +	if (err) +		*err = errbuf; +	if (sock >= 0) +		close(sock); +out: +	if (devstr) +		free(devstr); +	return dev; +} + +static void +free_attr_list(struct list_head *list) +{ +	struct wprobe_attribute *attr, *tmp; + +	list_for_each_entry_safe(attr, tmp, list, list) { +		list_del(&attr->list); +		free(attr); +	} +} + +void +wprobe_free_dev(struct wprobe_iface *dev) +{ +	if (dev->ops->free) +		dev->ops->free(dev); +	free_attr_list(&dev->global_attr); +	free_attr_list(&dev->link_attr); +	free((void *)dev->ifname); +	free(dev); +} + +static struct wprobe_link * +get_link(struct list_head *list, const char *addr) +{ +	struct wprobe_link *l; + +	list_for_each_entry(l, list, list) { +		if (!memcmp(l->addr, addr, 6)) { +			list_del_init(&l->list); +			goto out; +		} +	} + +	/* no previous link found, allocate a new one */ +	l = malloc(sizeof(struct wprobe_link)); +	if (!l) +		goto out; + +	memset(l, 0, sizeof(struct wprobe_link)); +	memcpy(l->addr, addr, sizeof(l->addr)); +	INIT_LIST_HEAD(&l->list); + +out: +	return l; +} + +struct wprobe_save_cb { +	struct list_head *list; +	struct list_head old_list; +}; + +static int +save_link_handler(struct nl_msg *msg, void *arg) +{ +	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); +	struct wprobe_link *link; +	struct wprobe_save_cb *cb = arg; +	const char *addr; + +	nla_parse(tb, WPROBE_ATTR_LAST, genlmsg_attrdata(gnlh, 0), +			genlmsg_attrlen(gnlh, 0), attribute_policy); + +	if (!tb[WPROBE_ATTR_MAC] || (nla_len(tb[WPROBE_ATTR_MAC]) != 6)) +		return -1; + +	addr = nla_data(tb[WPROBE_ATTR_MAC]); +	link = get_link(&cb->old_list, addr); +	if (!link) +		return -1; + +	if (tb[WPROBE_ATTR_FLAGS]) +		link->flags = nla_get_u32(tb[WPROBE_ATTR_FLAGS]); + +	list_add_tail(&link->list, cb->list); +	return 0; +} + + +int +wprobe_update_links(struct wprobe_iface *dev) +{ +	struct wprobe_link *l, *tmp; +	struct nl_msg *msg; +	struct wprobe_save_cb cb; +	int err; + +	INIT_LIST_HEAD(&cb.old_list); +	list_splice_init(&dev->links, &cb.old_list); +	cb.list = &dev->links; + +	msg = wprobe_new_msg(dev, WPROBE_CMD_GET_LINKS, true); +	if (!msg) +		return -ENOMEM; + +	err = dev->ops->send_msg(dev, msg, save_link_handler, &cb); +	if (err < 0) +		return err; + +	list_for_each_entry_safe(l, tmp, &cb.old_list, list) { +		list_del(&l->list); +		free(l); +	} + +	return 0; +} + + +struct wprobe_filter_data +{ +	wprobe_filter_cb cb; +	void *arg; +	struct wprobe_filter_item *buf; +	int buflen; +}; + +static int +dump_filter_handler(struct nl_msg *msg, void *arg) +{ +	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); +	struct wprobe_filter_data *data = arg; +	struct nlattr *p; +	const char *name; +	int count = 0; +	int len; + +	nla_parse(tb, WPROBE_ATTR_LAST, genlmsg_attrdata(gnlh, 0), +			genlmsg_attrlen(gnlh, 0), attribute_policy); + +	if (!tb[WPROBE_ATTR_NAME] || !tb[WPROBE_ATTR_FILTER_GROUP]) +		return -1; + +	name = nla_data(tb[WPROBE_ATTR_NAME]); +	nla_for_each_nested(p, tb[WPROBE_ATTR_FILTER_GROUP], len) { +		count++; +	} + +	if (data->buflen < count) { +		if (data->buf) +			free(data->buf); +		data->buflen = count; +		data->buf = malloc(sizeof(struct wprobe_filter_item) * count); +		memset(data->buf, 0, sizeof(struct wprobe_filter_item) * count); +	} + +	count = 0; +	nla_for_each_nested(p, tb[WPROBE_ATTR_FILTER_GROUP], len) { +		struct wprobe_filter_item *fi; + +		nla_parse(tb, WPROBE_ATTR_LAST, nla_data(p), +				nla_len(p), attribute_policy); + +		if (!tb[WPROBE_ATTR_NAME] || !tb[WPROBE_ATTR_RXCOUNT] +				|| !tb[WPROBE_ATTR_TXCOUNT]) +			continue; + +		fi = &data->buf[count++]; +		strncpy(fi->name, nla_data(tb[WPROBE_ATTR_NAME]), sizeof(fi->name) - 1); +		fi->name[sizeof(fi->name) - 1] = 0; +		fi->rx = nla_get_u64(tb[WPROBE_ATTR_RXCOUNT]); +		fi->tx = nla_get_u64(tb[WPROBE_ATTR_TXCOUNT]); +	} +	data->cb(data->arg, name, data->buf, count); + +	return 0; +} + +int +wprobe_dump_filters(struct wprobe_iface *dev, wprobe_filter_cb cb, void *arg) +{ +	struct wprobe_filter_data data; +	struct nl_msg *msg; +	int err; + +	data.buf = 0; +	data.buflen = 0; +	data.cb = cb; +	data.arg = arg; + +	msg = wprobe_new_msg(dev, WPROBE_CMD_GET_FILTER, true); +	if (!msg) +		return -ENOMEM; + +	err = dev->ops->send_msg(dev, msg, dump_filter_handler, &data); +	if (err < 0) +		return err; + +	return 0; +} + +int +wprobe_apply_config(struct wprobe_iface *dev) +{ +	struct nl_msg *msg; + +	msg = wprobe_new_msg(dev, WPROBE_CMD_CONFIG, false); +	if (!msg) +		return -ENOMEM; + +	if (dev->interval >= 0) +		NLA_PUT_MSECS(msg, WPROBE_ATTR_INTERVAL, dev->interval); + +	if (dev->filter_len < 0) { +		NLA_PUT(msg, WPROBE_ATTR_FILTER, 0, NULL); +		dev->filter_len = 0; +	} else if (dev->filter && dev->filter_len > 0) { +		NLA_PUT(msg, WPROBE_ATTR_FILTER, dev->filter_len, dev->filter); +	} +	dev->filter = NULL; + +	dev->ops->send_msg(dev, msg, NULL, NULL); +	return 0; + +nla_put_failure: +	nlmsg_free(msg); +	return -ENOMEM; +} + +int +wprobe_measure(struct wprobe_iface *dev) +{ +	struct nl_msg *msg; + +	msg = wprobe_new_msg(dev, WPROBE_CMD_MEASURE, false); +	if (!msg) +		return -ENOMEM; + +	dev->ops->send_msg(dev, msg, NULL, NULL); +	return 0; +} + +struct wprobe_request_cb { +	struct list_head *list; +	struct list_head old_list; +	char *addr; +}; + +static int +save_attrdata_handler(struct nl_msg *msg, void *arg) +{ +	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); +	struct wprobe_request_cb *cb = arg; +	struct wprobe_attribute *attr; +	int type, id; + +	nla_parse(tb, WPROBE_ATTR_LAST, genlmsg_attrdata(gnlh, 0), +			genlmsg_attrlen(gnlh, 0), attribute_policy); + +	if (!tb[WPROBE_ATTR_ID]) +		return -1; + +	if (!tb[WPROBE_ATTR_TYPE]) +		return -1; + +	id = nla_get_u32(tb[WPROBE_ATTR_ID]); +	list_for_each_entry(attr, &cb->old_list, list) { +		if (attr->id == id) +			goto found; +	} +	/* not found */ +	return -1; + +found: +	list_del_init(&attr->list); + +	type = nla_get_u8(tb[WPROBE_ATTR_TYPE]); +	if (type != attr->type) { +		DPRINTF("WARNING: type mismatch for %s attribute '%s' (%d != %d)\n", +			(cb->addr ? "link" : "global"), +			attr->name, +			type, attr->type); +		goto out; +	} + +	if ((type < WPROBE_VAL_STRING) || +		(type > WPROBE_VAL_U64)) +		goto out; + +	memset(&attr->val, 0, sizeof(attr->val)); + +#define HANDLE_INT_TYPE(_idx, _type) \ +	case WPROBE_VAL_S##_type: \ +	case WPROBE_VAL_U##_type: \ +		attr->val.U##_type = nla_get_u##_type(tb[_idx]); \ +		break + +	switch(type) { +		HANDLE_INT_TYPE(type, 8); +		HANDLE_INT_TYPE(type, 16); +		HANDLE_INT_TYPE(type, 32); +		HANDLE_INT_TYPE(type, 64); +		case WPROBE_VAL_STRING: +			/* unimplemented */ +			break; +	} +#undef HANDLE_TYPE + +	if (attr->flags & WPROBE_F_KEEPSTAT) { +		if (tb[WPROBE_VAL_SUM]) +			attr->val.s = nla_get_u64(tb[WPROBE_VAL_SUM]); + +		if (tb[WPROBE_VAL_SUM_SQ]) +			attr->val.ss = nla_get_u64(tb[WPROBE_VAL_SUM_SQ]); + +		if (tb[WPROBE_VAL_SAMPLES]) +			attr->val.n = nla_get_u32(tb[WPROBE_VAL_SAMPLES]); + +		if (attr->val.n > 0) { +			float avg = ((float) attr->val.s) / attr->val.n; +			float stdev = sqrt((((float) attr->val.ss) / attr->val.n) - (avg * avg)); +			if (isnan(stdev)) +				stdev = 0.0f; +			if (isnan(avg)) +				avg = 0.0f; +			attr->val.avg = avg; +			attr->val.stdev = stdev; +		} +	} + +out: +	list_add_tail(&attr->list, cb->list); +	return 0; +} + + +int +wprobe_request_data(struct wprobe_iface *dev, const unsigned char *addr) +{ +	struct wprobe_request_cb cb; +	struct list_head *attrs; +	struct nl_msg *msg; +	int err; + +	msg = wprobe_new_msg(dev, WPROBE_CMD_GET_INFO, true); +	if (!msg) +		return -ENOMEM; + +	if (addr) { +		attrs = &dev->link_attr; +		NLA_PUT(msg, WPROBE_ATTR_MAC, 6, addr); +	} else { +		attrs = &dev->global_attr; +	} + +	INIT_LIST_HEAD(&cb.old_list); +	list_splice_init(attrs, &cb.old_list); +	cb.list = attrs; + +	err = dev->ops->send_msg(dev, msg, save_attrdata_handler, &cb); +	list_splice(&cb.old_list, attrs->prev); +	return err; + +nla_put_failure: +	nlmsg_free(msg); +	return -ENOMEM; +} + + diff --git a/package/wprobe/src/user/wprobe-util.c b/package/wprobe/src/user/wprobe-util.c new file mode 100644 index 000000000..d5d440a30 --- /dev/null +++ b/package/wprobe/src/user/wprobe-util.c @@ -0,0 +1,441 @@ +/* + * wprobe-test.c: Wireless probe user space test code + * 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 <sys/types.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <arpa/inet.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <inttypes.h> +#include <errno.h> +#include <stdint.h> +#include <getopt.h> +#include <stdbool.h> +#include <unistd.h> +#include <netdb.h> +#include <fcntl.h> +#include <signal.h> + +#include <linux/wprobe.h> +#include "wprobe.h" + +static bool simple_mode = false; + +static const char * +wprobe_dump_value(struct wprobe_attribute *attr) +{ +	static char buf[128]; + +#define HANDLE_TYPE(_type, _format) \ +	case WPROBE_VAL_##_type: \ +		snprintf(buf, sizeof(buf), _format, attr->val._type); \ +		break + +	switch(attr->type) { +		HANDLE_TYPE(S8, "%d"); +		HANDLE_TYPE(S16, "%d"); +		HANDLE_TYPE(S32, "%d"); +		HANDLE_TYPE(S64, "%lld"); +		HANDLE_TYPE(U8, "%d"); +		HANDLE_TYPE(U16, "%d"); +		HANDLE_TYPE(U32, "%d"); +		HANDLE_TYPE(U64, "%lld"); +		case WPROBE_VAL_STRING: +			/* FIXME: implement this */ +		default: +			strncpy(buf, "<unknown>", sizeof(buf)); +			break; +	} +	if ((attr->flags & WPROBE_F_KEEPSTAT) && +		(attr->val.n > 0)) { +		int len = strlen(buf); +		if (simple_mode) +			snprintf(buf + len, sizeof(buf) - len, ";%.02f;%.02f;%d;%lld;%lld", attr->val.avg, attr->val.stdev, attr->val.n, attr->val.s, attr->val.ss); +		else +			snprintf(buf + len, sizeof(buf) - len, " (avg: %.02f; stdev: %.02f, n=%d)", attr->val.avg, attr->val.stdev, attr->val.n); +	} +#undef HANDLE_TYPE + +	return buf; +} + + +static void +wprobe_dump_data(struct wprobe_iface *dev) +{ +	struct wprobe_attribute *attr; +	struct wprobe_link *link; +	bool first = true; + +	if (!simple_mode) +		fprintf(stderr, "\n"); +	wprobe_request_data(dev, NULL); +	list_for_each_entry(attr, &dev->global_attr, list) { +		if (simple_mode) { +			if (first) +				fprintf(stdout, "[global]\n"); +			fprintf(stdout, "%s=%s\n", attr->name, wprobe_dump_value(attr)); +		} else { +			fprintf(stderr, (first ? +				"Global:            %s=%s\n" : +				"                   %s=%s\n"), +				attr->name, +				wprobe_dump_value(attr) +			); +		} +		first = false; +	} + +	list_for_each_entry(link, &dev->links, list) { +		first = true; +		wprobe_request_data(dev, link->addr); +		list_for_each_entry(attr, &dev->link_attr, list) { +			if (first) { +				fprintf((simple_mode ? stdout : stderr), +					(simple_mode ?  +					 "[%02x:%02x:%02x:%02x:%02x:%02x]\n%s=%s\n" : +					 "%02x:%02x:%02x:%02x:%02x:%02x: %s=%s\n"), +					link->addr[0], link->addr[1], link->addr[2], +					link->addr[3], link->addr[4], link->addr[5], +					attr->name, +					wprobe_dump_value(attr)); +				first = false; +			} else { +				fprintf((simple_mode ? stdout : stderr), +					(simple_mode ? "%s=%s\n" : +					 "                   %s=%s\n"), +					attr->name, +					wprobe_dump_value(attr)); +			} +		} +	} +	fflush(stdout); +} + +static const char *attr_typestr[] = { +	[0] = "Unknown", +	[WPROBE_VAL_STRING] = "String", +	[WPROBE_VAL_U8] = "Unsigned 8 bit", +	[WPROBE_VAL_U16] = "Unsigned 16 bit", +	[WPROBE_VAL_U32] = "Unsigned 32 bit", +	[WPROBE_VAL_U64] = "Unsigned 64 bit", +	[WPROBE_VAL_S8] = "Signed 8 bit", +	[WPROBE_VAL_S16] = "Signed 16 bit", +	[WPROBE_VAL_S32] = "Signed 32 bit", +	[WPROBE_VAL_S64] = "Signed 64 bit", +}; + +static int usage(const char *prog) +{ +	fprintf(stderr, +#ifndef NO_LOCAL_ACCESS  +		"Usage: %s <interface>|<host>:<device>|-P [options]\n" +#else +		"Usage: %s <host>:<device> [options]\n" +#endif +		"\n" +		"Options:\n" +		"  -c:            Only apply configuration\n" +		"  -d:            Delay between measurement dumps (in milliseconds, default: 1000)\n" +		"  -f:            Dump contents of layer 2 filter counters during measurement\n" +		"  -F <file>:     Apply layer 2 filters from <file>\n" +		"  -h:            This help text\n" +		"  -i <interval>: Set measurement interval\n" +		"  -m:            Run measurement loop\n" +		"  -p:            Set the TCP port for server/client (default: 17990)\n" +#ifndef NO_LOCAL_ACCESS  +		"  -P:            Run in proxy mode (listen on network)\n" +#endif +		"\n" +		, prog); +	exit(1); +} + +static void show_attributes(struct wprobe_iface *dev) +{ +	struct wprobe_attribute *attr; +	if (simple_mode) +		return; +	list_for_each_entry(attr, &dev->global_attr, list) { +		fprintf(stderr, "Global attribute: '%s' (%s)\n", +			attr->name, attr_typestr[attr->type]); +	} +	list_for_each_entry(attr, &dev->link_attr, list) { +		fprintf(stderr, "Link attribute: '%s' (%s)\n", +			attr->name, attr_typestr[attr->type]); +	} +} + +static void show_filter_simple(void *arg, const char *group, struct wprobe_filter_item *items, int n_items) +{ +	int i; + +	fprintf(stdout, "[filter:%s]\n", group); +	for (i = 0; i < n_items; i++) { +		fprintf(stdout, "%s=%lld;%lld\n", +			items[i].name, items[i].tx, items[i].rx); +	} +	fflush(stdout); +} + + +static void show_filter(void *arg, const char *group, struct wprobe_filter_item *items, int n_items) +{ +	int i; +	fprintf(stderr, "Filter group: '%s' (tx/rx)\n", group); +	for (i = 0; i < n_items; i++) { +		fprintf(stderr, " - %s (%lld/%lld)\n", +			items[i].name, items[i].tx, items[i].rx); +	} +} + +static void loop_measurement(struct wprobe_iface *dev, bool print_filters, unsigned long delay) +{ +	while (1) { +		usleep(delay * 1000); +		wprobe_update_links(dev); +		wprobe_dump_data(dev); +		if (print_filters) +			wprobe_dump_filters(dev, simple_mode ? show_filter_simple : show_filter, NULL); +	} +} + +static void set_filter(struct wprobe_iface *dev, const char *filename) +{ +	unsigned char *buf = NULL; +	unsigned int buflen = 0; +	unsigned int len = 0; +	int fd; + +	/* clear filter */ +	if (filename[0] == 0) { +		dev->filter_len = -1; +		return; +	} + +	fd = open(filename, O_RDONLY); +	if (fd < 0) { +		perror("open filter"); +		return; +	} + +	do { +		int rlen; + +		if (!buf) { +			len = 0; +			buflen = 1024; +			buf = malloc(1024); +		} else { +			buflen *= 2; +			buf = realloc(buf, buflen); +		} +		rlen = read(fd, buf + len, buflen - len); +		if (rlen < 0) +			break; + +		len += rlen; +	} while (len == buflen); + +	dev->filter = buf; +	dev->filter_len = len; +	close(fd); +} + +#ifndef NO_LOCAL_ACCESS + +static void sigchld_handler(int s) +{ +	while (waitpid(-1, NULL, WNOHANG) > 0); +} + +static int run_proxy(int port) +{ +	struct sockaddr_in sa; +	struct sigaction sig; +	int v = 1; +	int s; + +	s = socket(AF_INET, SOCK_STREAM, 0); +	if (s < 0) { +		perror("socket"); +		return 1; +	} + +	sig.sa_handler = sigchld_handler;  // Signal Handler fuer Zombie Prozesse +	sigemptyset(&sig.sa_mask); +	sig.sa_flags = SA_RESTART; +	sigaction(SIGCHLD, &sig, NULL); + +	memset(&sa, 0, sizeof(sa)); +	sa.sin_family = AF_INET; +	sa.sin_addr.s_addr = htonl(INADDR_ANY); +	sa.sin_port = htons(wprobe_port); + +	setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)); +	if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0) { +		perror("bind"); +		return 1; +	} +	if (listen(s, 10)) { +		perror("listen"); +		return 1; +	} +	while(1) { +		unsigned int addrlen; +		int ret, c; + +		c = accept(s, (struct sockaddr *)&sa, &addrlen); +		if (c < 0) { +			if (errno == EINTR) +				continue; + +			perror("accept"); +			return 1; +		} +		if (fork() == 0) { +			/* close server socket, stdin, stdout, stderr */ +			close(s); +			close(0); +			close(1); +			close(2); + +			wprobe_server_init(c); +			do { +				ret = wprobe_server_handle(c); +			} while (ret >= 0); +			wprobe_server_done(); +			close(c); +			exit(0); +		} +		close(c); +	} + +	return 0; +} +#endif + +int main(int argc, char **argv) +{ +	struct wprobe_iface *dev = NULL; +	const char *ifname; +	const char *prog = argv[0]; +	char *err = NULL; +	enum { +		CMD_NONE, +		CMD_CONFIG, +		CMD_MEASURE, +		CMD_PROXY, +	} cmd = CMD_NONE; +	const char *filter = NULL; +	bool print_filters = false; +	unsigned long delay = 1000; +	int interval = -1; +	int ch; + +	if (argc < 2) +		return usage(prog); + +#ifndef NO_LOCAL_ACCESS  +	if (!strcmp(argv[1], "-P")) { +		while ((ch = getopt(argc - 1, argv + 1, "p:")) != -1) { +			switch(ch) { +			case 'p': +				/* set port */ +				wprobe_port = strtoul(optarg, NULL, 0); +				break; +			default: +				return usage(prog); +			} +		} +		return run_proxy(wprobe_port); +	} +#endif + +	if (argv[1][0] == '-') +		return usage(prog); + +	ifname = argv[1]; +	argv++; +	argc--; + +	while ((ch = getopt(argc, argv, "cd:fF:hi:msp:")) != -1) { +		switch(ch) { +		case 'c': +			cmd = CMD_CONFIG; +			break; +		case 'd': +			delay = strtoul(optarg, NULL, 10); +			break; +		case 'm': +			cmd = CMD_MEASURE; +			break; +		case 'i': +			interval = strtoul(optarg, NULL, 10); +			break; +		case 'f': +			print_filters = true; +			break; +		case 'F': +			if (filter) { +				fprintf(stderr, "Cannot set multiple filters\n"); +				return usage(prog); +			} +			filter = optarg; +			break; +		case 's': +			simple_mode = true; +			break; +		case 'p': +			/* set port */ +			wprobe_port = strtoul(optarg, NULL, 0); +			break; +		case 'h': +		default: +			usage(prog); +			break; +		} +	} + +	dev = wprobe_get_auto(ifname, &err); +	if (!dev || (list_empty(&dev->global_attr) && +		list_empty(&dev->link_attr))) { +		if (err) +			fprintf(stderr, "%s\n", err); +		else +			fprintf(stderr, "Interface '%s' not found\n", ifname); +		return 1; +	} + +	if (filter || interval >= 0) { +		if (filter) +			set_filter(dev, filter); +		if (interval >= 0) +			dev->interval = interval; + +		wprobe_apply_config(dev); +	} + +	if (cmd != CMD_CONFIG) +		show_attributes(dev); +	if (cmd == CMD_MEASURE) +		loop_measurement(dev, print_filters, delay); + +	wprobe_free_dev(dev); + +	return 0; +} diff --git a/package/wprobe/src/user/wprobe.c b/package/wprobe/src/user/wprobe.c deleted file mode 100644 index e48f768e5..000000000 --- a/package/wprobe/src/user/wprobe.c +++ /dev/null @@ -1,571 +0,0 @@ -/* - * wprobe.c: Wireless probe user space library - * 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 <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <errno.h> -#include <getopt.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <math.h> -#include <linux/wprobe.h> -#include <netlink/netlink.h> -#include <netlink/genl/genl.h> -#include <netlink/genl/ctrl.h> -#include <netlink/genl/family.h> -#include "wprobe.h" - -#define DEBUG 1 -#ifdef DEBUG -#define DPRINTF(fmt, ...) fprintf(stderr, "%s(%d): " fmt, __func__, __LINE__, ##__VA_ARGS__) -#else -#define DPRINTF(fmt, ...) do {} while (0) -#endif - -static int n_devs = 0; -static struct nl_sock *handle = NULL; -static struct nl_cache *cache = NULL; -static struct genl_family *family = NULL; -static struct nlattr *tb[WPROBE_ATTR_LAST+1]; -static struct nla_policy attribute_policy[WPROBE_ATTR_LAST+1] = { -	[WPROBE_ATTR_ID] = { .type = NLA_U32 }, -	[WPROBE_ATTR_MAC] = { .type = NLA_UNSPEC, .minlen = 6, .maxlen = 6 }, -	[WPROBE_ATTR_NAME] = { .type = NLA_STRING }, -	[WPROBE_ATTR_FLAGS] = { .type = NLA_U32 }, -	[WPROBE_ATTR_TYPE] = { .type = NLA_U8 }, -	[WPROBE_VAL_S8] = { .type = NLA_U8 }, -	[WPROBE_VAL_S16] = { .type = NLA_U16 }, -	[WPROBE_VAL_S32] = { .type = NLA_U32 }, -	[WPROBE_VAL_S64] = { .type = NLA_U64 }, -	[WPROBE_VAL_U8] = { .type = NLA_U8 }, -	[WPROBE_VAL_U16] = { .type = NLA_U16 }, -	[WPROBE_VAL_U32] = { .type = NLA_U32 }, -	[WPROBE_VAL_U64] = { .type = NLA_U64 }, -	[WPROBE_VAL_SUM] = { .type = NLA_U64 }, -	[WPROBE_VAL_SUM_SQ] = { .type = NLA_U64 }, -	[WPROBE_VAL_SAMPLES] = { .type = NLA_U32 }, -}; - -static int -error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, void *arg) -{ -	int *ret = arg; -	*ret = err->error; -	return NL_STOP; -} - -static int -finish_handler(struct nl_msg *msg, void *arg) -{ -	int *ret = arg; -	*ret = 0; -	return NL_SKIP; -} - -static int -ack_handler(struct nl_msg *msg, void *arg) -{ -	int *ret = arg; -	*ret = 0; -	return NL_STOP; -} - - -static void -wprobe_free(void) -{ -	/* should not happen */ -	if (n_devs == 0) -		return; - -	if (--n_devs != 0) -		return; - -	if (cache) -		nl_cache_free(cache); -	if (handle) -		nl_socket_free(handle); -	handle = NULL; -	cache = NULL; -} - -static int -wprobe_init(void) -{ -	int ret; - -	if (n_devs++ > 0) -		return 0; - -	handle = nl_socket_alloc(); -	if (!handle) { -		DPRINTF("Failed to create handle\n"); -		goto err; -	} - -	if (genl_connect(handle)) { -		DPRINTF("Failed to connect to generic netlink\n"); -		goto err; -	} - -	ret = genl_ctrl_alloc_cache(handle, &cache); -	if (ret < 0) { -		DPRINTF("Failed to allocate netlink cache\n"); -		goto err; -	} - -	family = genl_ctrl_search_by_name(cache, "wprobe"); -	if (!family) { -		DPRINTF("wprobe API not present\n"); -		goto err; -	} -	return 0; - -err: -	wprobe_free(); -	return -EINVAL; -} - - -static struct nl_msg * -wprobe_new_msg(const char *ifname, int cmd, bool dump) -{ -	struct nl_msg *msg; -	uint32_t flags = 0; - -	msg = nlmsg_alloc(); -	if (!msg) -		return NULL; - -	if (dump) -		flags |= NLM_F_DUMP; - -	genlmsg_put(msg, 0, 0, genl_family_get_id(family), -			0, flags, cmd, 0); - -	NLA_PUT_STRING(msg, WPROBE_ATTR_INTERFACE, ifname); -nla_put_failure: -	return msg; -} - -static int -wprobe_send_msg(struct nl_msg *msg, void *callback, void *arg) -{ -	struct nl_cb *cb; -	int err = 0; - -	cb = nl_cb_alloc(NL_CB_DEFAULT); -	if (!cb) -		goto out_no_cb; - -	if (callback) -		nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, callback, arg); - -	err = nl_send_auto_complete(handle, msg); -	if (err < 0) -		goto out; - -	err = 1; - -	nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); -	nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); -	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); - -	while (err > 0) -		nl_recvmsgs(handle, cb); - -out: -	nl_cb_put(cb); -out_no_cb: -	nlmsg_free(msg); -	return err; -} - -struct wprobe_attr_cb { -	struct list_head *list; -	char *addr; -}; - -static int -save_attribute_handler(struct nl_msg *msg, void *arg) -{ -	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); -	const char *name = "N/A"; -	struct wprobe_attribute *attr; -	int type = 0; -	struct wprobe_attr_cb *cb = arg; - -	nla_parse(tb, WPROBE_ATTR_LAST, genlmsg_attrdata(gnlh, 0), -			genlmsg_attrlen(gnlh, 0), attribute_policy); - -	if (tb[WPROBE_ATTR_NAME]) -		name = nla_data(tb[WPROBE_ATTR_NAME]); - -	attr = malloc(sizeof(struct wprobe_attribute) + strlen(name) + 1); -	if (!attr) -		return -1; - -	memset(attr, 0, sizeof(struct wprobe_attribute)); - -	if (tb[WPROBE_ATTR_ID]) -		attr->id = nla_get_u32(tb[WPROBE_ATTR_ID]); - -	if (tb[WPROBE_ATTR_MAC] && cb->addr) -		memcpy(cb->addr, nla_data(tb[WPROBE_ATTR_MAC]), 6); - -	if (tb[WPROBE_ATTR_FLAGS]) -		attr->flags = nla_get_u32(tb[WPROBE_ATTR_FLAGS]); - -	if (tb[WPROBE_ATTR_TYPE]) -		type = nla_get_u8(tb[WPROBE_ATTR_TYPE]); - -	if ((type < WPROBE_VAL_STRING) || -		(type > WPROBE_VAL_U64)) -		type = 0; - -	attr->type = type; -	strcpy(attr->name, name); -	INIT_LIST_HEAD(&attr->list); -	list_add(&attr->list, cb->list); -	return 0; -} - - -static int -dump_attributes(const char *ifname, bool link, struct list_head *list, char *addr) -{ -	struct nl_msg *msg; -	struct wprobe_attr_cb cb; - -	cb.list = list; -	cb.addr = addr; -	msg = wprobe_new_msg(ifname, WPROBE_CMD_GET_LIST, true); -	if (!msg) -		return -ENOMEM; - -	if (link) -		NLA_PUT(msg, WPROBE_ATTR_MAC, 6, "\x00\x00\x00\x00\x00\x00"); - -	return wprobe_send_msg(msg, save_attribute_handler, &cb); - -nla_put_failure: -	nlmsg_free(msg); -	return -EINVAL; -} - -struct wprobe_iface * -wprobe_get_dev(const char *ifname) -{ -	struct wprobe_iface *dev; - -	if (wprobe_init() != 0) -		return NULL; - -	dev = malloc(sizeof(struct wprobe_iface)); -	if (!dev) -		return NULL; - -	memset(dev, 0, sizeof(struct wprobe_iface)); -	dev->ifname = strdup(ifname); -	if (!dev->ifname) -		goto error; - -	dev->interval = -1; -	dev->scale_min = -1; -	dev->scale_max = -1; -	dev->scale_m = -1; -	dev->scale_d = -1; - -	INIT_LIST_HEAD(&dev->global_attr); -	INIT_LIST_HEAD(&dev->link_attr); -	INIT_LIST_HEAD(&dev->links); - -	dump_attributes(ifname, false, &dev->global_attr, NULL); -	dump_attributes(ifname, true, &dev->link_attr, NULL); - -	return dev; - -error: -	free(dev); -	return NULL; -} - -static void -free_attr_list(struct list_head *list) -{ -	struct wprobe_attribute *attr, *tmp; - -	list_for_each_entry_safe(attr, tmp, list, list) { -		list_del(&attr->list); -		free(attr); -	} -} - -void -wprobe_free_dev(struct wprobe_iface *dev) -{ -	wprobe_free(); -	free_attr_list(&dev->global_attr); -	free_attr_list(&dev->link_attr); -	free((void *)dev->ifname); -	free(dev); -} - -static struct wprobe_link * -get_link(struct list_head *list, const char *addr) -{ -	struct wprobe_link *l; - -	list_for_each_entry(l, list, list) { -		if (!memcmp(l->addr, addr, 6)) { -			list_del_init(&l->list); -			goto out; -		} -	} - -	/* no previous link found, allocate a new one */ -	l = malloc(sizeof(struct wprobe_link)); -	if (!l) -		goto out; - -	memset(l, 0, sizeof(struct wprobe_link)); -	memcpy(l->addr, addr, sizeof(l->addr)); -	INIT_LIST_HEAD(&l->list); - -out: -	return l; -} - -struct wprobe_save_cb { -	struct list_head *list; -	struct list_head old_list; -}; - -static int -save_link_handler(struct nl_msg *msg, void *arg) -{ -	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); -	struct wprobe_link *link; -	struct wprobe_save_cb *cb = arg; -	const char *addr; - -	nla_parse(tb, WPROBE_ATTR_LAST, genlmsg_attrdata(gnlh, 0), -			genlmsg_attrlen(gnlh, 0), attribute_policy); - -	if (!tb[WPROBE_ATTR_MAC] || (nla_len(tb[WPROBE_ATTR_MAC]) != 6)) -		return -1; - -	addr = nla_data(tb[WPROBE_ATTR_MAC]); -	link = get_link(&cb->old_list, addr); -	if (!link) -		return -1; - -	if (tb[WPROBE_ATTR_FLAGS]) -		link->flags = nla_get_u32(tb[WPROBE_ATTR_FLAGS]); - -	list_add_tail(&link->list, cb->list); -	return 0; -} - - -int -wprobe_update_links(struct wprobe_iface *dev) -{ -	struct wprobe_link *l, *tmp; -	struct nl_msg *msg; -	struct wprobe_save_cb cb; -	int err; - -	INIT_LIST_HEAD(&cb.old_list); -	list_splice_init(&dev->links, &cb.old_list); -	cb.list = &dev->links; - -	msg = wprobe_new_msg(dev->ifname, WPROBE_CMD_GET_LINKS, true); -	if (!msg) -		return -ENOMEM; - -	err = wprobe_send_msg(msg, save_link_handler, &cb); -	if (err < 0) -		return err; - -	list_for_each_entry_safe(l, tmp, &cb.old_list, list) { -		list_del(&l->list); -		free(l); -	} - -	return 0; -} - -int -wprobe_apply_config(struct wprobe_iface *dev) -{ -	struct nl_msg *msg; - -	msg = wprobe_new_msg(dev->ifname, WPROBE_CMD_CONFIG, false); -	if (!msg) -		return -ENOMEM; - -	if (dev->interval >= 0) -		NLA_PUT_MSECS(msg, WPROBE_ATTR_INTERVAL, dev->interval); - -	wprobe_send_msg(msg, NULL, NULL); -	return 0; - -nla_put_failure: -	nlmsg_free(msg); -	return -ENOMEM; -} - -int -wprobe_measure(struct wprobe_iface *dev) -{ -	struct nl_msg *msg; - -	msg = wprobe_new_msg(dev->ifname, WPROBE_CMD_MEASURE, false); -	if (!msg) -		return -ENOMEM; - -	wprobe_send_msg(msg, NULL, NULL); -	return 0; -} - -struct wprobe_request_cb { -	struct list_head *list; -	struct list_head old_list; -	char *addr; -}; - -static int -save_attrdata_handler(struct nl_msg *msg, void *arg) -{ -	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); -	struct wprobe_request_cb *cb = arg; -	struct wprobe_attribute *attr; -	int type, id; - -	nla_parse(tb, WPROBE_ATTR_LAST, genlmsg_attrdata(gnlh, 0), -			genlmsg_attrlen(gnlh, 0), attribute_policy); - -	if (!tb[WPROBE_ATTR_ID]) -		return -1; - -	if (!tb[WPROBE_ATTR_TYPE]) -		return -1; - -	id = nla_get_u32(tb[WPROBE_ATTR_ID]); -	list_for_each_entry(attr, &cb->old_list, list) { -		if (attr->id == id) -			goto found; -	} -	/* not found */ -	return -1; - -found: -	list_del_init(&attr->list); - -	type = nla_get_u8(tb[WPROBE_ATTR_TYPE]); -	if (type != attr->type) { -		DPRINTF("WARNING: type mismatch for %s attribute '%s' (%d != %d)\n", -			(cb->addr ? "link" : "global"), -			attr->name, -			type, attr->type); -		goto out; -	} - -	if ((type < WPROBE_VAL_STRING) || -		(type > WPROBE_VAL_U64)) -		goto out; - -	memset(&attr->val, 0, sizeof(attr->val)); - -#define HANDLE_INT_TYPE(_idx, _type) \ -	case WPROBE_VAL_S##_type: \ -	case WPROBE_VAL_U##_type: \ -		attr->val.U##_type = nla_get_u##_type(tb[_idx]); \ -		break - -	switch(type) { -		HANDLE_INT_TYPE(type, 8); -		HANDLE_INT_TYPE(type, 16); -		HANDLE_INT_TYPE(type, 32); -		HANDLE_INT_TYPE(type, 64); -		case WPROBE_VAL_STRING: -			/* unimplemented */ -			break; -	} -#undef HANDLE_TYPE - -	if (attr->flags & WPROBE_F_KEEPSTAT) { -		if (tb[WPROBE_VAL_SUM]) -			attr->val.s = nla_get_u64(tb[WPROBE_VAL_SUM]); - -		if (tb[WPROBE_VAL_SUM_SQ]) -			attr->val.ss = nla_get_u64(tb[WPROBE_VAL_SUM_SQ]); - -		if (tb[WPROBE_VAL_SAMPLES]) -			attr->val.n = nla_get_u32(tb[WPROBE_VAL_SAMPLES]); - -		if (attr->val.n > 0) { -			float avg = ((float) attr->val.s) / attr->val.n; -			float stdev = sqrt((((float) attr->val.ss) / attr->val.n) - (avg * avg)); -			if (isnan(stdev)) -				stdev = 0.0f; -			if (isnan(avg)) -				avg = 0.0f; -			attr->val.avg = avg; -			attr->val.stdev = stdev; -		} -	} - -out: -	list_add_tail(&attr->list, cb->list); -	return 0; -} - - -int -wprobe_request_data(struct wprobe_iface *dev, const unsigned char *addr) -{ -	struct wprobe_request_cb cb; -	struct list_head *attrs; -	struct nl_msg *msg; -	int err; - -	msg = wprobe_new_msg(dev->ifname, WPROBE_CMD_GET_INFO, true); -	if (!msg) -		return -ENOMEM; - -	if (addr) { -		attrs = &dev->link_attr; -		NLA_PUT(msg, WPROBE_ATTR_MAC, 6, addr); -	} else { -		attrs = &dev->global_attr; -	} - -	INIT_LIST_HEAD(&cb.old_list); -	list_splice_init(attrs, &cb.old_list); -	cb.list = attrs; - -	err = wprobe_send_msg(msg, save_attrdata_handler, &cb); -	list_splice(&cb.old_list, attrs->prev); -	return err; - -nla_put_failure: -	nlmsg_free(msg); -	return -ENOMEM; -} - - diff --git a/package/wprobe/src/user/wprobe.h b/package/wprobe/src/user/wprobe.h index c0b4902f3..706facc80 100644 --- a/package/wprobe/src/user/wprobe.h +++ b/package/wprobe/src/user/wprobe.h @@ -87,8 +87,19 @@ struct wprobe_link {  	unsigned char addr[6];  }; +struct wprobe_filter_item { +	char name[32]; +	uint64_t rx; +	uint64_t tx; +}; + +struct wprobe_iface_ops;  struct wprobe_iface { +	const struct wprobe_iface_ops *ops; + +	int sockfd;  	const char *ifname; +	unsigned int genl_family;  	char addr[6];  	struct list_head global_attr; @@ -101,11 +112,23 @@ struct wprobe_iface {  	int scale_max;  	int scale_m;  	int scale_d; + +	/* filter */ +	void *filter; + +	/* filter_len: +	 *   set to -1 to drop the current filter +	 *   automatically reset to 0 after config apply +	 */ +	int filter_len;  }; +typedef void (*wprobe_filter_cb)(void *arg, const char *group, struct wprobe_filter_item *items, int n_items); +extern int wprobe_port; +  /**   * wprobe_update_links: get a list of all link partners - * @ifname: name of the wprobe interface + * @dev: wprobe device structure   * @list: linked list for storing link descriptions   *   * when wprobe_update_links is called multiple times, the linked list  @@ -114,8 +137,16 @@ struct wprobe_iface {  extern int wprobe_update_links(struct wprobe_iface *dev);  /** + * wprobe_dump_filters: dump all layer 2 filter counters + * @dev: wprobe device structure + * @cb: callback (called once per filter group) + * @arg: user argument for the callback + */ +extern int wprobe_dump_filters(struct wprobe_iface *dev, wprobe_filter_cb cb, void *arg); + +/**   * wprobe_measure: start a measurement request for all global attributes - * @ifname: name of the wprobe interface + * @dev: wprobe device structure   *   * not all attributes are automatically filled with data, since for some   * it may be desirable to control the sampling interval from user space @@ -124,7 +155,7 @@ extern int wprobe_update_links(struct wprobe_iface *dev);  extern int wprobe_measure(struct wprobe_iface *dev);  /** - * wprobe_get_dev: get device information + * wprobe_get_dev: get a handle to a local wprobe device   * @ifname: name of the wprobe interface   *   * queries the wprobe interface for all attributes @@ -133,6 +164,12 @@ extern int wprobe_measure(struct wprobe_iface *dev);  extern struct wprobe_iface *wprobe_get_dev(const char *ifname);  /** + * wprobe_get_auto: get a handle to a local or remote wprobe device + * @arg: pointer to the wprobe device, either <dev> (local) or <host>:<dev> (remote) + */ +extern struct wprobe_iface *wprobe_get_auto(const char *arg, char **err); + +/**   * wprobe_get_dev: free all device information   * @dev: wprobe device structure   */ @@ -156,4 +193,21 @@ extern int wprobe_apply_config(struct wprobe_iface *dev);   */  extern int wprobe_request_data(struct wprobe_iface *dev, const unsigned char *addr); +/** + * wprobe_server_init: send a wprobe server init message to a server's client socket + * @socket: socket of the connection to the client + */ +extern int wprobe_server_init(int socket); + +/** + * wprobe_server_handle: read a request from the client socket, process it, send the response + * @socket: socket of the connection to the client + */ +extern int wprobe_server_handle(int socket); + +/** + * wprobe_server_done: release memory allocated for the server connection + */ +extern void wprobe_server_done(void); +  #endif | 
