All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC] cfg80211 and nl80211
@ 2006-09-28  9:23 Johannes Berg
  2006-09-29 21:10 ` James Ketrenos
                   ` (2 more replies)
  0 siblings, 3 replies; 16+ messages in thread
From: Johannes Berg @ 2006-09-28  9:23 UTC (permalink / raw)
  To: netdev; +Cc: Jiri Benc, John W. Linville, Larry Finger

This patch adds cfg80211, a new configuration system for wireless hardware
as well as nl80211, the netlink-based userspace interface for it.

It currently features a bunch of configuration requests, support for
adding and removing virtual interfaces, the ability to inject packets and
more.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
This is the

  I think nl80211 is the future if I don't have to do it all by myself
    -- johill

release. Please take the time to read at least the stuff before the patch.

There should be support for notifications, but that isn't currently done
as I'm still thinking about how to do it best.

It probably still requires the patches in
http://marc.theaimsgroup.com/?l=linux-netdev&m=115625436628696&w=2
and
http://marc.theaimsgroup.com/?l=linux-netdev&m=115625168405439&w=2

(the latter doesn't apply cleanly against wireless-dev, but you can
safely ignore the pieces that don't, at least for wireless testing :) )

It also requires the NLA_PUT_FLAG patch I did:
http://marc.theaimsgroup.com/?l=linux-netdev&m=115650333420169&w=2

(obviously, all those patches aren't required any more if they have been
applied, but I've been lazy and not tracked which are and which aren't)

I've removed the use of NLA_CUSTOM_CHECK as it isn't going to be applied,
but that currently results in a warning about an unused static method.

Also new in this iteration is a lot more fleshed-out internal cfg80211
interface. Also new is the following paragraph ;)

Here's an explanation of what cfg80211 and nl80211 are. cfg80211 is
the interface between the configuration backend and the driver or stack
("users" of cfg80211). cfg80211 provides a wiphy index whenever a user
registers with it, and allows things to be enumerated etc. nl80211 is
currently the only interface between cfg80211 and userspace and will
most likely remain the primary interface forever, but it is possible
to add (for example) a configfs interface between userspace and cfg80211,
users of cfg80211 can remain blissfully unaware of this. Also, WExt
backward compatibility will be implemented as an interface between
userspace and cfg80211.

cfg80211 does, however, use some constants defined in nl80211 in order
to not define them twice (and the other way around isn't quite good
because they need to be visible in userspace and cfg80211 is not).

I'm still not sure if there should be an explicit 'device supports
configuration parameters x, y and z' or if userspace should just
grab the current configuration (which is supposed to be complete then)
and see what's in it. Along the same lines, should unsupported
parameters result in the rejection, or should they be ignored?
(currently they are ignored)

As mentioned to Larry, I'd like to integrate his great work on
regulation and use nl80211 as the userspace interface for it. I do
have a couple of questions wrt. that:

 * why should there be configuration per device? The user can only
   be operating in one country at a time... I think that information
   should just be available inside cfg80211 in a global structure
   for use by drivers whenever they need it (with some accessor
   methods to ensure locked access).

 * as far as I understood the communication is the kernel telling
   the daemon all the information it has (which may be none, the
   country from a broadcasting AP or more info from that) and the
   daemon then builds up a correct set of limitations and gives that
   to the kernel, without the daemon the kernel limits to some minimal
   set that is (likely) legal everywhere. Correct?

 * Should the userspace daemon be allowed to unilaterally update the
   regulatory information if it learns something new (via the user)?
   Or why not even just publish the regulatory information APs might
   broadcast in the scan results, and let the userspace daemon pick
   that apart? Then the kernel need not ask for anything at all...

 * I seem to have read between the lines that the EEPROM data is
   pretty much useless. Is that generally true, or should the userspace
   daemon be told what it contains (somehow)?

 * Should the kernel perform some kind of validation on the regulatory
   data the daemon gives it as well?

Right now I'd think that it would make sense to just leave the whole
task to our userspace daemon, iow. nl80211 just provides a command
to update the kernel's knowledge about regulory and the daemon periodically
checks the scan results for country information, asks the user for
the country, or similar. If it's not running, the kernel simply starts
from a generic no-frills set.

--- wireless-dev.orig/net/Kconfig	2006-09-27 19:10:21.384088173 +0200
+++ wireless-dev/net/Kconfig	2006-09-27 19:14:44.214088173 +0200
@@ -250,6 +250,9 @@ source "net/ieee80211/Kconfig"
 config WIRELESS_EXT
 	bool
 
+config CFG80211
+	tristate
+
 endif   # if NET
 endmenu # Networking
 
--- wireless-dev.orig/net/Makefile	2006-09-27 19:10:21.414088173 +0200
+++ wireless-dev/net/Makefile	2006-09-27 19:14:44.214088173 +0200
@@ -44,6 +44,7 @@ obj-$(CONFIG_ECONET)		+= econet/
 obj-$(CONFIG_VLAN_8021Q)	+= 8021q/
 obj-$(CONFIG_IP_DCCP)		+= dccp/
 obj-$(CONFIG_IP_SCTP)		+= sctp/
+obj-$(CONFIG_CFG80211)		+= wireless/
 obj-$(CONFIG_D80211)		+= d80211/
 obj-$(CONFIG_IEEE80211)		+= ieee80211/
 obj-$(CONFIG_TIPC)		+= tipc/
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ wireless-dev/net/wireless/Makefile	2006-09-27 19:14:44.214088173 +0200
@@ -0,0 +1,4 @@
+obj-$(CONFIG_CFG80211) += cfg80211.o
+
+cfg80211-objs := \
+	core.o nl80211.o
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ wireless-dev/net/wireless/nl80211.c	2006-09-28 01:27:06.094150162 +0200
@@ -0,0 +1,1048 @@
+/*
+ * This is the new netlink-based wireless configuration interface.
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+#include <linux/if.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <net/genetlink.h>
+#include <net/cfg80211.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/if_ether.h>
+#include "core.h"
+#include "nl80211.h"
+
+/* the netlink family */
+static struct genl_family nl80211_fam = {
+	.id = GENL_ID_GENERATE,	/* don't bother with a hardcoded ID */
+	.name = "nl80211",	/* have users key off the name instead */
+	.hdrsize = 0,		/* no private header */
+	.version = 1,		/* no particular meaning now */
+	.maxattr = NL80211_ATTR_MAX,
+};
+
+/* internal helper: validate an information element attribute */
+static int check_information_element(struct nlattr *nla)
+{
+	int len = nla_len(nla);
+	u8 *data = nla_data(nla);
+	int elementlen;
+
+	while (len >= 2) {
+		/* 1 byte ID, 1 byte len, `len' bytes data */
+		elementlen = *(data+1) + 2;
+		data += elementlen;
+		len -= elementlen;
+	}
+	return len ? -EINVAL : 0;
+}
+
+/* internal helper: get drv and dev */
+static int get_drv_dev_by_info_ifindex(struct genl_info *info,
+				       struct cfg80211_registered_driver **drv,
+				       struct net_device **dev)
+{
+	int ifindex;
+
+	if (!info->attrs[NL80211_ATTR_IFINDEX])
+		return -EINVAL;
+
+	ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
+	*dev = dev_get_by_index(ifindex);
+	if (!dev)
+		return -ENODEV;
+
+	*drv = cfg80211_get_drv_from_ifindex(ifindex);
+	if (IS_ERR(*drv)) {
+		dev_put(*dev);
+		return PTR_ERR(*drv);
+	}
+
+	return 0;
+}
+
+/* policy for the attributes */
+static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
+	[NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
+	[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
+	[NL80211_ATTR_FLAGS] = { .type = NLA_U32 },
+	[NL80211_ATTR_QUEUE] = { .type = NLA_U32 },
+	[NL80211_ATTR_FRAME] = { .type = NLA_STRING,
+				 .len = NL80211_MAX_FRAME_LEN },
+	[NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
+	[NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
+	[NL80211_ATTR_NETWORK_ID] = { .type = NLA_U16 },
+	[NL80211_ATTR_CHANNEL] = { .type = NLA_U32 },
+	[NL80211_ATTR_RX_SENSITIVITY] = { .type = NLA_U32 },
+	[NL80211_ATTR_BSSID] = { .len = ETH_ALEN },
+	[NL80211_ATTR_SSID] = { .type = NLA_NUL_STRING, .len = 32 },
+	[NL80211_ATTR_TRANSMIT_POWER] = { .type = NLA_U32 },
+	[NL80211_ATTR_FRAG_THRESHOLD] = { .type = NLA_U32 },
+	[NL80211_ATTR_INFORMATION_ELEMENT] = { .type = NLA_STRING,
+					       .len = NL80211_MAX_IE_LEN },
+	[NL80211_ATTR_ROAMING_CONTROL] = { .type = NLA_U32 },
+	[NL80211_ATTR_SCAN_TYPE] = { .type = NLA_U32 },
+};
+
+/* netlink command implementations */
+
+#define CHECK_CMD(ptr, cmd)				\
+	if (drv->ops->ptr)				\
+		NLA_PUT_FLAG(msg, NL80211_CMD_##cmd);
+
+static int nl80211_get_cmdlist(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	struct sk_buff *msg;
+	void *hdr;
+	int err;
+	struct nlattr *start;
+
+	drv = cfg80211_get_drv_from_info(info);
+	if (IS_ERR(drv))
+		return PTR_ERR(drv);
+
+	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+			     NL80211_CMD_NEW_CMDLIST);
+	if (IS_ERR(hdr)) {
+		err = PTR_ERR(hdr);
+		goto put_drv;
+	}
+
+	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->wiphy);
+
+	start = nla_nest_start(msg, NL80211_ATTR_CMDS);
+	if (!start)
+		goto nla_put_failure;
+
+	/* unconditionally allow some common commands we handle centrally */
+	NLA_PUT_FLAG(msg, NL80211_CMD_GET_CMDLIST);
+	NLA_PUT_FLAG(msg, NL80211_CMD_GET_WIPHYS);
+	NLA_PUT_FLAG(msg, NL80211_CMD_GET_INTERFACES);
+
+	CHECK_CMD(list_interfaces, GET_INTERFACES);
+	CHECK_CMD(inject_packet, INJECT);
+	CHECK_CMD(add_virtual_intf, ADD_VIRTUAL_INTERFACE);
+	CHECK_CMD(del_virtual_intf, DEL_VIRTUAL_INTERFACE);
+	CHECK_CMD(configure, CONFIGURE);
+	CHECK_CMD(get_config, GET_CONFIG);
+	CHECK_CMD(reassociate, REASSOCIATE);
+	CHECK_CMD(disassociate, DISASSOCIATE);
+	CHECK_CMD(deauth, DEAUTH);
+	CHECK_CMD(initiate_scan, INITIATE_SCAN);
+	CHECK_CMD(set_roaming, SET_ROAMING_CONTROL);
+	CHECK_CMD(get_roaming, GET_ROAMING_CONTROL);
+	CHECK_CMD(set_fixed_bssid, SET_FIXED_BSSID);
+	CHECK_CMD(get_fixed_bssid, GET_FIXED_BSSID);
+	CHECK_CMD(get_association, GET_ASSOCIATION);
+	CHECK_CMD(get_auth_list, GET_AUTH_LIST);
+
+	nla_nest_end(msg, start);
+
+	genlmsg_end(msg, hdr);
+
+	err = genlmsg_unicast(msg, info->snd_pid);
+	goto put_drv;
+
+ nla_put_failure:
+ 	err = -ENOBUFS;
+	nlmsg_free(msg);
+ put_drv:
+	cfg80211_put_drv(drv);
+	return err;
+}
+#undef CHECK_CMD
+
+static int nl80211_get_wiphys(struct sk_buff *skb, struct genl_info *info)
+{
+	struct sk_buff *msg;
+	void *hdr;
+	struct nlattr *start, *indexstart;
+	struct cfg80211_registered_driver *drv;
+	int idx = 1;
+
+	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+			     NL80211_CMD_NEW_WIPHYS);
+	if (IS_ERR(hdr))
+		return PTR_ERR(hdr);
+
+	start = nla_nest_start(msg, NL80211_ATTR_WIPHY_LIST);
+	if (!start)
+		goto nla_outer_nest_failure;
+
+	mutex_lock(&cfg80211_drv_mutex);
+	list_for_each_entry(drv, &cfg80211_drv_list, list) {
+		indexstart = nla_nest_start(msg, idx++);
+		if (!indexstart)
+			goto nla_put_failure;
+		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->wiphy);
+		nla_nest_end(msg, indexstart);
+	}
+	mutex_unlock(&cfg80211_drv_mutex);
+
+	nla_nest_end(msg, start);
+
+	genlmsg_end(msg, hdr);
+
+	return genlmsg_unicast(msg, info->snd_pid);
+
+ nla_put_failure:
+	mutex_unlock(&cfg80211_drv_mutex);
+ nla_outer_nest_failure:
+	nlmsg_free(msg);
+	return -ENOBUFS;
+}
+
+struct add_cb_data {
+	int idx;
+	struct sk_buff *skb;
+};
+
+static int addifidx(void *data, int ifidx)
+{
+	struct add_cb_data *cb = data;
+	struct net_device *dev = dev_get_by_index(ifidx);
+	int err = -ENOBUFS;
+	struct nlattr *start;
+
+	/* not that this can happen, since the caller
+	 * should hold the device open... */
+	if (!dev)
+		return -ENODEV;
+
+	start = nla_nest_start(cb->skb, cb->idx++);
+	if (!start)
+		goto nla_put_failure;
+
+	NLA_PUT_U32(cb->skb, NL80211_ATTR_IFINDEX, ifidx);
+	NLA_PUT_STRING(cb->skb, NL80211_ATTR_IFNAME, dev->name);
+
+	nla_nest_end(cb->skb, start);
+	err = 0;
+
+ nla_put_failure:
+	dev_put(dev);
+	return err;
+}
+
+static int nl80211_get_intfs(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	struct sk_buff *msg;
+	void *hdr;
+	int err;
+	struct nlattr *start;
+	struct add_cb_data cb;
+
+	drv = cfg80211_get_drv_from_info(info);
+	if (IS_ERR(drv))
+		return PTR_ERR(drv);
+
+	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+			     NL80211_CMD_NEW_INTERFACES);
+	if (IS_ERR(hdr)) {
+		err = PTR_ERR(hdr);
+		goto put_drv;
+	}
+
+	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->wiphy);
+
+	start = nla_nest_start(msg, NL80211_ATTR_INTERFACE_LIST);
+	if (!start) {
+		err = -ENOBUFS;
+		goto msg_free;
+	}
+
+	cb.skb = msg;
+	cb.idx = 1;
+	err = drv->ops->list_interfaces(drv->priv, &cb, addifidx);
+	if (err)
+		goto msg_free;
+
+	nla_nest_end(msg, start);
+
+	genlmsg_end(msg, hdr);
+
+	err = genlmsg_unicast(msg, info->snd_pid);
+	goto put_drv;
+
+ nla_put_failure:
+	err = -ENOBUFS;
+ msg_free:
+	nlmsg_free(msg);
+ put_drv:
+	cfg80211_put_drv(drv);
+	return err;
+}
+
+static int nl80211_do_inject(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	u32 flags = 0;
+	int err, queue = -1;
+
+	if (!info->attrs[NL80211_ATTR_FRAME])
+		return -EINVAL;
+	if (info->attrs[NL80211_ATTR_FLAGS])
+		flags = nla_get_u32(info->attrs[NL80211_ATTR_FLAGS]);
+	if (info->attrs[NL80211_ATTR_QUEUE])
+		queue = (int) nla_get_u32(info->attrs[NL80211_ATTR_QUEUE]);
+
+	drv = cfg80211_get_drv_from_info(info);
+	if (IS_ERR(drv))
+		return PTR_ERR(drv);
+
+	if (!drv->ops->inject_packet) {
+		err = -ENOSYS;
+		goto unlock;
+	}
+
+	err = drv->ops->inject_packet(drv->priv,
+		nla_data(info->attrs[NL80211_ATTR_FRAME]),
+		nla_len(info->attrs[NL80211_ATTR_FRAME]),
+		flags,
+		queue);
+ unlock:
+	cfg80211_put_drv(drv);
+	return err;
+}
+
+static int nl80211_add_virt_intf(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	unsigned int type = NL80211_IFTYPE_UNSPECIFIED;
+
+	if (!info->attrs[NL80211_ATTR_IFNAME])
+		return -EINVAL;
+
+	if (info->attrs[NL80211_ATTR_IFTYPE]) {
+		type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
+		if (type > NL80211_IFTYPE_MAX)
+			return -EINVAL;
+	}
+
+	drv = cfg80211_get_drv_from_info(info);
+	if (IS_ERR(drv))
+		return PTR_ERR(drv);
+
+	if (!drv->ops->add_virtual_intf) {
+		err = -ENOSYS;
+		goto unlock;
+	}
+
+	err = drv->ops->add_virtual_intf(drv->priv,
+		nla_data(info->attrs[NL80211_ATTR_IFNAME]), type);
+
+ unlock:
+	cfg80211_put_drv(drv);
+	return err;
+}
+
+static int nl80211_del_virt_intf(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int ifindex, err;
+	struct net_device *dev;
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+	ifindex = dev->ifindex;
+	dev_put(dev);
+
+	if (!drv->ops->del_virtual_intf) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	err = drv->ops->del_virtual_intf(drv->priv, ifindex);
+
+ out:
+	cfg80211_put_drv(drv);
+	return err;
+}
+
+static int nl80211_configure(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct net_device *dev;
+	struct cfg80211_config config;
+	struct nlattr *attr;
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	if (!drv->ops->configure) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	memset(&config, 0, sizeof(config));
+
+	attr = info->attrs[NL80211_ATTR_SSID];
+	if (attr)
+		config.ssid = nla_data(attr);
+
+	attr = info->attrs[NL80211_ATTR_NETWORK_ID];
+	if (attr) {
+		config.valid |= CFG80211_CFG_VALID_NWID;
+		config.network_id = nla_get_u16(attr);
+	}
+
+	attr = info->attrs[NL80211_ATTR_RX_SENSITIVITY];
+	if (attr) {
+		config.valid |= CFG80211_CFG_VALID_RX_SENSITIVITY;
+		config.rx_sensitivity = (s32) nla_get_u32(attr);
+	}
+
+	attr = info->attrs[NL80211_ATTR_TRANSMIT_POWER];
+	if (attr) {
+		config.valid |= CFG80211_CFG_VALID_TRANSMIT_POWER;
+		config.transmit_power = nla_get_u32(attr);
+	}
+
+	attr = info->attrs[NL80211_ATTR_FRAG_THRESHOLD];
+	if (attr) {
+		config.valid |= CFG80211_CFG_VALID_FRAG_THRESHOLD;
+		config.fragmentation_threshold = nla_get_u32(attr);
+	}
+
+	attr = info->attrs[NL80211_ATTR_CHANNEL];
+	if (attr) {
+		config.valid |= CFG80211_CFG_VALID_CHANNEL;
+		config.channel = nla_get_u32(attr);
+	}
+
+	err = drv->ops->configure(drv->priv, dev, &config);
+ out:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static int nl80211_get_config(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct net_device *dev;
+	struct cfg80211_config config;
+	struct sk_buff *msg;
+	void *hdr;
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	if (!drv->ops->get_config) {
+		err = -EOPNOTSUPP;
+		goto out_put_drv;
+	}
+
+	memset(&config, 0, sizeof(config));
+
+	drv->ops->get_config(drv->priv, dev, &config);
+
+	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+			     NL80211_CMD_NEW_CONFIG);
+
+	if (IS_ERR(hdr)) {
+		err = PTR_ERR(hdr);
+		goto out_put_drv;
+	}
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+
+	if (config.ssid)
+		NLA_PUT_STRING(msg, NL80211_ATTR_SSID, config.ssid);
+
+	if (config.valid & CFG80211_CFG_VALID_NWID)
+		NLA_PUT_U16(msg, NL80211_ATTR_NETWORK_ID, config.network_id);
+
+	if (config.valid & CFG80211_CFG_VALID_RX_SENSITIVITY)
+		NLA_PUT_U32(msg, NL80211_ATTR_RX_SENSITIVITY, (u32)config.rx_sensitivity);
+
+	if (config.valid & CFG80211_CFG_VALID_TRANSMIT_POWER)
+		NLA_PUT_U32(msg, NL80211_ATTR_TRANSMIT_POWER, config.transmit_power);
+
+	if (config.valid & CFG80211_CFG_VALID_FRAG_THRESHOLD)
+		NLA_PUT_U32(msg, NL80211_ATTR_FRAG_THRESHOLD, config.fragmentation_threshold);
+
+	if (config.valid & CFG80211_CFG_VALID_CHANNEL)
+		NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL, config.channel);
+
+	genlmsg_end(msg, hdr);
+	err = genlmsg_unicast(msg, info->snd_pid);
+	goto out_put_drv;
+
+ nla_put_failure:
+	err = -ENOBUFS;
+	nlmsg_free(msg);
+ out_put_drv:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static int nl80211_set_roaming(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct net_device *dev;
+	int roaming_control;
+
+	if (!info->attrs[NL80211_ATTR_ROAMING_CONTROL])
+		return -EINVAL;
+	roaming_control = nla_get_u32(info->attrs[NL80211_ATTR_ROAMING_CONTROL]);
+
+	if (roaming_control > NL80211_ROAMING_CONTROL_MAX)
+		return -EINVAL;
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	if (!drv->ops->set_roaming) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	err = drv->ops->set_roaming(drv->priv, dev, roaming_control);
+ out:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static int nl80211_get_roaming(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct net_device *dev;
+	struct sk_buff *msg;
+	void *hdr;
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	if (!drv->ops->get_roaming) {
+		err = -EOPNOTSUPP;
+		goto out_put_drv;
+	}
+
+	err = drv->ops->get_roaming(drv->priv, dev);
+	if (err < 0)
+		goto out_put_drv;
+
+	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+			     NL80211_CMD_ROAMING_CONTROL);
+
+	if (IS_ERR(hdr)) {
+		err = PTR_ERR(hdr);
+		goto out_put_drv;
+	}
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+	NLA_PUT_U32(msg, NL80211_ATTR_ROAMING_CONTROL, err);
+
+	genlmsg_end(msg, hdr);
+	err = genlmsg_unicast(msg, info->snd_pid);
+	goto out_put_drv;
+
+ nla_put_failure:
+	err = -ENOBUFS;
+	nlmsg_free(msg);
+ out_put_drv:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static int nl80211_set_fixed_bssid(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct net_device *dev;
+	u8 *bssid;
+
+	if (!info->attrs[NL80211_ATTR_BSSID])
+		return -EINVAL;
+	bssid = nla_data(info->attrs[NL80211_ATTR_BSSID]);
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	if (!drv->ops->set_fixed_bssid) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	err = drv->ops->set_fixed_bssid(drv->priv, dev, bssid);
+ out:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static int nl80211_get_fixed_bssid(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct net_device *dev;
+	struct sk_buff *msg;
+	void *hdr;
+	u8 bssid[ETH_ALEN];
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	if (!drv->ops->get_fixed_bssid) {
+		err = -EOPNOTSUPP;
+		goto out_put_drv;
+	}
+
+	err = drv->ops->get_fixed_bssid(drv->priv, dev, bssid);
+	if (err < 0)
+		goto out_put_drv;
+
+	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+			     NL80211_CMD_FIXED_BSSID);
+
+	if (IS_ERR(hdr)) {
+		err = PTR_ERR(hdr);
+		goto out_put_drv;
+	}
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+	NLA_PUT(msg, NL80211_ATTR_BSSID, ETH_ALEN, bssid);
+
+	genlmsg_end(msg, hdr);
+	err = genlmsg_unicast(msg, info->snd_pid);
+	goto out_put_drv;
+
+ nla_put_failure:
+	err = -ENOBUFS;
+	nlmsg_free(msg);
+ out_put_drv:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static int nl80211_get_association(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct net_device *dev;
+	struct sk_buff *msg;
+	void *hdr;
+	u8 bssid[ETH_ALEN];
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	if (!drv->ops->get_association) {
+		err = -EOPNOTSUPP;
+		goto out_put_drv;
+	}
+
+	err = drv->ops->get_association(drv->priv, dev, bssid);
+	if (err < 0)
+		goto out_put_drv;
+
+	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+			     NL80211_CMD_ASSOCIATION_CHANGED);
+
+	if (IS_ERR(hdr)) {
+		err = PTR_ERR(hdr);
+		goto out_put_drv;
+	}
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+	if (err == 1)
+		NLA_PUT(msg, NL80211_ATTR_BSSID, ETH_ALEN, bssid);
+
+	genlmsg_end(msg, hdr);
+	err = genlmsg_unicast(msg, info->snd_pid);
+	goto out_put_drv;
+
+ nla_put_failure:
+	err = -ENOBUFS;
+	nlmsg_free(msg);
+ out_put_drv:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static int nl80211_assoc_deauth(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct net_device *dev;
+	int (*act)(void *priv, struct net_device *dev);
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	switch (info->genlhdr->cmd) {
+	case NL80211_CMD_DISASSOCIATE:
+		act = drv->ops->disassociate;
+		break;
+	case NL80211_CMD_REASSOCIATE:
+		act = drv->ops->reassociate;
+		break;
+	case NL80211_CMD_DEAUTH:
+		act = drv->ops->deauth;
+		break;
+	default:
+		act = NULL;
+	}
+
+	if (!act) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	err = act(drv->priv, dev);
+ out:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static int add_bssid(void *data, u8 *bssid)
+{
+	struct add_cb_data *cb = data;
+	int err = -ENOBUFS;
+	struct nlattr *start;
+
+	start = nla_nest_start(cb->skb, cb->idx++);
+	if (!start)
+		goto nla_put_failure;
+
+	NLA_PUT(cb->skb, NL80211_ATTR_BSSID, ETH_ALEN, bssid);
+
+	nla_nest_end(cb->skb, start);
+	err = 0;
+
+ nla_put_failure:
+	return err;
+}
+
+static int nl80211_get_auth_list(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	struct net_device *dev;
+	struct sk_buff *msg;
+	void *hdr;
+	int err;
+	struct nlattr *start;
+	struct add_cb_data cb;
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	if (!drv->ops->get_auth_list) {
+		err = -EOPNOTSUPP;
+		goto put_drv;
+	}
+
+	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
+			     NL80211_CMD_AUTH_LIST);
+	if (IS_ERR(hdr)) {
+		err = PTR_ERR(hdr);
+		goto put_drv;
+	}
+
+	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
+
+	start = nla_nest_start(msg, NL80211_ATTR_BSSID_LIST);
+	if (!start) {
+		err = -ENOBUFS;
+		goto msg_free;
+	}
+
+	cb.skb = msg;
+	cb.idx = 1;
+	err = drv->ops->get_auth_list(drv->priv, dev, &cb, add_bssid);
+	if (err)
+		goto msg_free;
+
+	nla_nest_end(msg, start);
+
+	genlmsg_end(msg, hdr);
+
+	err = genlmsg_unicast(msg, info->snd_pid);
+	goto put_drv;
+
+ nla_put_failure:
+	err = -ENOBUFS;
+ msg_free:
+	nlmsg_free(msg);
+ put_drv:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static int nl80211_initiate_scan(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+	int err;
+	struct net_device *dev;
+	struct scan_params params;
+	struct scan_channel *channels = NULL;
+	int count = -1;
+
+	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
+	if (err)
+		return err;
+
+	if (!drv->ops->initiate_scan) {
+		err = -EOPNOTSUPP;
+		goto out;
+	}
+
+	params.active = 0;
+
+	if (info->attrs[NL80211_ATTR_FLAGS])
+		params.active = !!(nla_get_u32(info->attrs[NL80211_ATTR_FLAGS])
+					& NL80211_FLAG_SCAN_ACTIVELY);
+
+	if (info->attrs[NL80211_ATTR_CHANNEL_LIST]) {
+		struct nlattr *attr = info->attrs[NL80211_ATTR_CHANNEL_LIST];
+		struct nlattr *nla;
+		int rem;
+		struct nlattr **tb;
+
+		/* let's count first */
+		count = 0;
+		nla_for_each_attr(nla, nla_data(attr), nla_len(attr), rem)
+			count++;
+
+		if (count == 0) {
+			/* assume we should actually scan all channels,
+			 * scanning no channels make no sense */
+			count = -1;
+			goto done_channels;
+		}
+
+		channels = kmalloc(count * sizeof(struct scan_channel),
+				   GFP_KERNEL);
+		tb = kmalloc((NL80211_ATTR_MAX+1) * sizeof(struct nlattr),
+			     GFP_KERNEL);
+
+		count = 0;
+		nla_for_each_attr(nla, nla_data(attr), nla_len(attr), rem) {
+			err = nla_parse(tb, NL80211_ATTR_MAX, nla_data(nla),
+					nla_len(nla), nl80211_policy);
+			if (err || !tb[NL80211_ATTR_CHANNEL]) {
+				err = -EINVAL;
+				kfree(tb);
+				kfree(channels);
+				goto out;
+			}
+			channels[count].channel =
+				nla_get_u32(tb[NL80211_ATTR_CHANNEL]);
+
+			channels[count].active = params.active;
+
+			if (tb[NL80211_ATTR_FLAGS])
+				channels[count].active =
+					!!(nla_get_u32(tb[NL80211_ATTR_FLAGS])
+						& NL80211_FLAG_SCAN_ACTIVELY);
+			count++;
+		}
+		kfree(tb);
+	}
+
+ done_channels:
+	params.channels = channels;
+	params.n_channels = count;
+
+	err = drv->ops->initiate_scan(drv->priv, dev, &params);
+
+	kfree(channels);
+ out:
+	cfg80211_put_drv(drv);
+	dev_put(dev);
+	return err;
+}
+
+static struct genl_ops nl80211_ops[] = {
+	{
+		.cmd = NL80211_CMD_GET_CMDLIST,
+		.doit = nl80211_get_cmdlist,
+		.policy = nl80211_policy,
+		/* can be retrieved by unprivileged users */
+	},
+	{
+		.cmd = NL80211_CMD_GET_WIPHYS,
+		.doit = nl80211_get_wiphys,
+		.policy = nl80211_policy,
+		/* can be retrieved by unprivileged users */
+	},
+	{
+		.cmd = NL80211_CMD_GET_INTERFACES,
+		.doit = nl80211_get_intfs,
+		.policy = nl80211_policy,
+		/* can be retrieved by unprivileged users */
+	},
+	{
+		.cmd = NL80211_CMD_INJECT,
+		.doit = nl80211_do_inject,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NL80211_CMD_ADD_VIRTUAL_INTERFACE,
+		.doit = nl80211_add_virt_intf,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NL80211_CMD_DEL_VIRTUAL_INTERFACE,
+		.doit = nl80211_del_virt_intf,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NL80211_CMD_CONFIGURE,
+		.doit = nl80211_configure,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NL80211_CMD_GET_CONFIG,
+		.doit = nl80211_get_config,
+		.policy = nl80211_policy,
+		/* can be retrieved by unprivileged users */
+	},
+	{
+		.cmd = NL80211_CMD_SET_ROAMING_CONTROL,
+		.doit = nl80211_set_roaming,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NL80211_CMD_GET_ROAMING_CONTROL,
+		.doit = nl80211_get_roaming,
+		.policy = nl80211_policy,
+		/* can be retrieved by unprivileged users */
+	},
+	{
+		.cmd = NL80211_CMD_SET_FIXED_BSSID,
+		.doit = nl80211_set_fixed_bssid,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NL80211_CMD_GET_FIXED_BSSID,
+		.doit = nl80211_get_fixed_bssid,
+		.policy = nl80211_policy,
+		/* can be retrieved by unprivileged users */
+	},
+	{
+		.cmd = NL80211_CMD_GET_ASSOCIATION,
+		.doit = nl80211_get_association,
+		.policy = nl80211_policy,
+		/* can be retrieved by unprivileged users */
+	},
+	{
+		.cmd = NL80211_CMD_DISASSOCIATE,
+		.doit = nl80211_assoc_deauth,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NL80211_CMD_DEAUTH,
+		.doit = nl80211_assoc_deauth,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NL80211_CMD_REASSOCIATE,
+		.doit = nl80211_assoc_deauth,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+	{
+		.cmd = NL80211_CMD_GET_AUTH_LIST,
+		.doit = nl80211_get_auth_list,
+		.policy = nl80211_policy,
+		/* can be retrieved by unprivileged users */
+	},
+	{
+		.cmd = NL80211_CMD_INITIATE_SCAN,
+		.doit = nl80211_initiate_scan,
+		.policy = nl80211_policy,
+		.flags = GENL_ADMIN_PERM,
+	},
+};
+
+
+/* exported functions */
+
+void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq, int flags, u8 cmd)
+{
+	/* since there is no private header just add the generic one */
+	return genlmsg_put(skb, pid, seq, nl80211_fam.id, 0,
+			   flags, cmd, nl80211_fam.version);
+}
+EXPORT_SYMBOL_GPL(nl80211hdr_put);
+
+void *nl80211msg_new(struct sk_buff **skb, u32 pid, u32 seq, int flags, u8 cmd)
+{
+	void *hdr;
+
+	*skb = nlmsg_new(NLMSG_GOODSIZE);
+	if (!*skb)
+		return ERR_PTR(-ENOBUFS);
+
+	hdr = nl80211hdr_put(*skb, pid, seq, flags, cmd);
+	if (!hdr) {
+		nlmsg_free(*skb);
+		return ERR_PTR(-ENOBUFS);
+	}
+
+	return hdr;
+}
+EXPORT_SYMBOL_GPL(nl80211msg_new);
+
+/* initialisation/exit functions */
+
+int nl80211_init(void)
+{
+	int err, i;
+
+	err = genl_register_family(&nl80211_fam);
+	if (err)
+		return err;
+
+	for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) {
+		err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]);
+		if (err)
+			goto err_out;
+	}
+	return 0;
+ err_out:
+ 	genl_unregister_family(&nl80211_fam);
+	return err;
+}
+
+void nl80211_exit(void)
+{
+	genl_unregister_family(&nl80211_fam);
+}
--- wireless-dev.orig/include/linux/Kbuild	2006-09-27 19:10:21.564088173 +0200
+++ wireless-dev/include/linux/Kbuild	2006-09-27 19:14:44.224088173 +0200
@@ -28,7 +28,7 @@ header-y += affs_fs.h affs_hardblocks.h 
 	sound.h stddef.h synclink.h telephony.h termios.h ticable.h	\
 	times.h tiocl.h tipc.h toshiba.h ultrasound.h un.h utime.h	\
 	utsname.h video_decoder.h video_encoder.h videotext.h vt.h	\
-	wavefront.h wireless.h xattr.h x25.h zorro_ids.h
+	wavefront.h wireless.h xattr.h x25.h zorro_ids.h nl80211.h
 
 unifdef-y += acct.h adb.h adfs_fs.h agpgart.h apm_bios.h atalk.h	\
 	atmarp.h atmdev.h atm.h atm_tcp.h audit.h auto_fs.h binfmts.h	\
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ wireless-dev/include/linux/nl80211.h	2006-09-28 00:38:55.264150162 +0200
@@ -0,0 +1,276 @@
+#ifndef __LINUX_NL80211_H
+#define __LINUX_NL80211_H
+/*
+ * 802.11 netlink interface public header
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+/* currently supported commands
+ * don't change the order or add anything inbetween, this is ABI! */
+enum {
+	/* There's no technical reason to not use command 0 but malformed
+	 * zeroed messages may have it and this catches that */
+	NL80211_CMD_UNSPEC,
+
+	/* Get supported commands by ifindex,
+	 * uses NL80211_ATTR_CMDS (output) and NL80211_ATTR_IFINDEX (input) */
+	NL80211_CMD_GET_CMDLIST,
+
+	/* Supported commands returned */
+	NL80211_CMD_NEW_CMDLIST,
+
+	/* Inject a frame using NL80211_ATTR_FLAGS and NL80211_ATTR_FRAME.
+	 * If kernel sends this, it's a status notification for the injected
+	 * frame. */
+	NL80211_CMD_INJECT,
+
+	/* add a virtual interface to a group that is identified by any
+	 * other ifindex in the group of a wiphy index, needs the
+	 * NL80211_IF_NAME attribute */
+	NL80211_CMD_ADD_VIRTUAL_INTERFACE,
+
+	/* remove a given (with NL80211_ATTR_IFINDEX) virtual device */
+	NL80211_CMD_DEL_VIRTUAL_INTERFACE,
+
+	/* get list of all wiphys */
+	NL80211_CMD_GET_WIPHYS,
+
+	/* get list of all wiphys */
+	NL80211_CMD_NEW_WIPHYS,
+
+	/* get list of all interfaces belonging to a wiphy */
+	NL80211_CMD_GET_INTERFACES,
+
+	/* get list of all interfaces belonging to a wiphy */
+	NL80211_CMD_NEW_INTERFACES,
+
+	/* configure device */
+	NL80211_CMD_CONFIGURE,
+
+	/* request configuration */
+	NL80211_CMD_GET_CONFIG,
+
+	/* configuration sent from kernel */
+	NL80211_CMD_NEW_CONFIG,
+
+	/* initiate scan.
+	 * Takes a CHANNEL_LIST attribute containing nested
+	 * attributes which in turn contain CHANNEL and FLAGS
+	 * attributes.
+	 * The top level can also contain a FLAGS attribute
+	 * which is then the default for each channel.
+	 * If no channel list is given (or it is empty)
+	 * all channels shall be scanned. */
+	NL80211_CMD_INITIATE_SCAN,
+
+	/* scan result (kernel -> userspace) */
+	NL80211_CMD_SCAN_RESULT,
+
+	/* change roaming control */
+	NL80211_CMD_SET_ROAMING_CONTROL,
+
+	/* get roaming control setting */
+	NL80211_CMD_GET_ROAMING_CONTROL,
+
+	/* answer to that */
+	NL80211_CMD_ROAMING_CONTROL,
+
+	/* set access point BSSID for userspace roaming */
+	NL80211_CMD_SET_FIXED_BSSID,
+
+	/* get currently set userspace roaming BSSID */
+	NL80211_CMD_GET_FIXED_BSSID,
+
+	/* currently set roaming BSSID */
+	NL80211_CMD_FIXED_BSSID,
+
+	/* get current association information, if not associated then
+	 * the BSSID attribute is not present in response */
+	NL80211_CMD_GET_ASSOCIATION,
+
+	/* association notification and response to GET_BSSID */
+	NL80211_CMD_ASSOCIATION_CHANGED,
+
+	/* disassociate from current AP */
+	NL80211_CMD_DISASSOCIATE,
+
+	/* deauth from current AP */
+	NL80211_CMD_DEAUTH,
+
+	/* re-associate with current settings
+	 * (SSID and BSSID if roaming control in userspace) */
+	NL80211_CMD_REASSOCIATE,
+
+	/* request the full list of BSSs the device is
+	 * authenticated with */
+	NL80211_CMD_GET_AUTH_LIST,
+
+	/* sent as a response to GET_AUTH_LIST containing
+	 * an ATTR_BSSID_LIST */
+	NL80211_CMD_AUTH_LIST,
+
+	/* sent when authenticating/deauthenticating.
+	 * contains an ATTR_BSSID and possibly an
+	 * ATTR_DEAUTHENTICATED */
+	NL80211_CMD_AUTHENTICATION_CHANGED,
+
+	/* add commands here */
+
+	/* used to define NL80211_CMD_MAX below */
+	__NL80211_CMD_AFTER_LAST,
+};
+#define NL80211_CMD_MAX (__NL80211_CMD_AFTER_LAST - 1)
+
+
+/* currently supported attributes.
+ * don't change the order or add anything inbetween, this is ABI! */
+enum {
+	NL80211_ATTR_UNSPEC,
+
+	/* network device (ifindex) to operate on */
+	NL80211_ATTR_IFINDEX,
+
+	/* wiphy index to operate on */
+	NL80211_ATTR_WIPHY,
+
+	/* list of u8 cmds that a given device implements */
+	NL80211_ATTR_CMDS,
+
+	/* flags for injection and other commands, see below */
+	NL80211_ATTR_FLAGS,
+
+	/* which hardware queue to use */
+	NL80211_ATTR_QUEUE,
+
+	/* frame to inject or received frame for mgmt frame subscribers */
+	NL80211_ATTR_FRAME,
+
+	/* interface name */
+	NL80211_ATTR_IFNAME,
+
+	/* type of (virtual) interface */
+	NL80211_ATTR_IFTYPE,
+
+	/* interface list */
+	NL80211_ATTR_INTERFACE_LIST,
+
+	/* wiphy list */
+	NL80211_ATTR_WIPHY_LIST,
+
+	/* attributes used for configuration */
+	/* network ID (pre 802.11 HW) */
+	NL80211_ATTR_NETWORK_ID,
+
+	/* channel, 1-14 are B/G */
+	NL80211_ATTR_CHANNEL,
+
+	/* channel list for scan determination */
+	NL80211_ATTR_CHANNEL_LIST,
+
+	/* receiver sensitivity in dBm */
+	NL80211_ATTR_RX_SENSITIVITY,
+
+	/* BSSID to associate to, only used when roaming control
+	 * is in userspace */
+	NL80211_ATTR_BSSID,
+
+	/* list of multiple BSSIDs, this is a nested attribute
+	 * containing an index->(attrs) mapping */
+	NL80211_ATTR_BSSID_LIST,
+
+	/* this is a flag for when an authentication is lost */
+	NL80211_ATTR_DEAUTHENTICATED,
+
+	/* SSID of ESS to associate to */
+	NL80211_ATTR_SSID,
+
+	/* transmit power in mW */
+	NL80211_ATTR_TRANSMIT_POWER,
+
+	/* fragmentation threshold in bytes */
+	NL80211_ATTR_FRAG_THRESHOLD,
+
+	/* one or more information elements */
+	NL80211_ATTR_INFORMATION_ELEMENT,
+
+	NL80211_ATTR_ROAMING_CONTROL,
+
+	NL80211_ATTR_SCAN_TYPE,
+
+	/* add attributes here */
+
+	/* used to define NL80211_ATTR_MAX below */
+	__NL80211_ATTR_AFTER_LAST,
+};
+#define NL80211_ATTR_MAX (__NL80211_ATTR_AFTER_LAST - 1)
+
+/**
+ * NL80211_FLAG_TXSTATUS - send transmit status indication
+ */
+#define NL80211_FLAG_TXSTATUS		(1<<0)
+/**
+ * NL80211_FLAG_ENCRYPT - encrypt this packet
+ * Warning: This looks inside the packet header!
+ */
+#define NL80211_FLAG_ENCRYPT		(1<<1)
+
+/**
+ * NL80211_FLAG_SCAN_ACTIVELY - set this with a scan
+ * request to have it scan actively, can also be used
+ * within the nested CHANNEL_LIST...
+ */
+#define NL80211_FLAG_SCAN_ACTIVELY	(1<<2)
+
+/**
+ * maximum length of a frame that can be injected
+ */
+#define NL80211_MAX_FRAME_LEN 2500
+
+/* this is an arbitrary limit, 516 means two full-length
+ * IEs would fit... */
+/**
+ * maximum length of IE(s) passed in an NL80211_ATTR_INFORMATION_ELEMENT.
+ */
+#define NL80211_MAX_IE_LEN 516
+
+/* may need to be bumped? */
+/**
+ * maximum number of items in an ATTR_CHANNEL_LIST
+ */
+#define NL80211_MAX_CHANNEL_LIST_ITEM 20
+
+/**
+ * &enum nl80211_iftype - (virtual) interface types
+ *
+ * This structure is used with the NL80211_ATTR_IFTYPE
+ * to set the type of an interface.
+ * Note that these are intentionally compatible with
+ * the IW_MODE_* constants except for the removal of
+ * IW_MODE_AUTO.
+ *
+ */
+enum {
+	NL80211_IFTYPE_UNSPECIFIED,
+	NL80211_IFTYPE_ADHOC,
+	NL80211_IFTYPE_STATION,
+	NL80211_IFTYPE_AP,
+	NL80211_IFTYPE_WDS,
+	NL80211_IFTYPE_SECONDARY,
+	NL80211_IFTYPE_MONITOR,
+
+	/* keep last */
+	__NL80211_IFTYPE_AFTER_LAST
+};
+#define NL80211_IFTYPE_MAX (__NL80211_IFTYPE_AFTER_LAST - 1)
+
+enum {
+	NL80211_ROAMING_CONTROL_KERNEL,
+	NL80211_ROAMING_CONTROL_USERSPACE,
+
+	/* keep last */
+	__NL80211_ROAMING_CONTROL_AFTER_LAST
+};
+#define NL80211_ROAMING_CONTROL_MAX (__NL80211_ROAMING_CONTROL_AFTER_LAST-1)
+
+#endif /* __LINUX_NL80211_H */
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ wireless-dev/include/net/cfg80211.h	2006-09-28 00:38:55.274150162 +0200
@@ -0,0 +1,191 @@
+#ifndef __NET_CFG80211_H
+#define __NET_CFG80211_H
+
+#include <linux/netlink.h>
+#include <linux/nl80211.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <net/genetlink.h>
+
+/*
+ * 802.11 configuration in-kernel interface
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+/**
+ * struct cfg80211_config - description of a configuration (request)
+ */
+struct cfg80211_config {
+	/* first fields with 'internal' validity */
+
+	/* SSID to use, valid if not NULL. change forces reassociation */
+	u8 *ssid;
+
+	/* now fields with explicit validity */
+#define CFG80211_CFG_VALID_NWID			(1<<0)
+#define CFG80211_CFG_VALID_RX_SENSITIVITY	(1<<1)
+#define CFG80211_CFG_VALID_TRANSMIT_POWER	(1<<2)
+#define CFG80211_CFG_VALID_FRAG_THRESHOLD	(1<<3)
+#define CFG80211_CFG_VALID_CHANNEL		(1<<4)
+	unsigned int valid;
+
+	u16 network_id;
+	s32 rx_sensitivity;
+	u32 transmit_power;
+	u32 fragmentation_threshold;
+	u32 channel;
+};
+
+struct scan_channel {
+	u32 channel;
+	int active;
+};
+
+struct scan_params {
+	/* number of items in 'channels' array
+	 * or -1 to indicate scanning all channels
+	 * (in that case 'channels' is NULL) */
+	int n_channels;
+
+	/* use only when n_channels is -1 to determine
+	 * whether scanning should be active or not */
+	int active;
+
+	/* the channel list if any */
+	struct scan_channel *channels;
+};
+
+/**
+ * struct cfg80211_ops - backend description for wireless configuration
+ *
+ * This struct is registered by fullmac card drivers and/or wireless stacks
+ * in order to handle configuration requests on their interfaces.
+ *
+ * The priv pointer passed to each call is the pointer that was
+ * registered in cfg80211_register_driver().
+ *
+ * All callbacks except where otherwise noted should return 0
+ * on success or a negative error code.
+ *
+ * @list_interfaces: for each interfaces belonging to the wiphy identified
+ *		     by the priv pointer, call the one() function with the
+ *		     given data and the ifindex. This callback is required.
+ *
+ * @inject_packet: inject the given frame with the NL80211_FLAG_*
+ *		   flags onto the given queue.
+ *
+ * @add_virtual_intf: create a new virtual interface with the given name
+ *
+ * @del_virtual_intf: remove the virtual interface determined by ifindex.
+ *
+ * @configure: configure the given interface as requested in the config struct.
+ *	       must not ignore any configuration item, if something is
+ *	       is requested that cannot be fulfilled return an error
+ *
+ * @get_config: fill the given config structure with the current configuration
+ *
+ * @reassociate: reassociate with current settings (SSID, BSSID if
+ *		 userspace roaming is enabled)
+ *
+ * @disassociate: disassociate from current AP
+ *
+ * @deauth: deauth from current AP
+ *
+ * @initiate_scan: ...
+ *
+ * @set_roaming: set who gets to control roaming, the roaming_control
+ *		 parameter is passed NL80211_ROAMING_CONTROL_* values.
+ *
+ * @get_roaming: return where roaming control currently is done or
+ *		 a negative error.
+ *
+ * @set_fixed_bssid: set BSSID to use with userspace roaming, forces
+ *		     reassociation if changing.
+ * @get_fixed_bssid: get BSSID that is used with userspace roaming,
+ *		     the bssid parameter has space for 6 bytes
+ *
+ * @get_association: get BSSID of the BSS that the device is currently
+ *		     associated to and return 1, or return 0 if not
+ *		     associated (or a negative error code)
+ * @get_auth_list: get list of BSSIDs of all BSSs the device has
+ *		   authenticated with, must call next_bssid for each,
+ *		   next_bssid returns non-zero on error, the given data
+ *		   is to be passed to that callback
+ */
+struct cfg80211_ops {
+	int	(*list_interfaces)(void *priv, void *data,
+				   int (*one)(void *data, int ifindex));
+
+
+	int	(*inject_packet)(void *priv, void *frame, int framelen,
+				 u32 flags, int queue);
+
+
+	int	(*add_virtual_intf)(void *priv, char *name,
+				    unsigned int type);
+	int	(*del_virtual_intf)(void *priv, int ifindex);
+
+
+	int	(*configure)(void *priv, struct net_device *dev,
+			     struct cfg80211_config *cfg);
+	void	(*get_config)(void *priv, struct net_device *dev,
+			      struct cfg80211_config *cfg);
+
+
+	int	(*reassociate)(void *priv, struct net_device *dev);
+	int	(*disassociate)(void *priv, struct net_device *dev);
+	int	(*deauth)(void *priv, struct net_device *dev);
+
+
+	int	(*initiate_scan)(void *priv, struct net_device *dev,
+				 struct scan_params *params);
+
+
+	int	(*set_roaming)(void *priv, struct net_device *dev,
+			       int roaming_control);
+	int	(*get_roaming)(void *priv, struct net_device *dev);
+	int	(*set_fixed_bssid)(void *priv, struct net_device *dev,
+				   u8 *bssid);
+	int	(*get_fixed_bssid)(void *priv, struct net_device *dev,
+				   u8 *bssid);
+
+
+	int	(*get_association)(void *priv, struct net_device *dev,
+				   u8 *bssid);
+
+	int	(*get_auth_list)(void *priv, struct net_device *dev,
+				 void *data,
+				 int (*next_bssid)(void *data, u8 *bssid));
+};
+
+/**
+ * cfg80211_register - register a wiphy with cfg80211
+ *
+ * register a given method structure with the cfg80211 system
+ * and associate the 'priv' pointer with it.
+ *
+ * Returns a non-negative wiphy index or a negative error code.
+ *
+ * NOTE: for proper operation, this priv pointer MUST also be
+ * assigned to each &struct net_device's @ieee80211_ptr member!
+ */
+extern int cfg80211_register(struct cfg80211_ops *ops, void *priv);
+
+/**
+ * cfg80211_unregister - deregister a wiphy from cfg80211
+ *
+ * unregister a device with the given priv pointer.
+ * After this call, no more requests can be made with this priv
+ * pointer, but the call may sleep to wait for an outstanding
+ * request that is being handled.
+ */
+extern void cfg80211_unregister(void *priv);
+
+/* helper functions specific to nl80211 */
+extern void *nl80211hdr_put(struct sk_buff *skb, u32 pid,
+			    u32 seq, int flags, u8 cmd);
+extern void *nl80211msg_new(struct sk_buff **skb, u32 pid,
+			    u32 seq, int flags, u8 cmd);
+
+#endif /* __NET_CFG80211_H */
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ wireless-dev/net/wireless/core.c	2006-09-28 00:38:22.664150162 +0200
@@ -0,0 +1,233 @@
+/*
+ * This is the new wireless configuration interface.
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+#include "core.h"
+#include "nl80211.h"
+#include <linux/if.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <net/genetlink.h>
+#include <net/cfg80211.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+
+MODULE_AUTHOR("Johannes Berg");
+MODULE_LICENSE("GPL");
+
+/* RCU might be appropriate here since we usually
+ * only read the list, and that can happen quite
+ * often because we need to do it for each command */
+LIST_HEAD(cfg80211_drv_list);
+DEFINE_MUTEX(cfg80211_drv_mutex);
+static int wiphy_counter;
+
+/* requires nl80211_drv_mutex to be held! */
+static struct cfg80211_registered_driver *cfg80211_drv_by_priv(void *priv)
+{
+	struct cfg80211_registered_driver *result = NULL, *drv;
+
+	if (!priv)
+		return NULL;
+
+	list_for_each_entry(drv, &cfg80211_drv_list, list) {
+		if (drv->priv == priv) {
+			result = drv;
+			break;
+		}
+	}
+
+	return result;
+}
+
+/* requires cfg80211_drv_mutex to be held! */
+static struct cfg80211_registered_driver *cfg80211_drv_by_wiphy(int wiphy)
+{
+	struct cfg80211_registered_driver *result = NULL, *drv;
+
+	list_for_each_entry(drv, &cfg80211_drv_list, list) {
+		if (drv->wiphy == wiphy) {
+			result = drv;
+			break;
+		}
+	}
+
+	return result;
+}
+
+/* requires cfg80211_drv_mutex to be held! */
+static struct cfg80211_registered_driver *
+__cfg80211_drv_from_info(struct genl_info *info)
+{
+	int ifindex;
+	struct cfg80211_registered_driver *bywiphy = NULL, *byifidx = NULL;
+	struct net_device *dev;
+	int err = -EINVAL;
+
+	if (info->attrs[NL80211_ATTR_WIPHY]) {
+		bywiphy = cfg80211_drv_by_wiphy(
+				nla_get_u32(info->attrs[NL80211_ATTR_WIPHY]));
+		err = -ENODEV;
+	}
+
+	if (info->attrs[NL80211_ATTR_IFINDEX]) {
+		ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
+		dev = dev_get_by_index(ifindex);
+		if (dev) {
+			byifidx = cfg80211_drv_by_priv(dev->ieee80211_ptr);
+			dev_put(dev);
+		}
+		err = -ENODEV;
+	}
+
+	if (bywiphy && byifidx) {
+		if (bywiphy != byifidx)
+			return ERR_PTR(-EINVAL);
+		else
+			return bywiphy; /* == byifidx */
+	}
+	if (bywiphy)
+		return bywiphy;
+
+	if (byifidx)
+		return byifidx;
+
+	return ERR_PTR(err);
+}
+
+struct cfg80211_registered_driver *
+cfg80211_get_drv_from_info(struct genl_info *info)
+{
+	struct cfg80211_registered_driver *drv;
+
+	mutex_lock(&cfg80211_drv_mutex);
+	drv = __cfg80211_drv_from_info(info);
+
+	/* if it is not an error we grab the lock on
+	 * it to assure it won't be going away while
+	 * we operate on it */
+	if (!IS_ERR(drv))
+		mutex_lock(&drv->mtx);
+
+	mutex_unlock(&cfg80211_drv_mutex);
+
+	return drv;
+}
+
+/* wext will need this */
+struct cfg80211_registered_driver *
+cfg80211_get_drv_from_ifindex(int ifindex)
+{
+	struct cfg80211_registered_driver *drv;
+	struct net_device *dev;
+
+	mutex_lock(&cfg80211_drv_mutex);
+	dev = dev_get_by_index(ifindex);
+	if (!dev)
+		return ERR_PTR(-ENODEV);
+	drv = cfg80211_drv_by_priv(dev->ieee80211_ptr);
+	if (drv)
+		mutex_lock(&drv->mtx);
+	dev_put(dev);
+	if (drv)
+		return drv;
+	return ERR_PTR(-ENODEV);
+}
+
+void cfg80211_put_drv(struct cfg80211_registered_driver *drv)
+{
+	BUG_ON(IS_ERR(drv));
+	mutex_unlock(&drv->mtx);
+}
+
+/* exported functions */
+
+int cfg80211_register(struct cfg80211_ops *ops, void *priv)
+{
+	struct cfg80211_registered_driver *drv;
+	int res;
+
+	if (!priv || !ops->list_interfaces)
+		return -EINVAL;
+
+	mutex_lock(&cfg80211_drv_mutex);
+
+	if (cfg80211_drv_by_priv(priv)) {
+		res = -EALREADY;
+		goto out_unlock;
+	}
+
+	drv = kzalloc(sizeof(struct cfg80211_registered_driver), GFP_KERNEL);
+	if (!drv) {
+		res = -ENOMEM;
+		goto out_unlock;
+	}
+
+	drv->ops = ops;
+	drv->priv = priv;
+
+	if (unlikely(wiphy_counter<0)) {
+		/* ugh, wrapped! */
+		kfree(drv);
+		res = -ENOSPC;
+		goto out_unlock;
+	}
+	mutex_init(&drv->mtx);
+	drv->wiphy = wiphy_counter;
+	list_add(&drv->list, &cfg80211_drv_list);
+	/* return wiphy number */
+	res = drv->wiphy;
+
+	/* now increase counter for the next time */
+	wiphy_counter++;
+
+ out_unlock:
+	mutex_unlock(&cfg80211_drv_mutex);
+	return res;
+}
+EXPORT_SYMBOL_GPL(cfg80211_register);
+
+void cfg80211_unregister(void *priv)
+{
+	struct cfg80211_registered_driver *drv;
+
+	mutex_lock(&cfg80211_drv_mutex);
+	drv = cfg80211_drv_by_priv(priv);
+	if (!drv) {
+		printk(KERN_ERR "deregistering cfg80211 backend that "
+		       " was never registered!\n");
+		mutex_unlock(&cfg80211_drv_mutex);
+		return;
+	}
+
+	/* hold registered driver mutex during list removal as well
+	 * to make sure no commands are in progress at the moment */
+	mutex_lock(&drv->mtx);
+	list_del(&drv->list);
+	mutex_unlock(&drv->mtx);
+
+	mutex_unlock(&cfg80211_drv_mutex);
+
+	mutex_destroy(&drv->mtx);
+	kfree(drv);
+}
+EXPORT_SYMBOL_GPL(cfg80211_unregister);
+
+/* module initialisation/exit functions */
+
+static int cfg80211_init(void)
+{
+	/* possibly need to do more later */
+	return nl80211_init();
+}
+
+static void cfg80211_exit(void)
+{
+	/* possibly need to do more later */
+	nl80211_exit();
+}
+
+module_init(cfg80211_init);
+module_exit(cfg80211_exit);
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ wireless-dev/net/wireless/core.h	2006-09-28 00:38:22.734150162 +0200
@@ -0,0 +1,57 @@
+/*
+ * Wireless configuration interface internals.
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ */
+#ifndef __NET_WIRELESS_CORE_H
+#define __NET_WIRELESS_CORE_H
+#include <net/cfg80211.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <net/genetlink.h>
+
+struct cfg80211_registered_driver {
+	struct cfg80211_ops *ops;
+	int wiphy;
+	void *priv;
+	struct list_head list;
+	/* we hold this mutex during any call so that
+	 * we cannot do multiple calls at once, and also
+	 * to avoid the deregister call to proceed while
+	 * any call is in progress */
+	struct mutex mtx;
+};
+
+extern struct mutex cfg80211_drv_mutex;
+extern struct list_head cfg80211_drv_list;
+
+/*
+ * This function returns a pointer to the driver
+ * that the genl_info item that is passed refers to.
+ * If successful, it returns non-NULL and also locks
+ * the driver's mutex!
+ *
+ * This means that you need to call cfg80211_put_drv()
+ * before being allowed to acquire &cfg80211_drv_mutex!
+ *
+ * This is necessary because we need to lock the global
+ * mutex to get an item off the list safely, and then
+ * we lock the drv mutex so it doesn't go away under us.
+ *
+ * We don't want to keep cfg80211_drv_mutex locked
+ * for all the time in order to allow requests on
+ * other interfaces to go through at the same time.
+ *
+ * The result of this can be a PTR_ERR and hence must
+ * be checked with IS_ERR() for errors.
+ */
+extern struct cfg80211_registered_driver *
+cfg80211_get_drv_from_info(struct genl_info *info);
+
+/* identical to cfg80211_get_drv_from_info but only operate on ifindex */
+extern struct cfg80211_registered_driver *
+cfg80211_get_drv_from_ifindex(int ifindex);
+
+extern void cfg80211_put_drv(struct cfg80211_registered_driver *drv);
+
+#endif /* __NET_WIRELESS_CORE_H */
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ wireless-dev/net/wireless/nl80211.h	2006-09-27 19:36:17.744088173 +0200
@@ -0,0 +1,7 @@
+#ifndef __NET_WIRELESS_NL80211_H
+#define __NET_WIRELESS_NL80211_H
+
+extern int nl80211_init(void);
+extern void nl80211_exit(void);
+
+#endif /* __NET_WIRELESS_NL80211_H */
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ wireless-dev/net/wireless/wext-compat.c	2006-09-28 00:38:55.304150162 +0200
@@ -0,0 +1,25 @@
+/* NOT YET */
+
+To implement compatibility, we add a new field to struct net_device
+that contains the pending configuration structure. This is dynamically
+allocated when needed and freed when committed.
+In a way it replaces the wireless_handlers field in there which is now
+done by dynamic lookup. No worries. No one is going to have thousands
+of wireless devices, and if that changes we can still trivially change
+this assumption :)
+
+Commit is done some time after the last parameter was changed
+(with each parameter change simply (re-)schedule a timer) or
+if explicitly asked for. This is probably not what most people
+would expect, but perfectly fine in the WE API.
+
+compatibility mappings:
+
+SIOCSIWAP
+  -> if bssid is all-ones: set roaming to kernel, reassociate
+  -> if bssid is all-zeroes: set roaming to kernel
+  -> otherwise: set roaming to userspace, set bssid
+
+SIOCGIWAP
+  -> get association parameters and fill return bssid appropriately
+


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC] cfg80211 and nl80211
  2006-09-28  9:23 [RFC] cfg80211 and nl80211 Johannes Berg
@ 2006-09-29 21:10 ` James Ketrenos
  2006-09-30  3:00   ` Michael Wu
  2006-10-02  9:08   ` Johannes Berg
  2006-09-30  3:14 ` Michael Wu
  2006-10-02 16:15 ` Dan Williams
  2 siblings, 2 replies; 16+ messages in thread
From: James Ketrenos @ 2006-09-29 21:10 UTC (permalink / raw)
  To: Johannes Berg; +Cc: netdev, Jiri Benc, John W. Linville, Larry Finger

Johannes Berg wrote:

>  * Should the userspace daemon be allowed to unilaterally update the
>    regulatory information if it learns something new (via the user)?

Many countries forbid users (root is still a user) being enabled to
override the parameters set up by the hardware vendor as tested for use
with a specific device.

If the hardware and/or driver for the hardware advertises a set of
operating parameters, user space should honor those settings and the
kernel should enforce them.

User space should be able to shrink the set of available spectrum
parameters but should not expand it.

>    Or why not even just publish the regulatory information APs might
>    broadcast in the scan results, and let the userspace daemon pick
>    that apart? Then the kernel need not ask for anything at all...

I think exposing this information to the user would be useful, and
indicate which capabilities are restricted based on information provided
by the kernel and/or hardware driver.  When channel 13 doesn't work, it
should be clear to the user why so they know who to get mad at.

>  * I seem to have read between the lines that the EEPROM data is
>    pretty much useless. Is that generally true, or should the userspace
>    daemon be told what it contains (somehow)?

The EEPROM contents typically represent the configuration and operating
parameters which have been tested and certified to be operational.

These would represent the only settings which a user can operate with
and still be covered by existing certifications.

Regardless of which country you are operating a device in, the device
was intended for sale within a specific geographic and regulatory region
-- it was tested and certified accordingly.  The EEPROM contents
typically provide the information on what was tested and what the
approved operating parameters are for that device.

>  * Should the kernel perform some kind of validation on the regulatory
>    data the daemon gives it as well?

The kernel should enforce the parameters as specified by the
hardware/driver.  In the event that a driver does not advertise a set of
capabilities, the kernel should select the minimal "safe set", which
would be a subset of the 2.4Ghz spectrum and likely exclude all of 5.2Ghz.

> Right now I'd think that it would make sense to just leave the whole
> task to our userspace daemon, iow. nl80211 just provides a command
> to update the kernel's knowledge about regulory and the daemon periodically
> checks the scan results for country information, asks the user for
> the country, or similar. If it's not running, the kernel simply starts
> from a generic no-frills set.

With hardware that restricts operation to the capabilities it was tested
and calibrated for, this will likely result in a broken user experience
-- if they try and use a device on channel 13 and the device restricts
operation to channels 1 - 11, tuning operations will fail.

James

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC] cfg80211 and nl80211
  2006-09-29 21:10 ` James Ketrenos
@ 2006-09-30  3:00   ` Michael Wu
  2006-10-02  9:08   ` Johannes Berg
  1 sibling, 0 replies; 16+ messages in thread
From: Michael Wu @ 2006-09-30  3:00 UTC (permalink / raw)
  To: James Ketrenos
  Cc: Johannes Berg, netdev, Jiri Benc, John W. Linville, Larry Finger,
	Luis Rodriguez

[-- Attachment #1: Type: text/plain, Size: 1773 bytes --]

On Friday 29 September 2006 17:10, James Ketrenos wrote:
> Johannes Berg wrote:
> >  * Should the userspace daemon be allowed to unilaterally update the
> >    regulatory information if it learns something new (via the user)?
>
> Many countries forbid users (root is still a user) being enabled to
> override the parameters set up by the hardware vendor as tested for use
> with a specific device.
>
> If the hardware and/or driver for the hardware advertises a set of
> operating parameters, user space should honor those settings and the
> kernel should enforce them.
>
This makes 802.11d impossible. There are already many ways of violating local 
regulations. We should not make it easy to override the regulatory domain 
parameters, but going out of our way to make it hard is not gonna work.

> With hardware that restricts operation to the capabilities it was tested
> and calibrated for, this will likely result in a broken user experience
> -- if they try and use a device on channel 13 and the device restricts
> operation to channels 1 - 11, tuning operations will fail.
>
So the hardware should not even restrict tuning to channels 1-11, for example. 
That sort of enforcement belongs in the kernel, with information imported 
from userspace so parameters for various regulatory domains can be updated 
easily. (whereas with hardware enforcement, every change requires new 
firmware) Sure, it's a bit easier for an enthusiastic user to set the TX 
power too high or tune to channel 14, but it seems likely that the number of 
people who would do that is a bit smaller than the number of people who 
travel between different countries frequently (and would benefit from 
easy/automatic regulatory domain switching).

-Michael Wu

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC] cfg80211 and nl80211
  2006-09-28  9:23 [RFC] cfg80211 and nl80211 Johannes Berg
  2006-09-29 21:10 ` James Ketrenos
@ 2006-09-30  3:14 ` Michael Wu
  2006-10-02 16:15 ` Dan Williams
  2 siblings, 0 replies; 16+ messages in thread
From: Michael Wu @ 2006-09-30  3:14 UTC (permalink / raw)
  To: Johannes Berg; +Cc: netdev, Jiri Benc, John W. Linville, Larry Finger

[-- Attachment #1: Type: text/plain, Size: 1067 bytes --]

On Thursday 28 September 2006 05:23, Johannes Berg wrote:
>  * why should there be configuration per device? The user can only
>    be operating in one country at a time... I think that information
>    should just be available inside cfg80211 in a global structure
>    for use by drivers whenever they need it (with some accessor
>    methods to ensure locked access).
>
Making regulatory info global is good. We're not really interested in a laptop 
with two wireless adapters sitting on a border.

>  * I seem to have read between the lines that the EEPROM data is
>    pretty much useless. Is that generally true, or should the userspace
>    daemon be told what it contains (somehow)?
>
Even if the EEPROM data is useful, the driver should probably process it 
before giving it to userspace so we don't have to worry about updating eeprom 
parsing in userspace.

>  * Should the kernel perform some kind of validation on the regulatory
>    data the daemon gives it as well?
>
Probably just minimal sanity checks if needed.

-Michael Wu

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC] cfg80211 and nl80211
  2006-09-29 21:10 ` James Ketrenos
  2006-09-30  3:00   ` Michael Wu
@ 2006-10-02  9:08   ` Johannes Berg
  1 sibling, 0 replies; 16+ messages in thread
From: Johannes Berg @ 2006-10-02  9:08 UTC (permalink / raw)
  To: James Ketrenos
  Cc: netdev, Jiri Benc, John W. Linville, Larry Finger, Michael Wu

James,

Good points. Just a few comments.

> The EEPROM contents typically represent the configuration and operating
> parameters which have been tested and certified to be operational.
> 
> These would represent the only settings which a user can operate with
> and still be covered by existing certifications.

Also, the EEPROM could potentially contain limitations of the device
itself, a device manufactured for channels 1-11 might not even operate
correctly in channel 14, I guess.

> The kernel should enforce the parameters as specified by the
> hardware/driver.  In the event that a driver does not advertise a set of
> capabilities, the kernel should select the minimal "safe set", which
> would be a subset of the 2.4Ghz spectrum and likely exclude all of 5.2Ghz.

Right. Note that for bcm43xx for example, we know that these devices
have been certified all over the world and hence the EEPROM information
is effectively useless because it just tells us for which region this
particular device was manufactured, the vendor actually programs this
info and not Broadcom. Hence, in the driver, we'd probably announce some
rather liberal set of parameters.

Maybe there's a need for two different sets of parameters here: one that
the hardware can potentially be used at (usually known to the driver),
and one that it was certified for (the eeprom country info for a given
device)?

> With hardware that restricts operation to the capabilities it was tested
> and calibrated for, this will likely result in a broken user experience
> -- if they try and use a device on channel 13 and the device restricts
> operation to channels 1 - 11, tuning operations will fail.

Yes, makes sense to enforce this all the way through to userspace and
refuse broadening of the limitations from what the driver announced.

This does, however, mean that we cannot just have one global set of
parameters because different hardware might behave differently, if I use
a card manufactured for the somewhere else and it restricts operating to
the eeprom limitations, then that means that I can potentially not make
use of the full permitted spectrum for where I am, and hence will not
want this card to restrict other cards in the system that may be
certified for that broader spectrum. On the other hand, since the other
card is not certified for the broader spectrum there should not be a way
to force it to operate there since it may misbehave.

The conclusion, it seems, is to have two different sets of limitations:
 (a) a global set of regulatory limitations that is completely
     controlled by external information (802.11d anouncements, user
     specified operating country, ...) and set by the userspace daemon
     that analyses all these things and
 (b) a device specific set of limitations initialised by the driver to
     the ranges supported by the card (what the card was certified for
     etc)

Both (a) and (b) are initialised to some sort of (to be determined)
'safe set', (b) then given by the driver for each wiphy, and (a) by the
userspace daemon.

Then, when a given card operates, the kernel makes sure that operation
is limited to adhere to both sets of limitations. Of course there needs
to be some userspace interface to query both (a) and (b).

This makes sure that
 - devices always only operate in the ranges they were certified with
 - operations is additionally limited to what the current locale permits
 - 802.11d is possible within those ranges that the devices allow

Does that sound sane?

johannes

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC] cfg80211 and nl80211
  2006-09-28  9:23 [RFC] cfg80211 and nl80211 Johannes Berg
  2006-09-29 21:10 ` James Ketrenos
  2006-09-30  3:14 ` Michael Wu
@ 2006-10-02 16:15 ` Dan Williams
  2006-10-02 17:01   ` Dan Williams
                     ` (2 more replies)
  2 siblings, 3 replies; 16+ messages in thread
From: Dan Williams @ 2006-10-02 16:15 UTC (permalink / raw)
  To: Johannes Berg; +Cc: netdev, Jiri Benc, John W. Linville, Larry Finger

On Thu, 2006-09-28 at 11:23 +0200, Johannes Berg wrote:
> This patch adds cfg80211, a new configuration system for wireless hardware
> as well as nl80211, the netlink-based userspace interface for it.
> 
> It currently features a bunch of configuration requests, support for
> adding and removing virtual interfaces, the ability to inject packets and
> more.
> 
> Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
> ---
> This is the
> 
>   I think nl80211 is the future if I don't have to do it all by myself
>     -- johill
> 
> release. Please take the time to read at least the stuff before the patch.
> 
> There should be support for notifications, but that isn't currently done
> as I'm still thinking about how to do it best.
> 
> It probably still requires the patches in
> http://marc.theaimsgroup.com/?l=linux-netdev&m=115625436628696&w=2
> and
> http://marc.theaimsgroup.com/?l=linux-netdev&m=115625168405439&w=2
> 
> (the latter doesn't apply cleanly against wireless-dev, but you can
> safely ignore the pieces that don't, at least for wireless testing :) )
> 
> It also requires the NLA_PUT_FLAG patch I did:
> http://marc.theaimsgroup.com/?l=linux-netdev&m=115650333420169&w=2
> 
> (obviously, all those patches aren't required any more if they have been
> applied, but I've been lazy and not tracked which are and which aren't)
> 
> I've removed the use of NLA_CUSTOM_CHECK as it isn't going to be applied,
> but that currently results in a warning about an unused static method.
> 
> Also new in this iteration is a lot more fleshed-out internal cfg80211
> interface. Also new is the following paragraph ;)
> 
> Here's an explanation of what cfg80211 and nl80211 are. cfg80211 is
> the interface between the configuration backend and the driver or stack
> ("users" of cfg80211). cfg80211 provides a wiphy index whenever a user
> registers with it, and allows things to be enumerated etc. nl80211 is
> currently the only interface between cfg80211 and userspace and will
> most likely remain the primary interface forever, but it is possible
> to add (for example) a configfs interface between userspace and cfg80211,
> users of cfg80211 can remain blissfully unaware of this. Also, WExt
> backward compatibility will be implemented as an interface between
> userspace and cfg80211.
> 
> cfg80211 does, however, use some constants defined in nl80211 in order
> to not define them twice (and the other way around isn't quite good
> because they need to be visible in userspace and cfg80211 is not).
> 
> I'm still not sure if there should be an explicit 'device supports
> configuration parameters x, y and z' or if userspace should just
> grab the current configuration (which is supposed to be complete then)

I'm not sure what you mean here.  Do you really mean "grab the current
_cmdlist_"?  Because I'm not sure how grabbing the current configuration
(using GET_CONFIG) would necessarily return the right set of options for
the device.  Also, what do you mean by "is supposed to be complete
then"?

On large problem we had with WPA was that there was _no_ way to tell
whether or not a driver supported it.  Trying a WPA-related ioctl() and
hoping for the best is broken.  On the one hand, there were users
screaming that yes, Madwifi did in fact support WPA, but of course,
there was no standard way of figuring that out and present it to the
user in a sane manner.  We need to have an _explicit_ list of stuff that
driver does and does not support so that intelligent decisions can be
made before and/or without touching stuff that might turn the radio on
or mess up an existing configuration set by somebody else.

> and see what's in it. Along the same lines, should unsupported
> parameters result in the rejection, or should they be ignored?
> (currently they are ignored)

Does "parameters" here mean CMD or ATTR?  In any case, there's a good
case to be made for rejecting unsupported CMDs & ATTRs.  If the user,
for example, wishes to restrict the roaming to a set of BSSIDs for
security measures, for example (even if an insecure one), but the driver
doesn't support that, should nl80211 just blindly pretend that it
worked?  This might also get people to fix up their drivers and
userspace programs too.

I do realize there's an API extensibility benefit to ignoring stuff you
don't handle though (like 802.11 information element handling).  But if
we're talking about an explicit request by a user for an option, if the
driver doesn't support it, they should get an error.

> As mentioned to Larry, I'd like to integrate his great work on
> regulation and use nl80211 as the userspace interface for it. I do
> have a couple of questions wrt. that:
> 
>  * why should there be configuration per device? The user can only
>    be operating in one country at a time... I think that information
>    should just be available inside cfg80211 in a global structure
>    for use by drivers whenever they need it (with some accessor
>    methods to ensure locked access).
> 
>  * as far as I understood the communication is the kernel telling
>    the daemon all the information it has (which may be none, the
>    country from a broadcasting AP or more info from that) and the
>    daemon then builds up a correct set of limitations and gives that
>    to the kernel, without the daemon the kernel limits to some minimal
>    set that is (likely) legal everywhere. Correct?
> 
>  * Should the userspace daemon be allowed to unilaterally update the
>    regulatory information if it learns something new (via the user)?
>    Or why not even just publish the regulatory information APs might
>    broadcast in the scan results, and let the userspace daemon pick
>    that apart? Then the kernel need not ask for anything at all...

That sounds like a good idea.  Putting 802.11d handling stuff into the
kernel would essentially be duplicating the code and function of the
userspace regulatory daemon, right?  That seems pointless.

>  * I seem to have read between the lines that the EEPROM data is
>    pretty much useless. Is that generally true, or should the userspace
>    daemon be told what it contains (somehow)?

Expose it, but don't use it blindly and certainly don't trust it.  Let
the userspace daemon determine the policy as it sees fit from a variety
of sources, _including_ EEPROM.

>  * Should the kernel perform some kind of validation on the regulatory
>    data the daemon gives it as well?

"Trust, but verify." :)  At a minimum, do bounds-checking on the channel
numbers and stuff that's standardized.  No channel 15 for us.  But the
kernel probably shouldn't be including lookup tables for each region's
channel mask, which again essentially duplicates the work done by the
daemon.

Another questions; I didn't see anything for encryption and auth and
stuff yet.  Are you just trying to get the basics down before going on
to that stuff?  What do you still have on your ToDo list for nl80211
before you'd consider "ready" to take over real configuration functions?

More comments below...  All in all looks nice and clean, for now :)

> Right now I'd think that it would make sense to just leave the whole
> task to our userspace daemon, iow. nl80211 just provides a command
> to update the kernel's knowledge about regulory and the daemon periodically
> checks the scan results for country information, asks the user for
> the country, or similar. If it's not running, the kernel simply starts
> from a generic no-frills set.
> 
> --- wireless-dev.orig/net/Kconfig	2006-09-27 19:10:21.384088173 +0200
> +++ wireless-dev/net/Kconfig	2006-09-27 19:14:44.214088173 +0200
> @@ -250,6 +250,9 @@ source "net/ieee80211/Kconfig"
>  config WIRELESS_EXT
>  	bool
>  
> +config CFG80211
> +	tristate
> +
>  endif   # if NET
>  endmenu # Networking
>  
> --- wireless-dev.orig/net/Makefile	2006-09-27 19:10:21.414088173 +0200
> +++ wireless-dev/net/Makefile	2006-09-27 19:14:44.214088173 +0200
> @@ -44,6 +44,7 @@ obj-$(CONFIG_ECONET)		+= econet/
>  obj-$(CONFIG_VLAN_8021Q)	+= 8021q/
>  obj-$(CONFIG_IP_DCCP)		+= dccp/
>  obj-$(CONFIG_IP_SCTP)		+= sctp/
> +obj-$(CONFIG_CFG80211)		+= wireless/
>  obj-$(CONFIG_D80211)		+= d80211/
>  obj-$(CONFIG_IEEE80211)		+= ieee80211/
>  obj-$(CONFIG_TIPC)		+= tipc/
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ wireless-dev/net/wireless/Makefile	2006-09-27 19:14:44.214088173 +0200
> @@ -0,0 +1,4 @@
> +obj-$(CONFIG_CFG80211) += cfg80211.o
> +
> +cfg80211-objs := \
> +	core.o nl80211.o
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ wireless-dev/net/wireless/nl80211.c	2006-09-28 01:27:06.094150162 +0200
> @@ -0,0 +1,1048 @@
> +/*
> + * This is the new netlink-based wireless configuration interface.
> + *
> + * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
> + */
> +
> +#include <linux/if.h>
> +#include <linux/module.h>
> +#include <linux/err.h>
> +#include <net/genetlink.h>
> +#include <net/cfg80211.h>
> +#include <linux/mutex.h>
> +#include <linux/list.h>
> +#include <linux/if_ether.h>
> +#include "core.h"
> +#include "nl80211.h"
> +
> +/* the netlink family */
> +static struct genl_family nl80211_fam = {
> +	.id = GENL_ID_GENERATE,	/* don't bother with a hardcoded ID */
> +	.name = "nl80211",	/* have users key off the name instead */
> +	.hdrsize = 0,		/* no private header */
> +	.version = 1,		/* no particular meaning now */
> +	.maxattr = NL80211_ATTR_MAX,
> +};
> +
> +/* internal helper: validate an information element attribute */
> +static int check_information_element(struct nlattr *nla)
> +{
> +	int len = nla_len(nla);
> +	u8 *data = nla_data(nla);
> +	int elementlen;
> +
> +	while (len >= 2) {
> +		/* 1 byte ID, 1 byte len, `len' bytes data */
> +		elementlen = *(data+1) + 2;
> +		data += elementlen;
> +		len -= elementlen;
> +	}
> +	return len ? -EINVAL : 0;
> +}
> +
> +/* internal helper: get drv and dev */
> +static int get_drv_dev_by_info_ifindex(struct genl_info *info,
> +				       struct cfg80211_registered_driver **drv,
> +				       struct net_device **dev)
> +{
> +	int ifindex;
> +
> +	if (!info->attrs[NL80211_ATTR_IFINDEX])
> +		return -EINVAL;
> +
> +	ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
> +	*dev = dev_get_by_index(ifindex);
> +	if (!dev)
> +		return -ENODEV;
> +
> +	*drv = cfg80211_get_drv_from_ifindex(ifindex);
> +	if (IS_ERR(*drv)) {
> +		dev_put(*dev);
> +		return PTR_ERR(*drv);
> +	}
> +
> +	return 0;
> +}
> +
> +/* policy for the attributes */
> +static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
> +	[NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
> +	[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
> +	[NL80211_ATTR_FLAGS] = { .type = NLA_U32 },
> +	[NL80211_ATTR_QUEUE] = { .type = NLA_U32 },
> +	[NL80211_ATTR_FRAME] = { .type = NLA_STRING,
> +				 .len = NL80211_MAX_FRAME_LEN },
> +	[NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
> +	[NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
> +	[NL80211_ATTR_NETWORK_ID] = { .type = NLA_U16 },
> +	[NL80211_ATTR_CHANNEL] = { .type = NLA_U32 },
> +	[NL80211_ATTR_RX_SENSITIVITY] = { .type = NLA_U32 },
> +	[NL80211_ATTR_BSSID] = { .len = ETH_ALEN },
> +	[NL80211_ATTR_SSID] = { .type = NLA_NUL_STRING, .len = 32 },
> +	[NL80211_ATTR_TRANSMIT_POWER] = { .type = NLA_U32 },
> +	[NL80211_ATTR_FRAG_THRESHOLD] = { .type = NLA_U32 },
> +	[NL80211_ATTR_INFORMATION_ELEMENT] = { .type = NLA_STRING,
> +					       .len = NL80211_MAX_IE_LEN },
> +	[NL80211_ATTR_ROAMING_CONTROL] = { .type = NLA_U32 },
> +	[NL80211_ATTR_SCAN_TYPE] = { .type = NLA_U32 },
> +};
> +
> +/* netlink command implementations */
> +
> +#define CHECK_CMD(ptr, cmd)				\
> +	if (drv->ops->ptr)				\
> +		NLA_PUT_FLAG(msg, NL80211_CMD_##cmd);
> +
> +static int nl80211_get_cmdlist(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct cfg80211_registered_driver *drv;
> +	struct sk_buff *msg;
> +	void *hdr;
> +	int err;
> +	struct nlattr *start;
> +
> +	drv = cfg80211_get_drv_from_info(info);
> +	if (IS_ERR(drv))
> +		return PTR_ERR(drv);
> +
> +	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
> +			     NL80211_CMD_NEW_CMDLIST);
> +	if (IS_ERR(hdr)) {
> +		err = PTR_ERR(hdr);
> +		goto put_drv;
> +	}
> +
> +	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->wiphy);
> +
> +	start = nla_nest_start(msg, NL80211_ATTR_CMDS);
> +	if (!start)
> +		goto nla_put_failure;
> +
> +	/* unconditionally allow some common commands we handle centrally */
> +	NLA_PUT_FLAG(msg, NL80211_CMD_GET_CMDLIST);
> +	NLA_PUT_FLAG(msg, NL80211_CMD_GET_WIPHYS);
> +	NLA_PUT_FLAG(msg, NL80211_CMD_GET_INTERFACES);
> +
> +	CHECK_CMD(list_interfaces, GET_INTERFACES);

Don't these two do exactly the same thing?  The first one adds
GET_INTERFACES unconditionally, the next adds it (again!) if the
interface supports it.  Either we add it unconditionally or we add it
conditionally, but not both :)

> +	CHECK_CMD(inject_packet, INJECT);
> +	CHECK_CMD(add_virtual_intf, ADD_VIRTUAL_INTERFACE);
> +	CHECK_CMD(del_virtual_intf, DEL_VIRTUAL_INTERFACE);
> +	CHECK_CMD(configure, CONFIGURE);
> +	CHECK_CMD(get_config, GET_CONFIG);
> +	CHECK_CMD(reassociate, REASSOCIATE);
> +	CHECK_CMD(disassociate, DISASSOCIATE);
> +	CHECK_CMD(deauth, DEAUTH);
> +	CHECK_CMD(initiate_scan, INITIATE_SCAN);

I think we need a GET_SCAN here as well.  INITIATE_SCAN should
definitely be CAP_NET_ADMIN-only or whatever, but GET_SCAN can be
user-accessible.  Non-root stuff should still be able to get scan
results even if they can't initiate one.

You also don't necessarily want to have to initiate a scan every time
you want the results.  Drivers and/or d80211 should be caching the
results anyway.  If d80211 is not, it should be.

> +	CHECK_CMD(set_roaming, SET_ROAMING_CONTROL);
> +	CHECK_CMD(get_roaming, GET_ROAMING_CONTROL);
> +	CHECK_CMD(set_fixed_bssid, SET_FIXED_BSSID);
> +	CHECK_CMD(get_fixed_bssid, GET_FIXED_BSSID);
> +	CHECK_CMD(get_association, GET_ASSOCIATION);
> +	CHECK_CMD(get_auth_list, GET_AUTH_LIST);
> +
> +	nla_nest_end(msg, start);
> +
> +	genlmsg_end(msg, hdr);
> +
> +	err = genlmsg_unicast(msg, info->snd_pid);
> +	goto put_drv;
> +
> + nla_put_failure:
> + 	err = -ENOBUFS;
> +	nlmsg_free(msg);
> + put_drv:
> +	cfg80211_put_drv(drv);
> +	return err;
> +}
> +#undef CHECK_CMD
> +
> +static int nl80211_get_wiphys(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct sk_buff *msg;
> +	void *hdr;
> +	struct nlattr *start, *indexstart;
> +	struct cfg80211_registered_driver *drv;
> +	int idx = 1;
> +
> +	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
> +			     NL80211_CMD_NEW_WIPHYS);
> +	if (IS_ERR(hdr))
> +		return PTR_ERR(hdr);
> +
> +	start = nla_nest_start(msg, NL80211_ATTR_WIPHY_LIST);
> +	if (!start)
> +		goto nla_outer_nest_failure;
> +
> +	mutex_lock(&cfg80211_drv_mutex);
> +	list_for_each_entry(drv, &cfg80211_drv_list, list) {
> +		indexstart = nla_nest_start(msg, idx++);
> +		if (!indexstart)
> +			goto nla_put_failure;
> +		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->wiphy);
> +		nla_nest_end(msg, indexstart);
> +	}
> +	mutex_unlock(&cfg80211_drv_mutex);
> +
> +	nla_nest_end(msg, start);
> +
> +	genlmsg_end(msg, hdr);
> +
> +	return genlmsg_unicast(msg, info->snd_pid);
> +
> + nla_put_failure:
> +	mutex_unlock(&cfg80211_drv_mutex);
> + nla_outer_nest_failure:
> +	nlmsg_free(msg);
> +	return -ENOBUFS;
> +}
> +
> +struct add_cb_data {
> +	int idx;
> +	struct sk_buff *skb;
> +};
> +
> +static int addifidx(void *data, int ifidx)
> +{
> +	struct add_cb_data *cb = data;
> +	struct net_device *dev = dev_get_by_index(ifidx);
> +	int err = -ENOBUFS;
> +	struct nlattr *start;
> +
> +	/* not that this can happen, since the caller
> +	 * should hold the device open... */
> +	if (!dev)
> +		return -ENODEV;
> +
> +	start = nla_nest_start(cb->skb, cb->idx++);
> +	if (!start)
> +		goto nla_put_failure;
> +
> +	NLA_PUT_U32(cb->skb, NL80211_ATTR_IFINDEX, ifidx);
> +	NLA_PUT_STRING(cb->skb, NL80211_ATTR_IFNAME, dev->name);
> +
> +	nla_nest_end(cb->skb, start);
> +	err = 0;
> +
> + nla_put_failure:
> +	dev_put(dev);
> +	return err;
> +}
> +
> +static int nl80211_get_intfs(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct cfg80211_registered_driver *drv;
> +	struct sk_buff *msg;
> +	void *hdr;
> +	int err;
> +	struct nlattr *start;
> +	struct add_cb_data cb;
> +
> +	drv = cfg80211_get_drv_from_info(info);
> +	if (IS_ERR(drv))
> +		return PTR_ERR(drv);
> +
> +	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
> +			     NL80211_CMD_NEW_INTERFACES);
> +	if (IS_ERR(hdr)) {
> +		err = PTR_ERR(hdr);
> +		goto put_drv;
> +	}
> +
> +	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->wiphy);
> +
> +	start = nla_nest_start(msg, NL80211_ATTR_INTERFACE_LIST);
> +	if (!start) {
> +		err = -ENOBUFS;
> +		goto msg_free;
> +	}
> +
> +	cb.skb = msg;
> +	cb.idx = 1;
> +	err = drv->ops->list_interfaces(drv->priv, &cb, addifidx);
> +	if (err)
> +		goto msg_free;
> +
> +	nla_nest_end(msg, start);
> +
> +	genlmsg_end(msg, hdr);
> +
> +	err = genlmsg_unicast(msg, info->snd_pid);
> +	goto put_drv;
> +
> + nla_put_failure:
> +	err = -ENOBUFS;
> + msg_free:
> +	nlmsg_free(msg);
> + put_drv:
> +	cfg80211_put_drv(drv);
> +	return err;
> +}
> +
> +static int nl80211_do_inject(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct cfg80211_registered_driver *drv;
> +	u32 flags = 0;
> +	int err, queue = -1;
> +
> +	if (!info->attrs[NL80211_ATTR_FRAME])
> +		return -EINVAL;
> +	if (info->attrs[NL80211_ATTR_FLAGS])
> +		flags = nla_get_u32(info->attrs[NL80211_ATTR_FLAGS]);
> +	if (info->attrs[NL80211_ATTR_QUEUE])
> +		queue = (int) nla_get_u32(info->attrs[NL80211_ATTR_QUEUE]);
> +
> +	drv = cfg80211_get_drv_from_info(info);
> +	if (IS_ERR(drv))
> +		return PTR_ERR(drv);
> +
> +	if (!drv->ops->inject_packet) {
> +		err = -ENOSYS;
> +		goto unlock;
> +	}
> +
> +	err = drv->ops->inject_packet(drv->priv,
> +		nla_data(info->attrs[NL80211_ATTR_FRAME]),
> +		nla_len(info->attrs[NL80211_ATTR_FRAME]),
> +		flags,
> +		queue);
> + unlock:
> +	cfg80211_put_drv(drv);
> +	return err;
> +}
> +
> +static int nl80211_add_virt_intf(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct cfg80211_registered_driver *drv;
> +	int err;
> +	unsigned int type = NL80211_IFTYPE_UNSPECIFIED;
> +
> +	if (!info->attrs[NL80211_ATTR_IFNAME])
> +		return -EINVAL;
> +
> +	if (info->attrs[NL80211_ATTR_IFTYPE]) {
> +		type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
> +		if (type > NL80211_IFTYPE_MAX)
> +			return -EINVAL;
> +	}
> +
> +	drv = cfg80211_get_drv_from_info(info);
> +	if (IS_ERR(drv))
> +		return PTR_ERR(drv);
> +
> +	if (!drv->ops->add_virtual_intf) {
> +		err = -ENOSYS;
> +		goto unlock;
> +	}
> +
> +	err = drv->ops->add_virtual_intf(drv->priv,
> +		nla_data(info->attrs[NL80211_ATTR_IFNAME]), type);
> +
> + unlock:
> +	cfg80211_put_drv(drv);
> +	return err;
> +}
> +
> +static int nl80211_del_virt_intf(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct cfg80211_registered_driver *drv;
> +	int ifindex, err;
> +	struct net_device *dev;
> +
> +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> +	if (err)
> +		return err;
> +	ifindex = dev->ifindex;
> +	dev_put(dev);
> +
> +	if (!drv->ops->del_virtual_intf) {
> +		err = -EOPNOTSUPP;
> +		goto out;
> +	}
> +
> +	err = drv->ops->del_virtual_intf(drv->priv, ifindex);
> +
> + out:
> +	cfg80211_put_drv(drv);
> +	return err;
> +}
> +
> +static int nl80211_configure(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct cfg80211_registered_driver *drv;
> +	int err;
> +	struct net_device *dev;
> +	struct cfg80211_config config;
> +	struct nlattr *attr;
> +
> +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> +	if (err)
> +		return err;
> +
> +	if (!drv->ops->configure) {
> +		err = -EOPNOTSUPP;
> +		goto out;
> +	}
> +
> +	memset(&config, 0, sizeof(config));
> +
> +	attr = info->attrs[NL80211_ATTR_SSID];
> +	if (attr)
> +		config.ssid = nla_data(attr);
> +
> +	attr = info->attrs[NL80211_ATTR_NETWORK_ID];
> +	if (attr) {
> +		config.valid |= CFG80211_CFG_VALID_NWID;
> +		config.network_id = nla_get_u16(attr);
> +	}
> +
> +	attr = info->attrs[NL80211_ATTR_RX_SENSITIVITY];
> +	if (attr) {
> +		config.valid |= CFG80211_CFG_VALID_RX_SENSITIVITY;
> +		config.rx_sensitivity = (s32) nla_get_u32(attr);
> +	}
> +
> +	attr = info->attrs[NL80211_ATTR_TRANSMIT_POWER];
> +	if (attr) {
> +		config.valid |= CFG80211_CFG_VALID_TRANSMIT_POWER;
> +		config.transmit_power = nla_get_u32(attr);
> +	}
> +
> +	attr = info->attrs[NL80211_ATTR_FRAG_THRESHOLD];
> +	if (attr) {
> +		config.valid |= CFG80211_CFG_VALID_FRAG_THRESHOLD;
> +		config.fragmentation_threshold = nla_get_u32(attr);
> +	}
> +
> +	attr = info->attrs[NL80211_ATTR_CHANNEL];
> +	if (attr) {
> +		config.valid |= CFG80211_CFG_VALID_CHANNEL;
> +		config.channel = nla_get_u32(attr);
> +	}
> +

Back to the question about encryption settings here.  How complete
nl80211/cfg80211 is right now, etc.

I'd also argue that one specific BSSID is part of an initial
configuration.  We should support that in config command.  It's an
implicit SET_FIXED_BSSID, yes.  But one of the major points of
nl80211/cfg80211 was that you could bundle up a set of configuration
settings into a single atomic "packet", which you couldn't do with WE.

So if a specific BSSID isn't sent in the initial config command, when do
you set a specific BSSID?  Before?  After?  The behavior starts getting
complicated, and we're back to a situation where every driver implements
the semantics in a slightly different manner.

> +	err = drv->ops->configure(drv->priv, dev, &config);
> + out:
> +	cfg80211_put_drv(drv);
> +	dev_put(dev);
> +	return err;
> +}
> +
> +static int nl80211_get_config(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct cfg80211_registered_driver *drv;
> +	int err;
> +	struct net_device *dev;
> +	struct cfg80211_config config;
> +	struct sk_buff *msg;
> +	void *hdr;
> +
> +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> +	if (err)
> +		return err;
> +
> +	if (!drv->ops->get_config) {
> +		err = -EOPNOTSUPP;
> +		goto out_put_drv;
> +	}
> +
> +	memset(&config, 0, sizeof(config));
> +
> +	drv->ops->get_config(drv->priv, dev, &config);
> +
> +	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
> +			     NL80211_CMD_NEW_CONFIG);
> +
> +	if (IS_ERR(hdr)) {
> +		err = PTR_ERR(hdr);
> +		goto out_put_drv;
> +	}
> +
> +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
> +
> +	if (config.ssid)
> +		NLA_PUT_STRING(msg, NL80211_ATTR_SSID, config.ssid);
> +
> +	if (config.valid & CFG80211_CFG_VALID_NWID)
> +		NLA_PUT_U16(msg, NL80211_ATTR_NETWORK_ID, config.network_id);
> +
> +	if (config.valid & CFG80211_CFG_VALID_RX_SENSITIVITY)
> +		NLA_PUT_U32(msg, NL80211_ATTR_RX_SENSITIVITY, (u32)config.rx_sensitivity);
> +
> +	if (config.valid & CFG80211_CFG_VALID_TRANSMIT_POWER)
> +		NLA_PUT_U32(msg, NL80211_ATTR_TRANSMIT_POWER, config.transmit_power);
> +
> +	if (config.valid & CFG80211_CFG_VALID_FRAG_THRESHOLD)
> +		NLA_PUT_U32(msg, NL80211_ATTR_FRAG_THRESHOLD, config.fragmentation_threshold);
> +
> +	if (config.valid & CFG80211_CFG_VALID_CHANNEL)
> +		NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL, config.channel);
> +
> +	genlmsg_end(msg, hdr);
> +	err = genlmsg_unicast(msg, info->snd_pid);
> +	goto out_put_drv;
> +
> + nla_put_failure:
> +	err = -ENOBUFS;
> +	nlmsg_free(msg);
> + out_put_drv:
> +	cfg80211_put_drv(drv);
> +	dev_put(dev);
> +	return err;
> +}
> +
> +static int nl80211_set_roaming(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct cfg80211_registered_driver *drv;
> +	int err;
> +	struct net_device *dev;
> +	int roaming_control;
> +
> +	if (!info->attrs[NL80211_ATTR_ROAMING_CONTROL])
> +		return -EINVAL;
> +	roaming_control = nla_get_u32(info->attrs[NL80211_ATTR_ROAMING_CONTROL]);
> +
> +	if (roaming_control > NL80211_ROAMING_CONTROL_MAX)
> +		return -EINVAL;
> +
> +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> +	if (err)
> +		return err;
> +
> +	if (!drv->ops->set_roaming) {
> +		err = -EOPNOTSUPP;
> +		goto out;
> +	}
> +
> +	err = drv->ops->set_roaming(drv->priv, dev, roaming_control);
> + out:
> +	cfg80211_put_drv(drv);
> +	dev_put(dev);
> +	return err;
> +}
> +
> +static int nl80211_get_roaming(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct cfg80211_registered_driver *drv;
> +	int err;
> +	struct net_device *dev;
> +	struct sk_buff *msg;
> +	void *hdr;
> +
> +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> +	if (err)
> +		return err;
> +
> +	if (!drv->ops->get_roaming) {
> +		err = -EOPNOTSUPP;
> +		goto out_put_drv;
> +	}
> +
> +	err = drv->ops->get_roaming(drv->priv, dev);
> +	if (err < 0)
> +		goto out_put_drv;
> +
> +	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
> +			     NL80211_CMD_ROAMING_CONTROL);
> +
> +	if (IS_ERR(hdr)) {
> +		err = PTR_ERR(hdr);
> +		goto out_put_drv;
> +	}
> +
> +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
> +	NLA_PUT_U32(msg, NL80211_ATTR_ROAMING_CONTROL, err);
> +
> +	genlmsg_end(msg, hdr);
> +	err = genlmsg_unicast(msg, info->snd_pid);
> +	goto out_put_drv;
> +
> + nla_put_failure:
> +	err = -ENOBUFS;
> +	nlmsg_free(msg);
> + out_put_drv:
> +	cfg80211_put_drv(drv);
> +	dev_put(dev);
> +	return err;
> +}
> +
> +static int nl80211_set_fixed_bssid(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct cfg80211_registered_driver *drv;
> +	int err;
> +	struct net_device *dev;
> +	u8 *bssid;
> +
> +	if (!info->attrs[NL80211_ATTR_BSSID])
> +		return -EINVAL;
> +	bssid = nla_data(info->attrs[NL80211_ATTR_BSSID]);
> +
> +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> +	if (err)
> +		return err;
> +
> +	if (!drv->ops->set_fixed_bssid) {
> +		err = -EOPNOTSUPP;
> +		goto out;
> +	}
> +
> +	err = drv->ops->set_fixed_bssid(drv->priv, dev, bssid);
> + out:
> +	cfg80211_put_drv(drv);
> +	dev_put(dev);
> +	return err;
> +}
> +
> +static int nl80211_get_fixed_bssid(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct cfg80211_registered_driver *drv;
> +	int err;
> +	struct net_device *dev;
> +	struct sk_buff *msg;
> +	void *hdr;
> +	u8 bssid[ETH_ALEN];
> +
> +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> +	if (err)
> +		return err;
> +
> +	if (!drv->ops->get_fixed_bssid) {
> +		err = -EOPNOTSUPP;
> +		goto out_put_drv;
> +	}
> +
> +	err = drv->ops->get_fixed_bssid(drv->priv, dev, bssid);
> +	if (err < 0)
> +		goto out_put_drv;
> +
> +	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
> +			     NL80211_CMD_FIXED_BSSID);
> +
> +	if (IS_ERR(hdr)) {
> +		err = PTR_ERR(hdr);
> +		goto out_put_drv;
> +	}
> +
> +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
> +	NLA_PUT(msg, NL80211_ATTR_BSSID, ETH_ALEN, bssid);
> +
> +	genlmsg_end(msg, hdr);
> +	err = genlmsg_unicast(msg, info->snd_pid);
> +	goto out_put_drv;
> +
> + nla_put_failure:
> +	err = -ENOBUFS;
> +	nlmsg_free(msg);
> + out_put_drv:
> +	cfg80211_put_drv(drv);
> +	dev_put(dev);
> +	return err;
> +}
> +
> +static int nl80211_get_association(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct cfg80211_registered_driver *drv;
> +	int err;
> +	struct net_device *dev;
> +	struct sk_buff *msg;
> +	void *hdr;
> +	u8 bssid[ETH_ALEN];
> +
> +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> +	if (err)
> +		return err;
> +
> +	if (!drv->ops->get_association) {
> +		err = -EOPNOTSUPP;
> +		goto out_put_drv;
> +	}
> +
> +	err = drv->ops->get_association(drv->priv, dev, bssid);
> +	if (err < 0)
> +		goto out_put_drv;
> +
> +	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
> +			     NL80211_CMD_ASSOCIATION_CHANGED);
> +
> +	if (IS_ERR(hdr)) {
> +		err = PTR_ERR(hdr);
> +		goto out_put_drv;
> +	}
> +
> +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
> +	if (err == 1)
> +		NLA_PUT(msg, NL80211_ATTR_BSSID, ETH_ALEN, bssid);
> +
> +	genlmsg_end(msg, hdr);
> +	err = genlmsg_unicast(msg, info->snd_pid);
> +	goto out_put_drv;
> +
> + nla_put_failure:
> +	err = -ENOBUFS;
> +	nlmsg_free(msg);
> + out_put_drv:
> +	cfg80211_put_drv(drv);
> +	dev_put(dev);
> +	return err;
> +}
> +
> +static int nl80211_assoc_deauth(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct cfg80211_registered_driver *drv;
> +	int err;
> +	struct net_device *dev;
> +	int (*act)(void *priv, struct net_device *dev);
> +
> +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> +	if (err)
> +		return err;
> +
> +	switch (info->genlhdr->cmd) {
> +	case NL80211_CMD_DISASSOCIATE:
> +		act = drv->ops->disassociate;
> +		break;
> +	case NL80211_CMD_REASSOCIATE:
> +		act = drv->ops->reassociate;
> +		break;
> +	case NL80211_CMD_DEAUTH:
> +		act = drv->ops->deauth;
> +		break;
> +	default:
> +		act = NULL;
> +	}
> +
> +	if (!act) {
> +		err = -EOPNOTSUPP;
> +		goto out;
> +	}
> +
> +	err = act(drv->priv, dev);
> + out:
> +	cfg80211_put_drv(drv);
> +	dev_put(dev);
> +	return err;
> +}
> +
> +static int add_bssid(void *data, u8 *bssid)
> +{
> +	struct add_cb_data *cb = data;
> +	int err = -ENOBUFS;
> +	struct nlattr *start;
> +
> +	start = nla_nest_start(cb->skb, cb->idx++);
> +	if (!start)
> +		goto nla_put_failure;
> +
> +	NLA_PUT(cb->skb, NL80211_ATTR_BSSID, ETH_ALEN, bssid);
> +
> +	nla_nest_end(cb->skb, start);
> +	err = 0;
> +
> + nla_put_failure:
> +	return err;
> +}
> +
> +static int nl80211_get_auth_list(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct cfg80211_registered_driver *drv;
> +	struct net_device *dev;
> +	struct sk_buff *msg;
> +	void *hdr;
> +	int err;
> +	struct nlattr *start;
> +	struct add_cb_data cb;
> +
> +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> +	if (err)
> +		return err;
> +
> +	if (!drv->ops->get_auth_list) {
> +		err = -EOPNOTSUPP;
> +		goto put_drv;
> +	}
> +
> +	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
> +			     NL80211_CMD_AUTH_LIST);
> +	if (IS_ERR(hdr)) {
> +		err = PTR_ERR(hdr);
> +		goto put_drv;
> +	}
> +
> +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
> +
> +	start = nla_nest_start(msg, NL80211_ATTR_BSSID_LIST);
> +	if (!start) {
> +		err = -ENOBUFS;
> +		goto msg_free;
> +	}
> +
> +	cb.skb = msg;
> +	cb.idx = 1;
> +	err = drv->ops->get_auth_list(drv->priv, dev, &cb, add_bssid);
> +	if (err)
> +		goto msg_free;
> +
> +	nla_nest_end(msg, start);
> +
> +	genlmsg_end(msg, hdr);
> +
> +	err = genlmsg_unicast(msg, info->snd_pid);
> +	goto put_drv;
> +
> + nla_put_failure:
> +	err = -ENOBUFS;
> + msg_free:
> +	nlmsg_free(msg);
> + put_drv:
> +	cfg80211_put_drv(drv);
> +	dev_put(dev);
> +	return err;
> +}
> +
> +static int nl80211_initiate_scan(struct sk_buff *skb, struct genl_info *info)
> +{
> +	struct cfg80211_registered_driver *drv;
> +	int err;
> +	struct net_device *dev;
> +	struct scan_params params;
> +	struct scan_channel *channels = NULL;
> +	int count = -1;
> +
> +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> +	if (err)
> +		return err;
> +
> +	if (!drv->ops->initiate_scan) {
> +		err = -EOPNOTSUPP;
> +		goto out;
> +	}
> +
> +	params.active = 0;
> +
> +	if (info->attrs[NL80211_ATTR_FLAGS])
> +		params.active = !!(nla_get_u32(info->attrs[NL80211_ATTR_FLAGS])
> +					& NL80211_FLAG_SCAN_ACTIVELY);

^^^^ see note below about naming of NL80211_FLAG_SCAN_*

> +
> +	if (info->attrs[NL80211_ATTR_CHANNEL_LIST]) {
> +		struct nlattr *attr = info->attrs[NL80211_ATTR_CHANNEL_LIST];
> +		struct nlattr *nla;
> +		int rem;
> +		struct nlattr **tb;
> +
> +		/* let's count first */
> +		count = 0;
> +		nla_for_each_attr(nla, nla_data(attr), nla_len(attr), rem)
> +			count++;
> +
> +		if (count == 0) {
> +			/* assume we should actually scan all channels,
> +			 * scanning no channels make no sense */
> +			count = -1;
> +			goto done_channels;
> +		}
> +
> +		channels = kmalloc(count * sizeof(struct scan_channel),
> +				   GFP_KERNEL);
> +		tb = kmalloc((NL80211_ATTR_MAX+1) * sizeof(struct nlattr),
> +			     GFP_KERNEL);
> +
> +		count = 0;
> +		nla_for_each_attr(nla, nla_data(attr), nla_len(attr), rem) {
> +			err = nla_parse(tb, NL80211_ATTR_MAX, nla_data(nla),
> +					nla_len(nla), nl80211_policy);
> +			if (err || !tb[NL80211_ATTR_CHANNEL]) {
> +				err = -EINVAL;
> +				kfree(tb);
> +				kfree(channels);
> +				goto out;
> +			}
> +			channels[count].channel =
> +				nla_get_u32(tb[NL80211_ATTR_CHANNEL]);
> +
> +			channels[count].active = params.active;
> +
> +			if (tb[NL80211_ATTR_FLAGS])
> +				channels[count].active =
> +					!!(nla_get_u32(tb[NL80211_ATTR_FLAGS])
> +						& NL80211_FLAG_SCAN_ACTIVELY);
> +			count++;
> +		}
> +		kfree(tb);
> +	}
> +
> + done_channels:
> +	params.channels = channels;
> +	params.n_channels = count;
> +
> +	err = drv->ops->initiate_scan(drv->priv, dev, &params);
> +
> +	kfree(channels);
> + out:
> +	cfg80211_put_drv(drv);
> +	dev_put(dev);
> +	return err;
> +}
> +
> +static struct genl_ops nl80211_ops[] = {
> +	{
> +		.cmd = NL80211_CMD_GET_CMDLIST,
> +		.doit = nl80211_get_cmdlist,
> +		.policy = nl80211_policy,
> +		/* can be retrieved by unprivileged users */
> +	},
> +	{
> +		.cmd = NL80211_CMD_GET_WIPHYS,
> +		.doit = nl80211_get_wiphys,
> +		.policy = nl80211_policy,
> +		/* can be retrieved by unprivileged users */
> +	},
> +	{
> +		.cmd = NL80211_CMD_GET_INTERFACES,
> +		.doit = nl80211_get_intfs,
> +		.policy = nl80211_policy,
> +		/* can be retrieved by unprivileged users */
> +	},
> +	{
> +		.cmd = NL80211_CMD_INJECT,
> +		.doit = nl80211_do_inject,
> +		.policy = nl80211_policy,
> +		.flags = GENL_ADMIN_PERM,
> +	},
> +	{
> +		.cmd = NL80211_CMD_ADD_VIRTUAL_INTERFACE,
> +		.doit = nl80211_add_virt_intf,
> +		.policy = nl80211_policy,
> +		.flags = GENL_ADMIN_PERM,
> +	},
> +	{
> +		.cmd = NL80211_CMD_DEL_VIRTUAL_INTERFACE,
> +		.doit = nl80211_del_virt_intf,
> +		.policy = nl80211_policy,
> +		.flags = GENL_ADMIN_PERM,
> +	},
> +	{
> +		.cmd = NL80211_CMD_CONFIGURE,
> +		.doit = nl80211_configure,
> +		.policy = nl80211_policy,
> +		.flags = GENL_ADMIN_PERM,
> +	},
> +	{
> +		.cmd = NL80211_CMD_GET_CONFIG,
> +		.doit = nl80211_get_config,
> +		.policy = nl80211_policy,
> +		/* can be retrieved by unprivileged users */
> +	},
> +	{
> +		.cmd = NL80211_CMD_SET_ROAMING_CONTROL,
> +		.doit = nl80211_set_roaming,
> +		.policy = nl80211_policy,
> +		.flags = GENL_ADMIN_PERM,
> +	},
> +	{
> +		.cmd = NL80211_CMD_GET_ROAMING_CONTROL,
> +		.doit = nl80211_get_roaming,
> +		.policy = nl80211_policy,
> +		/* can be retrieved by unprivileged users */
> +	},
> +	{
> +		.cmd = NL80211_CMD_SET_FIXED_BSSID,
> +		.doit = nl80211_set_fixed_bssid,
> +		.policy = nl80211_policy,
> +		.flags = GENL_ADMIN_PERM,
> +	},
> +	{
> +		.cmd = NL80211_CMD_GET_FIXED_BSSID,
> +		.doit = nl80211_get_fixed_bssid,
> +		.policy = nl80211_policy,
> +		/* can be retrieved by unprivileged users */
> +	},
> +	{
> +		.cmd = NL80211_CMD_GET_ASSOCIATION,
> +		.doit = nl80211_get_association,
> +		.policy = nl80211_policy,
> +		/* can be retrieved by unprivileged users */
> +	},
> +	{
> +		.cmd = NL80211_CMD_DISASSOCIATE,
> +		.doit = nl80211_assoc_deauth,
> +		.policy = nl80211_policy,
> +		.flags = GENL_ADMIN_PERM,
> +	},
> +	{
> +		.cmd = NL80211_CMD_DEAUTH,
> +		.doit = nl80211_assoc_deauth,
> +		.policy = nl80211_policy,
> +		.flags = GENL_ADMIN_PERM,
> +	},
> +	{
> +		.cmd = NL80211_CMD_REASSOCIATE,
> +		.doit = nl80211_assoc_deauth,
> +		.policy = nl80211_policy,
> +		.flags = GENL_ADMIN_PERM,
> +	},
> +	{
> +		.cmd = NL80211_CMD_GET_AUTH_LIST,
> +		.doit = nl80211_get_auth_list,
> +		.policy = nl80211_policy,
> +		/* can be retrieved by unprivileged users */
> +	},
> +	{
> +		.cmd = NL80211_CMD_INITIATE_SCAN,
> +		.doit = nl80211_initiate_scan,
> +		.policy = nl80211_policy,
> +		.flags = GENL_ADMIN_PERM,
> +	},
> +};
> +
> +
> +/* exported functions */
> +
> +void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq, int flags, u8 cmd)
> +{
> +	/* since there is no private header just add the generic one */
> +	return genlmsg_put(skb, pid, seq, nl80211_fam.id, 0,
> +			   flags, cmd, nl80211_fam.version);
> +}
> +EXPORT_SYMBOL_GPL(nl80211hdr_put);
> +
> +void *nl80211msg_new(struct sk_buff **skb, u32 pid, u32 seq, int flags, u8 cmd)
> +{
> +	void *hdr;
> +
> +	*skb = nlmsg_new(NLMSG_GOODSIZE);
> +	if (!*skb)
> +		return ERR_PTR(-ENOBUFS);
> +
> +	hdr = nl80211hdr_put(*skb, pid, seq, flags, cmd);
> +	if (!hdr) {
> +		nlmsg_free(*skb);
> +		return ERR_PTR(-ENOBUFS);
> +	}
> +
> +	return hdr;
> +}
> +EXPORT_SYMBOL_GPL(nl80211msg_new);
> +
> +/* initialisation/exit functions */
> +
> +int nl80211_init(void)
> +{
> +	int err, i;
> +
> +	err = genl_register_family(&nl80211_fam);
> +	if (err)
> +		return err;
> +
> +	for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) {
> +		err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]);
> +		if (err)
> +			goto err_out;
> +	}
> +	return 0;
> + err_out:
> + 	genl_unregister_family(&nl80211_fam);
> +	return err;
> +}
> +
> +void nl80211_exit(void)
> +{
> +	genl_unregister_family(&nl80211_fam);
> +}
> --- wireless-dev.orig/include/linux/Kbuild	2006-09-27 19:10:21.564088173 +0200
> +++ wireless-dev/include/linux/Kbuild	2006-09-27 19:14:44.224088173 +0200
> @@ -28,7 +28,7 @@ header-y += affs_fs.h affs_hardblocks.h 
>  	sound.h stddef.h synclink.h telephony.h termios.h ticable.h	\
>  	times.h tiocl.h tipc.h toshiba.h ultrasound.h un.h utime.h	\
>  	utsname.h video_decoder.h video_encoder.h videotext.h vt.h	\
> -	wavefront.h wireless.h xattr.h x25.h zorro_ids.h
> +	wavefront.h wireless.h xattr.h x25.h zorro_ids.h nl80211.h
>  
>  unifdef-y += acct.h adb.h adfs_fs.h agpgart.h apm_bios.h atalk.h	\
>  	atmarp.h atmdev.h atm.h atm_tcp.h audit.h auto_fs.h binfmts.h	\
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ wireless-dev/include/linux/nl80211.h	2006-09-28 00:38:55.264150162 +0200
> @@ -0,0 +1,276 @@
> +#ifndef __LINUX_NL80211_H
> +#define __LINUX_NL80211_H
> +/*
> + * 802.11 netlink interface public header
> + *
> + * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
> + */
> +
> +/* currently supported commands
> + * don't change the order or add anything inbetween, this is ABI! */
> +enum {
> +	/* There's no technical reason to not use command 0 but malformed
> +	 * zeroed messages may have it and this catches that */
> +	NL80211_CMD_UNSPEC,
> +
> +	/* Get supported commands by ifindex,
> +	 * uses NL80211_ATTR_CMDS (output) and NL80211_ATTR_IFINDEX (input) */
> +	NL80211_CMD_GET_CMDLIST,
> +
> +	/* Supported commands returned */
> +	NL80211_CMD_NEW_CMDLIST,
> +
> +	/* Inject a frame using NL80211_ATTR_FLAGS and NL80211_ATTR_FRAME.
> +	 * If kernel sends this, it's a status notification for the injected
> +	 * frame. */
> +	NL80211_CMD_INJECT,
> +
> +	/* add a virtual interface to a group that is identified by any
> +	 * other ifindex in the group of a wiphy index, needs the
> +	 * NL80211_IF_NAME attribute */
> +	NL80211_CMD_ADD_VIRTUAL_INTERFACE,
> +
> +	/* remove a given (with NL80211_ATTR_IFINDEX) virtual device */
> +	NL80211_CMD_DEL_VIRTUAL_INTERFACE,
> +
> +	/* get list of all wiphys */
> +	NL80211_CMD_GET_WIPHYS,
> +
> +	/* get list of all wiphys */
> +	NL80211_CMD_NEW_WIPHYS,
> +
> +	/* get list of all interfaces belonging to a wiphy */
> +	NL80211_CMD_GET_INTERFACES,
> +
> +	/* get list of all interfaces belonging to a wiphy */
> +	NL80211_CMD_NEW_INTERFACES,
> +
> +	/* configure device */
> +	NL80211_CMD_CONFIGURE,
> +
> +	/* request configuration */
> +	NL80211_CMD_GET_CONFIG,
> +
> +	/* configuration sent from kernel */
> +	NL80211_CMD_NEW_CONFIG,
> +
> +	/* initiate scan.
> +	 * Takes a CHANNEL_LIST attribute containing nested
> +	 * attributes which in turn contain CHANNEL and FLAGS
> +	 * attributes.
> +	 * The top level can also contain a FLAGS attribute
> +	 * which is then the default for each channel.
> +	 * If no channel list is given (or it is empty)
> +	 * all channels shall be scanned. */
> +	NL80211_CMD_INITIATE_SCAN,
> +
> +	/* scan result (kernel -> userspace) */
> +	NL80211_CMD_SCAN_RESULT,
> +
> +	/* change roaming control */
> +	NL80211_CMD_SET_ROAMING_CONTROL,
> +
> +	/* get roaming control setting */
> +	NL80211_CMD_GET_ROAMING_CONTROL,
> +
> +	/* answer to that */
> +	NL80211_CMD_ROAMING_CONTROL,
> +
> +	/* set access point BSSID for userspace roaming */
> +	NL80211_CMD_SET_FIXED_BSSID,
> +
> +	/* get currently set userspace roaming BSSID */
> +	NL80211_CMD_GET_FIXED_BSSID,
> +
> +	/* currently set roaming BSSID */
> +	NL80211_CMD_FIXED_BSSID,
> +
> +	/* get current association information, if not associated then
> +	 * the BSSID attribute is not present in response */
> +	NL80211_CMD_GET_ASSOCIATION,
> +
> +	/* association notification and response to GET_BSSID */
> +	NL80211_CMD_ASSOCIATION_CHANGED,
> +
> +	/* disassociate from current AP */
> +	NL80211_CMD_DISASSOCIATE,
> +
> +	/* deauth from current AP */
> +	NL80211_CMD_DEAUTH,
> +
> +	/* re-associate with current settings
> +	 * (SSID and BSSID if roaming control in userspace) */
> +	NL80211_CMD_REASSOCIATE,
> +
> +	/* request the full list of BSSs the device is
> +	 * authenticated with */
> +	NL80211_CMD_GET_AUTH_LIST,
> +
> +	/* sent as a response to GET_AUTH_LIST containing
> +	 * an ATTR_BSSID_LIST */
> +	NL80211_CMD_AUTH_LIST,
> +
> +	/* sent when authenticating/deauthenticating.
> +	 * contains an ATTR_BSSID and possibly an
> +	 * ATTR_DEAUTHENTICATED */
> +	NL80211_CMD_AUTHENTICATION_CHANGED,
> +
> +	/* add commands here */
> +
> +	/* used to define NL80211_CMD_MAX below */
> +	__NL80211_CMD_AFTER_LAST,
> +};
> +#define NL80211_CMD_MAX (__NL80211_CMD_AFTER_LAST - 1)
> +
> +
> +/* currently supported attributes.
> + * don't change the order or add anything inbetween, this is ABI! */
> +enum {
> +	NL80211_ATTR_UNSPEC,
> +
> +	/* network device (ifindex) to operate on */
> +	NL80211_ATTR_IFINDEX,
> +
> +	/* wiphy index to operate on */
> +	NL80211_ATTR_WIPHY,
> +
> +	/* list of u8 cmds that a given device implements */
> +	NL80211_ATTR_CMDS,
> +
> +	/* flags for injection and other commands, see below */
> +	NL80211_ATTR_FLAGS,
> +
> +	/* which hardware queue to use */
> +	NL80211_ATTR_QUEUE,
> +
> +	/* frame to inject or received frame for mgmt frame subscribers */
> +	NL80211_ATTR_FRAME,
> +
> +	/* interface name */
> +	NL80211_ATTR_IFNAME,
> +
> +	/* type of (virtual) interface */
> +	NL80211_ATTR_IFTYPE,
> +
> +	/* interface list */
> +	NL80211_ATTR_INTERFACE_LIST,
> +
> +	/* wiphy list */
> +	NL80211_ATTR_WIPHY_LIST,
> +
> +	/* attributes used for configuration */
> +	/* network ID (pre 802.11 HW) */
> +	NL80211_ATTR_NETWORK_ID,
> +
> +	/* channel, 1-14 are B/G */
> +	NL80211_ATTR_CHANNEL,
> +
> +	/* channel list for scan determination */
> +	NL80211_ATTR_CHANNEL_LIST,
> +
> +	/* receiver sensitivity in dBm */
> +	NL80211_ATTR_RX_SENSITIVITY,
> +
> +	/* BSSID to associate to, only used when roaming control
> +	 * is in userspace */
> +	NL80211_ATTR_BSSID,
> +
> +	/* list of multiple BSSIDs, this is a nested attribute
> +	 * containing an index->(attrs) mapping */
> +	NL80211_ATTR_BSSID_LIST,
> +
> +	/* this is a flag for when an authentication is lost */
> +	NL80211_ATTR_DEAUTHENTICATED,
> +
> +	/* SSID of ESS to associate to */
> +	NL80211_ATTR_SSID,
> +
> +	/* transmit power in mW */
> +	NL80211_ATTR_TRANSMIT_POWER,
> +
> +	/* fragmentation threshold in bytes */
> +	NL80211_ATTR_FRAG_THRESHOLD,
> +
> +	/* one or more information elements */
> +	NL80211_ATTR_INFORMATION_ELEMENT,
> +
> +	NL80211_ATTR_ROAMING_CONTROL,
> +
> +	NL80211_ATTR_SCAN_TYPE,
> +
> +	/* add attributes here */
> +
> +	/* used to define NL80211_ATTR_MAX below */
> +	__NL80211_ATTR_AFTER_LAST,
> +};
> +#define NL80211_ATTR_MAX (__NL80211_ATTR_AFTER_LAST - 1)
> +
> +/**
> + * NL80211_FLAG_TXSTATUS - send transmit status indication
> + */
> +#define NL80211_FLAG_TXSTATUS		(1<<0)
> +/**
> + * NL80211_FLAG_ENCRYPT - encrypt this packet
> + * Warning: This looks inside the packet header!
> + */
> +#define NL80211_FLAG_ENCRYPT		(1<<1)
> +
> +/**
> + * NL80211_FLAG_SCAN_ACTIVELY - set this with a scan
> + * request to have it scan actively, can also be used
> + * within the nested CHANNEL_LIST...
> + */
> +#define NL80211_FLAG_SCAN_ACTIVELY	(1<<2)

ACTIVELY is an adverb, and that doesn't parse very well in this context.
It also just sounds weird here.  Better options:

FLAG_SCAN_ACTIVE
FLAG_SCAN_TYPE_ACTIVE (<-- my favorite)
FLAG_ACTIVE_SCAN

Looks great,

Dan

> +/**
> + * maximum length of a frame that can be injected
> + */
> +#define NL80211_MAX_FRAME_LEN 2500
> +
> +/* this is an arbitrary limit, 516 means two full-length
> + * IEs would fit... */
> +/**
> + * maximum length of IE(s) passed in an NL80211_ATTR_INFORMATION_ELEMENT.
> + */
> +#define NL80211_MAX_IE_LEN 516
> +
> +/* may need to be bumped? */
> +/**
> + * maximum number of items in an ATTR_CHANNEL_LIST
> + */
> +#define NL80211_MAX_CHANNEL_LIST_ITEM 20
> +
> +/**
> + * &enum nl80211_iftype - (virtual) interface types
> + *
> + * This structure is used with the NL80211_ATTR_IFTYPE
> + * to set the type of an interface.
> + * Note that these are intentionally compatible with
> + * the IW_MODE_* constants except for the removal of
> + * IW_MODE_AUTO.
> + *
> + */
> +enum {
> +	NL80211_IFTYPE_UNSPECIFIED,
> +	NL80211_IFTYPE_ADHOC,
> +	NL80211_IFTYPE_STATION,
> +	NL80211_IFTYPE_AP,
> +	NL80211_IFTYPE_WDS,
> +	NL80211_IFTYPE_SECONDARY,
> +	NL80211_IFTYPE_MONITOR,
> +
> +	/* keep last */
> +	__NL80211_IFTYPE_AFTER_LAST
> +};
> +#define NL80211_IFTYPE_MAX (__NL80211_IFTYPE_AFTER_LAST - 1)
> +
> +enum {
> +	NL80211_ROAMING_CONTROL_KERNEL,
> +	NL80211_ROAMING_CONTROL_USERSPACE,
> +
> +	/* keep last */
> +	__NL80211_ROAMING_CONTROL_AFTER_LAST
> +};
> +#define NL80211_ROAMING_CONTROL_MAX (__NL80211_ROAMING_CONTROL_AFTER_LAST-1)
> +
> +#endif /* __LINUX_NL80211_H */
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ wireless-dev/include/net/cfg80211.h	2006-09-28 00:38:55.274150162 +0200
> @@ -0,0 +1,191 @@
> +#ifndef __NET_CFG80211_H
> +#define __NET_CFG80211_H
> +
> +#include <linux/netlink.h>
> +#include <linux/nl80211.h>
> +#include <linux/skbuff.h>
> +#include <linux/netdevice.h>
> +#include <net/genetlink.h>
> +
> +/*
> + * 802.11 configuration in-kernel interface
> + *
> + * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
> + */
> +
> +/**
> + * struct cfg80211_config - description of a configuration (request)
> + */
> +struct cfg80211_config {
> +	/* first fields with 'internal' validity */
> +
> +	/* SSID to use, valid if not NULL. change forces reassociation */
> +	u8 *ssid;
> +
> +	/* now fields with explicit validity */
> +#define CFG80211_CFG_VALID_NWID			(1<<0)
> +#define CFG80211_CFG_VALID_RX_SENSITIVITY	(1<<1)
> +#define CFG80211_CFG_VALID_TRANSMIT_POWER	(1<<2)
> +#define CFG80211_CFG_VALID_FRAG_THRESHOLD	(1<<3)
> +#define CFG80211_CFG_VALID_CHANNEL		(1<<4)
> +	unsigned int valid;
> +
> +	u16 network_id;
> +	s32 rx_sensitivity;
> +	u32 transmit_power;
> +	u32 fragmentation_threshold;
> +	u32 channel;
> +};
> +
> +struct scan_channel {
> +	u32 channel;
> +	int active;
> +};
> +
> +struct scan_params {
> +	/* number of items in 'channels' array
> +	 * or -1 to indicate scanning all channels
> +	 * (in that case 'channels' is NULL) */
> +	int n_channels;
> +
> +	/* use only when n_channels is -1 to determine
> +	 * whether scanning should be active or not */
> +	int active;
> +
> +	/* the channel list if any */
> +	struct scan_channel *channels;
> +};
> +
> +/**
> + * struct cfg80211_ops - backend description for wireless configuration
> + *
> + * This struct is registered by fullmac card drivers and/or wireless stacks
> + * in order to handle configuration requests on their interfaces.
> + *
> + * The priv pointer passed to each call is the pointer that was
> + * registered in cfg80211_register_driver().
> + *
> + * All callbacks except where otherwise noted should return 0
> + * on success or a negative error code.
> + *
> + * @list_interfaces: for each interfaces belonging to the wiphy identified
> + *		     by the priv pointer, call the one() function with the
> + *		     given data and the ifindex. This callback is required.
> + *
> + * @inject_packet: inject the given frame with the NL80211_FLAG_*
> + *		   flags onto the given queue.
> + *
> + * @add_virtual_intf: create a new virtual interface with the given name
> + *
> + * @del_virtual_intf: remove the virtual interface determined by ifindex.
> + *
> + * @configure: configure the given interface as requested in the config struct.
> + *	       must not ignore any configuration item, if something is
> + *	       is requested that cannot be fulfilled return an error
> + *
> + * @get_config: fill the given config structure with the current configuration
> + *
> + * @reassociate: reassociate with current settings (SSID, BSSID if
> + *		 userspace roaming is enabled)
> + *
> + * @disassociate: disassociate from current AP
> + *
> + * @deauth: deauth from current AP
> + *
> + * @initiate_scan: ...
> + *
> + * @set_roaming: set who gets to control roaming, the roaming_control
> + *		 parameter is passed NL80211_ROAMING_CONTROL_* values.
> + *
> + * @get_roaming: return where roaming control currently is done or
> + *		 a negative error.
> + *
> + * @set_fixed_bssid: set BSSID to use with userspace roaming, forces
> + *		     reassociation if changing.
> + * @get_fixed_bssid: get BSSID that is used with userspace roaming,
> + *		     the bssid parameter has space for 6 bytes
> + *
> + * @get_association: get BSSID of the BSS that the device is currently
> + *		     associated to and return 1, or return 0 if not
> + *		     associated (or a negative error code)
> + * @get_auth_list: get list of BSSIDs of all BSSs the device has
> + *		   authenticated with, must call next_bssid for each,
> + *		   next_bssid returns non-zero on error, the given data
> + *		   is to be passed to that callback
> + */
> +struct cfg80211_ops {
> +	int	(*list_interfaces)(void *priv, void *data,
> +				   int (*one)(void *data, int ifindex));
> +
> +
> +	int	(*inject_packet)(void *priv, void *frame, int framelen,
> +				 u32 flags, int queue);
> +
> +
> +	int	(*add_virtual_intf)(void *priv, char *name,
> +				    unsigned int type);
> +	int	(*del_virtual_intf)(void *priv, int ifindex);
> +
> +
> +	int	(*configure)(void *priv, struct net_device *dev,
> +			     struct cfg80211_config *cfg);
> +	void	(*get_config)(void *priv, struct net_device *dev,
> +			      struct cfg80211_config *cfg);
> +
> +
> +	int	(*reassociate)(void *priv, struct net_device *dev);
> +	int	(*disassociate)(void *priv, struct net_device *dev);
> +	int	(*deauth)(void *priv, struct net_device *dev);
> +
> +
> +	int	(*initiate_scan)(void *priv, struct net_device *dev,
> +				 struct scan_params *params);
> +
> +
> +	int	(*set_roaming)(void *priv, struct net_device *dev,
> +			       int roaming_control);
> +	int	(*get_roaming)(void *priv, struct net_device *dev);
> +	int	(*set_fixed_bssid)(void *priv, struct net_device *dev,
> +				   u8 *bssid);
> +	int	(*get_fixed_bssid)(void *priv, struct net_device *dev,
> +				   u8 *bssid);
> +
> +
> +	int	(*get_association)(void *priv, struct net_device *dev,
> +				   u8 *bssid);
> +
> +	int	(*get_auth_list)(void *priv, struct net_device *dev,
> +				 void *data,
> +				 int (*next_bssid)(void *data, u8 *bssid));
> +};
> +
> +/**
> + * cfg80211_register - register a wiphy with cfg80211
> + *
> + * register a given method structure with the cfg80211 system
> + * and associate the 'priv' pointer with it.
> + *
> + * Returns a non-negative wiphy index or a negative error code.
> + *
> + * NOTE: for proper operation, this priv pointer MUST also be
> + * assigned to each &struct net_device's @ieee80211_ptr member!
> + */
> +extern int cfg80211_register(struct cfg80211_ops *ops, void *priv);
> +
> +/**
> + * cfg80211_unregister - deregister a wiphy from cfg80211
> + *
> + * unregister a device with the given priv pointer.
> + * After this call, no more requests can be made with this priv
> + * pointer, but the call may sleep to wait for an outstanding
> + * request that is being handled.
> + */
> +extern void cfg80211_unregister(void *priv);
> +
> +/* helper functions specific to nl80211 */
> +extern void *nl80211hdr_put(struct sk_buff *skb, u32 pid,
> +			    u32 seq, int flags, u8 cmd);
> +extern void *nl80211msg_new(struct sk_buff **skb, u32 pid,
> +			    u32 seq, int flags, u8 cmd);
> +
> +#endif /* __NET_CFG80211_H */
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ wireless-dev/net/wireless/core.c	2006-09-28 00:38:22.664150162 +0200
> @@ -0,0 +1,233 @@
> +/*
> + * This is the new wireless configuration interface.
> + *
> + * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
> + */
> +
> +#include "core.h"
> +#include "nl80211.h"
> +#include <linux/if.h>
> +#include <linux/module.h>
> +#include <linux/err.h>
> +#include <net/genetlink.h>
> +#include <net/cfg80211.h>
> +#include <linux/mutex.h>
> +#include <linux/list.h>
> +
> +MODULE_AUTHOR("Johannes Berg");
> +MODULE_LICENSE("GPL");
> +
> +/* RCU might be appropriate here since we usually
> + * only read the list, and that can happen quite
> + * often because we need to do it for each command */
> +LIST_HEAD(cfg80211_drv_list);
> +DEFINE_MUTEX(cfg80211_drv_mutex);
> +static int wiphy_counter;
> +
> +/* requires nl80211_drv_mutex to be held! */
> +static struct cfg80211_registered_driver *cfg80211_drv_by_priv(void *priv)
> +{
> +	struct cfg80211_registered_driver *result = NULL, *drv;
> +
> +	if (!priv)
> +		return NULL;
> +
> +	list_for_each_entry(drv, &cfg80211_drv_list, list) {
> +		if (drv->priv == priv) {
> +			result = drv;
> +			break;
> +		}
> +	}
> +
> +	return result;
> +}
> +
> +/* requires cfg80211_drv_mutex to be held! */
> +static struct cfg80211_registered_driver *cfg80211_drv_by_wiphy(int wiphy)
> +{
> +	struct cfg80211_registered_driver *result = NULL, *drv;
> +
> +	list_for_each_entry(drv, &cfg80211_drv_list, list) {
> +		if (drv->wiphy == wiphy) {
> +			result = drv;
> +			break;
> +		}
> +	}
> +
> +	return result;
> +}
> +
> +/* requires cfg80211_drv_mutex to be held! */
> +static struct cfg80211_registered_driver *
> +__cfg80211_drv_from_info(struct genl_info *info)
> +{
> +	int ifindex;
> +	struct cfg80211_registered_driver *bywiphy = NULL, *byifidx = NULL;
> +	struct net_device *dev;
> +	int err = -EINVAL;
> +
> +	if (info->attrs[NL80211_ATTR_WIPHY]) {
> +		bywiphy = cfg80211_drv_by_wiphy(
> +				nla_get_u32(info->attrs[NL80211_ATTR_WIPHY]));
> +		err = -ENODEV;
> +	}
> +
> +	if (info->attrs[NL80211_ATTR_IFINDEX]) {
> +		ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
> +		dev = dev_get_by_index(ifindex);
> +		if (dev) {
> +			byifidx = cfg80211_drv_by_priv(dev->ieee80211_ptr);
> +			dev_put(dev);
> +		}
> +		err = -ENODEV;
> +	}
> +
> +	if (bywiphy && byifidx) {
> +		if (bywiphy != byifidx)
> +			return ERR_PTR(-EINVAL);
> +		else
> +			return bywiphy; /* == byifidx */
> +	}
> +	if (bywiphy)
> +		return bywiphy;
> +
> +	if (byifidx)
> +		return byifidx;
> +
> +	return ERR_PTR(err);
> +}
> +
> +struct cfg80211_registered_driver *
> +cfg80211_get_drv_from_info(struct genl_info *info)
> +{
> +	struct cfg80211_registered_driver *drv;
> +
> +	mutex_lock(&cfg80211_drv_mutex);
> +	drv = __cfg80211_drv_from_info(info);
> +
> +	/* if it is not an error we grab the lock on
> +	 * it to assure it won't be going away while
> +	 * we operate on it */
> +	if (!IS_ERR(drv))
> +		mutex_lock(&drv->mtx);
> +
> +	mutex_unlock(&cfg80211_drv_mutex);
> +
> +	return drv;
> +}
> +
> +/* wext will need this */
> +struct cfg80211_registered_driver *
> +cfg80211_get_drv_from_ifindex(int ifindex)
> +{
> +	struct cfg80211_registered_driver *drv;
> +	struct net_device *dev;
> +
> +	mutex_lock(&cfg80211_drv_mutex);
> +	dev = dev_get_by_index(ifindex);
> +	if (!dev)
> +		return ERR_PTR(-ENODEV);
> +	drv = cfg80211_drv_by_priv(dev->ieee80211_ptr);
> +	if (drv)
> +		mutex_lock(&drv->mtx);
> +	dev_put(dev);
> +	if (drv)
> +		return drv;
> +	return ERR_PTR(-ENODEV);
> +}
> +
> +void cfg80211_put_drv(struct cfg80211_registered_driver *drv)
> +{
> +	BUG_ON(IS_ERR(drv));
> +	mutex_unlock(&drv->mtx);
> +}
> +
> +/* exported functions */
> +
> +int cfg80211_register(struct cfg80211_ops *ops, void *priv)
> +{
> +	struct cfg80211_registered_driver *drv;
> +	int res;
> +
> +	if (!priv || !ops->list_interfaces)
> +		return -EINVAL;
> +
> +	mutex_lock(&cfg80211_drv_mutex);
> +
> +	if (cfg80211_drv_by_priv(priv)) {
> +		res = -EALREADY;
> +		goto out_unlock;
> +	}
> +
> +	drv = kzalloc(sizeof(struct cfg80211_registered_driver), GFP_KERNEL);
> +	if (!drv) {
> +		res = -ENOMEM;
> +		goto out_unlock;
> +	}
> +
> +	drv->ops = ops;
> +	drv->priv = priv;
> +
> +	if (unlikely(wiphy_counter<0)) {
> +		/* ugh, wrapped! */
> +		kfree(drv);
> +		res = -ENOSPC;
> +		goto out_unlock;
> +	}
> +	mutex_init(&drv->mtx);
> +	drv->wiphy = wiphy_counter;
> +	list_add(&drv->list, &cfg80211_drv_list);
> +	/* return wiphy number */
> +	res = drv->wiphy;
> +
> +	/* now increase counter for the next time */
> +	wiphy_counter++;
> +
> + out_unlock:
> +	mutex_unlock(&cfg80211_drv_mutex);
> +	return res;
> +}
> +EXPORT_SYMBOL_GPL(cfg80211_register);
> +
> +void cfg80211_unregister(void *priv)
> +{
> +	struct cfg80211_registered_driver *drv;
> +
> +	mutex_lock(&cfg80211_drv_mutex);
> +	drv = cfg80211_drv_by_priv(priv);
> +	if (!drv) {
> +		printk(KERN_ERR "deregistering cfg80211 backend that "
> +		       " was never registered!\n");
> +		mutex_unlock(&cfg80211_drv_mutex);
> +		return;
> +	}
> +
> +	/* hold registered driver mutex during list removal as well
> +	 * to make sure no commands are in progress at the moment */
> +	mutex_lock(&drv->mtx);
> +	list_del(&drv->list);
> +	mutex_unlock(&drv->mtx);
> +
> +	mutex_unlock(&cfg80211_drv_mutex);
> +
> +	mutex_destroy(&drv->mtx);
> +	kfree(drv);
> +}
> +EXPORT_SYMBOL_GPL(cfg80211_unregister);
> +
> +/* module initialisation/exit functions */
> +
> +static int cfg80211_init(void)
> +{
> +	/* possibly need to do more later */
> +	return nl80211_init();
> +}
> +
> +static void cfg80211_exit(void)
> +{
> +	/* possibly need to do more later */
> +	nl80211_exit();
> +}
> +
> +module_init(cfg80211_init);
> +module_exit(cfg80211_exit);
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ wireless-dev/net/wireless/core.h	2006-09-28 00:38:22.734150162 +0200
> @@ -0,0 +1,57 @@
> +/*
> + * Wireless configuration interface internals.
> + *
> + * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
> + */
> +#ifndef __NET_WIRELESS_CORE_H
> +#define __NET_WIRELESS_CORE_H
> +#include <net/cfg80211.h>
> +#include <linux/mutex.h>
> +#include <linux/list.h>
> +#include <net/genetlink.h>
> +
> +struct cfg80211_registered_driver {
> +	struct cfg80211_ops *ops;
> +	int wiphy;
> +	void *priv;
> +	struct list_head list;
> +	/* we hold this mutex during any call so that
> +	 * we cannot do multiple calls at once, and also
> +	 * to avoid the deregister call to proceed while
> +	 * any call is in progress */
> +	struct mutex mtx;
> +};
> +
> +extern struct mutex cfg80211_drv_mutex;
> +extern struct list_head cfg80211_drv_list;
> +
> +/*
> + * This function returns a pointer to the driver
> + * that the genl_info item that is passed refers to.
> + * If successful, it returns non-NULL and also locks
> + * the driver's mutex!
> + *
> + * This means that you need to call cfg80211_put_drv()
> + * before being allowed to acquire &cfg80211_drv_mutex!
> + *
> + * This is necessary because we need to lock the global
> + * mutex to get an item off the list safely, and then
> + * we lock the drv mutex so it doesn't go away under us.
> + *
> + * We don't want to keep cfg80211_drv_mutex locked
> + * for all the time in order to allow requests on
> + * other interfaces to go through at the same time.
> + *
> + * The result of this can be a PTR_ERR and hence must
> + * be checked with IS_ERR() for errors.
> + */
> +extern struct cfg80211_registered_driver *
> +cfg80211_get_drv_from_info(struct genl_info *info);
> +
> +/* identical to cfg80211_get_drv_from_info but only operate on ifindex */
> +extern struct cfg80211_registered_driver *
> +cfg80211_get_drv_from_ifindex(int ifindex);
> +
> +extern void cfg80211_put_drv(struct cfg80211_registered_driver *drv);
> +
> +#endif /* __NET_WIRELESS_CORE_H */
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ wireless-dev/net/wireless/nl80211.h	2006-09-27 19:36:17.744088173 +0200
> @@ -0,0 +1,7 @@
> +#ifndef __NET_WIRELESS_NL80211_H
> +#define __NET_WIRELESS_NL80211_H
> +
> +extern int nl80211_init(void);
> +extern void nl80211_exit(void);
> +
> +#endif /* __NET_WIRELESS_NL80211_H */
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ wireless-dev/net/wireless/wext-compat.c	2006-09-28 00:38:55.304150162 +0200
> @@ -0,0 +1,25 @@
> +/* NOT YET */
> +
> +To implement compatibility, we add a new field to struct net_device
> +that contains the pending configuration structure. This is dynamically
> +allocated when needed and freed when committed.
> +In a way it replaces the wireless_handlers field in there which is now
> +done by dynamic lookup. No worries. No one is going to have thousands
> +of wireless devices, and if that changes we can still trivially change
> +this assumption :)
> +
> +Commit is done some time after the last parameter was changed
> +(with each parameter change simply (re-)schedule a timer) or
> +if explicitly asked for. This is probably not what most people
> +would expect, but perfectly fine in the WE API.
> +
> +compatibility mappings:
> +
> +SIOCSIWAP
> +  -> if bssid is all-ones: set roaming to kernel, reassociate
> +  -> if bssid is all-zeroes: set roaming to kernel
> +  -> otherwise: set roaming to userspace, set bssid
> +
> +SIOCGIWAP
> +  -> get association parameters and fill return bssid appropriately
> +
> 
> -
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC] cfg80211 and nl80211
  2006-10-02 16:15 ` Dan Williams
@ 2006-10-02 17:01   ` Dan Williams
  2006-10-04  7:41   ` Johannes Berg
  2006-10-05  7:47   ` Johannes Berg
  2 siblings, 0 replies; 16+ messages in thread
From: Dan Williams @ 2006-10-02 17:01 UTC (permalink / raw)
  To: Johannes Berg; +Cc: netdev, Jiri Benc, John W. Linville, Larry Finger

On Mon, 2006-10-02 at 12:15 -0400, Dan Williams wrote:
> On Thu, 2006-09-28 at 11:23 +0200, Johannes Berg wrote:
> > This patch adds cfg80211, a new configuration system for wireless hardware
> > as well as nl80211, the netlink-based userspace interface for it.
> > 
> > It currently features a bunch of configuration requests, support for
> > adding and removing virtual interfaces, the ability to inject packets and
> > more.
> > 
> > Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
> > ---
> > This is the
> > 
> >   I think nl80211 is the future if I don't have to do it all by myself
> >     -- johill
> > 
> > release. Please take the time to read at least the stuff before the patch.
> > 
> > There should be support for notifications, but that isn't currently done
> > as I'm still thinking about how to do it best.
> > 
> > It probably still requires the patches in
> > http://marc.theaimsgroup.com/?l=linux-netdev&m=115625436628696&w=2
> > and
> > http://marc.theaimsgroup.com/?l=linux-netdev&m=115625168405439&w=2
> > 
> > (the latter doesn't apply cleanly against wireless-dev, but you can
> > safely ignore the pieces that don't, at least for wireless testing :) )
> > 
> > It also requires the NLA_PUT_FLAG patch I did:
> > http://marc.theaimsgroup.com/?l=linux-netdev&m=115650333420169&w=2
> > 
> > (obviously, all those patches aren't required any more if they have been
> > applied, but I've been lazy and not tracked which are and which aren't)
> > 
> > I've removed the use of NLA_CUSTOM_CHECK as it isn't going to be applied,
> > but that currently results in a warning about an unused static method.
> > 
> > Also new in this iteration is a lot more fleshed-out internal cfg80211
> > interface. Also new is the following paragraph ;)
> > 
> > Here's an explanation of what cfg80211 and nl80211 are. cfg80211 is
> > the interface between the configuration backend and the driver or stack
> > ("users" of cfg80211). cfg80211 provides a wiphy index whenever a user
> > registers with it, and allows things to be enumerated etc. nl80211 is
> > currently the only interface between cfg80211 and userspace and will
> > most likely remain the primary interface forever, but it is possible
> > to add (for example) a configfs interface between userspace and cfg80211,
> > users of cfg80211 can remain blissfully unaware of this. Also, WExt
> > backward compatibility will be implemented as an interface between
> > userspace and cfg80211.
> > 
> > cfg80211 does, however, use some constants defined in nl80211 in order
> > to not define them twice (and the other way around isn't quite good
> > because they need to be visible in userspace and cfg80211 is not).
> > 
> > I'm still not sure if there should be an explicit 'device supports
> > configuration parameters x, y and z' or if userspace should just
> > grab the current configuration (which is supposed to be complete then)
> 
> I'm not sure what you mean here.  Do you really mean "grab the current
> _cmdlist_"?  Because I'm not sure how grabbing the current configuration
> (using GET_CONFIG) would necessarily return the right set of options for
> the device.  Also, what do you mean by "is supposed to be complete
> then"?
> 
> On large problem we had with WPA was that there was _no_ way to tell
> whether or not a driver supported it.  Trying a WPA-related ioctl() and
> hoping for the best is broken.  On the one hand, there were users

Just to clarify, there was no way to cleanly determine WPA support
before drivers started adding IW_ENC_CAPA_WPA to range->enc_capa.

> screaming that yes, Madwifi did in fact support WPA, but of course,
> there was no standard way of figuring that out and present it to the
> user in a sane manner.  We need to have an _explicit_ list of stuff that
> driver does and does not support so that intelligent decisions can be
> made before and/or without touching stuff that might turn the radio on
> or mess up an existing configuration set by somebody else.
> 
> > and see what's in it. Along the same lines, should unsupported
> > parameters result in the rejection, or should they be ignored?
> > (currently they are ignored)
> 
> Does "parameters" here mean CMD or ATTR?  In any case, there's a good
> case to be made for rejecting unsupported CMDs & ATTRs.  If the user,
> for example, wishes to restrict the roaming to a set of BSSIDs for
> security measures, for example (even if an insecure one), but the driver
> doesn't support that, should nl80211 just blindly pretend that it
> worked?  This might also get people to fix up their drivers and
> userspace programs too.
> 
> I do realize there's an API extensibility benefit to ignoring stuff you
> don't handle though (like 802.11 information element handling).  But if
> we're talking about an explicit request by a user for an option, if the
> driver doesn't support it, they should get an error.
> 
> > As mentioned to Larry, I'd like to integrate his great work on
> > regulation and use nl80211 as the userspace interface for it. I do
> > have a couple of questions wrt. that:
> > 
> >  * why should there be configuration per device? The user can only
> >    be operating in one country at a time... I think that information
> >    should just be available inside cfg80211 in a global structure
> >    for use by drivers whenever they need it (with some accessor
> >    methods to ensure locked access).
> > 
> >  * as far as I understood the communication is the kernel telling
> >    the daemon all the information it has (which may be none, the
> >    country from a broadcasting AP or more info from that) and the
> >    daemon then builds up a correct set of limitations and gives that
> >    to the kernel, without the daemon the kernel limits to some minimal
> >    set that is (likely) legal everywhere. Correct?
> > 
> >  * Should the userspace daemon be allowed to unilaterally update the
> >    regulatory information if it learns something new (via the user)?
> >    Or why not even just publish the regulatory information APs might
> >    broadcast in the scan results, and let the userspace daemon pick
> >    that apart? Then the kernel need not ask for anything at all...
> 
> That sounds like a good idea.  Putting 802.11d handling stuff into the
> kernel would essentially be duplicating the code and function of the
> userspace regulatory daemon, right?  That seems pointless.
> 
> >  * I seem to have read between the lines that the EEPROM data is
> >    pretty much useless. Is that generally true, or should the userspace
> >    daemon be told what it contains (somehow)?
> 
> Expose it, but don't use it blindly and certainly don't trust it.  Let
> the userspace daemon determine the policy as it sees fit from a variety
> of sources, _including_ EEPROM.
> 
> >  * Should the kernel perform some kind of validation on the regulatory
> >    data the daemon gives it as well?
> 
> "Trust, but verify." :)  At a minimum, do bounds-checking on the channel
> numbers and stuff that's standardized.  No channel 15 for us.  But the
> kernel probably shouldn't be including lookup tables for each region's
> channel mask, which again essentially duplicates the work done by the
> daemon.
> 
> Another questions; I didn't see anything for encryption and auth and
> stuff yet.  Are you just trying to get the basics down before going on
> to that stuff?  What do you still have on your ToDo list for nl80211
> before you'd consider "ready" to take over real configuration functions?
> 
> More comments below...  All in all looks nice and clean, for now :)
> 
> > Right now I'd think that it would make sense to just leave the whole
> > task to our userspace daemon, iow. nl80211 just provides a command
> > to update the kernel's knowledge about regulory and the daemon periodically
> > checks the scan results for country information, asks the user for
> > the country, or similar. If it's not running, the kernel simply starts
> > from a generic no-frills set.
> > 
> > --- wireless-dev.orig/net/Kconfig	2006-09-27 19:10:21.384088173 +0200
> > +++ wireless-dev/net/Kconfig	2006-09-27 19:14:44.214088173 +0200
> > @@ -250,6 +250,9 @@ source "net/ieee80211/Kconfig"
> >  config WIRELESS_EXT
> >  	bool
> >  
> > +config CFG80211
> > +	tristate
> > +
> >  endif   # if NET
> >  endmenu # Networking
> >  
> > --- wireless-dev.orig/net/Makefile	2006-09-27 19:10:21.414088173 +0200
> > +++ wireless-dev/net/Makefile	2006-09-27 19:14:44.214088173 +0200
> > @@ -44,6 +44,7 @@ obj-$(CONFIG_ECONET)		+= econet/
> >  obj-$(CONFIG_VLAN_8021Q)	+= 8021q/
> >  obj-$(CONFIG_IP_DCCP)		+= dccp/
> >  obj-$(CONFIG_IP_SCTP)		+= sctp/
> > +obj-$(CONFIG_CFG80211)		+= wireless/
> >  obj-$(CONFIG_D80211)		+= d80211/
> >  obj-$(CONFIG_IEEE80211)		+= ieee80211/
> >  obj-$(CONFIG_TIPC)		+= tipc/
> > --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> > +++ wireless-dev/net/wireless/Makefile	2006-09-27 19:14:44.214088173 +0200
> > @@ -0,0 +1,4 @@
> > +obj-$(CONFIG_CFG80211) += cfg80211.o
> > +
> > +cfg80211-objs := \
> > +	core.o nl80211.o
> > --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> > +++ wireless-dev/net/wireless/nl80211.c	2006-09-28 01:27:06.094150162 +0200
> > @@ -0,0 +1,1048 @@
> > +/*
> > + * This is the new netlink-based wireless configuration interface.
> > + *
> > + * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
> > + */
> > +
> > +#include <linux/if.h>
> > +#include <linux/module.h>
> > +#include <linux/err.h>
> > +#include <net/genetlink.h>
> > +#include <net/cfg80211.h>
> > +#include <linux/mutex.h>
> > +#include <linux/list.h>
> > +#include <linux/if_ether.h>
> > +#include "core.h"
> > +#include "nl80211.h"
> > +
> > +/* the netlink family */
> > +static struct genl_family nl80211_fam = {
> > +	.id = GENL_ID_GENERATE,	/* don't bother with a hardcoded ID */
> > +	.name = "nl80211",	/* have users key off the name instead */
> > +	.hdrsize = 0,		/* no private header */
> > +	.version = 1,		/* no particular meaning now */
> > +	.maxattr = NL80211_ATTR_MAX,
> > +};
> > +
> > +/* internal helper: validate an information element attribute */
> > +static int check_information_element(struct nlattr *nla)
> > +{
> > +	int len = nla_len(nla);
> > +	u8 *data = nla_data(nla);
> > +	int elementlen;
> > +
> > +	while (len >= 2) {
> > +		/* 1 byte ID, 1 byte len, `len' bytes data */
> > +		elementlen = *(data+1) + 2;
> > +		data += elementlen;
> > +		len -= elementlen;
> > +	}
> > +	return len ? -EINVAL : 0;
> > +}
> > +
> > +/* internal helper: get drv and dev */
> > +static int get_drv_dev_by_info_ifindex(struct genl_info *info,
> > +				       struct cfg80211_registered_driver **drv,
> > +				       struct net_device **dev)
> > +{
> > +	int ifindex;
> > +
> > +	if (!info->attrs[NL80211_ATTR_IFINDEX])
> > +		return -EINVAL;
> > +
> > +	ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
> > +	*dev = dev_get_by_index(ifindex);
> > +	if (!dev)
> > +		return -ENODEV;
> > +
> > +	*drv = cfg80211_get_drv_from_ifindex(ifindex);
> > +	if (IS_ERR(*drv)) {
> > +		dev_put(*dev);
> > +		return PTR_ERR(*drv);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/* policy for the attributes */
> > +static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
> > +	[NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
> > +	[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
> > +	[NL80211_ATTR_FLAGS] = { .type = NLA_U32 },
> > +	[NL80211_ATTR_QUEUE] = { .type = NLA_U32 },
> > +	[NL80211_ATTR_FRAME] = { .type = NLA_STRING,
> > +				 .len = NL80211_MAX_FRAME_LEN },
> > +	[NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
> > +	[NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
> > +	[NL80211_ATTR_NETWORK_ID] = { .type = NLA_U16 },
> > +	[NL80211_ATTR_CHANNEL] = { .type = NLA_U32 },
> > +	[NL80211_ATTR_RX_SENSITIVITY] = { .type = NLA_U32 },
> > +	[NL80211_ATTR_BSSID] = { .len = ETH_ALEN },
> > +	[NL80211_ATTR_SSID] = { .type = NLA_NUL_STRING, .len = 32 },
> > +	[NL80211_ATTR_TRANSMIT_POWER] = { .type = NLA_U32 },
> > +	[NL80211_ATTR_FRAG_THRESHOLD] = { .type = NLA_U32 },
> > +	[NL80211_ATTR_INFORMATION_ELEMENT] = { .type = NLA_STRING,
> > +					       .len = NL80211_MAX_IE_LEN },
> > +	[NL80211_ATTR_ROAMING_CONTROL] = { .type = NLA_U32 },
> > +	[NL80211_ATTR_SCAN_TYPE] = { .type = NLA_U32 },
> > +};
> > +
> > +/* netlink command implementations */
> > +
> > +#define CHECK_CMD(ptr, cmd)				\
> > +	if (drv->ops->ptr)				\
> > +		NLA_PUT_FLAG(msg, NL80211_CMD_##cmd);
> > +
> > +static int nl80211_get_cmdlist(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct cfg80211_registered_driver *drv;
> > +	struct sk_buff *msg;
> > +	void *hdr;
> > +	int err;
> > +	struct nlattr *start;
> > +
> > +	drv = cfg80211_get_drv_from_info(info);
> > +	if (IS_ERR(drv))
> > +		return PTR_ERR(drv);
> > +
> > +	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
> > +			     NL80211_CMD_NEW_CMDLIST);
> > +	if (IS_ERR(hdr)) {
> > +		err = PTR_ERR(hdr);
> > +		goto put_drv;
> > +	}
> > +
> > +	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->wiphy);
> > +
> > +	start = nla_nest_start(msg, NL80211_ATTR_CMDS);
> > +	if (!start)
> > +		goto nla_put_failure;
> > +
> > +	/* unconditionally allow some common commands we handle centrally */
> > +	NLA_PUT_FLAG(msg, NL80211_CMD_GET_CMDLIST);
> > +	NLA_PUT_FLAG(msg, NL80211_CMD_GET_WIPHYS);
> > +	NLA_PUT_FLAG(msg, NL80211_CMD_GET_INTERFACES);
> > +
> > +	CHECK_CMD(list_interfaces, GET_INTERFACES);
> 
> Don't these two do exactly the same thing?  The first one adds
> GET_INTERFACES unconditionally, the next adds it (again!) if the
> interface supports it.  Either we add it unconditionally or we add it
> conditionally, but not both :)
> 
> > +	CHECK_CMD(inject_packet, INJECT);
> > +	CHECK_CMD(add_virtual_intf, ADD_VIRTUAL_INTERFACE);
> > +	CHECK_CMD(del_virtual_intf, DEL_VIRTUAL_INTERFACE);
> > +	CHECK_CMD(configure, CONFIGURE);
> > +	CHECK_CMD(get_config, GET_CONFIG);
> > +	CHECK_CMD(reassociate, REASSOCIATE);
> > +	CHECK_CMD(disassociate, DISASSOCIATE);
> > +	CHECK_CMD(deauth, DEAUTH);
> > +	CHECK_CMD(initiate_scan, INITIATE_SCAN);
> 
> I think we need a GET_SCAN here as well.  INITIATE_SCAN should
> definitely be CAP_NET_ADMIN-only or whatever, but GET_SCAN can be
> user-accessible.  Non-root stuff should still be able to get scan
> results even if they can't initiate one.
> 
> You also don't necessarily want to have to initiate a scan every time
> you want the results.  Drivers and/or d80211 should be caching the
> results anyway.  If d80211 is not, it should be.
> 
> > +	CHECK_CMD(set_roaming, SET_ROAMING_CONTROL);
> > +	CHECK_CMD(get_roaming, GET_ROAMING_CONTROL);
> > +	CHECK_CMD(set_fixed_bssid, SET_FIXED_BSSID);
> > +	CHECK_CMD(get_fixed_bssid, GET_FIXED_BSSID);
> > +	CHECK_CMD(get_association, GET_ASSOCIATION);
> > +	CHECK_CMD(get_auth_list, GET_AUTH_LIST);
> > +
> > +	nla_nest_end(msg, start);
> > +
> > +	genlmsg_end(msg, hdr);
> > +
> > +	err = genlmsg_unicast(msg, info->snd_pid);
> > +	goto put_drv;
> > +
> > + nla_put_failure:
> > + 	err = -ENOBUFS;
> > +	nlmsg_free(msg);
> > + put_drv:
> > +	cfg80211_put_drv(drv);
> > +	return err;
> > +}
> > +#undef CHECK_CMD
> > +
> > +static int nl80211_get_wiphys(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct sk_buff *msg;
> > +	void *hdr;
> > +	struct nlattr *start, *indexstart;
> > +	struct cfg80211_registered_driver *drv;
> > +	int idx = 1;
> > +
> > +	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
> > +			     NL80211_CMD_NEW_WIPHYS);
> > +	if (IS_ERR(hdr))
> > +		return PTR_ERR(hdr);
> > +
> > +	start = nla_nest_start(msg, NL80211_ATTR_WIPHY_LIST);
> > +	if (!start)
> > +		goto nla_outer_nest_failure;
> > +
> > +	mutex_lock(&cfg80211_drv_mutex);
> > +	list_for_each_entry(drv, &cfg80211_drv_list, list) {
> > +		indexstart = nla_nest_start(msg, idx++);
> > +		if (!indexstart)
> > +			goto nla_put_failure;
> > +		NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->wiphy);
> > +		nla_nest_end(msg, indexstart);
> > +	}
> > +	mutex_unlock(&cfg80211_drv_mutex);
> > +
> > +	nla_nest_end(msg, start);
> > +
> > +	genlmsg_end(msg, hdr);
> > +
> > +	return genlmsg_unicast(msg, info->snd_pid);
> > +
> > + nla_put_failure:
> > +	mutex_unlock(&cfg80211_drv_mutex);
> > + nla_outer_nest_failure:
> > +	nlmsg_free(msg);
> > +	return -ENOBUFS;
> > +}
> > +
> > +struct add_cb_data {
> > +	int idx;
> > +	struct sk_buff *skb;
> > +};
> > +
> > +static int addifidx(void *data, int ifidx)
> > +{
> > +	struct add_cb_data *cb = data;
> > +	struct net_device *dev = dev_get_by_index(ifidx);
> > +	int err = -ENOBUFS;
> > +	struct nlattr *start;
> > +
> > +	/* not that this can happen, since the caller
> > +	 * should hold the device open... */
> > +	if (!dev)
> > +		return -ENODEV;
> > +
> > +	start = nla_nest_start(cb->skb, cb->idx++);
> > +	if (!start)
> > +		goto nla_put_failure;
> > +
> > +	NLA_PUT_U32(cb->skb, NL80211_ATTR_IFINDEX, ifidx);
> > +	NLA_PUT_STRING(cb->skb, NL80211_ATTR_IFNAME, dev->name);
> > +
> > +	nla_nest_end(cb->skb, start);
> > +	err = 0;
> > +
> > + nla_put_failure:
> > +	dev_put(dev);
> > +	return err;
> > +}
> > +
> > +static int nl80211_get_intfs(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct cfg80211_registered_driver *drv;
> > +	struct sk_buff *msg;
> > +	void *hdr;
> > +	int err;
> > +	struct nlattr *start;
> > +	struct add_cb_data cb;
> > +
> > +	drv = cfg80211_get_drv_from_info(info);
> > +	if (IS_ERR(drv))
> > +		return PTR_ERR(drv);
> > +
> > +	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
> > +			     NL80211_CMD_NEW_INTERFACES);
> > +	if (IS_ERR(hdr)) {
> > +		err = PTR_ERR(hdr);
> > +		goto put_drv;
> > +	}
> > +
> > +	NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, drv->wiphy);
> > +
> > +	start = nla_nest_start(msg, NL80211_ATTR_INTERFACE_LIST);
> > +	if (!start) {
> > +		err = -ENOBUFS;
> > +		goto msg_free;
> > +	}
> > +
> > +	cb.skb = msg;
> > +	cb.idx = 1;
> > +	err = drv->ops->list_interfaces(drv->priv, &cb, addifidx);
> > +	if (err)
> > +		goto msg_free;
> > +
> > +	nla_nest_end(msg, start);
> > +
> > +	genlmsg_end(msg, hdr);
> > +
> > +	err = genlmsg_unicast(msg, info->snd_pid);
> > +	goto put_drv;
> > +
> > + nla_put_failure:
> > +	err = -ENOBUFS;
> > + msg_free:
> > +	nlmsg_free(msg);
> > + put_drv:
> > +	cfg80211_put_drv(drv);
> > +	return err;
> > +}
> > +
> > +static int nl80211_do_inject(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct cfg80211_registered_driver *drv;
> > +	u32 flags = 0;
> > +	int err, queue = -1;
> > +
> > +	if (!info->attrs[NL80211_ATTR_FRAME])
> > +		return -EINVAL;
> > +	if (info->attrs[NL80211_ATTR_FLAGS])
> > +		flags = nla_get_u32(info->attrs[NL80211_ATTR_FLAGS]);
> > +	if (info->attrs[NL80211_ATTR_QUEUE])
> > +		queue = (int) nla_get_u32(info->attrs[NL80211_ATTR_QUEUE]);
> > +
> > +	drv = cfg80211_get_drv_from_info(info);
> > +	if (IS_ERR(drv))
> > +		return PTR_ERR(drv);
> > +
> > +	if (!drv->ops->inject_packet) {
> > +		err = -ENOSYS;
> > +		goto unlock;
> > +	}
> > +
> > +	err = drv->ops->inject_packet(drv->priv,
> > +		nla_data(info->attrs[NL80211_ATTR_FRAME]),
> > +		nla_len(info->attrs[NL80211_ATTR_FRAME]),
> > +		flags,
> > +		queue);
> > + unlock:
> > +	cfg80211_put_drv(drv);
> > +	return err;
> > +}
> > +
> > +static int nl80211_add_virt_intf(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct cfg80211_registered_driver *drv;
> > +	int err;
> > +	unsigned int type = NL80211_IFTYPE_UNSPECIFIED;
> > +
> > +	if (!info->attrs[NL80211_ATTR_IFNAME])
> > +		return -EINVAL;
> > +
> > +	if (info->attrs[NL80211_ATTR_IFTYPE]) {
> > +		type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
> > +		if (type > NL80211_IFTYPE_MAX)
> > +			return -EINVAL;
> > +	}
> > +
> > +	drv = cfg80211_get_drv_from_info(info);
> > +	if (IS_ERR(drv))
> > +		return PTR_ERR(drv);
> > +
> > +	if (!drv->ops->add_virtual_intf) {
> > +		err = -ENOSYS;
> > +		goto unlock;
> > +	}
> > +
> > +	err = drv->ops->add_virtual_intf(drv->priv,
> > +		nla_data(info->attrs[NL80211_ATTR_IFNAME]), type);
> > +
> > + unlock:
> > +	cfg80211_put_drv(drv);
> > +	return err;
> > +}
> > +
> > +static int nl80211_del_virt_intf(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct cfg80211_registered_driver *drv;
> > +	int ifindex, err;
> > +	struct net_device *dev;
> > +
> > +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> > +	if (err)
> > +		return err;
> > +	ifindex = dev->ifindex;
> > +	dev_put(dev);
> > +
> > +	if (!drv->ops->del_virtual_intf) {
> > +		err = -EOPNOTSUPP;
> > +		goto out;
> > +	}
> > +
> > +	err = drv->ops->del_virtual_intf(drv->priv, ifindex);
> > +
> > + out:
> > +	cfg80211_put_drv(drv);
> > +	return err;
> > +}
> > +
> > +static int nl80211_configure(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct cfg80211_registered_driver *drv;
> > +	int err;
> > +	struct net_device *dev;
> > +	struct cfg80211_config config;
> > +	struct nlattr *attr;
> > +
> > +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> > +	if (err)
> > +		return err;
> > +
> > +	if (!drv->ops->configure) {
> > +		err = -EOPNOTSUPP;
> > +		goto out;
> > +	}
> > +
> > +	memset(&config, 0, sizeof(config));
> > +
> > +	attr = info->attrs[NL80211_ATTR_SSID];
> > +	if (attr)
> > +		config.ssid = nla_data(attr);
> > +
> > +	attr = info->attrs[NL80211_ATTR_NETWORK_ID];
> > +	if (attr) {
> > +		config.valid |= CFG80211_CFG_VALID_NWID;
> > +		config.network_id = nla_get_u16(attr);
> > +	}
> > +
> > +	attr = info->attrs[NL80211_ATTR_RX_SENSITIVITY];
> > +	if (attr) {
> > +		config.valid |= CFG80211_CFG_VALID_RX_SENSITIVITY;
> > +		config.rx_sensitivity = (s32) nla_get_u32(attr);
> > +	}
> > +
> > +	attr = info->attrs[NL80211_ATTR_TRANSMIT_POWER];
> > +	if (attr) {
> > +		config.valid |= CFG80211_CFG_VALID_TRANSMIT_POWER;
> > +		config.transmit_power = nla_get_u32(attr);
> > +	}
> > +
> > +	attr = info->attrs[NL80211_ATTR_FRAG_THRESHOLD];
> > +	if (attr) {
> > +		config.valid |= CFG80211_CFG_VALID_FRAG_THRESHOLD;
> > +		config.fragmentation_threshold = nla_get_u32(attr);
> > +	}
> > +
> > +	attr = info->attrs[NL80211_ATTR_CHANNEL];
> > +	if (attr) {
> > +		config.valid |= CFG80211_CFG_VALID_CHANNEL;
> > +		config.channel = nla_get_u32(attr);
> > +	}
> > +
> 
> Back to the question about encryption settings here.  How complete
> nl80211/cfg80211 is right now, etc.
> 
> I'd also argue that one specific BSSID is part of an initial
> configuration.  We should support that in config command.  It's an
> implicit SET_FIXED_BSSID, yes.  But one of the major points of
> nl80211/cfg80211 was that you could bundle up a set of configuration
> settings into a single atomic "packet", which you couldn't do with WE.
> 
> So if a specific BSSID isn't sent in the initial config command, when do
> you set a specific BSSID?  Before?  After?  The behavior starts getting
> complicated, and we're back to a situation where every driver implements
> the semantics in a slightly different manner.
> 
> > +	err = drv->ops->configure(drv->priv, dev, &config);
> > + out:
> > +	cfg80211_put_drv(drv);
> > +	dev_put(dev);
> > +	return err;
> > +}
> > +
> > +static int nl80211_get_config(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct cfg80211_registered_driver *drv;
> > +	int err;
> > +	struct net_device *dev;
> > +	struct cfg80211_config config;
> > +	struct sk_buff *msg;
> > +	void *hdr;
> > +
> > +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> > +	if (err)
> > +		return err;
> > +
> > +	if (!drv->ops->get_config) {
> > +		err = -EOPNOTSUPP;
> > +		goto out_put_drv;
> > +	}
> > +
> > +	memset(&config, 0, sizeof(config));
> > +
> > +	drv->ops->get_config(drv->priv, dev, &config);
> > +
> > +	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
> > +			     NL80211_CMD_NEW_CONFIG);
> > +
> > +	if (IS_ERR(hdr)) {
> > +		err = PTR_ERR(hdr);
> > +		goto out_put_drv;
> > +	}
> > +
> > +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
> > +
> > +	if (config.ssid)
> > +		NLA_PUT_STRING(msg, NL80211_ATTR_SSID, config.ssid);
> > +
> > +	if (config.valid & CFG80211_CFG_VALID_NWID)
> > +		NLA_PUT_U16(msg, NL80211_ATTR_NETWORK_ID, config.network_id);
> > +
> > +	if (config.valid & CFG80211_CFG_VALID_RX_SENSITIVITY)
> > +		NLA_PUT_U32(msg, NL80211_ATTR_RX_SENSITIVITY, (u32)config.rx_sensitivity);
> > +
> > +	if (config.valid & CFG80211_CFG_VALID_TRANSMIT_POWER)
> > +		NLA_PUT_U32(msg, NL80211_ATTR_TRANSMIT_POWER, config.transmit_power);
> > +
> > +	if (config.valid & CFG80211_CFG_VALID_FRAG_THRESHOLD)
> > +		NLA_PUT_U32(msg, NL80211_ATTR_FRAG_THRESHOLD, config.fragmentation_threshold);
> > +
> > +	if (config.valid & CFG80211_CFG_VALID_CHANNEL)
> > +		NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL, config.channel);
> > +
> > +	genlmsg_end(msg, hdr);
> > +	err = genlmsg_unicast(msg, info->snd_pid);
> > +	goto out_put_drv;
> > +
> > + nla_put_failure:
> > +	err = -ENOBUFS;
> > +	nlmsg_free(msg);
> > + out_put_drv:
> > +	cfg80211_put_drv(drv);
> > +	dev_put(dev);
> > +	return err;
> > +}
> > +
> > +static int nl80211_set_roaming(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct cfg80211_registered_driver *drv;
> > +	int err;
> > +	struct net_device *dev;
> > +	int roaming_control;
> > +
> > +	if (!info->attrs[NL80211_ATTR_ROAMING_CONTROL])
> > +		return -EINVAL;
> > +	roaming_control = nla_get_u32(info->attrs[NL80211_ATTR_ROAMING_CONTROL]);
> > +
> > +	if (roaming_control > NL80211_ROAMING_CONTROL_MAX)
> > +		return -EINVAL;
> > +
> > +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> > +	if (err)
> > +		return err;
> > +
> > +	if (!drv->ops->set_roaming) {
> > +		err = -EOPNOTSUPP;
> > +		goto out;
> > +	}
> > +
> > +	err = drv->ops->set_roaming(drv->priv, dev, roaming_control);
> > + out:
> > +	cfg80211_put_drv(drv);
> > +	dev_put(dev);
> > +	return err;
> > +}
> > +
> > +static int nl80211_get_roaming(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct cfg80211_registered_driver *drv;
> > +	int err;
> > +	struct net_device *dev;
> > +	struct sk_buff *msg;
> > +	void *hdr;
> > +
> > +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> > +	if (err)
> > +		return err;
> > +
> > +	if (!drv->ops->get_roaming) {
> > +		err = -EOPNOTSUPP;
> > +		goto out_put_drv;
> > +	}
> > +
> > +	err = drv->ops->get_roaming(drv->priv, dev);
> > +	if (err < 0)
> > +		goto out_put_drv;
> > +
> > +	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
> > +			     NL80211_CMD_ROAMING_CONTROL);
> > +
> > +	if (IS_ERR(hdr)) {
> > +		err = PTR_ERR(hdr);
> > +		goto out_put_drv;
> > +	}
> > +
> > +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
> > +	NLA_PUT_U32(msg, NL80211_ATTR_ROAMING_CONTROL, err);
> > +
> > +	genlmsg_end(msg, hdr);
> > +	err = genlmsg_unicast(msg, info->snd_pid);
> > +	goto out_put_drv;
> > +
> > + nla_put_failure:
> > +	err = -ENOBUFS;
> > +	nlmsg_free(msg);
> > + out_put_drv:
> > +	cfg80211_put_drv(drv);
> > +	dev_put(dev);
> > +	return err;
> > +}
> > +
> > +static int nl80211_set_fixed_bssid(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct cfg80211_registered_driver *drv;
> > +	int err;
> > +	struct net_device *dev;
> > +	u8 *bssid;
> > +
> > +	if (!info->attrs[NL80211_ATTR_BSSID])
> > +		return -EINVAL;
> > +	bssid = nla_data(info->attrs[NL80211_ATTR_BSSID]);
> > +
> > +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> > +	if (err)
> > +		return err;
> > +
> > +	if (!drv->ops->set_fixed_bssid) {
> > +		err = -EOPNOTSUPP;
> > +		goto out;
> > +	}
> > +
> > +	err = drv->ops->set_fixed_bssid(drv->priv, dev, bssid);
> > + out:
> > +	cfg80211_put_drv(drv);
> > +	dev_put(dev);
> > +	return err;
> > +}
> > +
> > +static int nl80211_get_fixed_bssid(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct cfg80211_registered_driver *drv;
> > +	int err;
> > +	struct net_device *dev;
> > +	struct sk_buff *msg;
> > +	void *hdr;
> > +	u8 bssid[ETH_ALEN];
> > +
> > +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> > +	if (err)
> > +		return err;
> > +
> > +	if (!drv->ops->get_fixed_bssid) {
> > +		err = -EOPNOTSUPP;
> > +		goto out_put_drv;
> > +	}
> > +
> > +	err = drv->ops->get_fixed_bssid(drv->priv, dev, bssid);
> > +	if (err < 0)
> > +		goto out_put_drv;
> > +
> > +	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
> > +			     NL80211_CMD_FIXED_BSSID);
> > +
> > +	if (IS_ERR(hdr)) {
> > +		err = PTR_ERR(hdr);
> > +		goto out_put_drv;
> > +	}
> > +
> > +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
> > +	NLA_PUT(msg, NL80211_ATTR_BSSID, ETH_ALEN, bssid);
> > +
> > +	genlmsg_end(msg, hdr);
> > +	err = genlmsg_unicast(msg, info->snd_pid);
> > +	goto out_put_drv;
> > +
> > + nla_put_failure:
> > +	err = -ENOBUFS;
> > +	nlmsg_free(msg);
> > + out_put_drv:
> > +	cfg80211_put_drv(drv);
> > +	dev_put(dev);
> > +	return err;
> > +}
> > +
> > +static int nl80211_get_association(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct cfg80211_registered_driver *drv;
> > +	int err;
> > +	struct net_device *dev;
> > +	struct sk_buff *msg;
> > +	void *hdr;
> > +	u8 bssid[ETH_ALEN];
> > +
> > +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> > +	if (err)
> > +		return err;
> > +
> > +	if (!drv->ops->get_association) {
> > +		err = -EOPNOTSUPP;
> > +		goto out_put_drv;
> > +	}
> > +
> > +	err = drv->ops->get_association(drv->priv, dev, bssid);
> > +	if (err < 0)
> > +		goto out_put_drv;
> > +
> > +	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
> > +			     NL80211_CMD_ASSOCIATION_CHANGED);
> > +
> > +	if (IS_ERR(hdr)) {
> > +		err = PTR_ERR(hdr);
> > +		goto out_put_drv;
> > +	}
> > +
> > +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
> > +	if (err == 1)
> > +		NLA_PUT(msg, NL80211_ATTR_BSSID, ETH_ALEN, bssid);
> > +
> > +	genlmsg_end(msg, hdr);
> > +	err = genlmsg_unicast(msg, info->snd_pid);
> > +	goto out_put_drv;
> > +
> > + nla_put_failure:
> > +	err = -ENOBUFS;
> > +	nlmsg_free(msg);
> > + out_put_drv:
> > +	cfg80211_put_drv(drv);
> > +	dev_put(dev);
> > +	return err;
> > +}
> > +
> > +static int nl80211_assoc_deauth(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct cfg80211_registered_driver *drv;
> > +	int err;
> > +	struct net_device *dev;
> > +	int (*act)(void *priv, struct net_device *dev);
> > +
> > +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> > +	if (err)
> > +		return err;
> > +
> > +	switch (info->genlhdr->cmd) {
> > +	case NL80211_CMD_DISASSOCIATE:
> > +		act = drv->ops->disassociate;
> > +		break;
> > +	case NL80211_CMD_REASSOCIATE:
> > +		act = drv->ops->reassociate;
> > +		break;
> > +	case NL80211_CMD_DEAUTH:
> > +		act = drv->ops->deauth;
> > +		break;
> > +	default:
> > +		act = NULL;
> > +	}
> > +
> > +	if (!act) {
> > +		err = -EOPNOTSUPP;
> > +		goto out;
> > +	}
> > +
> > +	err = act(drv->priv, dev);
> > + out:
> > +	cfg80211_put_drv(drv);
> > +	dev_put(dev);
> > +	return err;
> > +}
> > +
> > +static int add_bssid(void *data, u8 *bssid)
> > +{
> > +	struct add_cb_data *cb = data;
> > +	int err = -ENOBUFS;
> > +	struct nlattr *start;
> > +
> > +	start = nla_nest_start(cb->skb, cb->idx++);
> > +	if (!start)
> > +		goto nla_put_failure;
> > +
> > +	NLA_PUT(cb->skb, NL80211_ATTR_BSSID, ETH_ALEN, bssid);
> > +
> > +	nla_nest_end(cb->skb, start);
> > +	err = 0;
> > +
> > + nla_put_failure:
> > +	return err;
> > +}
> > +
> > +static int nl80211_get_auth_list(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct cfg80211_registered_driver *drv;
> > +	struct net_device *dev;
> > +	struct sk_buff *msg;
> > +	void *hdr;
> > +	int err;
> > +	struct nlattr *start;
> > +	struct add_cb_data cb;
> > +
> > +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> > +	if (err)
> > +		return err;
> > +
> > +	if (!drv->ops->get_auth_list) {
> > +		err = -EOPNOTSUPP;
> > +		goto put_drv;
> > +	}
> > +
> > +	hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0,
> > +			     NL80211_CMD_AUTH_LIST);
> > +	if (IS_ERR(hdr)) {
> > +		err = PTR_ERR(hdr);
> > +		goto put_drv;
> > +	}
> > +
> > +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
> > +
> > +	start = nla_nest_start(msg, NL80211_ATTR_BSSID_LIST);
> > +	if (!start) {
> > +		err = -ENOBUFS;
> > +		goto msg_free;
> > +	}
> > +
> > +	cb.skb = msg;
> > +	cb.idx = 1;
> > +	err = drv->ops->get_auth_list(drv->priv, dev, &cb, add_bssid);
> > +	if (err)
> > +		goto msg_free;
> > +
> > +	nla_nest_end(msg, start);
> > +
> > +	genlmsg_end(msg, hdr);
> > +
> > +	err = genlmsg_unicast(msg, info->snd_pid);
> > +	goto put_drv;
> > +
> > + nla_put_failure:
> > +	err = -ENOBUFS;
> > + msg_free:
> > +	nlmsg_free(msg);
> > + put_drv:
> > +	cfg80211_put_drv(drv);
> > +	dev_put(dev);
> > +	return err;
> > +}
> > +
> > +static int nl80211_initiate_scan(struct sk_buff *skb, struct genl_info *info)
> > +{
> > +	struct cfg80211_registered_driver *drv;
> > +	int err;
> > +	struct net_device *dev;
> > +	struct scan_params params;
> > +	struct scan_channel *channels = NULL;
> > +	int count = -1;
> > +
> > +	err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
> > +	if (err)
> > +		return err;
> > +
> > +	if (!drv->ops->initiate_scan) {
> > +		err = -EOPNOTSUPP;
> > +		goto out;
> > +	}
> > +
> > +	params.active = 0;
> > +
> > +	if (info->attrs[NL80211_ATTR_FLAGS])
> > +		params.active = !!(nla_get_u32(info->attrs[NL80211_ATTR_FLAGS])
> > +					& NL80211_FLAG_SCAN_ACTIVELY);
> 
> ^^^^ see note below about naming of NL80211_FLAG_SCAN_*
> 
> > +
> > +	if (info->attrs[NL80211_ATTR_CHANNEL_LIST]) {
> > +		struct nlattr *attr = info->attrs[NL80211_ATTR_CHANNEL_LIST];
> > +		struct nlattr *nla;
> > +		int rem;
> > +		struct nlattr **tb;
> > +
> > +		/* let's count first */
> > +		count = 0;
> > +		nla_for_each_attr(nla, nla_data(attr), nla_len(attr), rem)
> > +			count++;
> > +
> > +		if (count == 0) {
> > +			/* assume we should actually scan all channels,
> > +			 * scanning no channels make no sense */
> > +			count = -1;
> > +			goto done_channels;
> > +		}
> > +
> > +		channels = kmalloc(count * sizeof(struct scan_channel),
> > +				   GFP_KERNEL);
> > +		tb = kmalloc((NL80211_ATTR_MAX+1) * sizeof(struct nlattr),
> > +			     GFP_KERNEL);
> > +
> > +		count = 0;
> > +		nla_for_each_attr(nla, nla_data(attr), nla_len(attr), rem) {
> > +			err = nla_parse(tb, NL80211_ATTR_MAX, nla_data(nla),
> > +					nla_len(nla), nl80211_policy);
> > +			if (err || !tb[NL80211_ATTR_CHANNEL]) {
> > +				err = -EINVAL;
> > +				kfree(tb);
> > +				kfree(channels);
> > +				goto out;
> > +			}
> > +			channels[count].channel =
> > +				nla_get_u32(tb[NL80211_ATTR_CHANNEL]);
> > +
> > +			channels[count].active = params.active;
> > +
> > +			if (tb[NL80211_ATTR_FLAGS])
> > +				channels[count].active =
> > +					!!(nla_get_u32(tb[NL80211_ATTR_FLAGS])
> > +						& NL80211_FLAG_SCAN_ACTIVELY);
> > +			count++;
> > +		}
> > +		kfree(tb);
> > +	}
> > +
> > + done_channels:
> > +	params.channels = channels;
> > +	params.n_channels = count;
> > +
> > +	err = drv->ops->initiate_scan(drv->priv, dev, &params);
> > +
> > +	kfree(channels);
> > + out:
> > +	cfg80211_put_drv(drv);
> > +	dev_put(dev);
> > +	return err;
> > +}
> > +
> > +static struct genl_ops nl80211_ops[] = {
> > +	{
> > +		.cmd = NL80211_CMD_GET_CMDLIST,
> > +		.doit = nl80211_get_cmdlist,
> > +		.policy = nl80211_policy,
> > +		/* can be retrieved by unprivileged users */
> > +	},
> > +	{
> > +		.cmd = NL80211_CMD_GET_WIPHYS,
> > +		.doit = nl80211_get_wiphys,
> > +		.policy = nl80211_policy,
> > +		/* can be retrieved by unprivileged users */
> > +	},
> > +	{
> > +		.cmd = NL80211_CMD_GET_INTERFACES,
> > +		.doit = nl80211_get_intfs,
> > +		.policy = nl80211_policy,
> > +		/* can be retrieved by unprivileged users */
> > +	},
> > +	{
> > +		.cmd = NL80211_CMD_INJECT,
> > +		.doit = nl80211_do_inject,
> > +		.policy = nl80211_policy,
> > +		.flags = GENL_ADMIN_PERM,
> > +	},
> > +	{
> > +		.cmd = NL80211_CMD_ADD_VIRTUAL_INTERFACE,
> > +		.doit = nl80211_add_virt_intf,
> > +		.policy = nl80211_policy,
> > +		.flags = GENL_ADMIN_PERM,
> > +	},
> > +	{
> > +		.cmd = NL80211_CMD_DEL_VIRTUAL_INTERFACE,
> > +		.doit = nl80211_del_virt_intf,
> > +		.policy = nl80211_policy,
> > +		.flags = GENL_ADMIN_PERM,
> > +	},
> > +	{
> > +		.cmd = NL80211_CMD_CONFIGURE,
> > +		.doit = nl80211_configure,
> > +		.policy = nl80211_policy,
> > +		.flags = GENL_ADMIN_PERM,
> > +	},
> > +	{
> > +		.cmd = NL80211_CMD_GET_CONFIG,
> > +		.doit = nl80211_get_config,
> > +		.policy = nl80211_policy,
> > +		/* can be retrieved by unprivileged users */
> > +	},
> > +	{
> > +		.cmd = NL80211_CMD_SET_ROAMING_CONTROL,
> > +		.doit = nl80211_set_roaming,
> > +		.policy = nl80211_policy,
> > +		.flags = GENL_ADMIN_PERM,
> > +	},
> > +	{
> > +		.cmd = NL80211_CMD_GET_ROAMING_CONTROL,
> > +		.doit = nl80211_get_roaming,
> > +		.policy = nl80211_policy,
> > +		/* can be retrieved by unprivileged users */
> > +	},
> > +	{
> > +		.cmd = NL80211_CMD_SET_FIXED_BSSID,
> > +		.doit = nl80211_set_fixed_bssid,
> > +		.policy = nl80211_policy,
> > +		.flags = GENL_ADMIN_PERM,
> > +	},
> > +	{
> > +		.cmd = NL80211_CMD_GET_FIXED_BSSID,
> > +		.doit = nl80211_get_fixed_bssid,
> > +		.policy = nl80211_policy,
> > +		/* can be retrieved by unprivileged users */
> > +	},
> > +	{
> > +		.cmd = NL80211_CMD_GET_ASSOCIATION,
> > +		.doit = nl80211_get_association,
> > +		.policy = nl80211_policy,
> > +		/* can be retrieved by unprivileged users */
> > +	},
> > +	{
> > +		.cmd = NL80211_CMD_DISASSOCIATE,
> > +		.doit = nl80211_assoc_deauth,
> > +		.policy = nl80211_policy,
> > +		.flags = GENL_ADMIN_PERM,
> > +	},
> > +	{
> > +		.cmd = NL80211_CMD_DEAUTH,
> > +		.doit = nl80211_assoc_deauth,
> > +		.policy = nl80211_policy,
> > +		.flags = GENL_ADMIN_PERM,
> > +	},
> > +	{
> > +		.cmd = NL80211_CMD_REASSOCIATE,
> > +		.doit = nl80211_assoc_deauth,
> > +		.policy = nl80211_policy,
> > +		.flags = GENL_ADMIN_PERM,
> > +	},
> > +	{
> > +		.cmd = NL80211_CMD_GET_AUTH_LIST,
> > +		.doit = nl80211_get_auth_list,
> > +		.policy = nl80211_policy,
> > +		/* can be retrieved by unprivileged users */
> > +	},
> > +	{
> > +		.cmd = NL80211_CMD_INITIATE_SCAN,
> > +		.doit = nl80211_initiate_scan,
> > +		.policy = nl80211_policy,
> > +		.flags = GENL_ADMIN_PERM,
> > +	},
> > +};
> > +
> > +
> > +/* exported functions */
> > +
> > +void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq, int flags, u8 cmd)
> > +{
> > +	/* since there is no private header just add the generic one */
> > +	return genlmsg_put(skb, pid, seq, nl80211_fam.id, 0,
> > +			   flags, cmd, nl80211_fam.version);
> > +}
> > +EXPORT_SYMBOL_GPL(nl80211hdr_put);
> > +
> > +void *nl80211msg_new(struct sk_buff **skb, u32 pid, u32 seq, int flags, u8 cmd)
> > +{
> > +	void *hdr;
> > +
> > +	*skb = nlmsg_new(NLMSG_GOODSIZE);
> > +	if (!*skb)
> > +		return ERR_PTR(-ENOBUFS);
> > +
> > +	hdr = nl80211hdr_put(*skb, pid, seq, flags, cmd);
> > +	if (!hdr) {
> > +		nlmsg_free(*skb);
> > +		return ERR_PTR(-ENOBUFS);
> > +	}
> > +
> > +	return hdr;
> > +}
> > +EXPORT_SYMBOL_GPL(nl80211msg_new);
> > +
> > +/* initialisation/exit functions */
> > +
> > +int nl80211_init(void)
> > +{
> > +	int err, i;
> > +
> > +	err = genl_register_family(&nl80211_fam);
> > +	if (err)
> > +		return err;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(nl80211_ops); i++) {
> > +		err = genl_register_ops(&nl80211_fam, &nl80211_ops[i]);
> > +		if (err)
> > +			goto err_out;
> > +	}
> > +	return 0;
> > + err_out:
> > + 	genl_unregister_family(&nl80211_fam);
> > +	return err;
> > +}
> > +
> > +void nl80211_exit(void)
> > +{
> > +	genl_unregister_family(&nl80211_fam);
> > +}
> > --- wireless-dev.orig/include/linux/Kbuild	2006-09-27 19:10:21.564088173 +0200
> > +++ wireless-dev/include/linux/Kbuild	2006-09-27 19:14:44.224088173 +0200
> > @@ -28,7 +28,7 @@ header-y += affs_fs.h affs_hardblocks.h 
> >  	sound.h stddef.h synclink.h telephony.h termios.h ticable.h	\
> >  	times.h tiocl.h tipc.h toshiba.h ultrasound.h un.h utime.h	\
> >  	utsname.h video_decoder.h video_encoder.h videotext.h vt.h	\
> > -	wavefront.h wireless.h xattr.h x25.h zorro_ids.h
> > +	wavefront.h wireless.h xattr.h x25.h zorro_ids.h nl80211.h
> >  
> >  unifdef-y += acct.h adb.h adfs_fs.h agpgart.h apm_bios.h atalk.h	\
> >  	atmarp.h atmdev.h atm.h atm_tcp.h audit.h auto_fs.h binfmts.h	\
> > --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> > +++ wireless-dev/include/linux/nl80211.h	2006-09-28 00:38:55.264150162 +0200
> > @@ -0,0 +1,276 @@
> > +#ifndef __LINUX_NL80211_H
> > +#define __LINUX_NL80211_H
> > +/*
> > + * 802.11 netlink interface public header
> > + *
> > + * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
> > + */
> > +
> > +/* currently supported commands
> > + * don't change the order or add anything inbetween, this is ABI! */
> > +enum {
> > +	/* There's no technical reason to not use command 0 but malformed
> > +	 * zeroed messages may have it and this catches that */
> > +	NL80211_CMD_UNSPEC,
> > +
> > +	/* Get supported commands by ifindex,
> > +	 * uses NL80211_ATTR_CMDS (output) and NL80211_ATTR_IFINDEX (input) */
> > +	NL80211_CMD_GET_CMDLIST,
> > +
> > +	/* Supported commands returned */
> > +	NL80211_CMD_NEW_CMDLIST,
> > +
> > +	/* Inject a frame using NL80211_ATTR_FLAGS and NL80211_ATTR_FRAME.
> > +	 * If kernel sends this, it's a status notification for the injected
> > +	 * frame. */
> > +	NL80211_CMD_INJECT,
> > +
> > +	/* add a virtual interface to a group that is identified by any
> > +	 * other ifindex in the group of a wiphy index, needs the
> > +	 * NL80211_IF_NAME attribute */
> > +	NL80211_CMD_ADD_VIRTUAL_INTERFACE,
> > +
> > +	/* remove a given (with NL80211_ATTR_IFINDEX) virtual device */
> > +	NL80211_CMD_DEL_VIRTUAL_INTERFACE,
> > +
> > +	/* get list of all wiphys */
> > +	NL80211_CMD_GET_WIPHYS,
> > +
> > +	/* get list of all wiphys */
> > +	NL80211_CMD_NEW_WIPHYS,
> > +
> > +	/* get list of all interfaces belonging to a wiphy */
> > +	NL80211_CMD_GET_INTERFACES,
> > +
> > +	/* get list of all interfaces belonging to a wiphy */
> > +	NL80211_CMD_NEW_INTERFACES,
> > +
> > +	/* configure device */
> > +	NL80211_CMD_CONFIGURE,
> > +
> > +	/* request configuration */
> > +	NL80211_CMD_GET_CONFIG,
> > +
> > +	/* configuration sent from kernel */
> > +	NL80211_CMD_NEW_CONFIG,
> > +
> > +	/* initiate scan.
> > +	 * Takes a CHANNEL_LIST attribute containing nested
> > +	 * attributes which in turn contain CHANNEL and FLAGS
> > +	 * attributes.
> > +	 * The top level can also contain a FLAGS attribute
> > +	 * which is then the default for each channel.
> > +	 * If no channel list is given (or it is empty)
> > +	 * all channels shall be scanned. */
> > +	NL80211_CMD_INITIATE_SCAN,
> > +
> > +	/* scan result (kernel -> userspace) */
> > +	NL80211_CMD_SCAN_RESULT,
> > +
> > +	/* change roaming control */
> > +	NL80211_CMD_SET_ROAMING_CONTROL,
> > +
> > +	/* get roaming control setting */
> > +	NL80211_CMD_GET_ROAMING_CONTROL,
> > +
> > +	/* answer to that */
> > +	NL80211_CMD_ROAMING_CONTROL,
> > +
> > +	/* set access point BSSID for userspace roaming */
> > +	NL80211_CMD_SET_FIXED_BSSID,
> > +
> > +	/* get currently set userspace roaming BSSID */
> > +	NL80211_CMD_GET_FIXED_BSSID,
> > +
> > +	/* currently set roaming BSSID */
> > +	NL80211_CMD_FIXED_BSSID,
> > +
> > +	/* get current association information, if not associated then
> > +	 * the BSSID attribute is not present in response */
> > +	NL80211_CMD_GET_ASSOCIATION,
> > +
> > +	/* association notification and response to GET_BSSID */
> > +	NL80211_CMD_ASSOCIATION_CHANGED,
> > +
> > +	/* disassociate from current AP */
> > +	NL80211_CMD_DISASSOCIATE,
> > +
> > +	/* deauth from current AP */
> > +	NL80211_CMD_DEAUTH,
> > +
> > +	/* re-associate with current settings
> > +	 * (SSID and BSSID if roaming control in userspace) */
> > +	NL80211_CMD_REASSOCIATE,
> > +
> > +	/* request the full list of BSSs the device is
> > +	 * authenticated with */
> > +	NL80211_CMD_GET_AUTH_LIST,
> > +
> > +	/* sent as a response to GET_AUTH_LIST containing
> > +	 * an ATTR_BSSID_LIST */
> > +	NL80211_CMD_AUTH_LIST,
> > +
> > +	/* sent when authenticating/deauthenticating.
> > +	 * contains an ATTR_BSSID and possibly an
> > +	 * ATTR_DEAUTHENTICATED */
> > +	NL80211_CMD_AUTHENTICATION_CHANGED,
> > +
> > +	/* add commands here */
> > +
> > +	/* used to define NL80211_CMD_MAX below */
> > +	__NL80211_CMD_AFTER_LAST,
> > +};
> > +#define NL80211_CMD_MAX (__NL80211_CMD_AFTER_LAST - 1)
> > +
> > +
> > +/* currently supported attributes.
> > + * don't change the order or add anything inbetween, this is ABI! */
> > +enum {
> > +	NL80211_ATTR_UNSPEC,
> > +
> > +	/* network device (ifindex) to operate on */
> > +	NL80211_ATTR_IFINDEX,
> > +
> > +	/* wiphy index to operate on */
> > +	NL80211_ATTR_WIPHY,
> > +
> > +	/* list of u8 cmds that a given device implements */
> > +	NL80211_ATTR_CMDS,
> > +
> > +	/* flags for injection and other commands, see below */
> > +	NL80211_ATTR_FLAGS,
> > +
> > +	/* which hardware queue to use */
> > +	NL80211_ATTR_QUEUE,
> > +
> > +	/* frame to inject or received frame for mgmt frame subscribers */
> > +	NL80211_ATTR_FRAME,
> > +
> > +	/* interface name */
> > +	NL80211_ATTR_IFNAME,
> > +
> > +	/* type of (virtual) interface */
> > +	NL80211_ATTR_IFTYPE,
> > +
> > +	/* interface list */
> > +	NL80211_ATTR_INTERFACE_LIST,
> > +
> > +	/* wiphy list */
> > +	NL80211_ATTR_WIPHY_LIST,
> > +
> > +	/* attributes used for configuration */
> > +	/* network ID (pre 802.11 HW) */
> > +	NL80211_ATTR_NETWORK_ID,
> > +
> > +	/* channel, 1-14 are B/G */
> > +	NL80211_ATTR_CHANNEL,
> > +
> > +	/* channel list for scan determination */
> > +	NL80211_ATTR_CHANNEL_LIST,
> > +
> > +	/* receiver sensitivity in dBm */
> > +	NL80211_ATTR_RX_SENSITIVITY,
> > +
> > +	/* BSSID to associate to, only used when roaming control
> > +	 * is in userspace */
> > +	NL80211_ATTR_BSSID,
> > +
> > +	/* list of multiple BSSIDs, this is a nested attribute
> > +	 * containing an index->(attrs) mapping */
> > +	NL80211_ATTR_BSSID_LIST,
> > +
> > +	/* this is a flag for when an authentication is lost */
> > +	NL80211_ATTR_DEAUTHENTICATED,
> > +
> > +	/* SSID of ESS to associate to */
> > +	NL80211_ATTR_SSID,
> > +
> > +	/* transmit power in mW */
> > +	NL80211_ATTR_TRANSMIT_POWER,
> > +
> > +	/* fragmentation threshold in bytes */
> > +	NL80211_ATTR_FRAG_THRESHOLD,
> > +
> > +	/* one or more information elements */
> > +	NL80211_ATTR_INFORMATION_ELEMENT,
> > +
> > +	NL80211_ATTR_ROAMING_CONTROL,
> > +
> > +	NL80211_ATTR_SCAN_TYPE,
> > +
> > +	/* add attributes here */
> > +
> > +	/* used to define NL80211_ATTR_MAX below */
> > +	__NL80211_ATTR_AFTER_LAST,
> > +};
> > +#define NL80211_ATTR_MAX (__NL80211_ATTR_AFTER_LAST - 1)
> > +
> > +/**
> > + * NL80211_FLAG_TXSTATUS - send transmit status indication
> > + */
> > +#define NL80211_FLAG_TXSTATUS		(1<<0)
> > +/**
> > + * NL80211_FLAG_ENCRYPT - encrypt this packet
> > + * Warning: This looks inside the packet header!
> > + */
> > +#define NL80211_FLAG_ENCRYPT		(1<<1)
> > +
> > +/**
> > + * NL80211_FLAG_SCAN_ACTIVELY - set this with a scan
> > + * request to have it scan actively, can also be used
> > + * within the nested CHANNEL_LIST...
> > + */
> > +#define NL80211_FLAG_SCAN_ACTIVELY	(1<<2)
> 
> ACTIVELY is an adverb, and that doesn't parse very well in this context.
> It also just sounds weird here.  Better options:
> 
> FLAG_SCAN_ACTIVE
> FLAG_SCAN_TYPE_ACTIVE (<-- my favorite)
> FLAG_ACTIVE_SCAN
> 
> Looks great,
> 
> Dan
> 
> > +/**
> > + * maximum length of a frame that can be injected
> > + */
> > +#define NL80211_MAX_FRAME_LEN 2500
> > +
> > +/* this is an arbitrary limit, 516 means two full-length
> > + * IEs would fit... */
> > +/**
> > + * maximum length of IE(s) passed in an NL80211_ATTR_INFORMATION_ELEMENT.
> > + */
> > +#define NL80211_MAX_IE_LEN 516
> > +
> > +/* may need to be bumped? */
> > +/**
> > + * maximum number of items in an ATTR_CHANNEL_LIST
> > + */
> > +#define NL80211_MAX_CHANNEL_LIST_ITEM 20
> > +
> > +/**
> > + * &enum nl80211_iftype - (virtual) interface types
> > + *
> > + * This structure is used with the NL80211_ATTR_IFTYPE
> > + * to set the type of an interface.
> > + * Note that these are intentionally compatible with
> > + * the IW_MODE_* constants except for the removal of
> > + * IW_MODE_AUTO.
> > + *
> > + */
> > +enum {
> > +	NL80211_IFTYPE_UNSPECIFIED,
> > +	NL80211_IFTYPE_ADHOC,
> > +	NL80211_IFTYPE_STATION,
> > +	NL80211_IFTYPE_AP,
> > +	NL80211_IFTYPE_WDS,
> > +	NL80211_IFTYPE_SECONDARY,
> > +	NL80211_IFTYPE_MONITOR,
> > +
> > +	/* keep last */
> > +	__NL80211_IFTYPE_AFTER_LAST
> > +};
> > +#define NL80211_IFTYPE_MAX (__NL80211_IFTYPE_AFTER_LAST - 1)
> > +
> > +enum {
> > +	NL80211_ROAMING_CONTROL_KERNEL,
> > +	NL80211_ROAMING_CONTROL_USERSPACE,
> > +
> > +	/* keep last */
> > +	__NL80211_ROAMING_CONTROL_AFTER_LAST
> > +};
> > +#define NL80211_ROAMING_CONTROL_MAX (__NL80211_ROAMING_CONTROL_AFTER_LAST-1)
> > +
> > +#endif /* __LINUX_NL80211_H */
> > --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> > +++ wireless-dev/include/net/cfg80211.h	2006-09-28 00:38:55.274150162 +0200
> > @@ -0,0 +1,191 @@
> > +#ifndef __NET_CFG80211_H
> > +#define __NET_CFG80211_H
> > +
> > +#include <linux/netlink.h>
> > +#include <linux/nl80211.h>
> > +#include <linux/skbuff.h>
> > +#include <linux/netdevice.h>
> > +#include <net/genetlink.h>
> > +
> > +/*
> > + * 802.11 configuration in-kernel interface
> > + *
> > + * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
> > + */
> > +
> > +/**
> > + * struct cfg80211_config - description of a configuration (request)
> > + */
> > +struct cfg80211_config {
> > +	/* first fields with 'internal' validity */
> > +
> > +	/* SSID to use, valid if not NULL. change forces reassociation */
> > +	u8 *ssid;
> > +
> > +	/* now fields with explicit validity */
> > +#define CFG80211_CFG_VALID_NWID			(1<<0)
> > +#define CFG80211_CFG_VALID_RX_SENSITIVITY	(1<<1)
> > +#define CFG80211_CFG_VALID_TRANSMIT_POWER	(1<<2)
> > +#define CFG80211_CFG_VALID_FRAG_THRESHOLD	(1<<3)
> > +#define CFG80211_CFG_VALID_CHANNEL		(1<<4)
> > +	unsigned int valid;
> > +
> > +	u16 network_id;
> > +	s32 rx_sensitivity;
> > +	u32 transmit_power;
> > +	u32 fragmentation_threshold;
> > +	u32 channel;
> > +};
> > +
> > +struct scan_channel {
> > +	u32 channel;
> > +	int active;
> > +};
> > +
> > +struct scan_params {
> > +	/* number of items in 'channels' array
> > +	 * or -1 to indicate scanning all channels
> > +	 * (in that case 'channels' is NULL) */
> > +	int n_channels;
> > +
> > +	/* use only when n_channels is -1 to determine
> > +	 * whether scanning should be active or not */
> > +	int active;
> > +
> > +	/* the channel list if any */
> > +	struct scan_channel *channels;
> > +};
> > +
> > +/**
> > + * struct cfg80211_ops - backend description for wireless configuration
> > + *
> > + * This struct is registered by fullmac card drivers and/or wireless stacks
> > + * in order to handle configuration requests on their interfaces.
> > + *
> > + * The priv pointer passed to each call is the pointer that was
> > + * registered in cfg80211_register_driver().
> > + *
> > + * All callbacks except where otherwise noted should return 0
> > + * on success or a negative error code.
> > + *
> > + * @list_interfaces: for each interfaces belonging to the wiphy identified
> > + *		     by the priv pointer, call the one() function with the
> > + *		     given data and the ifindex. This callback is required.
> > + *
> > + * @inject_packet: inject the given frame with the NL80211_FLAG_*
> > + *		   flags onto the given queue.
> > + *
> > + * @add_virtual_intf: create a new virtual interface with the given name
> > + *
> > + * @del_virtual_intf: remove the virtual interface determined by ifindex.
> > + *
> > + * @configure: configure the given interface as requested in the config struct.
> > + *	       must not ignore any configuration item, if something is
> > + *	       is requested that cannot be fulfilled return an error
> > + *
> > + * @get_config: fill the given config structure with the current configuration
> > + *
> > + * @reassociate: reassociate with current settings (SSID, BSSID if
> > + *		 userspace roaming is enabled)
> > + *
> > + * @disassociate: disassociate from current AP
> > + *
> > + * @deauth: deauth from current AP
> > + *
> > + * @initiate_scan: ...
> > + *
> > + * @set_roaming: set who gets to control roaming, the roaming_control
> > + *		 parameter is passed NL80211_ROAMING_CONTROL_* values.
> > + *
> > + * @get_roaming: return where roaming control currently is done or
> > + *		 a negative error.
> > + *
> > + * @set_fixed_bssid: set BSSID to use with userspace roaming, forces
> > + *		     reassociation if changing.
> > + * @get_fixed_bssid: get BSSID that is used with userspace roaming,
> > + *		     the bssid parameter has space for 6 bytes
> > + *
> > + * @get_association: get BSSID of the BSS that the device is currently
> > + *		     associated to and return 1, or return 0 if not
> > + *		     associated (or a negative error code)
> > + * @get_auth_list: get list of BSSIDs of all BSSs the device has
> > + *		   authenticated with, must call next_bssid for each,
> > + *		   next_bssid returns non-zero on error, the given data
> > + *		   is to be passed to that callback
> > + */
> > +struct cfg80211_ops {
> > +	int	(*list_interfaces)(void *priv, void *data,
> > +				   int (*one)(void *data, int ifindex));
> > +
> > +
> > +	int	(*inject_packet)(void *priv, void *frame, int framelen,
> > +				 u32 flags, int queue);
> > +
> > +
> > +	int	(*add_virtual_intf)(void *priv, char *name,
> > +				    unsigned int type);
> > +	int	(*del_virtual_intf)(void *priv, int ifindex);
> > +
> > +
> > +	int	(*configure)(void *priv, struct net_device *dev,
> > +			     struct cfg80211_config *cfg);
> > +	void	(*get_config)(void *priv, struct net_device *dev,
> > +			      struct cfg80211_config *cfg);
> > +
> > +
> > +	int	(*reassociate)(void *priv, struct net_device *dev);
> > +	int	(*disassociate)(void *priv, struct net_device *dev);
> > +	int	(*deauth)(void *priv, struct net_device *dev);
> > +
> > +
> > +	int	(*initiate_scan)(void *priv, struct net_device *dev,
> > +				 struct scan_params *params);
> > +
> > +
> > +	int	(*set_roaming)(void *priv, struct net_device *dev,
> > +			       int roaming_control);
> > +	int	(*get_roaming)(void *priv, struct net_device *dev);
> > +	int	(*set_fixed_bssid)(void *priv, struct net_device *dev,
> > +				   u8 *bssid);
> > +	int	(*get_fixed_bssid)(void *priv, struct net_device *dev,
> > +				   u8 *bssid);
> > +
> > +
> > +	int	(*get_association)(void *priv, struct net_device *dev,
> > +				   u8 *bssid);
> > +
> > +	int	(*get_auth_list)(void *priv, struct net_device *dev,
> > +				 void *data,
> > +				 int (*next_bssid)(void *data, u8 *bssid));
> > +};
> > +
> > +/**
> > + * cfg80211_register - register a wiphy with cfg80211
> > + *
> > + * register a given method structure with the cfg80211 system
> > + * and associate the 'priv' pointer with it.
> > + *
> > + * Returns a non-negative wiphy index or a negative error code.
> > + *
> > + * NOTE: for proper operation, this priv pointer MUST also be
> > + * assigned to each &struct net_device's @ieee80211_ptr member!
> > + */
> > +extern int cfg80211_register(struct cfg80211_ops *ops, void *priv);
> > +
> > +/**
> > + * cfg80211_unregister - deregister a wiphy from cfg80211
> > + *
> > + * unregister a device with the given priv pointer.
> > + * After this call, no more requests can be made with this priv
> > + * pointer, but the call may sleep to wait for an outstanding
> > + * request that is being handled.
> > + */
> > +extern void cfg80211_unregister(void *priv);
> > +
> > +/* helper functions specific to nl80211 */
> > +extern void *nl80211hdr_put(struct sk_buff *skb, u32 pid,
> > +			    u32 seq, int flags, u8 cmd);
> > +extern void *nl80211msg_new(struct sk_buff **skb, u32 pid,
> > +			    u32 seq, int flags, u8 cmd);
> > +
> > +#endif /* __NET_CFG80211_H */
> > --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> > +++ wireless-dev/net/wireless/core.c	2006-09-28 00:38:22.664150162 +0200
> > @@ -0,0 +1,233 @@
> > +/*
> > + * This is the new wireless configuration interface.
> > + *
> > + * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
> > + */
> > +
> > +#include "core.h"
> > +#include "nl80211.h"
> > +#include <linux/if.h>
> > +#include <linux/module.h>
> > +#include <linux/err.h>
> > +#include <net/genetlink.h>
> > +#include <net/cfg80211.h>
> > +#include <linux/mutex.h>
> > +#include <linux/list.h>
> > +
> > +MODULE_AUTHOR("Johannes Berg");
> > +MODULE_LICENSE("GPL");
> > +
> > +/* RCU might be appropriate here since we usually
> > + * only read the list, and that can happen quite
> > + * often because we need to do it for each command */
> > +LIST_HEAD(cfg80211_drv_list);
> > +DEFINE_MUTEX(cfg80211_drv_mutex);
> > +static int wiphy_counter;
> > +
> > +/* requires nl80211_drv_mutex to be held! */
> > +static struct cfg80211_registered_driver *cfg80211_drv_by_priv(void *priv)
> > +{
> > +	struct cfg80211_registered_driver *result = NULL, *drv;
> > +
> > +	if (!priv)
> > +		return NULL;
> > +
> > +	list_for_each_entry(drv, &cfg80211_drv_list, list) {
> > +		if (drv->priv == priv) {
> > +			result = drv;
> > +			break;
> > +		}
> > +	}
> > +
> > +	return result;
> > +}
> > +
> > +/* requires cfg80211_drv_mutex to be held! */
> > +static struct cfg80211_registered_driver *cfg80211_drv_by_wiphy(int wiphy)
> > +{
> > +	struct cfg80211_registered_driver *result = NULL, *drv;
> > +
> > +	list_for_each_entry(drv, &cfg80211_drv_list, list) {
> > +		if (drv->wiphy == wiphy) {
> > +			result = drv;
> > +			break;
> > +		}
> > +	}
> > +
> > +	return result;
> > +}
> > +
> > +/* requires cfg80211_drv_mutex to be held! */
> > +static struct cfg80211_registered_driver *
> > +__cfg80211_drv_from_info(struct genl_info *info)
> > +{
> > +	int ifindex;
> > +	struct cfg80211_registered_driver *bywiphy = NULL, *byifidx = NULL;
> > +	struct net_device *dev;
> > +	int err = -EINVAL;
> > +
> > +	if (info->attrs[NL80211_ATTR_WIPHY]) {
> > +		bywiphy = cfg80211_drv_by_wiphy(
> > +				nla_get_u32(info->attrs[NL80211_ATTR_WIPHY]));
> > +		err = -ENODEV;
> > +	}
> > +
> > +	if (info->attrs[NL80211_ATTR_IFINDEX]) {
> > +		ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
> > +		dev = dev_get_by_index(ifindex);
> > +		if (dev) {
> > +			byifidx = cfg80211_drv_by_priv(dev->ieee80211_ptr);
> > +			dev_put(dev);
> > +		}
> > +		err = -ENODEV;
> > +	}
> > +
> > +	if (bywiphy && byifidx) {
> > +		if (bywiphy != byifidx)
> > +			return ERR_PTR(-EINVAL);
> > +		else
> > +			return bywiphy; /* == byifidx */
> > +	}
> > +	if (bywiphy)
> > +		return bywiphy;
> > +
> > +	if (byifidx)
> > +		return byifidx;
> > +
> > +	return ERR_PTR(err);
> > +}
> > +
> > +struct cfg80211_registered_driver *
> > +cfg80211_get_drv_from_info(struct genl_info *info)
> > +{
> > +	struct cfg80211_registered_driver *drv;
> > +
> > +	mutex_lock(&cfg80211_drv_mutex);
> > +	drv = __cfg80211_drv_from_info(info);
> > +
> > +	/* if it is not an error we grab the lock on
> > +	 * it to assure it won't be going away while
> > +	 * we operate on it */
> > +	if (!IS_ERR(drv))
> > +		mutex_lock(&drv->mtx);
> > +
> > +	mutex_unlock(&cfg80211_drv_mutex);
> > +
> > +	return drv;
> > +}
> > +
> > +/* wext will need this */
> > +struct cfg80211_registered_driver *
> > +cfg80211_get_drv_from_ifindex(int ifindex)
> > +{
> > +	struct cfg80211_registered_driver *drv;
> > +	struct net_device *dev;
> > +
> > +	mutex_lock(&cfg80211_drv_mutex);
> > +	dev = dev_get_by_index(ifindex);
> > +	if (!dev)
> > +		return ERR_PTR(-ENODEV);
> > +	drv = cfg80211_drv_by_priv(dev->ieee80211_ptr);
> > +	if (drv)
> > +		mutex_lock(&drv->mtx);
> > +	dev_put(dev);
> > +	if (drv)
> > +		return drv;
> > +	return ERR_PTR(-ENODEV);
> > +}
> > +
> > +void cfg80211_put_drv(struct cfg80211_registered_driver *drv)
> > +{
> > +	BUG_ON(IS_ERR(drv));
> > +	mutex_unlock(&drv->mtx);
> > +}
> > +
> > +/* exported functions */
> > +
> > +int cfg80211_register(struct cfg80211_ops *ops, void *priv)
> > +{
> > +	struct cfg80211_registered_driver *drv;
> > +	int res;
> > +
> > +	if (!priv || !ops->list_interfaces)
> > +		return -EINVAL;
> > +
> > +	mutex_lock(&cfg80211_drv_mutex);
> > +
> > +	if (cfg80211_drv_by_priv(priv)) {
> > +		res = -EALREADY;
> > +		goto out_unlock;
> > +	}
> > +
> > +	drv = kzalloc(sizeof(struct cfg80211_registered_driver), GFP_KERNEL);
> > +	if (!drv) {
> > +		res = -ENOMEM;
> > +		goto out_unlock;
> > +	}
> > +
> > +	drv->ops = ops;
> > +	drv->priv = priv;
> > +
> > +	if (unlikely(wiphy_counter<0)) {
> > +		/* ugh, wrapped! */
> > +		kfree(drv);
> > +		res = -ENOSPC;
> > +		goto out_unlock;
> > +	}
> > +	mutex_init(&drv->mtx);
> > +	drv->wiphy = wiphy_counter;
> > +	list_add(&drv->list, &cfg80211_drv_list);
> > +	/* return wiphy number */
> > +	res = drv->wiphy;
> > +
> > +	/* now increase counter for the next time */
> > +	wiphy_counter++;
> > +
> > + out_unlock:
> > +	mutex_unlock(&cfg80211_drv_mutex);
> > +	return res;
> > +}
> > +EXPORT_SYMBOL_GPL(cfg80211_register);
> > +
> > +void cfg80211_unregister(void *priv)
> > +{
> > +	struct cfg80211_registered_driver *drv;
> > +
> > +	mutex_lock(&cfg80211_drv_mutex);
> > +	drv = cfg80211_drv_by_priv(priv);
> > +	if (!drv) {
> > +		printk(KERN_ERR "deregistering cfg80211 backend that "
> > +		       " was never registered!\n");
> > +		mutex_unlock(&cfg80211_drv_mutex);
> > +		return;
> > +	}
> > +
> > +	/* hold registered driver mutex during list removal as well
> > +	 * to make sure no commands are in progress at the moment */
> > +	mutex_lock(&drv->mtx);
> > +	list_del(&drv->list);
> > +	mutex_unlock(&drv->mtx);
> > +
> > +	mutex_unlock(&cfg80211_drv_mutex);
> > +
> > +	mutex_destroy(&drv->mtx);
> > +	kfree(drv);
> > +}
> > +EXPORT_SYMBOL_GPL(cfg80211_unregister);
> > +
> > +/* module initialisation/exit functions */
> > +
> > +static int cfg80211_init(void)
> > +{
> > +	/* possibly need to do more later */
> > +	return nl80211_init();
> > +}
> > +
> > +static void cfg80211_exit(void)
> > +{
> > +	/* possibly need to do more later */
> > +	nl80211_exit();
> > +}
> > +
> > +module_init(cfg80211_init);
> > +module_exit(cfg80211_exit);
> > --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> > +++ wireless-dev/net/wireless/core.h	2006-09-28 00:38:22.734150162 +0200
> > @@ -0,0 +1,57 @@
> > +/*
> > + * Wireless configuration interface internals.
> > + *
> > + * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
> > + */
> > +#ifndef __NET_WIRELESS_CORE_H
> > +#define __NET_WIRELESS_CORE_H
> > +#include <net/cfg80211.h>
> > +#include <linux/mutex.h>
> > +#include <linux/list.h>
> > +#include <net/genetlink.h>
> > +
> > +struct cfg80211_registered_driver {
> > +	struct cfg80211_ops *ops;
> > +	int wiphy;
> > +	void *priv;
> > +	struct list_head list;
> > +	/* we hold this mutex during any call so that
> > +	 * we cannot do multiple calls at once, and also
> > +	 * to avoid the deregister call to proceed while
> > +	 * any call is in progress */
> > +	struct mutex mtx;
> > +};
> > +
> > +extern struct mutex cfg80211_drv_mutex;
> > +extern struct list_head cfg80211_drv_list;
> > +
> > +/*
> > + * This function returns a pointer to the driver
> > + * that the genl_info item that is passed refers to.
> > + * If successful, it returns non-NULL and also locks
> > + * the driver's mutex!
> > + *
> > + * This means that you need to call cfg80211_put_drv()
> > + * before being allowed to acquire &cfg80211_drv_mutex!
> > + *
> > + * This is necessary because we need to lock the global
> > + * mutex to get an item off the list safely, and then
> > + * we lock the drv mutex so it doesn't go away under us.
> > + *
> > + * We don't want to keep cfg80211_drv_mutex locked
> > + * for all the time in order to allow requests on
> > + * other interfaces to go through at the same time.
> > + *
> > + * The result of this can be a PTR_ERR and hence must
> > + * be checked with IS_ERR() for errors.
> > + */
> > +extern struct cfg80211_registered_driver *
> > +cfg80211_get_drv_from_info(struct genl_info *info);
> > +
> > +/* identical to cfg80211_get_drv_from_info but only operate on ifindex */
> > +extern struct cfg80211_registered_driver *
> > +cfg80211_get_drv_from_ifindex(int ifindex);
> > +
> > +extern void cfg80211_put_drv(struct cfg80211_registered_driver *drv);
> > +
> > +#endif /* __NET_WIRELESS_CORE_H */
> > --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> > +++ wireless-dev/net/wireless/nl80211.h	2006-09-27 19:36:17.744088173 +0200
> > @@ -0,0 +1,7 @@
> > +#ifndef __NET_WIRELESS_NL80211_H
> > +#define __NET_WIRELESS_NL80211_H
> > +
> > +extern int nl80211_init(void);
> > +extern void nl80211_exit(void);
> > +
> > +#endif /* __NET_WIRELESS_NL80211_H */
> > --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> > +++ wireless-dev/net/wireless/wext-compat.c	2006-09-28 00:38:55.304150162 +0200
> > @@ -0,0 +1,25 @@
> > +/* NOT YET */
> > +
> > +To implement compatibility, we add a new field to struct net_device
> > +that contains the pending configuration structure. This is dynamically
> > +allocated when needed and freed when committed.
> > +In a way it replaces the wireless_handlers field in there which is now
> > +done by dynamic lookup. No worries. No one is going to have thousands
> > +of wireless devices, and if that changes we can still trivially change
> > +this assumption :)
> > +
> > +Commit is done some time after the last parameter was changed
> > +(with each parameter change simply (re-)schedule a timer) or
> > +if explicitly asked for. This is probably not what most people
> > +would expect, but perfectly fine in the WE API.
> > +
> > +compatibility mappings:
> > +
> > +SIOCSIWAP
> > +  -> if bssid is all-ones: set roaming to kernel, reassociate
> > +  -> if bssid is all-zeroes: set roaming to kernel
> > +  -> otherwise: set roaming to userspace, set bssid
> > +
> > +SIOCGIWAP
> > +  -> get association parameters and fill return bssid appropriately
> > +
> > 
> > -
> > To unsubscribe from this list: send the line "unsubscribe netdev" in
> > the body of a message to majordomo@vger.kernel.org
> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
> -
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC] cfg80211 and nl80211
  2006-10-02 16:15 ` Dan Williams
  2006-10-02 17:01   ` Dan Williams
@ 2006-10-04  7:41   ` Johannes Berg
  2006-10-04 14:19     ` Johannes Berg
  2006-10-05  7:47   ` Johannes Berg
  2 siblings, 1 reply; 16+ messages in thread
From: Johannes Berg @ 2006-10-04  7:41 UTC (permalink / raw)
  To: Dan Williams; +Cc: netdev, Jiri Benc, John W. Linville, Larry Finger

On Mon, 2006-10-02 at 12:15 -0400, Dan Williams wrote:

> I'm not sure what you mean here.  Do you really mean "grab the current
> _cmdlist_"?  Because I'm not sure how grabbing the current configuration
> (using GET_CONFIG) would necessarily return the right set of options for
> the device.  Also, what do you mean by "is supposed to be complete
> then"?

Yeah, I was thinking GET_CONFIG would fill in all values that you can
also set. But then again, if no SSID was ever set it can't fill in an
SSID, so that's bogus. We'll need to add a command to return a bitmask
of what parameters a card supports.

> On large problem we had with WPA was that there was _no_ way to tell
> whether or not a driver supported it.  Trying a WPA-related ioctl() and
> hoping for the best is broken.  On the one hand, there were users
> screaming that yes, Madwifi did in fact support WPA, but of course,
> there was no standard way of figuring that out and present it to the
> user in a sane manner.  We need to have an _explicit_ list of stuff that
> driver does and does not support so that intelligent decisions can be
> made before and/or without touching stuff that might turn the radio on
> or mess up an existing configuration set by somebody else.

Right. Though with WPA, that's likely going to be in a few special
commands, so checking if the driver supports those commands (by getting
the command list) ought to be enough, no?

> Does "parameters" here mean CMD or ATTR? 

ATTR. Unknown commands are rejected anyway right in the genetlink layer
(afaik), the question was just whether to ignore or reject attributes we
don't understand.

>  In any case, there's a good
> case to be made for rejecting unsupported CMDs & ATTRs.  If the user,
> for example, wishes to restrict the roaming to a set of BSSIDs for
> security measures, for example (even if an insecure one), but the driver
> doesn't support that, should nl80211 just blindly pretend that it
> worked?  This might also get people to fix up their drivers and
> userspace programs too.

Good point. However, I'd think that such a feature could well have a new
command like CMD_SET_RESTRICTED_BSSID_LIST with a BSSID_LIST attribute,
and then the whole problem doesn't really happen since userspace will
see when that command is not supported.

> I do realize there's an API extensibility benefit to ignoring stuff you
> don't handle though (like 802.11 information element handling).  But if
> we're talking about an explicit request by a user for an option, if the
> driver doesn't support it, they should get an error.

Yeah, though the whole "stuff a bunch of options into SET_CONFIG" isn't
really where I'd like to be ideally, it's more that it is necessary to
be able to restart firmware less. Hence, I think set_config is not very
likely to be extended further, and new stuff gets new commands.

> That sounds like a good idea.  Putting 802.11d handling stuff into the
> kernel would essentially be duplicating the code and function of the
> userspace regulatory daemon, right?  That seems pointless.

Probably. But also see James's answers and the short thread resulting
from that.

> Another questions; I didn't see anything for encryption and auth and
> stuff yet.  Are you just trying to get the basics down before going on
> to that stuff? 

Yes, I'm not that far yet.

> What do you still have on your ToDo list for nl80211 before you'd
> consider "ready" to take over real configuration functions?

I don't really have an explicit ToDo list, but here are a few points
that come to mind
 * notification support when parameters change multicast a netlink
   message to all subscribers of that group
 * transmit status notification support for packet injection (sort of
   ties in with the first point)
 * scan results (also somewhat related to the first point)
 * crypto and auth support

> > +	/* unconditionally allow some common commands we handle centrally */
> > +	NLA_PUT_FLAG(msg, NL80211_CMD_GET_CMDLIST);
> > +	NLA_PUT_FLAG(msg, NL80211_CMD_GET_WIPHYS);
> > +	NLA_PUT_FLAG(msg, NL80211_CMD_GET_INTERFACES);
> > +
> > +	CHECK_CMD(list_interfaces, GET_INTERFACES);
> 
> Don't these two do exactly the same thing?  The first one adds
> GET_INTERFACES unconditionally, the next adds it (again!) if the
> interface supports it.  Either we add it unconditionally or we add it
> conditionally, but not both :)

Heh, yeah, it should be added unconditionally since it's one of the very
few calls that all operation structs must have assigned. Not that it
hurts to have it in there twice ;)

> I think we need a GET_SCAN here as well.  INITIATE_SCAN should
> definitely be CAP_NET_ADMIN-only or whatever, but GET_SCAN can be
> user-accessible.  Non-root stuff should still be able to get scan
> results even if they can't initiate one.

Yeah, we'll want that, but I haven't really thought about how we pass
scan results to userspace. We could be passing them out whenever we see
a change by way of some UPDATE_SCAN_RESULT command that is multicast to
all subscribers of a "scan results" group, for example.

> You also don't necessarily want to have to initiate a scan every time
> you want the results.  Drivers and/or d80211 should be caching the
> results anyway.  If d80211 is not, it should be.

Yeah. I think d80211 is caching but I don't really know.

>> "FLAG_SCAN_ACTIVELY"

> ACTIVELY is an adverb, and that doesn't parse very well in this context.

Heh

> It also just sounds weird here.  Better options:
> 
> FLAG_SCAN_ACTIVE
> FLAG_SCAN_TYPE_ACTIVE (<-- my favorite)
> FLAG_ACTIVE_SCAN

Yeah, I like TYPE_ACTIVE. Will change for the next posting.

Thanks for your comments, much appreciated.

johannes

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC] cfg80211 and nl80211
  2006-10-04  7:41   ` Johannes Berg
@ 2006-10-04 14:19     ` Johannes Berg
  2006-10-04 17:57       ` Dan Williams
  0 siblings, 1 reply; 16+ messages in thread
From: Johannes Berg @ 2006-10-04 14:19 UTC (permalink / raw)
  To: Dan Williams
  Cc: netdev, Jiri Benc, John W. Linville, Larry Finger, Jouni Malinen,
	Thomas Graf

On Wed, 2006-10-04 at 09:41 +0200, Johannes Berg wrote:

> I don't really have an explicit ToDo list, but here are a few points
> that come to mind
>  * notification support when parameters change multicast a netlink
>    message to all subscribers of that group

I think we'll want at least two groups here:
 - scan results
 - configuration changes

can anyone think of more? NM might like to listen for config changes and
scan events, but most tools won't be that much concerned about config
changes... current wireless events are subsumed here as well.

>  * transmit status notification support for packet injection (sort of
>    ties in with the first point)

This should be a matter of saving the socket id of the requesting
nl80211 packet in the request structure, and having d80211/the driver
keep track of it and pass it back with the tx status structure to an
cfg80211_transmit_status(). Maybe a second cookie would be good too if
we ever gain packet injection capabilities over something other than
netlink. Thoughts?

>  * scan results (also somewhat related to the first point)

Should cfg80211 do the chore of keeping track of the whole scan results?
On the other hand, that doesn't seem to be doable with legacy hardware
that does all the scanning. So probably one call for
   cfg80211_notify_scan()
that takes a new scan result structure (taking a single BSSID etc.) and
notifies all listeners.
The same structure is used for get_scan() from the wiphy ops in an
iterator interface like some other calls.

>  * crypto and auth support

I really need someone to help me out with this one. So far I've seen
that we have commands for
 * getting crypto capabilities (several orthogonal issues, crypto
   algorithms as well as auth algorithms as well as key management?)
 * setting keys for both rx and tx
    - up to 4 default keys
    - setting WPA key(s?)
    - setting sta pairwise key (AP)
    - setting group key (AP, same as for STA?)
 * setting the WPA IE(s)
 * authentication (WE sticks so much into that call that I can't pull it
   apart, what exactly is needed?) looks like it needs (sub)commands for
    - allowed wpa versions (why is this AUTH??)
    - setting pairwise and group ciphers (again.. AUTH??)
    - setting key management (again.. AUTH??)
    - authentication algorithm (whew, something that really is auth)
    - roaming control (haha, already covered)
 * more?

johannes

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC] cfg80211 and nl80211
  2006-10-04 14:19     ` Johannes Berg
@ 2006-10-04 17:57       ` Dan Williams
  2006-10-05  7:59         ` Johannes Berg
                           ` (2 more replies)
  0 siblings, 3 replies; 16+ messages in thread
From: Dan Williams @ 2006-10-04 17:57 UTC (permalink / raw)
  To: Johannes Berg
  Cc: netdev, Jiri Benc, John W. Linville, Larry Finger, Jouni Malinen,
	Thomas Graf

On Wed, 2006-10-04 at 16:19 +0200, Johannes Berg wrote:
> On Wed, 2006-10-04 at 09:41 +0200, Johannes Berg wrote:
> 
> > I don't really have an explicit ToDo list, but here are a few points
> > that come to mind
> >  * notification support when parameters change multicast a netlink
> >    message to all subscribers of that group
> 
> I think we'll want at least two groups here:
>  - scan results
>  - configuration changes
> 
> can anyone think of more? NM might like to listen for config changes and
> scan events, but most tools won't be that much concerned about config
> changes... current wireless events are subsumed here as well.

Are we talking about config changes when some other process pushes a new
config to the card, or when something happens over the air, like new
association or deauth?

> >  * transmit status notification support for packet injection (sort of
> >    ties in with the first point)
> 
> This should be a matter of saving the socket id of the requesting
> nl80211 packet in the request structure, and having d80211/the driver
> keep track of it and pass it back with the tx status structure to an
> cfg80211_transmit_status(). Maybe a second cookie would be good too if
> we ever gain packet injection capabilities over something other than
> netlink. Thoughts?
> 
> >  * scan results (also somewhat related to the first point)
> 
> Should cfg80211 do the chore of keeping track of the whole scan results?
> On the other hand, that doesn't seem to be doable with legacy hardware
> that does all the scanning. So probably one call for
>    cfg80211_notify_scan()
> that takes a new scan result structure (taking a single BSSID etc.) and
> notifies all listeners.
> The same structure is used for get_scan() from the wiphy ops in an
> iterator interface like some other calls.

Is it a problem to actually push the _entire_ scan list out to clients
over netlink?  The scan list could be quite large, maybe even a few
kilobytes when stuff like Information Elements, ratesets, etc is
available.  I've seen 35-item scan lists that are already around 1.5K.

Ideally, we could push the whole scan list to clients, and then we avoid
the race between getting the scan result notification and hitting the
card.  That said, as long as the driver does proper locking, the race
condition shouldn't matter at all.

I'd vote for pushing results along with the notification, because one of
the most annoying things in the past was the inconsistency between how
drivers reported results and what BSSID attributes they sent.  If we can
_standardize_ the result list and its construction inside
cfg80211/nl80211 that would be a great benefit.

If we can't push results with the notification, at least provide some
functions to build up the GET_SCAN reply message, which you'll likely
have to do anyway once you implement GET_SCAN.  We _really_ need drivers
to be consistent here.

> >  * crypto and auth support

I've done a lot of thinking about crypto/auth this morning while beating
the hell out of the libertas 8388 driver to clean up the ENCODE support.

There are several issues here.  They can be roughly split by encryption
algorithm.  But the big question:

    Is there a case for _multiple_ encryption algorithms enabled
    on a single "virtual" interface at one time?

I don't think there is, and I think that just complicates things in
d80211 anyway.  If we agree that you can only set one of [none, WEP,
WPA] on a virtual interface at any given time, it makes the crypto
interface for nl80211 a lot easier.

Part of the problem of WE right now is that there's no clear API
separation between the different options.  You can pass some WEP options
through when you really want to do WPA (like key indexes).  That makes
the driver handling code for ENCODE and ENCODEEXT too complex.

Taking one-at-a-time as a given, and the pseudo-structure

struct cmd_crypto {
	enum crypto_alg alg;
	union data {
		none_data;
		wep_data;
		wpa_data;
		...
	};
};

Set alg == <whatever>, set the options, and the driver will _enable_
that crypto mode with the given options.  It makes no sense at all to,
say, set the WEP transmit key index or WEP key when the card is in WPA
mode or no-crypto mode.

It's important to note that some options are independent of the initial
operation that enabled the crypto, and need to be set later without
triggering deauth and such.  Setting non-TX-index WEP key is one such
operation.  I should be able to set WEP keys at indexes other than the
transmit key index without affecting operation of the card (unless some
hardware/firmware issue prevents this).

- No crypto
- WEP encryption (following ops are independent of each other):
    - Set TX key index
    - Set privacy invoked
    - Set exclude unencrypted packets
    - Set authentication mode (open, shared-key, or both)
    - Set (or clear) WEP key 1, 2, 3, or 4
- WPA/WPA2/IEEE8021X
    - Jouni/others would know better and my brain is fried right now

All the WEP options should be independent attributes in nl80211.  You
could even have a generic WEPKey attribute that is defined like so:

ATTR_WEP_KEY {
	enum type (one of DISABLE, TYPE_40, TYPE_104, TYPE_152)
	char * key_material
	enum index (one of DEFAULT, 1, 2, 3, 4)
}

(I tend to think the more enums we use, and not magic numbers, the
clearer it gets in the code.)

That way, you can attach as many key attribues (or as few) as you wish.
You could set all 4 keys at one time.  You could set 1 now and 1 later.

> I really need someone to help me out with this one. So far I've seen
> that we have commands for
>  * getting crypto capabilities (several orthogonal issues, crypto
>    algorithms as well as auth algorithms as well as key management?)
>  * setting keys for both rx and tx
>     - up to 4 default keys

Some cards, like older aironet ones, can only do 1 key.  I don't want to
pollute nl80211 with limitations like that though, so the driver can
just return -EINVAL if the card doesn't support it.  I don't think it
pays to have capability bits for something as far down in the driver
implementation as this. 

>     - setting WPA key(s?)
>     - setting sta pairwise key (AP)
>     - setting group key (AP, same as for STA?)
>  * setting the WPA IE(s)
>  * authentication (WE sticks so much into that call that I can't pull it
>    apart, what exactly is needed?) looks like it needs (sub)commands for

Much of this comes from the Mind Of Jouni, so he's best qualified to
comment...

My impression of ENCODE and ENCODEEXT was that they were _just_ for
setting keying material (except in the case of WEP where you set the
auth mode open/shared too), and that AUTH was for higher level stuff.

>     - allowed wpa versions (why is this AUTH??)

Can you do both WPA and WPA2 at the same time?  You can certainly
advertise both a WPA IE and an RSN IE at the same time.  Is it possible
to auth both types of clients?

>     - setting pairwise and group ciphers (again.. AUTH??)

Note that ENCODEEXT has a group key flag IW_ENCODE_EXT_GROUP_KEY too.
The pairwise and group cipher stuff in AUTH only sets the cipher _type_,
not the actual key material AFAICT.

------------

I think there's a lot of room to rework the API here.  It seems you can
more or less mix & match authentication, encryption, and key management,
with some restrictions.  I definitely don't know this as well as people
like Jouni so I'm likely to be wrong about some stuff, but here goes to
start with:

* None
    - Crypto: None
    - 802.11 Auth: Open System

* Static WEP
    - Keys: up to 4 group keys
    - Crypto: WEP-40, WEP-104, WEP-152, WEP-256
    - 802.11 Auth: Open System or Shared Key
    - Key Mgmt/Auth: none

* Dynamic WEP (LEAP?)
    - Keys: up to 4 group keys
    - Crypto: WEP-40, WEP-104, WEP-152, WEP-256
    - 802.11 Auth: Open System (only?)
    - Key Mgmt/Auth: IEEE 802.1x with LEAP or EAP

* WPA PSK
    - Keys: pairwise & group
    - Use WPA IEs
    - 802.11 Auth: Open System
    - Crypto: TKIP or CCMP
    - Key Mgmt/Auth: WPA-PSK (elided 802.1x)

* WPA Enterprise
    - Keys: pairwise & group
    - Use WPA IEs
    - 802.11 Auth: Open System
    - Crypto: TKIP or CCMP
    - Key Mgmt/Auth: WPA-EAP (full 802.1x)

* WPA2 PSK
    - Keys: pairwise & group
    - Use RSN IEs
    - 802.11 Auth: Open System
    - Crypto: TKIP or CCMP
    - Key Mgmt/Auth: WPA-PSK (elided 802.1x)

* WPA2 Enterprise
    - Keys: pairwise & group
    - Use RSN IEs
    - 802.11 Auth: Open System
    - Crypto: TKIP or CCMP
    - Key Mgmt/Auth: WPA-EAP (full 802.1x)

Wheee!  So you basically have a bunch of buckets and you just pull shit
out of them at random, stick it all together, and you've got a wireless
connection :)  Thank you, Cisco.  Thank you, Wi-Fi Alliance.

Dan



^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC] cfg80211 and nl80211
  2006-10-02 16:15 ` Dan Williams
  2006-10-02 17:01   ` Dan Williams
  2006-10-04  7:41   ` Johannes Berg
@ 2006-10-05  7:47   ` Johannes Berg
  2 siblings, 0 replies; 16+ messages in thread
From: Johannes Berg @ 2006-10-05  7:47 UTC (permalink / raw)
  To: Dan Williams; +Cc: netdev, Jiri Benc, John W. Linville, Larry Finger

Umm, looks like I skipped this paragraph in my earlier reply to you. Sorry
about that.

> I'd also argue that one specific BSSID is part of an initial
> configuration.  We should support that in config command.  It's an
> implicit SET_FIXED_BSSID, yes.  But one of the major points of
> nl80211/cfg80211 was that you could bundle up a set of configuration
> settings into a single atomic "packet", which you couldn't do with WE.
> 
> So if a specific BSSID isn't sent in the initial config command, when do
> you set a specific BSSID?  Before?  After?  The behavior starts getting
> complicated, and we're back to a situation where every driver implements
> the semantics in a slightly different manner.

Ah, good point. But then, why would you want to set a specific (initial)
BSSID at all? Either you set userspace roaming (which you'd do before
setting the SSID) then the kernel can't do anything without you setting a
BSSID, or you don't set userspace roaming, then all the kernel needs is the
SSID.

I'm thinking you probably want something like 'list of BSSIDs to use for
userspace roaming' and possibly a blacklist too, although I'm inclined to
let userspace manage the blacklist by way of having a whitelist *only* and
having userspace simply add everything to the whitelist that it discovers
through scanning and isn't on the blacklist...

Hence, would you be satisfied with a BSSID-whitelist for kernel-controlled
roaming (userspace roaming doesn't need the kernel to know about the
whitelist)? Heck, you could even use a single-element whitelist for when you
want to force the kernel to associate to that AP... Maybe we should thus
drop the userspace roaming support? I think it's a simpler API though...
Then again, why do we need a BSSID-whitelist? Just have userspace control
roaming then...

Also, the use case you want could probably be achieved by turning on
userspace roaming, setting the BSSID for it, configuring the SSID and then
turning off userspace roaming again.

Or let me put it another way: I'm not sure what the use case actually is :)

johannes

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC] cfg80211 and nl80211
  2006-10-04 17:57       ` Dan Williams
@ 2006-10-05  7:59         ` Johannes Berg
  2006-10-05 13:13         ` Stuffed Crust
  2006-10-05 16:20         ` Jouni Malinen
  2 siblings, 0 replies; 16+ messages in thread
From: Johannes Berg @ 2006-10-05  7:59 UTC (permalink / raw)
  To: Dan Williams
  Cc: netdev, Jiri Benc, John W. Linville, Larry Finger, Jouni Malinen,
	Thomas Graf

On Wed, 2006-10-04 at 13:57 -0400, Dan Williams wrote:

> Are we talking about config changes when some other process pushes a new
> config to the card, or when something happens over the air, like new
> association or deauth?

Well, both actually :) Yeah, we should have different groups for that.

> Is it a problem to actually push the _entire_ scan list out to clients
> over netlink?  The scan list could be quite large, maybe even a few
> kilobytes when stuff like Information Elements, ratesets, etc is
> available.  I've seen 35-item scan lists that are already around 1.5K.

35-item list at 1.5K, heh, the allocated skbs are always at least 4k so
we can just push that out in full I guess. Though scan lists with
nl80211 will be slightly larger (some genetlink overhead) than the
bit-packed WE stuff.

> Ideally, we could push the whole scan list to clients, and then we avoid
> the race between getting the scan result notification and hitting the
> card.  That said, as long as the driver does proper locking, the race
> condition shouldn't matter at all.

Heh yeah.

> I'd vote for pushing results along with the notification, because one of
> the most annoying things in the past was the inconsistency between how
> drivers reported results and what BSSID attributes they sent.  If we can
> _standardize_ the result list and its construction inside
> cfg80211/nl80211 that would be a great benefit.

Well, I'm thinking that drivers provide an iterator that provides a scan
result structure for each result and nl80211 iterates over that building
up the netlink message. That way, they just have to fill in such a
structure for each iteration, which should ease things.

> If we can't push results with the notification, at least provide some
> functions to build up the GET_SCAN reply message, which you'll likely
> have to do anyway once you implement GET_SCAN.  We _really_ need drivers
> to be consistent here.

Same thing, get_scan() gives an iterator function that the driver calls
for each result it has with the same scan result struct.

> > >  * crypto and auth support
> 
> I've done a lot of thinking about crypto/auth this morning while beating
> the hell out of the libertas 8388 driver to clean up the ENCODE support.
> 
> There are several issues here.  They can be roughly split by encryption
> algorithm.  But the big question:
> 
>     Is there a case for _multiple_ encryption algorithms enabled
>     on a single "virtual" interface at one time?
> 
> I don't think there is, and I think that just complicates things in
> d80211 anyway.  If we agree that you can only set one of [none, WEP,
> WPA] on a virtual interface at any given time, it makes the crypto
> interface for nl80211 a lot easier.

I can't see a case point for that. Although maybe for AP interfaces? But
does that make sense to have some stations with say TKIP and others with
AES, and is that even negotiable? In any case, I think a STA inside an
AP should be treated mostly like a single "STA virtual interface" which
surely doesn't need multiple algorithms.

> Part of the problem of WE right now is that there's no clear API
> separation between the different options.  You can pass some WEP options
> through when you really want to do WPA (like key indexes).  That makes
> the driver handling code for ENCODE and ENCODEEXT too complex.
> 
> Taking one-at-a-time as a given, and the pseudo-structure
> 
> struct cmd_crypto {
> 	enum crypto_alg alg;
> 	union data {
> 		none_data;
> 		wep_data;
> 		wpa_data;
> 		...
> 	};
> };
> 
> Set alg == <whatever>, set the options, and the driver will _enable_
> that crypto mode with the given options.  It makes no sense at all to,
> say, set the WEP transmit key index or WEP key when the card is in WPA
> mode or no-crypto mode.

That makes sense; in netlink it'd be represented by a message containing
a algorithm attribute and then attributes for all the other things and
not those attributes for say WPA if you use WEP.

> It's important to note that some options are independent of the initial
> operation that enabled the crypto, and need to be set later without
> triggering deauth and such.  Setting non-TX-index WEP key is one such
> operation.  I should be able to set WEP keys at indexes other than the
> transmit key index without affecting operation of the card (unless some
> hardware/firmware issue prevents this).
> 
> - No crypto
> - WEP encryption (following ops are independent of each other):
>     - Set TX key index
>     - Set privacy invoked

what is that?

>     - Set exclude unencrypted packets
>     - Set authentication mode (open, shared-key, or both)
>     - Set (or clear) WEP key 1, 2, 3, or 4
> - WPA/WPA2/IEEE8021X
>     - Jouni/others would know better and my brain is fried right now
> 
> All the WEP options should be independent attributes in nl80211.  You
> could even have a generic WEPKey attribute that is defined like so:
> 
> ATTR_WEP_KEY {
> 	enum type (one of DISABLE, TYPE_40, TYPE_104, TYPE_152)
> 	char * key_material
> 	enum index (one of DEFAULT, 1, 2, 3, 4)
> }
> 
> (I tend to think the more enums we use, and not magic numbers, the
> clearer it gets in the code.)

Yeah. I'd probably add an ATTR_KEY_INDEX and ATTR_KEY_MATERIAL and
ATTR_WEP_TYPE which are to be used together with ATTR_KEY_TYPE==WEP, for
AES you'd also use ATTR_KEY_MATERIAL but not the others, etc.

> That way, you can attach as many key attribues (or as few) as you wish.
> You could set all 4 keys at one time.  You could set 1 now and 1 later.

Hm, I don't see why you need to set 4 keys at a time and it somewhat
complicates the netlink API.

> > I really need someone to help me out with this one. So far I've seen
> > that we have commands for
> >  * getting crypto capabilities (several orthogonal issues, crypto
> >    algorithms as well as auth algorithms as well as key management?)
> >  * setting keys for both rx and tx
> >     - up to 4 default keys
> 
> Some cards, like older aironet ones, can only do 1 key.  I don't want to
> pollute nl80211 with limitations like that though, so the driver can
> just return -EINVAL if the card doesn't support it.  I don't think it
> pays to have capability bits for something as far down in the driver
> implementation as this. 

Yeah, that's fine, drivers can return errors all the time.

> >     - setting WPA key(s?)
> >     - setting sta pairwise key (AP)
> >     - setting group key (AP, same as for STA?)
> >  * setting the WPA IE(s)
> >  * authentication (WE sticks so much into that call that I can't pull it
> >    apart, what exactly is needed?) looks like it needs (sub)commands for
> 
> Much of this comes from the Mind Of Jouni, so he's best qualified to
> comment...

:)

> My impression of ENCODE and ENCODEEXT was that they were _just_ for
> setting keying material (except in the case of WEP where you set the
> auth mode open/shared too), and that AUTH was for higher level stuff.
> 
> >     - allowed wpa versions (why is this AUTH??)
> 
> Can you do both WPA and WPA2 at the same time?  You can certainly
> advertise both a WPA IE and an RSN IE at the same time.  Is it possible
> to auth both types of clients?

I have no idea. Jouni?

> >     - setting pairwise and group ciphers (again.. AUTH??)
> 
> Note that ENCODEEXT has a group key flag IW_ENCODE_EXT_GROUP_KEY too.
> The pairwise and group cipher stuff in AUTH only sets the cipher _type_,
> not the actual key material AFAICT.

Eh, ok.

> I think there's a lot of room to rework the API here.  It seems you can
> more or less mix & match authentication, encryption, and key management,
> with some restrictions.  I definitely don't know this as well as people
> like Jouni so I'm likely to be wrong about some stuff, but here goes to
> start with:
> 
> * None
>     - Crypto: None
>     - 802.11 Auth: Open System
> 
> * Static WEP
>     - Keys: up to 4 group keys
>     - Crypto: WEP-40, WEP-104, WEP-152, WEP-256
>     - 802.11 Auth: Open System or Shared Key
>     - Key Mgmt/Auth: none
> 
> * Dynamic WEP (LEAP?)
>     - Keys: up to 4 group keys
>     - Crypto: WEP-40, WEP-104, WEP-152, WEP-256
>     - 802.11 Auth: Open System (only?)
>     - Key Mgmt/Auth: IEEE 802.1x with LEAP or EAP
> 
> * WPA PSK
>     - Keys: pairwise & group
>     - Use WPA IEs
>     - 802.11 Auth: Open System
>     - Crypto: TKIP or CCMP
>     - Key Mgmt/Auth: WPA-PSK (elided 802.1x)
> 
> * WPA Enterprise
>     - Keys: pairwise & group
>     - Use WPA IEs
>     - 802.11 Auth: Open System
>     - Crypto: TKIP or CCMP
>     - Key Mgmt/Auth: WPA-EAP (full 802.1x)
> 
> * WPA2 PSK
>     - Keys: pairwise & group
>     - Use RSN IEs
>     - 802.11 Auth: Open System
>     - Crypto: TKIP or CCMP
>     - Key Mgmt/Auth: WPA-PSK (elided 802.1x)
> 
> * WPA2 Enterprise
>     - Keys: pairwise & group
>     - Use RSN IEs
>     - 802.11 Auth: Open System
>     - Crypto: TKIP or CCMP
>     - Key Mgmt/Auth: WPA-EAP (full 802.1x)
> 
> Wheee!  So you basically have a bunch of buckets and you just pull shit
> out of them at random, stick it all together, and you've got a wireless
> connection :)  Thank you, Cisco.  Thank you, Wi-Fi Alliance.

:)

I'll have to think about this for a bit, but thanks again for your
comments :)

johannes

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC] cfg80211 and nl80211
  2006-10-04 17:57       ` Dan Williams
  2006-10-05  7:59         ` Johannes Berg
@ 2006-10-05 13:13         ` Stuffed Crust
  2006-10-05 15:46           ` Jouni Malinen
  2006-10-05 16:20         ` Jouni Malinen
  2 siblings, 1 reply; 16+ messages in thread
From: Stuffed Crust @ 2006-10-05 13:13 UTC (permalink / raw)
  To: Dan Williams
  Cc: Johannes Berg, netdev, Jiri Benc, John W. Linville, Larry Finger,
	Jouni Malinen, Thomas Graf

[-- Attachment #1: Type: text/plain, Size: 4325 bytes --]

On Wed, Oct 04, 2006 at 01:57:38PM -0400, Dan Williams wrote:
> * None
>     - Crypto: None
>     - 802.11 Auth: Open System
> 
> * Static WEP
>     - Keys: up to 4 group keys
>     - Crypto: WEP-40, WEP-104, WEP-152, WEP-256
>     - 802.11 Auth: Open System or Shared Key
>     - Key Mgmt/Auth: none
> 
> * Dynamic WEP (LEAP?)
>     - Keys: up to 4 group keys
>     - Crypto: WEP-40, WEP-104, WEP-152, WEP-256
>     - 802.11 Auth: Open System (only?)
>     - Key Mgmt/Auth: IEEE 802.1x with LEAP or EAP
> 
> * WPA PSK
>     - Keys: pairwise & group
>     - Use WPA IEs
>     - 802.11 Auth: Open System
>     - Crypto: TKIP or CCMP
>     - Key Mgmt/Auth: WPA-PSK (elided 802.1x)
> 
> * WPA Enterprise
>     - Keys: pairwise & group
>     - Use WPA IEs
>     - 802.11 Auth: Open System
>     - Crypto: TKIP or CCMP
>     - Key Mgmt/Auth: WPA-EAP (full 802.1x)
> 
> * WPA2 PSK
>     - Keys: pairwise & group
>     - Use RSN IEs
>     - 802.11 Auth: Open System
>     - Crypto: TKIP or CCMP
>     - Key Mgmt/Auth: WPA-PSK (elided 802.1x)
> 
> * WPA2 Enterprise
>     - Keys: pairwise & group
>     - Use RSN IEs
>     - 802.11 Auth: Open System
>     - Crypto: TKIP or CCMP
>     - Key Mgmt/Auth: WPA-EAP (full 802.1x)

This strikes me as overly complicated; to figure out what's necessary 
you shoudn't be looking at the WEXT API -- The 802.11 standards are 
all you need, and they lay things out fairly clearly, complete with 
rx/tx path flowcharts.  :)

Essentially, you have two crypto paradigms, pre-802.11i and 
post-802.11i.  (WPA uses the latter, and LEAP/CCX v1 is mostly the 
former; newer ones use the latter)

(Leave out the RSNIE, AuthType and KeyMgmt stuff; while they're 
 used in the actual key negotiation/derivation, they're separate 
 problems and have no bearing on the crypto layer.  From the driver's 
 perspective the RSNIE is just an opaque blob to be appended to 
 beacons,presps and [re]assoc frames, KeyMgmt is purely a matter for 
 the authenticator/supplicant, and AuthType is just a toggle that 
 happens to be off for post-802.11i, although LEAP v1 adds some 
 complications there..)

The old way:

* Four "default" keys. (used globally)
* PrivacyInvoked
* SetDefaultKeyIndex

The new way:

* PrivacyInvoked
* SetProtection (tx&|rx -- essentially "require crypto for a given macaddr)
* SetKeyMapping (one key per macaddr)

Each key has:

* Key type (WEP/TKIP/AES-CCMP/NONE)
* Key length (implied, but WEP can have varying key lengths)
* Key index (only '0' is generally used for unicast frames, but 802.11i 
             requires use of simultaneous broadcast keys)
* Macaddr (ucast addr or broadcast aka pairwise vs group)
* RxSequence (mainly for bcast aka group keys)

It's fairly easy to implement the old stuff in terms of the new stuff, 
if you assume that "if I don't have a per-sta key, just use the 
global/bcast key".   The 802.11i rx/tx frame path flow handles the old 
crypto style just fine.

...Meanwhile.  It's foolish to ignore the 802.11 MLME.  It lists out
pretty much everything that's necessary to get a working connection, and
looking at its evolution (and changes in the pipeline) shows that it's
impossible to do it all (right) the first time, and that changes, not
just additions, will be necessary.

(Did I mention that I really like how the ALSA people manage this?  The 
 userspace-kernelspace API is effectively private; apps write to the  
 libs, which do the hard work of maintaining backwards compatibility as 
 the internals change and get new features, but now I'm really just 
 armchair quarterbacking, so I'll shut up now.)

> Wheee!  So you basically have a bunch of buckets and you just pull shit
> out of them at random, stick it all together, and you've got a wireless
> connection :)  Thank you, Cisco.  Thank you, Wi-Fi Alliance.

You forgot the part about sacrificing rubber chickens with pulleys 
in the middle.  While hopping on one foot.  Under a new moon. 

Bah, it's too early in the morning to be thinking about this stuff.  

 - Solomon 
-- 
Solomon Peachy        		       pizza at shaftnet dot org	 
Melbourne, FL                          ^^ (mail/jabber/gtalk) ^^
Quidquid latine dictum sit, altum viditur.          ICQ: 1318344


[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC] cfg80211 and nl80211
  2006-10-05 13:13         ` Stuffed Crust
@ 2006-10-05 15:46           ` Jouni Malinen
  0 siblings, 0 replies; 16+ messages in thread
From: Jouni Malinen @ 2006-10-05 15:46 UTC (permalink / raw)
  To: Dan Williams, Johannes Berg, netdev, Jiri Benc, John W. Linville,
	Larry Finger, Thomas Graf

On Thu, Oct 05, 2006 at 09:13:53AM -0400, Stuffed Crust wrote:

> (Leave out the RSNIE, AuthType and KeyMgmt stuff; while they're 
>  used in the actual key negotiation/derivation, they're separate 
>  problems and have no bearing on the crypto layer.  From the driver's 
>  perspective the RSNIE is just an opaque blob to be appended to 
>  beacons,presps and [re]assoc frames, KeyMgmt is purely a matter for 
>  the authenticator/supplicant, and AuthType is just a toggle that 
>  happens to be off for post-802.11i, although LEAP v1 adds some 
>  complications there..)

They are separate problems, but they do need to be taken into account in
802.11 interface to user space. Some drivers generate WPA/RSN IE
internally and they need to be told about the allowed protocol version,
authenticated key management suite, and pairwise/group cipher suites. In
other words, key management is not purely for authenticator/supplicant.

> Each key has:
> 
> * Key type (WEP/TKIP/AES-CCMP/NONE)
> * Key length (implied, but WEP can have varying key lengths)
> * Key index (only '0' is generally used for unicast frames, but 802.11i 
>              requires use of simultaneous broadcast keys)

Pre-802.11i supported key mapping and multiple default keys.. To make
things complex, many Cisco APs are configured to use non-zero key
indexes with dynamic WEP keys..

> ...Meanwhile.  It's foolish to ignore the 802.11 MLME.  It lists out
> pretty much everything that's necessary to get a working connection, and
> looking at its evolution (and changes in the pipeline) shows that it's
> impossible to do it all (right) the first time, and that changes, not
> just additions, will be necessary.

There are non-standard WLAN security protocols (look at Cisco) and one
needs to keep in mind that just looking at 802.11 MLME may not cover all
cases that, in practice, have to be supported.. Anyway, I agree that
MLME primitives do change and there will be new commands needed to cover
needs of future amendments to 802.11 (see, e.g., 802.11r and 802.11w
drafts).

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC] cfg80211 and nl80211
  2006-10-04 17:57       ` Dan Williams
  2006-10-05  7:59         ` Johannes Berg
  2006-10-05 13:13         ` Stuffed Crust
@ 2006-10-05 16:20         ` Jouni Malinen
  2006-10-06  9:41           ` Johannes Berg
  2 siblings, 1 reply; 16+ messages in thread
From: Jouni Malinen @ 2006-10-05 16:20 UTC (permalink / raw)
  To: Dan Williams
  Cc: Johannes Berg, netdev, Jiri Benc, John W. Linville, Larry Finger,
	Thomas Graf

On Wed, Oct 04, 2006 at 01:57:38PM -0400, Dan Williams wrote:
> On Wed, 2006-10-04 at 16:19 +0200, Johannes Berg wrote:
> > On Wed, 2006-10-04 at 09:41 +0200, Johannes Berg wrote:

> > Should cfg80211 do the chore of keeping track of the whole scan results?
> > On the other hand, that doesn't seem to be doable with legacy hardware
> > that does all the scanning. So probably one call for
> >    cfg80211_notify_scan()
> > that takes a new scan result structure (taking a single BSSID etc.) and
> > notifies all listeners.
> > The same structure is used for get_scan() from the wiphy ops in an
> > iterator interface like some other calls.
> 
> Is it a problem to actually push the _entire_ scan list out to clients
> over netlink?  The scan list could be quite large, maybe even a few
> kilobytes when stuff like Information Elements, ratesets, etc is
> available.  I've seen 35-item scan lists that are already around 1.5K.

1.5 KB sounds like a small scan result set to me.. I'm hitting 100+
BSSes at work (well, not really your normal environment ;-), and 50 at
home.. These go way beyond 1.5 KB; closer to 32 KB at times, I'd guess.

> There are several issues here.  They can be roughly split by encryption
> algorithm.  But the big question:
> 
>     Is there a case for _multiple_ encryption algorithms enabled
>     on a single "virtual" interface at one time?

What exactly do you mean with this? WPA allows different STAs associated
with an AP to use different unicast encryption algorithms. This means
that a client may need to use CCMP with key index 0 for unicast and TKIP
with key index 1 for multicast.

> Taking one-at-a-time as a given, and the pseudo-structure
> 
> struct cmd_crypto {
> 	enum crypto_alg alg;
> 	union data {
> 		none_data;
> 		wep_data;
> 		wpa_data;

wep vs. wpa in crypto configuration does not make sense to me. WPA uses
multiple ciphers; even WEP is allowed for group keys..

> Set alg == <whatever>, set the options, and the driver will _enable_
> that crypto mode with the given options.  It makes no sense at all to,
> say, set the WEP transmit key index or WEP key when the card is in WPA
> mode or no-crypto mode.

What is this "WPA" mode? Please note that IEEE 802.11i allows WEP to be
used for group (multicast/broadcast) keys.. WPA should not be mixed in
here with encryption key configuration. The are different encryption
algorithms, like WEP, TKIP, CCMP, and they need to have keys and other
parameters like key index and seq# configured. This is regardless of
whether WPA is used or not.

> It's important to note that some options are independent of the initial
> operation that enabled the crypto, and need to be set later without
> triggering deauth and such.  Setting non-TX-index WEP key is one such
> operation.  I should be able to set WEP keys at indexes other than the
> transmit key index without affecting operation of the card (unless some
> hardware/firmware issue prevents this).

And same for TKIP and CCMP.

> - WEP encryption (following ops are independent of each other):
>     - Set TX key index
>     - Set privacy invoked

These two are not WEP specific in any way.

>     - Set exclude unencrypted packets

I would consider this more as a global variable for the BSS to match
with dot11ExcludeUnecrypted variable defined in IEEE 802.11. In theory,
this is not specific to WEP, but in practice, only WEP is sometimes used
in mode which allows both encrypted and unencrypted frames.

>     - Set authentication mode (open, shared-key, or both)

Not really WEP specific. Shared Key authentication can only be used with
static WEP keys, but still, this configuration is not really part of
crypto configuration. In addition, Cisco uses a proprietary "Network
EAP" authentication algorithm and IEEE 802.11r is adding a new
authentication algorithm, so there are more options to this
configuration variable.

>     - Set (or clear) WEP key 1, 2, 3, or 4

Not specific to WEP.

> - WPA/WPA2/IEEE8021X
>     - Jouni/others would know better and my brain is fried right now

This item should not be WPA/WPA2/IEEE8021X, but TKIP/CCMP, i.e.,
ciphers like WEP.. Just like with WEP, there would need to be key index
parameter. Default TX key could be set with separate operation (it is
valid to switch between two keys without changing either one). TKIP and
CCMP will also need options for setting and getting the sequence number
for replay protection (TSC/PN).

In other words, WEP, TKIP, CCMP (and likely all future ciphers added to
802.11) would be using the same configuration interface with same set of
parameters. Some of these parameters are just ignored for some of the
ciphers (e.g., WEP does not really need seq# get/set).

> All the WEP options should be independent attributes in nl80211.  You
> could even have a generic WEPKey attribute that is defined like so:
> 
> ATTR_WEP_KEY {
> 	enum type (one of DISABLE, TYPE_40, TYPE_104, TYPE_152)

I would rather use key length than come up with enum that needs to be
changed whenever some vendor comes up with the great (yeah, right)
security improvement by using a new WEP key length. In addition, this
would mean that the same field can be shared (just like key_material)
with all ciphers..

> 	char * key_material

unsigned/u8

> 	enum index (one of DEFAULT, 1, 2, 3, 4)

This is not specific to WEP, so could be shared with other algorithms..
None of the ATTR_WEP_KEY fields were actually specific to WEP.. Take a
look at struct iw_encode_ext to see the needed parameters..

> Some cards, like older aironet ones, can only do 1 key.  I don't want to
> pollute nl80211 with limitations like that though, so the driver can
> just return -EINVAL if the card doesn't support it.  I don't think it
> pays to have capability bits for something as far down in the driver
> implementation as this. 

Actually, this particular case could be nice to include in capability
flags if someone were interested enough to implement WPA with Group Key
only keys, i.e., this case is described in WPA/IEEE 802.11i as an
exception and authenticator/supplicant would need to behave differently
with it. Then again, I've never really needed this or have even heard a
request to implement it, so maybe it can by ignored now.

> My impression of ENCODE and ENCODEEXT was that they were _just_ for
> setting keying material (except in the case of WEP where you set the
> auth mode open/shared too), and that AUTH was for higher level stuff.

I think it is a design flaw to use ENCODE for open/shared auth_alg
configuration and I never really understood that part of WE. That's why
authentication algorithm was added separately in WE-19.

IW_AUTH_* is just a subtyped way of being able to configure parameters
without having to add a new ioctl for every one. They are not really
tied to authentication, so the name is not exactly correct.

> >     - allowed wpa versions (why is this AUTH??)
> 
> Can you do both WPA and WPA2 at the same time?  You can certainly
> advertise both a WPA IE and an RSN IE at the same time.  Is it possible
> to auth both types of clients?

Sure you can. APs can have multiple stations and some of them can use
WPA while others are using WPA2 (IEEE 802.11i/RSN).

> >     - setting pairwise and group ciphers (again.. AUTH??)
> 
> Note that ENCODEEXT has a group key flag IW_ENCODE_EXT_GROUP_KEY too.
> The pairwise and group cipher stuff in AUTH only sets the cipher _type_,
> not the actual key material AFAICT.

These parameters in IW_AUTH_ are for setting the allowed cipher suites.
They are only used with drivers that generate WPA/RSN IE internally.

> * Static WEP
>     - Keys: up to 4 group keys
>     - Crypto: WEP-40, WEP-104, WEP-152, WEP-256
>     - 802.11 Auth: Open System or Shared Key
>     - Key Mgmt/Auth: none

Static key mapping keys could be used, even though that is not very
commonly used part of IEEE 802.11. In other words, there is individual
WEP keys and up to four default keys.

> * Dynamic WEP (LEAP?)
>     - Keys: up to 4 group keys
>     - Crypto: WEP-40, WEP-104, WEP-152, WEP-256
>     - 802.11 Auth: Open System (only?)
>     - Key Mgmt/Auth: IEEE 802.1x with LEAP or EAP

LEAP is an EAP method just like TLS, PEAP, etc. (it just happens to not
follow the standard, but.. well, close enough). This should have the
same keys as "Static WEP", i.e., possibly multiple individual (unicast)
keys. In addition, thanks to Cisco, there's "Network EAP" authentication
algorithm and possibility of using non-zero key indexes for unicast
keys..

> * WPA PSK
>     - Keys: pairwise & group
>     - Use WPA IEs
>     - 802.11 Auth: Open System
>     - Crypto: TKIP or CCMP
>     - Key Mgmt/Auth: WPA-PSK (elided 802.1x)
> 

There are pairwise keys and up to four group keys (Notice the pattern?
this is same as WEP above..).

> * WPA Enterprise
> * WPA2 PSK
> * WPA2 Enterprise

And these have the exact same encryption options as WPA-PSK. In other
words, encryption keys should be handled separately from
auth_alg/key_mgmt configuration.

-- 
Jouni Malinen                                            PGP id EFC895FA

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [RFC] cfg80211 and nl80211
  2006-10-05 16:20         ` Jouni Malinen
@ 2006-10-06  9:41           ` Johannes Berg
  0 siblings, 0 replies; 16+ messages in thread
From: Johannes Berg @ 2006-10-06  9:41 UTC (permalink / raw)
  To: Jouni Malinen
  Cc: Dan Williams, netdev, Jiri Benc, John W. Linville, Larry Finger,
	Thomas Graf

Let me try to summarise this... probably wrong :)

> 1.5 KB sounds like a small scan result set to me.. I'm hitting 100+
> BSSes at work (well, not really your normal environment ;-), and 50 at
> home.. These go way beyond 1.5 KB; closer to 32 KB at times, I'd guess.

Ok this is easy, we need huge results and thus can't reasonably push
them out on each change... Maybe some sequence number thingie could be
used? I'd like to have a .dumpit call with genl to actually dump all the
scan results to userspace, maybe that message could be multicast to
interested stations if someone requests one? No idea... Thomas?

As for the auth/crypto/key mgmt issue... It looks like these three are
basically orthogonal. If I understand correctly, you need to be able to
 * set a key including algorithm for the possible
     - key indexes 0,1,2,3
     - a STA identified by MAC address
     (and the key is identified by these uniquely)
   This includes
     - algorithm (none to clear, wep, tkip, ccmp, ...)
     - key material
     - TSC/SN (only valid for some algorithms)
     (key material length is used to decide between wep types, and some
     attributes may be left off, e.g. to change the tsc/sn without
     changing the key material or algorithm)
 * set transmit key index (STA only?)
 * set multicast/broadcast key index
 * dot11ExcludeUnecrypted (default true)
 * authentication mode, including
     - allowed algorithms (open, shared-key, ...)
     - key index of key to use
     - preference of use? That would make the allowed algorithms a list
       instead of a bitfield
 * set IE(s) (only some cards)
 * set allowed cipher suites for RSN/WPA IE generation (only some cards)
   [do we need to be able to distinguish between these or is it fine to
   just try both and get an error for one?]
 * a way to get the device's capabilities (per-net_device)
    - crypto algorithms
    - auth algorithms
    - ...?

Does that sound right? (throw in get versions for most of these, of
course)

Open issues:
 * associating a key index with a mac address for unicast wep key?

Hmm. That's at least 8 operations that WE sticks into 3. No wonder no
one understands it...

johannes

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2006-10-06  9:40 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-09-28  9:23 [RFC] cfg80211 and nl80211 Johannes Berg
2006-09-29 21:10 ` James Ketrenos
2006-09-30  3:00   ` Michael Wu
2006-10-02  9:08   ` Johannes Berg
2006-09-30  3:14 ` Michael Wu
2006-10-02 16:15 ` Dan Williams
2006-10-02 17:01   ` Dan Williams
2006-10-04  7:41   ` Johannes Berg
2006-10-04 14:19     ` Johannes Berg
2006-10-04 17:57       ` Dan Williams
2006-10-05  7:59         ` Johannes Berg
2006-10-05 13:13         ` Stuffed Crust
2006-10-05 15:46           ` Jouni Malinen
2006-10-05 16:20         ` Jouni Malinen
2006-10-06  9:41           ` Johannes Berg
2006-10-05  7:47   ` Johannes Berg

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.