* [RFC] nl80211 + packet injection with it and d80211
@ 2006-08-17 14:13 Johannes Berg
2006-08-18 14:49 ` Johannes Berg
0 siblings, 1 reply; 7+ messages in thread
From: Johannes Berg @ 2006-08-17 14:13 UTC (permalink / raw)
To: netdev; +Cc: John W. Linville
Hey,
This is a patch laying the groundwork for nl80211. I've decided to
implement it below net/wireless/ so that when we add further things like
configfs support for some things that can just be added there.
Currently, it lays the groundwork for netlink userspace communication.
None of the attribute stuff and configuration stuff I talked about is
implemented yet, nor anything like having wext go over netlink
obviously.
This patch now serves mainly to illustrate how I intend to multiplex
everything. Also, as added bonus, it implements packet injection over
netlink into the d80211 stack :) NB: missing is tx status notification
via netlink. The patch was large enough already.
I'd appreciate any feedback on the implementation.
johannes
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-dev/include/net/nl80211.h 2006-08-17 11:07:18.000000000 +0200
@@ -0,0 +1,55 @@
+#ifndef __NET_NL80211_H
+#define __NET_NL80211_H
+
+#include <linux/netlink.h>
+#include <net/genetlink.h>
+#include <linux/nl80211.h>
+#include <linux/skbuff.h>
+
+/*
+ * 802.11 netlink in-kernel interface
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+/**
+ * struct nl80211_call - backend call for wireless configuration
+ *
+ * An array of pointers to &struct nl80211_call is registered
+ * by each backend (driver or wireless stack) within
+ * &struct nl80211_driver.
+ *
+ * @doit: see &struct genl_ops doit
+ * @dumpit: see &struct genl_ops dumpit
+ */
+struct nl80211_call {
+ int (*doit)(struct sk_buff *skb,
+ struct genl_info *info);
+ int (*dumpit)(struct sk_buff *skb,
+ struct netlink_callback *cb);
+};
+
+/**
+ * struct nl80211_driver - 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.
+ *
+ * @get_calls_for_ifindex: return an array of size NL80211_CMD_MAX
+ * with an entry for each
+ */
+struct nl80211_driver {
+ struct nl80211_call** (*get_calls_for_ifindex)(int ifindex);
+
+ /* private: */
+ struct list_head driver_list;
+};
+
+extern void nl80211_register_driver(struct nl80211_driver *drv);
+extern void nl80211_unregister_driver(struct nl80211_driver *drv);
+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_NL80211_H */
--- wireless-dev.orig/net/Kconfig 2006-08-15 12:27:10.000000000 +0200
+++ wireless-dev/net/Kconfig 2006-08-15 12:30:45.000000000 +0200
@@ -250,6 +250,9 @@
config WIRELESS_EXT
bool
+config NETLINK_80211
+ tristate
+
endif # if NET
endmenu # Networking
--- wireless-dev.orig/net/Makefile 2006-08-15 12:30:56.000000000 +0200
+++ wireless-dev/net/Makefile 2006-08-15 12:31:13.000000000 +0200
@@ -44,6 +44,7 @@
obj-$(CONFIG_VLAN_8021Q) += 8021q/
obj-$(CONFIG_IP_DCCP) += dccp/
obj-$(CONFIG_IP_SCTP) += sctp/
+obj-$(CONFIG_NETLINK_80211) += wireless/
obj-$(CONFIG_D80211) += d80211/
obj-$(CONFIG_IEEE80211) += ieee80211/
obj-$(CONFIG_TIPC) += tipc/
--- wireless-dev.orig/net/d80211/Kconfig 2006-08-16 15:39:44.000000000 +0200
+++ wireless-dev/net/d80211/Kconfig 2006-08-16 15:39:48.000000000 +0200
@@ -3,6 +3,7 @@
select CRYPTO
select CRYPTO_ARC4
select CRYPTO_AES
+ select NETLINK_80211
---help---
This option enables the hardware independent IEEE 802.11
networking stack.
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-dev/net/wireless/Makefile 2006-08-15 14:43:42.000000000 +0200
@@ -0,0 +1,4 @@
+obj-$(CONFIG_NETLINK_80211) += cfg80211.o
+
+cfg80211-objs := \
+ nl80211.o
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-dev/net/wireless/nl80211.c 2006-08-17 15:32:13.000000000 +0200
@@ -0,0 +1,209 @@
+/*
+ * This is the new netlink-based wireless configuration interface.
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ */
+
+#include <linux/module.h>
+#include <net/genetlink.h>
+#include <net/nl80211.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+
+MODULE_AUTHOR("Johannes Berg");
+MODULE_LICENSE("GPL");
+
+static LIST_HEAD(nl80211_drv_list);
+static DEFINE_MUTEX(nl80211_drv_mutex);
+
+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,
+};
+
+static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
+ [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
+ [NL80211_ATTR_FLAGS] = { .type = NLA_U32 },
+ [NL80211_ATTR_CMDS] = { .type = NLA_STRING },
+ [NL80211_ATTR_QUEUE] = { .type = NLA_U32 },
+ [NL80211_ATTR_FRAME] = { .type = NLA_STRING },
+};
+
+static struct nl80211_call **nl80211_cmdvec_by_ifindex(int ifindex)
+{
+ struct nl80211_driver *drv;
+ struct nl80211_call **vec = NULL;
+
+ mutex_lock(&nl80211_drv_mutex);
+ list_for_each_entry(drv, &nl80211_drv_list, driver_list) {
+ vec = drv->get_calls_for_ifindex(ifindex);
+ if (vec)
+ break;
+ }
+ mutex_unlock(&nl80211_drv_mutex);
+
+ return vec;
+}
+
+/* this must be change when we introduce another lookup attribute */
+static struct nl80211_call **nl80211_cmdvec_from_info(struct genl_info *info)
+{
+ int ifindex;
+
+ if (!info->attrs[NL80211_ATTR_IFINDEX])
+ return NULL;
+
+ ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
+ return nl80211_cmdvec_by_ifindex(ifindex);
+}
+
+static int nl80211_get_commands(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nl80211_call **vec = nl80211_cmdvec_from_info(info);
+ struct sk_buff *msg;
+ void *hdr;
+ int cmdidx, err;
+ struct nlattr *start;
+ u8 *data;
+
+ if (!vec)
+ return -EINVAL;
+
+ hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0, NL80211_CMD_GET_COMMANDS);
+ if (IS_ERR(hdr))
+ return PTR_ERR(hdr);
+
+ start = nla_nest_start(msg, NL80211_ATTR_CMDS);
+ if (!start)
+ goto nla_put_failure;
+ data = nla_data(start);
+
+ /* unconditionally allow NL80211_CMD_GET_COMMANDS */
+ skb_put(skb, 1);
+ *data++ = NL80211_CMD_GET_COMMANDS;
+
+ for (cmdidx = 0; cmdidx <= NL80211_CMD_MAX; cmdidx++)
+ if (vec[cmdidx]) {
+ }
+
+ nla_nest_end(msg, start);
+
+ err = genlmsg_end(msg, hdr);
+ if (err)
+ goto msg_free;
+
+ return genlmsg_unicast(msg, info->snd_pid);
+ nla_put_failure:
+ err = genlmsg_cancel(skb, hdr);
+ msg_free:
+ nlmsg_free(skb);
+ return err;
+}
+
+static int nl80211_do_cmd(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nl80211_call **vec;
+
+ vec = nl80211_cmdvec_from_info(info);
+ if (!vec)
+ return -EINVAL;
+
+ if (!vec[info->genlhdr->cmd])
+ return -ENOSYS;
+ if (!vec[info->genlhdr->cmd]->doit)
+ return -ENOSYS;
+
+ return vec[info->genlhdr->cmd]->doit(skb, info);
+}
+
+static struct genl_ops nl80211_ops[] = {
+ {
+ .cmd = NL80211_CMD_GET_COMMANDS,
+ .doit = nl80211_get_commands,
+ .policy = nl80211_policy,
+ },
+ {
+ .cmd = NL80211_CMD_INJECT,
+ .doit = nl80211_do_cmd,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
+};
+
+
+/* exported functions */
+
+void nl80211_register_driver(struct nl80211_driver *drv)
+{
+ mutex_lock(&nl80211_drv_mutex);
+ list_add(&drv->driver_list, &nl80211_drv_list);
+ mutex_unlock(&nl80211_drv_mutex);
+}
+EXPORT_SYMBOL_GPL(nl80211_register_driver);
+
+void nl80211_unregister_driver(struct nl80211_driver *drv)
+{
+ mutex_lock(&nl80211_drv_mutex);
+ list_del(&drv->driver_list);
+ mutex_unlock(&nl80211_drv_mutex);
+}
+EXPORT_SYMBOL_GPL(nl80211_unregister_driver);
+
+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);
+ /* what would be a good error here? */
+ return ERR_PTR(-EINVAL);
+ }
+
+ return hdr;
+}
+EXPORT_SYMBOL_GPL(nl80211msg_new);
+
+/* module initialisation/exit functions */
+
+static 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;
+}
+
+static void nl80211_exit(void)
+{
+ genl_unregister_family(&nl80211_fam);
+}
+
+module_init(nl80211_init);
+module_exit(nl80211_exit);
--- wireless-dev.orig/include/linux/Kbuild 2006-08-16 15:58:31.000000000 +0200
+++ wireless-dev/include/linux/Kbuild 2006-08-16 15:59:36.000000000 +0200
@@ -28,7 +28,7 @@
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-08-17 12:11:27.000000000 +0200
@@ -0,0 +1,79 @@
+#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_COMMANDS,
+
+ /* 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 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,
+
+ /* 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,
+
+ /* 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<<00)
+/**
+ * NL80211_FLAG_ENCRYPT - encrypt this packet
+ * Warning: This looks inside the packet header!
+ */
+#define NL80211_FLAG_ENCRYPT (1<<01)
+/**
+ * NL80211_FLAG_REQUEUE - WME requeue
+ * (what exactly is this?)
+ */
+#define NL80211_FLAG_REQUEUE (1<<02)
+/**
+ * NL80211_FLAG_PROBE_RESPONSE - injected packet is a probe response
+ */
+#define NL80211_FLAG_PROBE_RESPONSE (1<<03)
+
+#endif /* __LINUX_NL80211_H */
--- wireless-dev.orig/net/d80211/Makefile 2006-08-17 11:05:52.000000000 +0200
+++ wireless-dev/net/d80211/Makefile 2006-08-17 11:06:02.000000000 +0200
@@ -15,7 +15,8 @@
michael.o \
tkip.o \
aes_ccm.o \
- wme.o
+ wme.o \
+ ieee80211_cfg.o
ifeq ($(CONFIG_NET_SCHED),)
80211-objs += fifo_qdisc.o
--- wireless-dev.orig/net/d80211/ieee80211.c 2006-08-17 10:46:05.000000000 +0200
+++ wireless-dev/net/d80211/ieee80211.c 2006-08-17 15:25:01.000000000 +0200
@@ -20,6 +20,7 @@
#include <linux/wireless.h>
#include <net/iw_handler.h>
#include <linux/compiler.h>
+#include <linux/nl80211.h>
#include <net/d80211.h>
#include <net/d80211_common.h>
@@ -31,7 +32,7 @@
#include "tkip.h"
#include "wme.h"
#include "aes_ccm.h"
-
+#include "ieee80211_cfg.h"
/* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */
/* Ethernet-II snap header (RFC1042 for most EtherTypes) */
@@ -354,6 +355,16 @@
{
struct rate_control_extra extra;
+ /* FIXME
+ if (tx->dev == tx->local->mdev &&
+ (inject rate set)) {
+ a
+ tx->u.tx.rate = ...
+ etc etc
+ return TXRX_CONTINUE;
+ }
+ */
+
memset(&extra, 0, sizeof(extra));
extra.mgmt_data = tx->sdata &&
tx->sdata->type == IEEE80211_IF_TYPE_MGMT;
@@ -761,6 +772,13 @@
u16 dur;
struct ieee80211_tx_control *control = tx->u.tx.control;
+ /* FIXME
+ if (tx->dev == tx->local->mdev) {
+ set up retry limit, ...
+ based on injection parameters
+ }
+ */
+
if (!is_multicast_ether_addr(hdr->addr1)) {
if (tx->skb->len + FCS_LEN > tx->local->rts_threshold &&
tx->local->rts_threshold < IEEE80211_MAX_RTS_THRESHOLD) {
@@ -886,6 +904,9 @@
#endif /* CONFIG_D80211_VERBOSE_DEBUG */
u32 sta_flags;
+ if (unlikely(tx->dev == tx->local->mdev))
+ return TXRX_CONTINUE;
+
if (unlikely(tx->local->sta_scanning != 0) &&
((tx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT ||
(tx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PROBE_REQ))
@@ -989,6 +1010,12 @@
static inline ieee80211_txrx_result
ieee80211_tx_h_multicast_ps_buf(struct ieee80211_txrx_data *tx)
{
+ /* FIXME
+ if (unlikely(tx->dev == tx->local->mdev &&
+ (inject flags) & NL80211_FLAG_NOBUFFER))
+ return TXRX_CONTINUE;
+ */
+
/* broadcast/multicast frame */
/* If any of the associated stations is in power save mode,
* the frame is buffered to be sent after DTIM beacon frame */
@@ -1420,11 +1447,12 @@
control.ifindex = odev->ifindex;
control.type = osdata->type;
- control.req_tx_status = pkt_data->req_tx_status;
- control.do_not_encrypt = pkt_data->do_not_encrypt;
+ control.req_tx_status = !!(pkt_data->flags & NL80211_FLAG_TXSTATUS);
+ control.do_not_encrypt = !(pkt_data->flags & NL80211_FLAG_ENCRYPT);
control.pkt_type =
- pkt_data->pkt_probe_resp ? PKT_PROBE_RESP : PKT_NORMAL;
- control.requeue = pkt_data->requeue;
+ (pkt_data->flags & NL80211_FLAG_PROBE_RESPONSE) ?
+ PKT_PROBE_RESP : PKT_NORMAL;
+ control.requeue = !!(pkt_data->flags & NL80211_FLAG_REQUEUE);
control.queue = pkt_data->queue;
ret = ieee80211_tx(odev, skb, &control,
@@ -1600,8 +1628,9 @@
pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
pkt_data->ifindex = sdata->dev->ifindex;
- pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT);
- pkt_data->do_not_encrypt = no_encrypt;
+ pkt_data->injected = (sdata->type == IEEE80211_IF_TYPE_MGMT);
+ if (!no_encrypt)
+ pkt_data->flags |= NL80211_FLAG_ENCRYPT;
skb->dev = sdata->master;
sdata->stats.tx_packets++;
@@ -1652,11 +1681,11 @@
pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
pkt_data->ifindex = sdata->dev->ifindex;
- pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT);
+ pkt_data->injected = (sdata->type == IEEE80211_IF_TYPE_MGMT);
if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT &&
(fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP)
- pkt_data->pkt_probe_resp = 1;
+ pkt_data->flags |= NL80211_FLAG_PROBE_RESPONSE;
skb->priority = 20; /* use hardcoded priority for mgmt TX queue */
skb->dev = sdata->master;
@@ -1666,12 +1695,13 @@
* to request TX callback for hostapd. BIT(1) is checked.
*/
if ((fc & BIT(1)) == BIT(1)) {
- pkt_data->req_tx_status = 1;
+ pkt_data->flags |= NL80211_FLAG_TXSTATUS;
fc &= ~BIT(1);
hdr->frame_control = cpu_to_le16(fc);
}
- pkt_data->do_not_encrypt = !(fc & IEEE80211_FCTL_PROTECTED);
+ if (fc & IEEE80211_FCTL_PROTECTED)
+ pkt_data->flags |= NL80211_FLAG_ENCRYPT;
sdata->stats.tx_packets++;
sdata->stats.tx_bytes += skb->len;
@@ -2725,7 +2755,7 @@
while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) {
pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
sent++;
- pkt_data->requeue = 1;
+ pkt_data->flags |= NL80211_FLAG_REQUEUE;
dev_queue_xmit(skb);
}
while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) {
@@ -2737,7 +2767,7 @@
"since STA not sleeping anymore\n", dev->name,
MAC_ARG(sta->addr), sta->aid);
#endif /* IEEE80211_VERBOSE_DEBUG_PS */
- pkt_data->requeue = 1;
+ pkt_data->flags |= NL80211_FLAG_REQUEUE;
dev_queue_xmit(skb);
}
@@ -3969,12 +3999,17 @@
struct ieee80211_tx_packet_data *pkt_data;
pkt_data = (struct ieee80211_tx_packet_data *)skb->cb;
+ pkt_data->flags = 0;
pkt_data->ifindex = control->ifindex;
- pkt_data->mgmt_iface = (control->type == IEEE80211_IF_TYPE_MGMT);
- pkt_data->req_tx_status = control->req_tx_status;
- pkt_data->do_not_encrypt = control->do_not_encrypt;
- pkt_data->pkt_probe_resp = (control->pkt_type == PKT_PROBE_RESP);
- pkt_data->requeue = control->requeue;
+ pkt_data->injected = (control->type == IEEE80211_IF_TYPE_MGMT);
+ if (control->req_tx_status)
+ pkt_data->flags |= NL80211_FLAG_TXSTATUS;
+ if (!control->do_not_encrypt)
+ pkt_data->flags |= NL80211_FLAG_ENCRYPT;
+ if (control->pkt_type == PKT_PROBE_RESP)
+ pkt_data->flags |= NL80211_FLAG_PROBE_RESPONSE;
+ if (control->requeue)
+ pkt_data->flags |= NL80211_FLAG_REQUEUE;
pkt_data->queue = control->queue;
hdrlen = ieee80211_get_hdrlen_from_skb(skb);
@@ -4795,12 +4830,15 @@
}
}
+ ieee80211_cfg_init();
+
return 0;
}
static void __exit ieee80211_exit(void)
{
+ ieee80211_cfg_exit();
ieee80211_wme_unregister();
ieee80211_sysfs_deinit();
}
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-dev/net/d80211/ieee80211_cfg.c 2006-08-17 15:41:03.000000000 +0200
@@ -0,0 +1,97 @@
+/*
+ * nl80211-based configuration for d80211
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
+ */
+#include <net/nl80211.h>
+#include <linux/netdevice.h>
+#include "ieee80211_cfg.h"
+#include "ieee80211_i.h"
+
+/* currently only supports injection by ifindex */
+static int d80211_inject(struct sk_buff *skb, struct genl_info *info)
+{
+ struct net_device *dev;
+ struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_tx_packet_data *pkt_data;
+ struct sk_buff *pkt;
+ unsigned char *pktdata;
+ u32 flags = 0;
+
+ if (!info->attrs[NL80211_ATTR_IFINDEX])
+ return -EINVAL;
+ if (!info->attrs[NL80211_ATTR_FRAME])
+ return -EINVAL;
+ if (nla_len(info->attrs[NL80211_ATTR_FRAME]) < 10)
+ return -EINVAL;
+ if (info->attrs[NL80211_ATTR_FLAGS])
+ flags = nla_get_u32(info->attrs[NL80211_ATTR_FLAGS]);
+
+ dev = dev_get_by_index(nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]));
+ if (!dev)
+ return -EINVAL;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ pkt = alloc_skb(nla_len(info->attrs[NL80211_ATTR_FRAME]), GFP_KERNEL);
+ pktdata = skb_put(pkt, nla_len(info->attrs[NL80211_ATTR_FRAME]));
+ memcpy(pktdata, nla_data(info->attrs[NL80211_ATTR_FRAME]),
+ nla_len(info->attrs[NL80211_ATTR_FRAME]));
+
+ pkt_data = (struct ieee80211_tx_packet_data *) pkt->cb;
+ memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
+ pkt_data->ifindex = sdata->local->mdev->ifindex;
+ pkt_data->injected = 1;
+ pkt_data->flags = flags;
+
+ /* FIXME */
+ pkt->priority = 20; /* use hardcoded priority for mgmt TX queue */
+
+ sdata->stats.tx_packets++;
+ sdata->stats.tx_bytes += pkt->len;
+
+ pkt->dev = sdata->master;
+ dev_queue_xmit(pkt);
+
+ dev_put(dev);
+
+ return 0;
+}
+
+static struct nl80211_call d80211_inject_call = {
+ .doit = d80211_inject,
+};
+
+static struct nl80211_call* d80211_calls[NL80211_CMD_MAX+1] = {
+ [NL80211_CMD_INJECT] = &d80211_inject_call,
+};
+
+static struct nl80211_call** d80211_calls_for_ifindex(int ifindex)
+{
+ struct net_device *dev = dev_get_by_index(ifindex);
+
+ if (!dev)
+ return NULL;
+
+ if (dev->ieee80211_ptr) {
+ dev_put(dev);
+ return d80211_calls;
+ }
+
+ dev_put(dev);
+ return NULL;
+}
+
+static struct nl80211_driver d80211nl = {
+ .get_calls_for_ifindex = d80211_calls_for_ifindex,
+};
+
+void ieee80211_cfg_init(void)
+{
+ nl80211_register_driver(&d80211nl);
+}
+
+void ieee80211_cfg_exit(void)
+{
+ nl80211_unregister_driver(&d80211nl);
+}
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ wireless-dev/net/d80211/ieee80211_cfg.h 2006-08-17 11:05:43.000000000 +0200
@@ -0,0 +1,7 @@
+#ifndef __IEEE80211_CFG_H
+#define __IEEE80211_CFG_H
+
+extern void ieee80211_cfg_init(void);
+extern void ieee80211_cfg_exit(void);
+
+#endif /* __IEEE80211_CFG_H */
--- wireless-dev.orig/net/d80211/ieee80211_i.h 2006-08-17 11:48:34.000000000 +0200
+++ wireless-dev/net/d80211/ieee80211_i.h 2006-08-17 12:17:56.000000000 +0200
@@ -151,12 +151,10 @@
struct ieee80211_tx_packet_data {
int ifindex;
unsigned long jiffies;
- unsigned int req_tx_status:1;
- unsigned int do_not_encrypt:1;
- unsigned int pkt_probe_resp:1;
- unsigned int requeue:1;
- unsigned int queue:4;
- unsigned int mgmt_iface:1;
+/* we simply re-use NL80211_FLAG_* here */
+ unsigned int flags;
+ unsigned int queue;
+ unsigned int injected;
};
struct ieee80211_tx_stored_packet {
--- wireless-dev.orig/net/d80211/ieee80211_sta.c 2006-08-17 12:27:46.000000000 +0200
+++ wireless-dev/net/d80211/ieee80211_sta.c 2006-08-17 12:28:37.000000000 +0200
@@ -23,6 +23,7 @@
#include <linux/if_arp.h>
#include <linux/wireless.h>
#include <linux/random.h>
+#include <linux/nl80211.h>
#include <net/iw_handler.h>
#include <asm/types.h>
#include <asm/delay.h>
@@ -400,10 +401,11 @@
pkt_data = (struct ieee80211_tx_packet_data *) skb->cb;
memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data));
pkt_data->ifindex = sdata->dev->ifindex;
- pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT);
- pkt_data->do_not_encrypt = !encrypt;
+ pkt_data->injected = (sdata->type == IEEE80211_IF_TYPE_MGMT);
+ if (encrypt)
+ pkt_data->flags |= NL80211_FLAG_ENCRYPT;
if (probe_resp)
- pkt_data->pkt_probe_resp = 1;
+ pkt_data->flags |= NL80211_FLAG_PROBE_RESPONSE;
dev_queue_xmit(skb);
}
--- wireless-dev.orig/net/d80211/wme.c 2006-08-17 12:28:47.000000000 +0200
+++ wireless-dev/net/d80211/wme.c 2006-08-17 12:30:30.000000000 +0200
@@ -12,6 +12,7 @@
#include <linux/skbuff.h>
#include <linux/module.h>
#include <linux/if_arp.h>
+#include <linux/nl80211.h>
#include <net/ip.h>
#include <net/d80211.h>
@@ -190,7 +191,8 @@
return IEEE80211_TX_QUEUE_DATA0;
}
- if (unlikely(pkt_data->mgmt_iface)) {
+ /* FIXME: this needs to be revisited for more generic injection */
+ if (unlikely(pkt_data->injected)) {
/* Data frames from hostapd (mainly, EAPOL) use AC_VO
* and they will include QoS control fields if
* the target STA is using WME. */
@@ -236,7 +238,7 @@
struct Qdisc *qdisc;
int err, queue;
- if (pkt_data->requeue) {
+ if (pkt_data->flags & NL80211_FLAG_REQUEUE) {
skb_queue_tail(&q->requeued[pkt_data->queue], skb);
return 0;
}
^ permalink raw reply [flat|nested] 7+ messages in thread* [RFC] nl80211 + packet injection with it and d80211 2006-08-17 14:13 [RFC] nl80211 + packet injection with it and d80211 Johannes Berg @ 2006-08-18 14:49 ` Johannes Berg 2006-08-20 18:47 ` Jiri Benc 2006-08-21 6:44 ` Johannes Berg 0 siblings, 2 replies; 7+ messages in thread From: Johannes Berg @ 2006-08-18 14:49 UTC (permalink / raw) To: netdev; +Cc: John W. Linville, Jiri Benc Here's a new version with a cleaned up interface and methods for each command instead of multiplexing the commands right away. This cuts down code in nl80211 users because attributes are already checked and unpacked etc. Packet injection also still works :P johannes --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ wireless-dev/include/net/nl80211.h 2006-08-18 15:12:55.000000000 +0200 @@ -0,0 +1,62 @@ +#ifndef __NET_NL80211_H +#define __NET_NL80211_H + +#include <linux/netlink.h> +#include <net/genetlink.h> +#include <linux/nl80211.h> +#include <linux/skbuff.h> + +/* + * 802.11 netlink in-kernel interface + * + * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> + */ + +/** + * struct nl80211_driver - 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. + * + * @get_priv_for_ifindex: return the private pointer that is then passed to + * the other calls as the first argument. + * Note that if this returns %NULL, then it is assumed + * that the driver doesn't handle that interface, + * hence priv is unused for some reason it still needs + * to return a non-%NULL pointer! + * + * @release_priv: release the private pointer if necessary. Note that for + * example if you used the ifindex and dev_get_by_index(), + * you have to dev_put() here. + * + * @inject_packet: inject the given frame with the NL80211_FLAG_* + * flags onto the given queue. + */ +struct nl80211_driver { + void* (*get_priv_for_ifindex)(int ifindex); + void (*release_priv)(void *priv); + + int (*inject_packet)(void *priv, void *frame, int framelen, + u32 flags, int queue); + + /* more things to be added... + * + * for a (*configure)(...) call I'd probably guess that the + * best bet would be to have one call that returns all + * possible options, one that sets them based on the + * struct genl_info *info, and one for that optimised + * set-at-once thing. + */ + + /* private: */ + struct list_head driver_list; +}; + +extern void nl80211_register_driver(struct nl80211_driver *drv); +extern void nl80211_unregister_driver(struct nl80211_driver *drv); +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_NL80211_H */ --- wireless-dev.orig/net/Kconfig 2006-08-15 12:27:10.000000000 +0200 +++ wireless-dev/net/Kconfig 2006-08-15 12:30:45.000000000 +0200 @@ -250,6 +250,9 @@ config WIRELESS_EXT bool +config NETLINK_80211 + tristate + endif # if NET endmenu # Networking --- wireless-dev.orig/net/Makefile 2006-08-15 12:30:56.000000000 +0200 +++ wireless-dev/net/Makefile 2006-08-15 12:31:13.000000000 +0200 @@ -44,6 +44,7 @@ obj-$(CONFIG_VLAN_8021Q) += 8021q/ obj-$(CONFIG_IP_DCCP) += dccp/ obj-$(CONFIG_IP_SCTP) += sctp/ +obj-$(CONFIG_NETLINK_80211) += wireless/ obj-$(CONFIG_D80211) += d80211/ obj-$(CONFIG_IEEE80211) += ieee80211/ obj-$(CONFIG_TIPC) += tipc/ --- wireless-dev.orig/net/d80211/Kconfig 2006-08-16 15:39:44.000000000 +0200 +++ wireless-dev/net/d80211/Kconfig 2006-08-16 15:39:48.000000000 +0200 @@ -3,6 +3,7 @@ select CRYPTO select CRYPTO_ARC4 select CRYPTO_AES + select NETLINK_80211 ---help--- This option enables the hardware independent IEEE 802.11 networking stack. --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ wireless-dev/net/wireless/Makefile 2006-08-15 14:43:42.000000000 +0200 @@ -0,0 +1,4 @@ +obj-$(CONFIG_NETLINK_80211) += cfg80211.o + +cfg80211-objs := \ + nl80211.o --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ wireless-dev/net/wireless/nl80211.c 2006-08-18 15:51:09.000000000 +0200 @@ -0,0 +1,240 @@ +/* + * This is the new netlink-based wireless configuration interface. + * + * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> + */ + +#include <linux/module.h> +#include <net/genetlink.h> +#include <net/nl80211.h> +#include <linux/mutex.h> +#include <linux/list.h> + +MODULE_AUTHOR("Johannes Berg"); +MODULE_LICENSE("GPL"); + +static LIST_HEAD(nl80211_drv_list); +static DEFINE_MUTEX(nl80211_drv_mutex); + +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, +}; + +static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { + [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, + [NL80211_ATTR_FLAGS] = { .type = NLA_U32 }, + [NL80211_ATTR_CMDS] = { .type = NLA_STRING }, + [NL80211_ATTR_QUEUE] = { .type = NLA_U32 }, + [NL80211_ATTR_FRAME] = { .type = NLA_STRING }, +}; + +static int nl80211_drvpriv_by_ifidx(int ifindex, void **priv, struct nl80211_driver **drv) +{ + BUG_ON(!priv); + BUG_ON(!drv); + + mutex_lock(&nl80211_drv_mutex); + list_for_each_entry((*drv), &nl80211_drv_list, driver_list) { + *priv = (*drv)->get_priv_for_ifindex(ifindex); + if (*priv) + break; + } + mutex_unlock(&nl80211_drv_mutex); + + return !!*priv; +} + +/* this must be change when we introduce another lookup attribute */ +static int nl80211_drvpriv_from_info(struct genl_info *info, + struct nl80211_driver **drv, + void **priv) +{ + int ifindex; + + if (!info->attrs[NL80211_ATTR_IFINDEX]) + return 0; + + ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); + return nl80211_drvpriv_by_ifidx(ifindex, priv, drv); +} + +static int nl80211_get_commands(struct sk_buff *skb, struct genl_info *info) +{ + void *priv; + struct nl80211_driver *drv; + struct sk_buff *msg; + void *hdr; + int err; + struct nlattr *start; + u8 *data; + + if (!nl80211_drvpriv_from_info(info, &drv, &priv)) + return -EINVAL; + + hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0, NL80211_CMD_GET_COMMANDS); + if (IS_ERR(hdr)) { + err = PTR_ERR(hdr); + goto release; + } + + start = nla_nest_start(msg, NL80211_ATTR_CMDS); + if (!start) + goto nla_put_failure; + data = nla_data(start); + + /* unconditionally allow NL80211_CMD_GET_COMMANDS */ + skb_put(skb, 1); + *data++ = NL80211_CMD_GET_COMMANDS; + +#define CHECK_CMD(ptr, cmd) \ + if (drv->ptr) { \ + skb_put(skb, 1); \ + *data++ = NL80211_CMD_##cmd; \ + } + + CHECK_CMD(inject_packet, INJECT); + + nla_nest_end(msg, start); + + err = genlmsg_end(msg, hdr); + if (err) + goto msg_free; + + err = genlmsg_unicast(msg, info->snd_pid); + goto release; + + nla_put_failure: + err = genlmsg_cancel(skb, hdr); + msg_free: + nlmsg_free(skb); + release: + if (drv->release_priv) + drv->release_priv(priv); + return err; +} + +static int nl80211_do_inject(struct sk_buff *skb, struct genl_info *info) +{ + struct nl80211_driver *drv; + void *priv; + 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]); + + if (!nl80211_drvpriv_from_info(info, &drv, &priv)) + return -EINVAL; + + if (!drv->inject_packet) { + err = -ENOSYS; + goto release; + } + + err = drv->inject_packet(priv, + nla_data(info->attrs[NL80211_ATTR_FRAME]), + nla_len(info->attrs[NL80211_ATTR_FRAME]), + flags, + queue); + release: + if (drv->release_priv) + drv->release_priv(priv); + return err; +} + +static struct genl_ops nl80211_ops[] = { + { + .cmd = NL80211_CMD_GET_COMMANDS, + .doit = nl80211_get_commands, + .policy = nl80211_policy, + }, + { + .cmd = NL80211_CMD_INJECT, + .doit = nl80211_do_inject, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, +}; + + +/* exported functions */ + +void nl80211_register_driver(struct nl80211_driver *drv) +{ + mutex_lock(&nl80211_drv_mutex); + list_add(&drv->driver_list, &nl80211_drv_list); + mutex_unlock(&nl80211_drv_mutex); +} +EXPORT_SYMBOL_GPL(nl80211_register_driver); + +void nl80211_unregister_driver(struct nl80211_driver *drv) +{ + mutex_lock(&nl80211_drv_mutex); + list_del(&drv->driver_list); + mutex_unlock(&nl80211_drv_mutex); +} +EXPORT_SYMBOL_GPL(nl80211_unregister_driver); + +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); + /* what would be a good error here? */ + return ERR_PTR(-EINVAL); + } + + return hdr; +} +EXPORT_SYMBOL_GPL(nl80211msg_new); + +/* module initialisation/exit functions */ + +static 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; +} + +static void nl80211_exit(void) +{ + genl_unregister_family(&nl80211_fam); +} + +module_init(nl80211_init); +module_exit(nl80211_exit); --- wireless-dev.orig/include/linux/Kbuild 2006-08-16 15:58:31.000000000 +0200 +++ wireless-dev/include/linux/Kbuild 2006-08-16 15:59:36.000000000 +0200 @@ -28,7 +28,7 @@ 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-08-18 13:37:51.000000000 +0200 @@ -0,0 +1,70 @@ +#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_COMMANDS, + + /* 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 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, + + /* 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, + + /* 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<<00) +/** + * NL80211_FLAG_ENCRYPT - encrypt this packet + * Warning: This looks inside the packet header! + */ +#define NL80211_FLAG_ENCRYPT (1<<01) + +#endif /* __LINUX_NL80211_H */ --- wireless-dev.orig/net/d80211/Makefile 2006-08-17 11:05:52.000000000 +0200 +++ wireless-dev/net/d80211/Makefile 2006-08-17 11:06:02.000000000 +0200 @@ -15,7 +15,8 @@ michael.o \ tkip.o \ aes_ccm.o \ - wme.o + wme.o \ + ieee80211_cfg.o ifeq ($(CONFIG_NET_SCHED),) 80211-objs += fifo_qdisc.o --- wireless-dev.orig/net/d80211/ieee80211.c 2006-08-17 10:46:05.000000000 +0200 +++ wireless-dev/net/d80211/ieee80211.c 2006-08-18 15:50:54.000000000 +0200 @@ -20,6 +20,7 @@ #include <linux/wireless.h> #include <net/iw_handler.h> #include <linux/compiler.h> +#include <linux/nl80211.h> #include <net/d80211.h> #include <net/d80211_common.h> @@ -31,7 +32,7 @@ #include "tkip.h" #include "wme.h" #include "aes_ccm.h" - +#include "ieee80211_cfg.h" /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ /* Ethernet-II snap header (RFC1042 for most EtherTypes) */ @@ -354,6 +355,16 @@ { struct rate_control_extra extra; + /* FIXME + if (tx->dev == tx->local->mdev && + (inject rate set)) { + a + tx->u.tx.rate = ... + etc etc + return TXRX_CONTINUE; + } + */ + memset(&extra, 0, sizeof(extra)); extra.mgmt_data = tx->sdata && tx->sdata->type == IEEE80211_IF_TYPE_MGMT; @@ -761,6 +772,13 @@ u16 dur; struct ieee80211_tx_control *control = tx->u.tx.control; + /* FIXME + if (tx->dev == tx->local->mdev) { + set up retry limit, ... + based on injection parameters + } + */ + if (!is_multicast_ether_addr(hdr->addr1)) { if (tx->skb->len + FCS_LEN > tx->local->rts_threshold && tx->local->rts_threshold < IEEE80211_MAX_RTS_THRESHOLD) { @@ -886,6 +904,9 @@ #endif /* CONFIG_D80211_VERBOSE_DEBUG */ u32 sta_flags; + if (unlikely(tx->dev == tx->local->mdev)) + return TXRX_CONTINUE; + if (unlikely(tx->local->sta_scanning != 0) && ((tx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT || (tx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PROBE_REQ)) @@ -989,6 +1010,12 @@ static inline ieee80211_txrx_result ieee80211_tx_h_multicast_ps_buf(struct ieee80211_txrx_data *tx) { + /* FIXME + if (unlikely(tx->dev == tx->local->mdev && + (inject flags) & NL80211_FLAG_NOBUFFER)) + return TXRX_CONTINUE; + */ + /* broadcast/multicast frame */ /* If any of the associated stations is in power save mode, * the frame is buffered to be sent after DTIM beacon frame */ @@ -1420,11 +1447,12 @@ control.ifindex = odev->ifindex; control.type = osdata->type; - control.req_tx_status = pkt_data->req_tx_status; - control.do_not_encrypt = pkt_data->do_not_encrypt; + control.req_tx_status = !!(pkt_data->flags & NL80211_FLAG_TXSTATUS); + control.do_not_encrypt = !(pkt_data->flags & NL80211_FLAG_ENCRYPT); control.pkt_type = - pkt_data->pkt_probe_resp ? PKT_PROBE_RESP : PKT_NORMAL; - control.requeue = pkt_data->requeue; + (pkt_data->internal_flags & TX_FLAG_PROBERESP) ? + PKT_PROBE_RESP : PKT_NORMAL; + control.requeue = !!(pkt_data->internal_flags & TX_FLAG_REQUEUE); control.queue = pkt_data->queue; ret = ieee80211_tx(odev, skb, &control, @@ -1600,8 +1628,10 @@ pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); pkt_data->ifindex = sdata->dev->ifindex; - pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT); - pkt_data->do_not_encrypt = no_encrypt; + if (sdata->type == IEEE80211_IF_TYPE_MGMT) + pkt_data->internal_flags |= TX_FLAG_INJECTED; + if (!no_encrypt) + pkt_data->flags |= NL80211_FLAG_ENCRYPT; skb->dev = sdata->master; sdata->stats.tx_packets++; @@ -1652,11 +1682,12 @@ pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); pkt_data->ifindex = sdata->dev->ifindex; - pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT); + if (sdata->type == IEEE80211_IF_TYPE_MGMT) + pkt_data->internal_flags |= TX_FLAG_INJECTED; if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT && (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP) - pkt_data->pkt_probe_resp = 1; + pkt_data->internal_flags |= TX_FLAG_PROBERESP; skb->priority = 20; /* use hardcoded priority for mgmt TX queue */ skb->dev = sdata->master; @@ -1666,12 +1697,13 @@ * to request TX callback for hostapd. BIT(1) is checked. */ if ((fc & BIT(1)) == BIT(1)) { - pkt_data->req_tx_status = 1; + pkt_data->flags |= NL80211_FLAG_TXSTATUS; fc &= ~BIT(1); hdr->frame_control = cpu_to_le16(fc); } - pkt_data->do_not_encrypt = !(fc & IEEE80211_FCTL_PROTECTED); + if (fc & IEEE80211_FCTL_PROTECTED) + pkt_data->flags |= NL80211_FLAG_ENCRYPT; sdata->stats.tx_packets++; sdata->stats.tx_bytes += skb->len; @@ -2725,7 +2757,7 @@ while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; sent++; - pkt_data->requeue = 1; + pkt_data->internal_flags |= TX_FLAG_REQUEUE; dev_queue_xmit(skb); } while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { @@ -2737,7 +2769,7 @@ "since STA not sleeping anymore\n", dev->name, MAC_ARG(sta->addr), sta->aid); #endif /* IEEE80211_VERBOSE_DEBUG_PS */ - pkt_data->requeue = 1; + pkt_data->internal_flags |= TX_FLAG_REQUEUE; dev_queue_xmit(skb); } @@ -3969,12 +4001,19 @@ struct ieee80211_tx_packet_data *pkt_data; pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; + pkt_data->flags = 0; + pkt_data->internal_flags = 0; pkt_data->ifindex = control->ifindex; - pkt_data->mgmt_iface = (control->type == IEEE80211_IF_TYPE_MGMT); - pkt_data->req_tx_status = control->req_tx_status; - pkt_data->do_not_encrypt = control->do_not_encrypt; - pkt_data->pkt_probe_resp = (control->pkt_type == PKT_PROBE_RESP); - pkt_data->requeue = control->requeue; + if (control->type == IEEE80211_IF_TYPE_MGMT) + pkt_data->internal_flags |= TX_FLAG_INJECTED; + if (control->req_tx_status) + pkt_data->flags |= NL80211_FLAG_TXSTATUS; + if (!control->do_not_encrypt) + pkt_data->flags |= NL80211_FLAG_ENCRYPT; + if (control->pkt_type == PKT_PROBE_RESP) + pkt_data->internal_flags |= TX_FLAG_PROBERESP; + if (control->requeue) + pkt_data->internal_flags |= TX_FLAG_REQUEUE; pkt_data->queue = control->queue; hdrlen = ieee80211_get_hdrlen_from_skb(skb); @@ -4795,12 +4834,15 @@ } } + ieee80211_cfg_init(); + return 0; } static void __exit ieee80211_exit(void) { + ieee80211_cfg_exit(); ieee80211_wme_unregister(); ieee80211_sysfs_deinit(); } --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ wireless-dev/net/d80211/ieee80211_cfg.c 2006-08-18 15:50:45.000000000 +0200 @@ -0,0 +1,78 @@ +/* + * nl80211-based configuration for d80211 + * + * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> + */ +#include <net/nl80211.h> +#include <linux/netdevice.h> +#include "ieee80211_cfg.h" +#include "ieee80211_i.h" + +static int d80211_inject(void *priv, void *frame, int framelen, u32 flags, + int queue) +{ + struct net_device *dev = priv; + struct ieee80211_sub_if_data *sdata; + struct ieee80211_tx_packet_data *pkt_data; + struct sk_buff *pkt; + void *pktdata; + + sdata = IEEE80211_DEV_TO_SUB_IF(dev); + + pkt = alloc_skb(framelen, GFP_KERNEL); + pktdata = skb_put(pkt, framelen); + memcpy(pktdata, frame, framelen); + + pkt_data = (struct ieee80211_tx_packet_data *) pkt->cb; + memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); + pkt_data->ifindex = sdata->local->mdev->ifindex; + pkt_data->internal_flags = TX_FLAG_INJECTED; + pkt_data->flags = flags; + /* FIXME: never used, I think? Or could be invalid? */ + pkt_data->queue = queue; + + /* FIXME */ + pkt->priority = 20; /* use hardcoded priority for mgmt TX queue */ + + sdata->stats.tx_packets++; + sdata->stats.tx_bytes += pkt->len; + + pkt->dev = sdata->master; + dev_queue_xmit(pkt); + + return 0; +} + +static void* d80211_priv_for_ifindex(int ifindex) +{ + struct net_device *dev = dev_get_by_index(ifindex); + + if (!dev) + return NULL; + + if (!dev->ieee80211_ptr) + return NULL; + + return dev; +} + +static void d80211_release_priv(void *priv) +{ + dev_put(priv); +} + +static struct nl80211_driver d80211nl = { + .get_priv_for_ifindex = d80211_priv_for_ifindex, + .release_priv = d80211_release_priv, + .inject_packet = d80211_inject, +}; + +void ieee80211_cfg_init(void) +{ + nl80211_register_driver(&d80211nl); +} + +void ieee80211_cfg_exit(void) +{ + nl80211_unregister_driver(&d80211nl); +} --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ wireless-dev/net/d80211/ieee80211_cfg.h 2006-08-17 11:05:43.000000000 +0200 @@ -0,0 +1,7 @@ +#ifndef __IEEE80211_CFG_H +#define __IEEE80211_CFG_H + +extern void ieee80211_cfg_init(void); +extern void ieee80211_cfg_exit(void); + +#endif /* __IEEE80211_CFG_H */ --- wireless-dev.orig/net/d80211/ieee80211_i.h 2006-08-17 11:48:34.000000000 +0200 +++ wireless-dev/net/d80211/ieee80211_i.h 2006-08-18 13:38:11.000000000 +0200 @@ -151,12 +151,13 @@ struct ieee80211_tx_packet_data { int ifindex; unsigned long jiffies; - unsigned int req_tx_status:1; - unsigned int do_not_encrypt:1; - unsigned int pkt_probe_resp:1; - unsigned int requeue:1; - unsigned int queue:4; - unsigned int mgmt_iface:1; +/* we simply re-use NL80211_FLAG_* here */ + unsigned int flags; + unsigned int queue; +#define TX_FLAG_INJECTED (1<<0) +#define TX_FLAG_REQUEUE (1<<1) +#define TX_FLAG_PROBERESP (1<<2) + unsigned int internal_flags; }; struct ieee80211_tx_stored_packet { --- wireless-dev.orig/net/d80211/ieee80211_sta.c 2006-08-17 12:27:46.000000000 +0200 +++ wireless-dev/net/d80211/ieee80211_sta.c 2006-08-18 13:38:47.000000000 +0200 @@ -23,6 +23,7 @@ #include <linux/if_arp.h> #include <linux/wireless.h> #include <linux/random.h> +#include <linux/nl80211.h> #include <net/iw_handler.h> #include <asm/types.h> #include <asm/delay.h> @@ -400,10 +401,12 @@ pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); pkt_data->ifindex = sdata->dev->ifindex; - pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT); - pkt_data->do_not_encrypt = !encrypt; + if (sdata->type == IEEE80211_IF_TYPE_MGMT) + pkt_data->internal_flags |= TX_FLAG_INJECTED; + if (encrypt) + pkt_data->flags |= NL80211_FLAG_ENCRYPT; if (probe_resp) - pkt_data->pkt_probe_resp = 1; + pkt_data->internal_flags |= TX_FLAG_PROBERESP; dev_queue_xmit(skb); } --- wireless-dev.orig/net/d80211/wme.c 2006-08-17 12:28:47.000000000 +0200 +++ wireless-dev/net/d80211/wme.c 2006-08-18 13:30:06.000000000 +0200 @@ -12,6 +12,7 @@ #include <linux/skbuff.h> #include <linux/module.h> #include <linux/if_arp.h> +#include <linux/nl80211.h> #include <net/ip.h> #include <net/d80211.h> @@ -190,7 +191,8 @@ return IEEE80211_TX_QUEUE_DATA0; } - if (unlikely(pkt_data->mgmt_iface)) { + /* FIXME: this needs to be revisited for more generic injection */ + if (unlikely(pkt_data->internal_flags & TX_FLAG_INJECTED)) { /* Data frames from hostapd (mainly, EAPOL) use AC_VO * and they will include QoS control fields if * the target STA is using WME. */ @@ -236,7 +238,7 @@ struct Qdisc *qdisc; int err, queue; - if (pkt_data->requeue) { + if (pkt_data->internal_flags & TX_FLAG_REQUEUE) { skb_queue_tail(&q->requeued[pkt_data->queue], skb); return 0; } ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC] nl80211 + packet injection with it and d80211 2006-08-18 14:49 ` Johannes Berg @ 2006-08-20 18:47 ` Jiri Benc 2006-08-21 6:43 ` Johannes Berg 2006-08-21 15:32 ` Johannes Berg 2006-08-21 6:44 ` Johannes Berg 1 sibling, 2 replies; 7+ messages in thread From: Jiri Benc @ 2006-08-20 18:47 UTC (permalink / raw) To: Johannes Berg; +Cc: netdev, John W. Linville Johannes, thanks a lot for your work on nl80211! New netlink interface is one of the most important things regarding 802.11 stack now. Fri, 18 Aug 2006 16:49:25 +0200, Johannes Berg wrote: > [...] > +/** > + * struct nl80211_driver - 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. > + * > + * @get_priv_for_ifindex: return the private pointer that is then passed to > + * the other calls as the first argument. > + * Note that if this returns %NULL, then it is assumed > + * that the driver doesn't handle that interface, > + * hence priv is unused for some reason it still needs > + * to return a non-%NULL pointer! > + * > + * @release_priv: release the private pointer if necessary. Note that for > + * example if you used the ifindex and dev_get_by_index(), > + * you have to dev_put() here. Seems ineffective to me. Couldn't you require users of nl80211 to fill ieee80211_ptr field in all of their net_devices and use that to find out owner of the device? Then you don't need to call into every registered nl80211 user for each message. > + * > + * @inject_packet: inject the given frame with the NL80211_FLAG_* > + * flags onto the given queue. > + */ > +struct nl80211_driver { > + void* (*get_priv_for_ifindex)(int ifindex); > + void (*release_priv)(void *priv); Maybe put_priv would be better name for release_priv? > [...] > +static int nl80211_drvpriv_by_ifidx(int ifindex, void **priv, struct nl80211_driver **drv) Cut long lines, please. > [...] > +static int nl80211_get_commands(struct sk_buff *skb, struct genl_info *info) > [...] > + > + /* unconditionally allow NL80211_CMD_GET_COMMANDS */ > + skb_put(skb, 1); Shouldn't this be skb_put(msg, 1)? And in several other cases below too? > + *data++ = NL80211_CMD_GET_COMMANDS; > + > +#define CHECK_CMD(ptr, cmd) \ > + if (drv->ptr) { \ > + skb_put(skb, 1); \ > + *data++ = NL80211_CMD_##cmd; \ > + } Could you move this define out of the function? > + > + CHECK_CMD(inject_packet, INJECT); > + > + nla_nest_end(msg, start); > + > + err = genlmsg_end(msg, hdr); > + if (err) > + goto msg_free; > + > + err = genlmsg_unicast(msg, info->snd_pid); > + goto release; > + > + nla_put_failure: > + err = genlmsg_cancel(skb, hdr); > + msg_free: > + nlmsg_free(skb); > + release: > + if (drv->release_priv) > + drv->release_priv(priv); If get_priv_for_ifindex turns out to be really necessary, please rename nl80211_drvpriv_from_info to nl80211_get_drvpriv_from_info and introduce a new nl80211_put_drvpriv function (so it's immediately clear that returned pointers need some releasing): void nl80211_put_drvpriv(struct nl80211_driver *drv, void *priv) { if (drv->release_priv) drv->release_priv(priv); } And rename nl80211_drvpriv_by_ifidx to nl80211_get_drvpriv_by_ifidx, too. > [...] > +void nl80211_register_driver(struct nl80211_driver *drv) > +{ > + mutex_lock(&nl80211_drv_mutex); > + list_add(&drv->driver_list, &nl80211_drv_list); > + mutex_unlock(&nl80211_drv_mutex); > +} Don't modify passed nl80211_driver. This way drivers cannot pass a static structure. Allocate you own structure and store a pointer to passed nl80211_driver there (or copy it into that your structure if you don't like dereferencing two pointers when calling callbacks). > [...] > +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); Why is this exported? It should be used by nl80211msg_new only, shouldn't it? > [...] > +/* 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, Currently, master device is represented by a special net_device in d80211. It's not nice, "wiphy" device should be represented in some other way. Although I don't expect this would change (at least not in a foreseeable future) as it would require rewriting of great part of networking core, we should be prepared for it, so we would not be forced to change ABI or inventing workarounds if that eventually would happen. Also, d80211 allows some operations even without having any network interface present (of course, master interface is still present, but it's not nice to use its ifindex, especially in the light of previous paragraph). I propose this: - add NL80211_ATTR_WIPHY_INDEX attributte - add wiphy_index field to nl80211_driver and require non-d80211 users to set it to -1 - allow NL80211_ATTR_IFINDEX to be optional in some calls - the exact list of these calls is independent of any driver and can be hardcoded in nl80211 - in such calls, specifying either NL80211_ATTR_IFINDEX or NL80211_ATTR_WIPHY_INDEX is enough; of course, when corresponding driver does not support wiphys (i.e. nl80211_driver.wiphy_index is -1), only NL80211_ATTR_IFINDEX can be specified - in other calls, NL80211_ATTR_WIPHY_INDEX should not be present Alternatively, you could introduce a flag to denote if NL80211_ATTR_IFINDEX field contains ifindex or wiphy index. Comments to the d80211 part later (probably on Tuesday). Thanks, Jiri -- Jiri Benc SUSE Labs ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC] nl80211 + packet injection with it and d80211 2006-08-20 18:47 ` Jiri Benc @ 2006-08-21 6:43 ` Johannes Berg 2006-08-21 15:32 ` Johannes Berg 1 sibling, 0 replies; 7+ messages in thread From: Johannes Berg @ 2006-08-21 6:43 UTC (permalink / raw) To: Jiri Benc; +Cc: netdev, John W. Linville Jiri, > thanks a lot for your work on nl80211! New netlink interface is > one of the most important things regarding 802.11 stack now. Wait till you see my weekend patch series ;) > Seems ineffective to me. Couldn't you require users of nl80211 to fill > ieee80211_ptr field in all of their net_devices and use that to find out > owner of the device? Then you don't need to call into every registered > nl80211 user for each message. What about non-d80211 fullmac drivers? > Maybe put_priv would be better name for release_priv? Yeah, ok. > > [...] > > +static int nl80211_drvpriv_by_ifidx(int ifindex, void **priv, struct nl80211_driver **drv) > > Cut long lines, please. Oops. > > + /* unconditionally allow NL80211_CMD_GET_COMMANDS */ > > + skb_put(skb, 1); > > Shouldn't this be skb_put(msg, 1)? And in several other cases below too? Woah, yes. I really should've tested that code as well. > > +#define CHECK_CMD(ptr, cmd) \ > > + if (drv->ptr) { \ > > + skb_put(skb, 1); \ > > + *data++ = NL80211_CMD_##cmd; \ > > + } > > Could you move this define out of the function? Sure. I'll also undef it afterwards. > void nl80211_put_drvpriv(struct nl80211_driver *drv, void *priv) > { > if (drv->release_priv) > drv->release_priv(priv); > } > > And rename nl80211_drvpriv_by_ifidx to nl80211_get_drvpriv_by_ifidx, too. Sure. > Don't modify passed nl80211_driver. This way drivers cannot pass a static > structure. Allocate you own structure and store a pointer to passed > nl80211_driver there (or copy it into that your structure if you don't like > dereferencing two pointers when calling callbacks). Hmm, ok. I don't really like that too much either, but yeah, probably a good idea. OTOH, if we make a copy anyway we could just as well require drivers to make a copy, no? (And allocating just 12 bytes for three pointers for the linked list feels stupid) > > [...] > > +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); > > Why is this exported? It should be used by nl80211msg_new only, shouldn't > it? No, for dumpit() callbacks you need this. Not sure if we'll ever have any of those, but... > Currently, master device is represented by a special net_device in d80211. > It's not nice, "wiphy" device should be represented in some other way. > Although I don't expect this would change (at least not in a foreseeable > future) as it would require rewriting of great part of networking core, we > should be prepared for it, so we would not be forced to change ABI or > inventing workarounds if that eventually would happen. > > Also, d80211 allows some operations even without having any network > interface present (of course, master interface is still present, but it's > not nice to use its ifindex, especially in the light of previous paragraph). > > I propose this: > - add NL80211_ATTR_WIPHY_INDEX attributte We can do that at any time we want. > - add wiphy_index field to nl80211_driver and require non-d80211 users to > set it to -1 Woah, wait. Let's do it the other way around then, we give them a wiphy index. That way, we have a central registry for it here in nl80211. > - allow NL80211_ATTR_IFINDEX to be optional in some calls - the exact list > of these calls is independent of any driver and can be hardcoded in > nl80211 That's what I pretty much do already. > - in such calls, specifying either NL80211_ATTR_IFINDEX or > NL80211_ATTR_WIPHY_INDEX is enough; of course, when corresponding driver > does not support wiphys (i.e. nl80211_driver.wiphy_index is -1), only > NL80211_ATTR_IFINDEX can be specified > - in other calls, NL80211_ATTR_WIPHY_INDEX should not be present > > Alternatively, you could introduce a flag to denote if NL80211_ATTR_IFINDEX > field contains ifindex or wiphy index. Ick, no flag. Let's just have two separate attributes, and on some calls either one or both are valid. johannes ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC] nl80211 + packet injection with it and d80211 2006-08-20 18:47 ` Jiri Benc 2006-08-21 6:43 ` Johannes Berg @ 2006-08-21 15:32 ` Johannes Berg 1 sibling, 0 replies; 7+ messages in thread From: Johannes Berg @ 2006-08-21 15:32 UTC (permalink / raw) To: Jiri Benc; +Cc: netdev, John W. Linville Here's a respun patch hopefully addressing most of Jiri's comments, and allowing to add/remove virtual interfaces via netlink as well. I introduced a new notion of 'wiphy' which is just a number handed out by nl80211 for each registered backend, and I also changed it so that each backend must register once for each physical device. Also, drivers wishing to use this to full capacity are required to fill in ieee80211_ptr in struct net_device for all of their devices, otherwise the wiphy number is useless! NB: The new notion of wiphy will be confusing because now nl80211 and d80211 can hand out different numbers. I expect that either d80211 will just use the wiphy number nl80211 handed out (will need new API to query it) or we rename things or whatever. To be shaken out :) Oh, I also expect there to be a configfs/sysfs interface to nl80211 directly so that backends (d80211...) don't create their sysfs/configfs dirs but use debugfs for truly not too interesting things like debug counters, and all the other stuff is in nl80211. Or something like that anyway :) With this patch, NL80211_ATTR_IFINDEX of any netdevice attached to a wiphy can be used in place of NL80211_ATTR_WIPHY, but some calls like adding a virtual device to a wiphy that has no netdev (something that cannot happen at the moment due to wmaster) require ATTR_WIPHY for obvious reasons. Oh and I figured that the list walking is maybe dumb, but we will never have thousands of wiphys in a single machine... Anyway, with this patch we ought to be able to get rid of wmaster0 without userspace visible changes (except of course that doing wconf dev add xxx0 dev wmaster0 will no longer work and you'll need to do wconf dev add xxx0 wiphy 0 or something :) Hrm, I keep forgetting things :) Another thing that I just remembered: This relies on the patch by Thomas Graf he posted in http://marc.theaimsgroup.com/?l=linux-netdev&m=115218779631578&w=2 for validation of the netdevice name length/null termination. Patch below. NB: only compiled, not tried this time, want to go home :) johannes --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ wireless-dev/include/net/nl80211.h 2006-08-21 17:19:04.000000000 +0200 @@ -0,0 +1,74 @@ +#ifndef __NET_NL80211_H +#define __NET_NL80211_H + +#include <linux/netlink.h> +#include <linux/nl80211.h> +#include <linux/skbuff.h> +#include <linux/netdevice.h> +#include <net/genetlink.h> + +/* + * 802.11 netlink in-kernel interface + * + * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> + */ + +/** + * struct nl80211_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 nl80211_register_driver(). + * + * All callbacks except where otherwise noted should return 0 + * on success or a negative error code. + * + * @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 given virtual interface (during the call, + * one reference to the interface is held) + */ +struct nl80211_ops { + int (*inject_packet)(void *priv, void *frame, int framelen, + u32 flags, int queue); + + int (*add_virtual_intf)(void *priv, char *name); + int (*del_virtual_intf)(void *priv, struct net_device *dev); + + /* more things to be added... + * + * for a (*configure)(...) call I'd probably guess that the + * best bet would be to have one call that returns all + * possible options, one that sets them based on the + * struct genl_info *info, and one for that optimised + * set-at-once thing. + */ +}; + +/* + * register a given method structure with the nl80211 system + * and associate the 'priv' pointer with it. + * NOTE: for proper operation, this priv pointer MUST also be + * assigned to each &struct net_device's @ieee80211_ptr member! + */ +extern int nl80211_register(struct nl80211_ops *ops, void *priv); +/* + * 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 nl80211_unregister(void *priv); + +/* helper functions */ +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_NL80211_H */ --- wireless-dev.orig/net/Kconfig 2006-08-21 17:16:24.000000000 +0200 +++ wireless-dev/net/Kconfig 2006-08-21 17:16:42.000000000 +0200 @@ -250,6 +250,9 @@ config WIRELESS_EXT bool +config NETLINK_80211 + tristate + endif # if NET endmenu # Networking --- wireless-dev.orig/net/Makefile 2006-08-21 17:16:24.000000000 +0200 +++ wireless-dev/net/Makefile 2006-08-21 17:16:42.000000000 +0200 @@ -44,6 +44,7 @@ obj-$(CONFIG_VLAN_8021Q) += 8021q/ obj-$(CONFIG_IP_DCCP) += dccp/ obj-$(CONFIG_IP_SCTP) += sctp/ +obj-$(CONFIG_NETLINK_80211) += wireless/ obj-$(CONFIG_D80211) += d80211/ obj-$(CONFIG_IEEE80211) += ieee80211/ obj-$(CONFIG_TIPC) += tipc/ --- wireless-dev.orig/net/d80211/Kconfig 2006-08-21 17:16:24.000000000 +0200 +++ wireless-dev/net/d80211/Kconfig 2006-08-21 17:16:42.000000000 +0200 @@ -3,6 +3,7 @@ select CRYPTO select CRYPTO_ARC4 select CRYPTO_AES + select NETLINK_80211 ---help--- This option enables the hardware independent IEEE 802.11 networking stack. --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ wireless-dev/net/wireless/Makefile 2006-08-21 17:16:42.000000000 +0200 @@ -0,0 +1,4 @@ +obj-$(CONFIG_NETLINK_80211) += cfg80211.o + +cfg80211-objs := \ + nl80211.o --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ wireless-dev/net/wireless/nl80211.c 2006-08-21 17:16:42.000000000 +0200 @@ -0,0 +1,447 @@ +/* + * 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 <net/genetlink.h> +#include <net/nl80211.h> +#include <linux/mutex.h> +#include <linux/list.h> + +MODULE_AUTHOR("Johannes Berg"); +MODULE_LICENSE("GPL"); + +struct nl80211_registered_driver { + struct nl80211_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; +}; + +/* 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 */ +static LIST_HEAD(nl80211_drv_list); +static DEFINE_MUTEX(nl80211_drv_mutex); +static unsigned int wiphy_counter; + +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, +}; + +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_CMDS] = { .type = NLA_STRING }, + [NL80211_ATTR_QUEUE] = { .type = NLA_U32 }, + [NL80211_ATTR_FRAME] = { .type = NLA_STRING }, + [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ }, +}; + +static struct nl80211_registered_driver *nl80211_drv_by_priv_locked(void *priv) +{ + struct nl80211_registered_driver *result = NULL, *drv; + + if (!priv) + return NULL; + + list_for_each_entry(drv, &nl80211_drv_list, list) { + if (drv->priv == priv) { + result = drv; + break; + } + } + + return result; +} + +static struct nl80211_registered_driver *nl80211_drv_by_wiphy_locked(int wiphy) +{ + struct nl80211_registered_driver *result = NULL, *drv; + + list_for_each_entry(drv, &nl80211_drv_list, list) { + if (drv->wiphy == wiphy) { + result = drv; + break; + } + } + + return result; +} + +/* requires nl80211_drv_mutex to be held! */ +static struct nl80211_registered_driver * +nl80211_drv_from_info_locked(struct genl_info *info) +{ + int ifindex; + struct nl80211_registered_driver *result = NULL; + struct net_device *dev; + + if (info->attrs[NL80211_ATTR_WIPHY]) { + result = nl80211_drv_by_wiphy_locked( + nla_get_u32(info->attrs[NL80211_ATTR_WIPHY])); + if (result) + return result; + } + + if (info->attrs[NL80211_ATTR_IFINDEX]) { + ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); + dev = dev_get_by_index(ifindex); + result = nl80211_drv_by_priv_locked(dev->ieee80211_ptr); + dev_put(dev); + if (result) + return result; + } + + return NULL; +} + +/* 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! + * DON'T EVER ACQUIRE nl80211_drv_mutex AFTER THIS! + * + * 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 nl80211_drv_mutex locked + * for all the time in order to allow requests on + * other interfaces to go through at the same time. */ +static struct nl80211_registered_driver * +nl80211_drv_from_info_with_locking(struct genl_info *info) +{ + struct nl80211_registered_driver *drv; + + mutex_lock(&nl80211_drv_mutex); + drv = nl80211_drv_from_info_locked(info); + if (!drv) { + mutex_unlock(&nl80211_drv_mutex); + return NULL; + } + mutex_lock(&drv->mtx); + mutex_unlock(&nl80211_drv_mutex); + + return drv; +} + +#define CHECK_CMD(ptr, cmd) \ + if (drv->ops->ptr) { \ + skb_put(msg, 1); \ + *data++ = NL80211_CMD_##cmd; \ + } + +static int nl80211_get_commands(struct sk_buff *skb, struct genl_info *info) +{ + struct nl80211_registered_driver *drv; + struct sk_buff *msg; + void *hdr; + int err; + struct nlattr *start; + u8 *data; + + drv = nl80211_drv_from_info_with_locking(info); + if (!drv) + return -EINVAL; + + hdr = nl80211msg_new(&msg, info->snd_pid, info->snd_seq, 0, + NL80211_CMD_GET_COMMANDS); + if (IS_ERR(hdr)) { + err = PTR_ERR(hdr); + goto unlock; + } + + start = nla_nest_start(msg, NL80211_ATTR_CMDS); + if (!start) + goto nla_nest_failure; + data = nla_data(start); + + /* unconditionally allow NL80211_CMD_GET_COMMANDS */ + skb_put(msg, 1); + *data++ = NL80211_CMD_GET_COMMANDS; + + CHECK_CMD(inject_packet, INJECT); + CHECK_CMD(add_virtual_intf, ADD_VIRTUAL_INTERFACE); + CHECK_CMD(del_virtual_intf, DEL_VIRTUAL_INTERFACE); + + nla_nest_end(msg, start); + + err = genlmsg_end(msg, hdr); + if (err) + goto msg_free; + + err = genlmsg_unicast(msg, info->snd_pid); + goto unlock; + + nla_nest_failure: + err = genlmsg_cancel(skb, hdr); + msg_free: + nlmsg_free(skb); + unlock: + mutex_unlock(&drv->mtx); + return err; +} +#undef CHECK_CMD + +static int nl80211_do_inject(struct sk_buff *skb, struct genl_info *info) +{ + struct nl80211_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 = nl80211_drv_from_info_with_locking(info); + if (!drv) + return -EINVAL; + + 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: + mutex_unlock(&drv->mtx); + return err; +} + +static int nl80211_add_virt_intf(struct sk_buff *skb, struct genl_info *info) +{ + struct nl80211_registered_driver *drv; + int err; + + if (!info->attrs[NL80211_ATTR_IFNAME]) + return -EINVAL; + + drv = nl80211_drv_from_info_with_locking(info); + if (!drv) + return -EINVAL; + + 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])); + + unlock: + mutex_unlock(&drv->mtx); + return err; +} + +static int nl80211_del_virt_intf(struct sk_buff *skb, struct genl_info *info) +{ + struct nl80211_registered_driver *drv; + int ifindex, err; + struct net_device *dev; + + 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 -EINVAL; + + /* need to act on that ifindex */ + mutex_lock(&nl80211_drv_mutex); + drv = nl80211_drv_by_priv_locked(dev->ieee80211_ptr); + if (!drv) { + mutex_unlock(&nl80211_drv_mutex); + return -EINVAL; + } + mutex_lock(&drv->mtx); + mutex_unlock(&nl80211_drv_mutex); + + if (!drv->ops->del_virtual_intf) { + err = -ENOSYS; + goto unlock; + } + + err = drv->ops->del_virtual_intf(drv->priv, dev); + + dev_put(dev); + + unlock: + mutex_unlock(&drv->mtx); + return err; +} + +static struct genl_ops nl80211_ops[] = { + { + .cmd = NL80211_CMD_GET_COMMANDS, + .doit = nl80211_get_commands, + .policy = nl80211_policy, + }, + { + .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, + }, +}; + + +/* exported functions */ + +int nl80211_register(struct nl80211_ops *ops, void *priv) +{ + struct nl80211_registered_driver *drv; + int err; + + if (!priv) + return -EINVAL; + + mutex_lock(&nl80211_drv_mutex); + + if (nl80211_drv_by_priv_locked(priv)) { + err = -EALREADY; + goto out_unlock; + } + + drv = kzalloc(sizeof(struct nl80211_registered_driver), GFP_KERNEL); + if (!drv) { + err = -ENOMEM; + goto out_unlock; + } + + drv->ops = ops; + drv->priv = priv; + + wiphy_counter++; + if (unlikely(!wiphy_counter)) { + /* ugh, wrapped! */ + kfree(drv); + err = -ENOSPC; + goto out_unlock; + } + mutex_init(&drv->mtx); + drv->wiphy = wiphy_counter; + list_add(&drv->list, &nl80211_drv_list); + err = 0; + + out_unlock: + mutex_unlock(&nl80211_drv_mutex); + return err; +} +EXPORT_SYMBOL_GPL(nl80211_register); + +void nl80211_unregister(void *priv) +{ + struct nl80211_registered_driver *drv; + + mutex_lock(&nl80211_drv_mutex); + drv = nl80211_drv_by_priv_locked(priv); + if (!drv) { + printk(KERN_ERR "deregistering nl80211 backend that " + " was never registered!\n"); + mutex_unlock(&nl80211_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(&nl80211_drv_mutex); + + mutex_destroy(&drv->mtx); + kfree(drv); +} +EXPORT_SYMBOL_GPL(nl80211_unregister); + +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); + /* what would be a good error here? */ + return ERR_PTR(-EINVAL); + } + + return hdr; +} +EXPORT_SYMBOL_GPL(nl80211msg_new); + +/* module initialisation/exit functions */ + +static 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; +} + +static void nl80211_exit(void) +{ + genl_unregister_family(&nl80211_fam); +} + +module_init(nl80211_init); +module_exit(nl80211_exit); --- wireless-dev.orig/include/linux/Kbuild 2006-08-21 17:16:24.000000000 +0200 +++ wireless-dev/include/linux/Kbuild 2006-08-21 17:16:42.000000000 +0200 @@ -28,7 +28,7 @@ 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-08-21 17:16:42.000000000 +0200 @@ -0,0 +1,84 @@ +#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_COMMANDS, + + /* 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, + + /* 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 for adding a virtual interface */ + NL80211_ATTR_IFNAME, + + /* 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<<00) +/** + * NL80211_FLAG_ENCRYPT - encrypt this packet + * Warning: This looks inside the packet header! + */ +#define NL80211_FLAG_ENCRYPT (1<<01) + +#endif /* __LINUX_NL80211_H */ --- wireless-dev.orig/net/d80211/Makefile 2006-08-21 17:16:24.000000000 +0200 +++ wireless-dev/net/d80211/Makefile 2006-08-21 17:16:42.000000000 +0200 @@ -15,7 +15,8 @@ michael.o \ tkip.o \ aes_ccm.o \ - wme.o + wme.o \ + ieee80211_cfg.o ifeq ($(CONFIG_NET_SCHED),) 80211-objs += fifo_qdisc.o --- wireless-dev.orig/net/d80211/ieee80211.c 2006-08-21 17:16:24.000000000 +0200 +++ wireless-dev/net/d80211/ieee80211.c 2006-08-21 17:16:42.000000000 +0200 @@ -20,6 +20,7 @@ #include <linux/wireless.h> #include <net/iw_handler.h> #include <linux/compiler.h> +#include <linux/nl80211.h> #include <net/d80211.h> #include <net/d80211_common.h> @@ -31,7 +32,7 @@ #include "tkip.h" #include "wme.h" #include "aes_ccm.h" - +#include "ieee80211_cfg.h" /* See IEEE 802.1H for LLC/SNAP encapsulation/decapsulation */ /* Ethernet-II snap header (RFC1042 for most EtherTypes) */ @@ -354,6 +355,16 @@ { struct rate_control_extra extra; + /* FIXME + if (tx->dev == tx->local->mdev && + (inject rate set)) { + a + tx->u.tx.rate = ... + etc etc + return TXRX_CONTINUE; + } + */ + memset(&extra, 0, sizeof(extra)); extra.mgmt_data = tx->sdata && tx->sdata->type == IEEE80211_IF_TYPE_MGMT; @@ -761,6 +772,13 @@ u16 dur; struct ieee80211_tx_control *control = tx->u.tx.control; + /* FIXME + if (tx->dev == tx->local->mdev) { + set up retry limit, ... + based on injection parameters + } + */ + if (!is_multicast_ether_addr(hdr->addr1)) { if (tx->skb->len + FCS_LEN > tx->local->rts_threshold && tx->local->rts_threshold < IEEE80211_MAX_RTS_THRESHOLD) { @@ -886,6 +904,9 @@ #endif /* CONFIG_D80211_VERBOSE_DEBUG */ u32 sta_flags; + if (unlikely(tx->dev == tx->local->mdev)) + return TXRX_CONTINUE; + if (unlikely(tx->local->sta_scanning != 0) && ((tx->fc & IEEE80211_FCTL_FTYPE) != IEEE80211_FTYPE_MGMT || (tx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PROBE_REQ)) @@ -989,6 +1010,12 @@ static inline ieee80211_txrx_result ieee80211_tx_h_multicast_ps_buf(struct ieee80211_txrx_data *tx) { + /* FIXME + if (unlikely(tx->dev == tx->local->mdev && + (inject flags) & NL80211_FLAG_NOBUFFER)) + return TXRX_CONTINUE; + */ + /* broadcast/multicast frame */ /* If any of the associated stations is in power save mode, * the frame is buffered to be sent after DTIM beacon frame */ @@ -1420,11 +1447,12 @@ control.ifindex = odev->ifindex; control.type = osdata->type; - control.req_tx_status = pkt_data->req_tx_status; - control.do_not_encrypt = pkt_data->do_not_encrypt; + control.req_tx_status = !!(pkt_data->flags & NL80211_FLAG_TXSTATUS); + control.do_not_encrypt = !(pkt_data->flags & NL80211_FLAG_ENCRYPT); control.pkt_type = - pkt_data->pkt_probe_resp ? PKT_PROBE_RESP : PKT_NORMAL; - control.requeue = pkt_data->requeue; + (pkt_data->internal_flags & TX_FLAG_PROBERESP) ? + PKT_PROBE_RESP : PKT_NORMAL; + control.requeue = !!(pkt_data->internal_flags & TX_FLAG_REQUEUE); control.queue = pkt_data->queue; ret = ieee80211_tx(odev, skb, &control, @@ -1600,8 +1628,10 @@ pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); pkt_data->ifindex = sdata->dev->ifindex; - pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT); - pkt_data->do_not_encrypt = no_encrypt; + if (sdata->type == IEEE80211_IF_TYPE_MGMT) + pkt_data->internal_flags |= TX_FLAG_INJECTED; + if (!no_encrypt) + pkt_data->flags |= NL80211_FLAG_ENCRYPT; skb->dev = sdata->master; sdata->stats.tx_packets++; @@ -1652,11 +1682,12 @@ pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); pkt_data->ifindex = sdata->dev->ifindex; - pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT); + if (sdata->type == IEEE80211_IF_TYPE_MGMT) + pkt_data->internal_flags |= TX_FLAG_INJECTED; if ((fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_MGMT && (fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PROBE_RESP) - pkt_data->pkt_probe_resp = 1; + pkt_data->internal_flags |= TX_FLAG_PROBERESP; skb->priority = 20; /* use hardcoded priority for mgmt TX queue */ skb->dev = sdata->master; @@ -1666,12 +1697,13 @@ * to request TX callback for hostapd. BIT(1) is checked. */ if ((fc & BIT(1)) == BIT(1)) { - pkt_data->req_tx_status = 1; + pkt_data->flags |= NL80211_FLAG_TXSTATUS; fc &= ~BIT(1); hdr->frame_control = cpu_to_le16(fc); } - pkt_data->do_not_encrypt = !(fc & IEEE80211_FCTL_PROTECTED); + if (fc & IEEE80211_FCTL_PROTECTED) + pkt_data->flags |= NL80211_FLAG_ENCRYPT; sdata->stats.tx_packets++; sdata->stats.tx_bytes += skb->len; @@ -2725,7 +2757,7 @@ while ((skb = skb_dequeue(&sta->tx_filtered)) != NULL) { pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; sent++; - pkt_data->requeue = 1; + pkt_data->internal_flags |= TX_FLAG_REQUEUE; dev_queue_xmit(skb); } while ((skb = skb_dequeue(&sta->ps_tx_buf)) != NULL) { @@ -2737,7 +2769,7 @@ "since STA not sleeping anymore\n", dev->name, MAC_ARG(sta->addr), sta->aid); #endif /* IEEE80211_VERBOSE_DEBUG_PS */ - pkt_data->requeue = 1; + pkt_data->internal_flags |= TX_FLAG_REQUEUE; dev_queue_xmit(skb); } @@ -3969,12 +4001,19 @@ struct ieee80211_tx_packet_data *pkt_data; pkt_data = (struct ieee80211_tx_packet_data *)skb->cb; + pkt_data->flags = 0; + pkt_data->internal_flags = 0; pkt_data->ifindex = control->ifindex; - pkt_data->mgmt_iface = (control->type == IEEE80211_IF_TYPE_MGMT); - pkt_data->req_tx_status = control->req_tx_status; - pkt_data->do_not_encrypt = control->do_not_encrypt; - pkt_data->pkt_probe_resp = (control->pkt_type == PKT_PROBE_RESP); - pkt_data->requeue = control->requeue; + if (control->type == IEEE80211_IF_TYPE_MGMT) + pkt_data->internal_flags |= TX_FLAG_INJECTED; + if (control->req_tx_status) + pkt_data->flags |= NL80211_FLAG_TXSTATUS; + if (!control->do_not_encrypt) + pkt_data->flags |= NL80211_FLAG_ENCRYPT; + if (control->pkt_type == PKT_PROBE_RESP) + pkt_data->internal_flags |= TX_FLAG_PROBERESP; + if (control->requeue) + pkt_data->internal_flags |= TX_FLAG_REQUEUE; pkt_data->queue = control->queue; hdrlen = ieee80211_get_hdrlen_from_skb(skb); @@ -4334,6 +4373,7 @@ priv_size = ((sizeof(struct ieee80211_sub_if_data) + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST) + priv_data_len; + mdev = alloc_netdev(priv_size, "wmaster%d", ether_setup); if (mdev == NULL) { ieee80211_dev_free(local); @@ -4436,6 +4476,9 @@ if (result < 0) return -1; + if (ieee80211_cfg_init(local)) + goto fail_nl80211; + local->class_dev.dev = dev->class_dev.dev; result = ieee80211_dev_sysfs_add(local); if (result < 0) @@ -4513,6 +4556,8 @@ fail_sta_info: ieee80211_dev_sysfs_del(local); fail_sysfs: + ieee80211_cfg_exit(local); +fail_nl80211: ieee80211_dev_free_index(local); return result; } @@ -4591,6 +4636,8 @@ &local->class_dev.kobj); ieee80211_dev_sysfs_del(local); + ieee80211_cfg_exit(local); + for (i = 0; i < NUM_IEEE80211_MODES; i++) { kfree(local->supp_rates[i]); kfree(local->basic_rates[i]); --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ wireless-dev/net/d80211/ieee80211_cfg.c 2006-08-21 17:16:42.000000000 +0200 @@ -0,0 +1,97 @@ +/* + * nl80211-based configuration for d80211 + * + * Copyright 2006 Johannes Berg <johannes@sipsolutions.net> + */ +#include <net/nl80211.h> +#include <linux/netdevice.h> +#include <linux/rtnetlink.h> +#include "ieee80211_cfg.h" +#include "ieee80211_i.h" + +/* copied from ieee80211_sysfs.c for now ... */ +static inline int rtnl_lock_local(struct ieee80211_local *local) +{ + rtnl_lock(); + if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED)) { + rtnl_unlock(); + return -ENODEV; + } + return 0; +} + +static int d80211_inject(void *priv, void *frame, int framelen, u32 flags, + int queue) +{ + struct ieee80211_local *local = priv; + struct ieee80211_tx_packet_data *pkt_data; + struct sk_buff *pkt; + void *pktdata; + + pkt = alloc_skb(framelen, GFP_KERNEL); + pktdata = skb_put(pkt, framelen); + memcpy(pktdata, frame, framelen); + + pkt_data = (struct ieee80211_tx_packet_data *) pkt->cb; + memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); + pkt_data->ifindex = local->mdev->ifindex; + pkt_data->internal_flags = TX_FLAG_INJECTED; + pkt_data->flags = flags; + /* FIXME: never used, I think? Or could be invalid? */ + pkt_data->queue = queue; + + /* FIXME */ + pkt->priority = 20; /* use hardcoded priority for mgmt TX queue */ + + pkt->dev = local->mdev; + dev_queue_xmit(pkt); + + return 0; +} + +static int d80211_add_virtual_intf(void *priv, char *name) +{ + struct ieee80211_local *local = priv; + struct net_device *new_dev; + int res; + + res = rtnl_lock_local(local); + if (res) + return res; + + res = ieee80211_if_add(local->mdev, name, 0, &new_dev); + if (res == 0) + ieee80211_if_set_type(new_dev, IEEE80211_IF_TYPE_STA); + rtnl_unlock(); + return res; +} + +static int d80211_del_virtual_intf(void *priv, struct net_device *dev) +{ + struct ieee80211_local *local = priv; + int res; + + res = rtnl_lock_local(local); + if (res) + return res; + + res = ieee80211_if_remove(local->mdev, dev->name, -1); + rtnl_unlock(); + return res; +} + +static struct nl80211_ops d80211nl = { + .inject_packet = d80211_inject, + .add_virtual_intf = d80211_add_virtual_intf, + .del_virtual_intf = d80211_del_virtual_intf, +}; + +int ieee80211_cfg_init(struct ieee80211_local *local) +{ + return nl80211_register(&d80211nl, local); +} + +void ieee80211_cfg_exit(struct ieee80211_local *local) +{ + nl80211_unregister(local); +} --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ wireless-dev/net/d80211/ieee80211_cfg.h 2006-08-21 17:16:42.000000000 +0200 @@ -0,0 +1,9 @@ +#ifndef __IEEE80211_CFG_H +#define __IEEE80211_CFG_H + +#include "ieee80211_i.h" + +extern int ieee80211_cfg_init(struct ieee80211_local *local); +extern void ieee80211_cfg_exit(struct ieee80211_local *local); + +#endif /* __IEEE80211_CFG_H */ --- wireless-dev.orig/net/d80211/ieee80211_i.h 2006-08-21 17:16:24.000000000 +0200 +++ wireless-dev/net/d80211/ieee80211_i.h 2006-08-21 17:16:42.000000000 +0200 @@ -151,12 +151,13 @@ struct ieee80211_tx_packet_data { int ifindex; unsigned long jiffies; - unsigned int req_tx_status:1; - unsigned int do_not_encrypt:1; - unsigned int pkt_probe_resp:1; - unsigned int requeue:1; - unsigned int queue:4; - unsigned int mgmt_iface:1; +/* we simply re-use NL80211_FLAG_* here */ + unsigned int flags; + unsigned int queue; +#define TX_FLAG_INJECTED (1<<0) +#define TX_FLAG_REQUEUE (1<<1) +#define TX_FLAG_PROBERESP (1<<2) + unsigned int internal_flags; }; struct ieee80211_tx_stored_packet { --- wireless-dev.orig/net/d80211/ieee80211_sta.c 2006-08-21 17:16:24.000000000 +0200 +++ wireless-dev/net/d80211/ieee80211_sta.c 2006-08-21 17:16:42.000000000 +0200 @@ -23,6 +23,7 @@ #include <linux/if_arp.h> #include <linux/wireless.h> #include <linux/random.h> +#include <linux/nl80211.h> #include <net/iw_handler.h> #include <asm/types.h> #include <asm/delay.h> @@ -400,10 +401,12 @@ pkt_data = (struct ieee80211_tx_packet_data *) skb->cb; memset(pkt_data, 0, sizeof(struct ieee80211_tx_packet_data)); pkt_data->ifindex = sdata->dev->ifindex; - pkt_data->mgmt_iface = (sdata->type == IEEE80211_IF_TYPE_MGMT); - pkt_data->do_not_encrypt = !encrypt; + if (sdata->type == IEEE80211_IF_TYPE_MGMT) + pkt_data->internal_flags |= TX_FLAG_INJECTED; + if (encrypt) + pkt_data->flags |= NL80211_FLAG_ENCRYPT; if (probe_resp) - pkt_data->pkt_probe_resp = 1; + pkt_data->internal_flags |= TX_FLAG_PROBERESP; dev_queue_xmit(skb); } --- wireless-dev.orig/net/d80211/wme.c 2006-08-21 17:16:24.000000000 +0200 +++ wireless-dev/net/d80211/wme.c 2006-08-21 17:16:42.000000000 +0200 @@ -12,6 +12,7 @@ #include <linux/skbuff.h> #include <linux/module.h> #include <linux/if_arp.h> +#include <linux/nl80211.h> #include <net/ip.h> #include <net/d80211.h> @@ -190,7 +191,8 @@ return IEEE80211_TX_QUEUE_DATA0; } - if (unlikely(pkt_data->mgmt_iface)) { + /* FIXME: this needs to be revisited for more generic injection */ + if (unlikely(pkt_data->internal_flags & TX_FLAG_INJECTED)) { /* Data frames from hostapd (mainly, EAPOL) use AC_VO * and they will include QoS control fields if * the target STA is using WME. */ @@ -236,7 +238,7 @@ struct Qdisc *qdisc; int err, queue; - if (pkt_data->requeue) { + if (pkt_data->internal_flags & TX_FLAG_REQUEUE) { skb_queue_tail(&q->requeued[pkt_data->queue], skb); return 0; } ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC] nl80211 + packet injection with it and d80211 2006-08-18 14:49 ` Johannes Berg 2006-08-20 18:47 ` Jiri Benc @ 2006-08-21 6:44 ` Johannes Berg 2006-08-21 12:12 ` John W. Linville 1 sibling, 1 reply; 7+ messages in thread From: Johannes Berg @ 2006-08-21 6:44 UTC (permalink / raw) To: netdev; +Cc: John W. Linville, Jiri Benc Another thing that crossed my mind: Should we have a netlink call for adding virtual interfaces as well? Because then, the ipw2x00 drivers can have their monitor interface(s) added the same way. johannes ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC] nl80211 + packet injection with it and d80211 2006-08-21 6:44 ` Johannes Berg @ 2006-08-21 12:12 ` John W. Linville 0 siblings, 0 replies; 7+ messages in thread From: John W. Linville @ 2006-08-21 12:12 UTC (permalink / raw) To: Johannes Berg; +Cc: netdev, Jiri Benc On Mon, Aug 21, 2006 at 08:44:52AM +0200, Johannes Berg wrote: > Another thing that crossed my mind: Should we have a netlink call for > adding virtual interfaces as well? Because then, the ipw2x00 drivers can > have their monitor interface(s) added the same way. Yes. -- John W. Linville linville@tuxdriver.com ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2006-08-21 15:32 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2006-08-17 14:13 [RFC] nl80211 + packet injection with it and d80211 Johannes Berg 2006-08-18 14:49 ` Johannes Berg 2006-08-20 18:47 ` Jiri Benc 2006-08-21 6:43 ` Johannes Berg 2006-08-21 15:32 ` Johannes Berg 2006-08-21 6:44 ` Johannes Berg 2006-08-21 12:12 ` John W. Linville
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).