2011-04-18 20:45:33

by Javier López

[permalink] [raw]
Subject: [PATCH] mac80211_hwsim driver support userspace frame tx/rx

This patch adds the capability to hwsim to send traffic via userspace.

Frame exchange between kernel and userspace is done through a generic
netlink communication protocol, a new family called HWSIM is proposed
in order to communicate with userspace applications. This family
contains three basic commands; HWSIM_CMD_REGISTER which is used to
register a userspace application with this driver, HWSIM_CMD_FRAME to
exchange frames from kernel to user and vice-versa, and finally
HWSIM_CMD_TX_INFO_FRAME which returns to the kernel all the information
about a frame transmission over the medium, number of tries, rates,
ack signal and so on.

How it works:

Once the driver is loaded, it registers the HWSIM generic netlink
family. In the absence of a daemon the driver implements a perfect
wireless medium in the kernel as it did in the past. Once a daemon a
registers itself via HWSIM_CMD_REGISTER, the kernel module stores the
daemon PID and from this moment all frames will be sent to the daemon.

The userspace application will be in charge of process/forward all
frames broadcast by any radio using the mac80211_hwsim driver. If the
user application is stopped the module will switch back to in-kernel
perfect channel simulation. This transmission of kernel to user space
is done through the HWSIM_CMD_FRAME command.

The userspace application once sends the HWSIM_CMD_REGISTER, waits
for incoming messages from kernel, for each HWSIM_CMD_FRAME command
received, the application will try to broadcast it, cloning the frame
to all known mac80211_hwsim radios. The daemon may decide to send or
drop this frame. In the case of sending a frame back to kernel, the
daemon will create a new HWSIM_CMD_FRAME command, fill the necessary
attributes and send it back. In the case of a frame drop, the example
userspace daemon retransmits frames according to the ieee80211_tx_info
values received from the kernel.

After each frame complete transmission a HWSIM_CMD_TX_INFO command
may be sent to the kernel to inform about the tx status, this command
contains information such as number of tries, rates, ack'ed or not
ack'ed, ack_signal, etc.

If you want to test the actual implementation of the wireless medium
daemon (wmediumd) and test this patch, just get it from GitHub:

- Last version tarball: https://github.com/jlopex/cozybit/tarball/master
- Or visiting my github tree: https://github.com/jlopex/cozybit/tree

Best regards,

Signed-off-by: Javier Lopez <[email protected]>
Signed-off-by: Javier Cardona <[email protected]>
---
drivers/net/wireless/mac80211_hwsim.c | 378 ++++++++++++++++++++++++++++++++-
drivers/net/wireless/mac80211_hwsim.h | 123 +++++++++++
2 files changed, 495 insertions(+), 6 deletions(-)
create mode 100644 drivers/net/wireless/mac80211_hwsim.h

diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index f4f4257..0be0ad7 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -1,6 +1,7 @@
/*
* mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
* Copyright (c) 2008, Jouni Malinen <[email protected]>
+ * Copyright (c) 2011, Javier Lopez <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -25,11 +26,15 @@
#include <linux/rtnetlink.h>
#include <linux/etherdevice.h>
#include <linux/debugfs.h>
+#include <net/genetlink.h>
+#include "mac80211_hwsim.h"

MODULE_AUTHOR("Jouni Malinen");
MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211");
MODULE_LICENSE("GPL");

+static atomic_t wmediumd_pid;
+
static int radios = 2;
module_param(radios, int, 0444);
MODULE_PARM_DESC(radios, "Number of simulated radios");
@@ -479,20 +484,92 @@ static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
}


+static int hwsim_frame_send_nl(struct mac_address *src,
+ struct sk_buff *my_skb, int _pid)
+{
+
+ struct ieee80211_tx_info *txi;
+ struct sk_buff *skb;
+ void *msg_head;
+ int rc;
+
+ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ msg_head = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
+ HWSIM_CMD_FRAME);
+ if (msg_head == NULL) {
+ printk(KERN_DEBUG "mac80211_hwsim: problem with msg_head\n");
+ goto out;
+ }
+
+ rc = nla_put(skb, HWSIM_ATTR_ADDR_TRANSMITTER,
+ sizeof(struct mac_address), src);
+
+ rc = nla_put_u32(skb, HWSIM_ATTR_MSG_LEN, my_skb->len);
+
+ rc = nla_put(skb, HWSIM_ATTR_MSG, my_skb->len, my_skb->data);
+ if (rc != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: "
+ "error filling msg payload\n");
+ goto out;
+ }
+
+ /* We get a copy of the control buffer for this tx*/
+ rc = nla_put(skb, HWSIM_ATTR_CB_SKB, sizeof(my_skb->cb),
+ my_skb->cb);
+
+ txi = IEEE80211_SKB_CB(my_skb);
+
+ /* We get the flags for this transmission, wmediumd maybe
+ changes its behaviour depending on the flags */
+ rc = nla_put_u32(skb, HWSIM_ATTR_FLAGS, txi->flags);
+ /* We get the tx control (rate and retries) info*/
+ rc = nla_put(skb, HWSIM_ATTR_TX_INFO,
+ sizeof(struct ieee80211_tx_rate)*IEEE80211_TX_MAX_RATES,
+ txi->control.rates);
+
+ genlmsg_end(skb, msg_head);
+ rc = genlmsg_unicast(&init_net, skb, _pid);
+ if (rc != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: wmediumd not responding "
+ "at PID:%d, switching to no wmediumd mode.\n", _pid);
+ atomic_set(&wmediumd_pid, 0);
+ }
+
+ return 0;
+
+out:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+}
+
static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
struct sk_buff *skb)
{
struct mac80211_hwsim_data *data = hw->priv, *data2;
- bool ack = false;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_rx_status rx_status;

+ bool ack = false;
+
if (data->idle) {
wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
return false;
}

+ if (data->ps != PS_DISABLED)
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+
+ /* wmediumd mode */
+ if (atomic_read(&wmediumd_pid)) {
+ hwsim_frame_send_nl((struct mac_address *)
+ &data->addresses[1].addr, skb,
+ atomic_read(&wmediumd_pid));
+ return true;
+ }
+
+ /* NO wmediumd, normal mac80211_hwsim behaviour*/
+
memset(&rx_status, 0, sizeof(rx_status));
/* TODO: set mactime */
rx_status.freq = data->channel->center_freq;
@@ -501,9 +578,6 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
/* TODO: simulate real signal strength (and optional packet loss) */
rx_status.signal = data->power_level - 50;

- if (data->ps != PS_DISABLED)
- hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
-
/* release the skb's source info */
skb_orphan(skb);
skb_dst_drop(skb);
@@ -530,9 +604,10 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
if (nskb == NULL)
continue;

+ memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
+
if (mac80211_hwsim_addr_match(data2, hdr->addr1))
ack = true;
- memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
ieee80211_rx_irqsafe(data2->hw, nskb);
}
spin_unlock(&hwsim_radio_lock);
@@ -540,12 +615,12 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
return ack;
}

-
static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
bool ack;
struct ieee80211_tx_info *txi;

+
mac80211_hwsim_monitor_rx(hw, skb);

if (skb->len < 10) {
@@ -554,6 +629,13 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
return;
}

+ /* wmediumd mode*/
+ if (atomic_read(&wmediumd_pid)) {
+ mac80211_hwsim_tx_frame(hw, skb);
+ return;
+ }
+
+ /* NO wmediumd, normal mac80211_hwsim behaviour*/
ack = mac80211_hwsim_tx_frame(hw, skb);
if (ack && skb->len >= 16) {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
@@ -1244,6 +1326,276 @@ static int hwsim_fops_group_write(void *dat, u64 val)
return 0;
}

+
+struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(
+ struct mac_address *addr)
+{
+ struct mac80211_hwsim_data *data;
+ bool _found = false;
+
+ spin_lock(&hwsim_radio_lock);
+ list_for_each_entry(data, &hwsim_radios, list) {
+ if (memcmp(data->addresses[1].addr, addr,
+ sizeof(struct mac_address)) == 0) {
+ _found = true;
+ break;
+ }
+ }
+ spin_unlock(&hwsim_radio_lock);
+
+ if (!_found) {
+ printk(KERN_DEBUG "mac80211_hwsim: invalid radio ID\n");
+ return NULL;
+ }
+
+ return data;
+
+
+}
+
+static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct ieee80211_hdr *hdr;
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_tx_info *txi;
+ struct ieee80211_tx_rate *tx_attempts;
+ char *cb;
+
+ int i;
+
+ struct mac_address *dst = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]);
+ int frame_data_len = nla_get_u32(info->attrs[HWSIM_ATTR_MSG_LEN]);
+ char *frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_MSG]);
+ int flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]);
+
+ /* Allocate new skb here */
+ struct sk_buff *skb = alloc_skb(IEEE80211_MAX_DATA_LEN, GFP_KERNEL);
+
+ if (frame_data_len <= IEEE80211_MAX_DATA_LEN) {
+ /* Copy the data */
+ memcpy(skb_put(skb, frame_data_len), frame_data,
+ frame_data_len);
+ } else
+ goto out;
+
+ data2 = get_hwsim_data_ref_from_addr(dst);
+
+ if (data2 == NULL)
+ goto out;
+
+ printk(KERN_DEBUG "mac80211_hwsim: TX_INFO received\n");
+
+ /*Tx info received because the frame was acked on user space,
+ so we get all the necessary info: tx attempts and skb control buffer*/
+
+ tx_attempts = (struct ieee80211_tx_rate *)nla_data(
+ info->attrs[HWSIM_ATTR_TX_INFO]);
+
+ /* ieee80211_tx_status() does not dereference anything from the
+ ieee80211_tx_info structure included in this cb, so it is safe
+ to get whatever we get from userspace and copy it here. */
+
+ cb = (char *)nla_data(info->attrs[HWSIM_ATTR_CB_SKB]);
+ memcpy(skb->cb, cb, sizeof(skb->cb));
+
+ /* now send back TX status */
+ txi = IEEE80211_SKB_CB(skb);
+
+ if (txi->control.vif)
+ hwsim_check_magic(txi->control.vif);
+ if (txi->control.sta)
+ hwsim_check_sta_magic(txi->control.sta);
+
+ ieee80211_tx_info_clear_status(txi);
+ txi->flags = flags;
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ txi->status.rates[i].idx = tx_attempts[i].idx;
+ txi->status.rates[i].count = tx_attempts[i].count;
+ txi->status.rates[i].flags = tx_attempts[i].flags;
+ }
+
+ /* unknown values that must be filled in:
+ txi->status.ampdu_ack_len = ¿?¿¿?¿?¿;
+ txi->status.ampdu_ack_map = ¿?¿?;
+ */
+
+ txi->status.ack_signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) &&
+ (txi->flags & IEEE80211_TX_STAT_ACK)) {
+ if (skb->len >= 16) {
+ hdr = (struct ieee80211_hdr *) skb->data;
+ mac80211_hwsim_monitor_ack(data2->hw, hdr->addr2);
+ }
+ }
+ ieee80211_tx_status_irqsafe(data2->hw, skb);
+ return 0;
+
+out:
+ kfree_skb(skb);
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+
+}
+
+static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_rx_status rx_status;
+
+ struct mac_address *dst = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_RECEIVER]);
+
+ int frame_data_len = nla_get_u32(info->attrs[HWSIM_ATTR_MSG_LEN]);
+ char* frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_MSG]);
+
+ /* Allocate new skb here */
+ struct sk_buff *skb = alloc_skb(IEEE80211_MAX_DATA_LEN, GFP_KERNEL);
+
+ if (frame_data_len <= IEEE80211_MAX_DATA_LEN) {
+ /* Copy the data */
+ memcpy(skb_put(skb, frame_data_len), frame_data,
+ frame_data_len);
+ } else
+ goto out;
+
+ data2 = get_hwsim_data_ref_from_addr(dst);
+
+ if (data2 == NULL)
+ goto out;
+
+ /*A frame is received from user space*/
+ printk(KERN_DEBUG "mac80211_hwsim: CLONED FRAME received\n");
+ memset(&rx_status, 0, sizeof(rx_status));
+ /* TODO: set mactime */
+ rx_status.freq = data2->channel->center_freq;
+ rx_status.band = data2->channel->band;
+ rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
+ rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
+ ieee80211_rx_irqsafe(data2->hw, skb);
+
+ return 0;
+out:
+ kfree_skb(skb);
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+}
+
+static int hwsim_register_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+ if (info == NULL)
+ goto out;
+
+ atomic_set(&wmediumd_pid, info->snd_pid);
+
+ printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, "
+ "switching to wmediumd mode with pid %d\n", info->snd_pid);
+
+ return 0;
+
+out:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+}
+
+/* Generic Netlink operations array */
+static struct genl_ops hwsim_ops[] = {
+ {
+ .cmd = HWSIM_CMD_REGISTER,
+ .flags = 0,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_register_received_nl,
+ .dumpit = NULL,
+ },
+ {
+ .cmd = HWSIM_CMD_FRAME,
+ .flags = 0,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_cloned_frame_received_nl,
+ .dumpit = NULL,
+ },
+ {
+ .cmd = HWSIM_CMD_TX_INFO_FRAME,
+ .flags = 0,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_tx_info_frame_received_nl,
+ .dumpit = NULL,
+ },
+};
+
+static int hwsim_init_netlink(void)
+{
+ int rc;
+ printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
+
+ atomic_set(&wmediumd_pid, 0);
+
+ rc = genl_register_family(&hwsim_genl_family);
+ if (rc != 0)
+ goto failure;
+ rc = genl_register_ops(&hwsim_genl_family, &hwsim_ops[0]);
+ if (rc != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: register ops: %i\n", rc);
+ genl_unregister_family(&hwsim_genl_family);
+ goto failure;
+ }
+ rc = genl_register_ops(&hwsim_genl_family, &hwsim_ops[1]);
+ if (rc != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: register ops: %i\n", rc);
+ genl_unregister_family(&hwsim_genl_family);
+ goto failure;
+ }
+ rc = genl_register_ops(&hwsim_genl_family, &hwsim_ops[2]);
+ if (rc != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: register ops: %i\n", rc);
+ genl_unregister_family(&hwsim_genl_family);
+ goto failure;
+ }
+ return 0;
+
+failure:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+}
+
+static void hwsim_exit_netlink(void)
+{
+ int ret;
+
+ printk(KERN_INFO "mac80211_hwsim: closing netlink\n");
+ /*unregister the functions*/
+
+ ret = genl_unregister_ops(&hwsim_genl_family, &hwsim_ops[0]);
+ if (ret != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: unregister ops: %i\n", ret);
+ return;
+ }
+ ret = genl_unregister_ops(&hwsim_genl_family, &hwsim_ops[1]);
+ if (ret != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: unregister ops: %i\n", ret);
+ return;
+ }
+ ret = genl_unregister_ops(&hwsim_genl_family, &hwsim_ops[2]);
+ if (ret != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: unregister ops: %i\n", ret);
+ return;
+ }
+ ret = genl_unregister_family(&hwsim_genl_family);
+ if (ret != 0)
+ printk(KERN_DEBUG "mac80211_hwsim: "
+ "unregister family %i\n", ret);
+}
+
+
DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group,
hwsim_fops_group_read, hwsim_fops_group_write,
"%llx\n");
@@ -1379,6 +1731,10 @@ static int __init init_mac80211_hwsim(void)
data->group = 1;
mutex_init(&data->mutex);

+ /* Enable frame retransmission mechanism for a lossy channel */
+ hw->max_rates = 4;
+ hw->max_rate_tries = 11;
+
/* Work to be done prior to ieee80211_register_hw() */
switch (regtest) {
case HWSIM_REGTEST_DISABLED:
@@ -1528,8 +1884,16 @@ static int __init init_mac80211_hwsim(void)

rtnl_unlock();

+ err = hwsim_init_netlink();
+ if (err < 0)
+ goto failed_nl;
+
return 0;

+failed_nl:
+ printk(KERN_DEBUG "mac80211_hwsim: failed initializing netlink\n");
+ return err;
+
failed_mon:
rtnl_unlock();
free_netdev(hwsim_mon);
@@ -1550,6 +1914,8 @@ static void __exit exit_mac80211_hwsim(void)
{
printk(KERN_DEBUG "mac80211_hwsim: unregister radios\n");

+ hwsim_exit_netlink();
+
mac80211_hwsim_free();
unregister_netdev(hwsim_mon);
}
diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h
new file mode 100644
index 0000000..be37f44
--- /dev/null
+++ b/drivers/net/wireless/mac80211_hwsim.h
@@ -0,0 +1,123 @@
+/*
+ * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
+ * Copyright (c) 2008, Jouni Malinen <[email protected]>
+ * Copyright (c) 2011, Javier Lopez <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <net/mac80211.h>
+
+/**
+ * DOC: Frame transmission/registration support
+ *
+ * Frame transmission and registration support exists to allow userspace
+ * entities such as wmediumd to receive and process all broadcasted
+ * frames from a mac80211_hwsim radio device.
+ *
+ * This allow user space applications to decide if the frame should be
+ * dropped or not and implement a wireless medium simulator at user space.
+ *
+ * Registration is done by sending a register message to the driver and
+ * will be automatically unregistered if the user application doesn't
+ * responds to sent frames.
+ * Once registered the user application has to take responsibility of
+ * broadcasting the frames to all listening mac80211_hwsim radio
+ * interfaces.
+ *
+ * For more technical details, see the corresponding command descriptions
+ * below.
+ */
+
+/**
+ * enum hwsim_commands - supported hwsim commands
+ *
+ * @HWSIM_CMD_UNSPEC: unspecified command to catch errors
+ *
+ * @HWSIM_CMD_REGISTER: request to register and received all broadcasted
+ * frames by any mac80211_hwsim radio device.
+ * @HWSIM_CMD_FRAME: send/receive a broadcasted frame from/to kernel/user
+ * space, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER,
+ * %HWSIM_ATTR_MSG, %HWSIM_ATTR_MSG_LEN, %HWSIM_ATTR_FLAGS,
+ * %HWSIM_ATTR_RX_RATE, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_CB_SKB
+ * @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to
+ * kernel, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_MSG, %HWSIM_ATTR_MSG_LEN,
+ * %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL
+ * @__HWSIM_CMD_MAX: enum limit
+ */
+enum {
+ HWSIM_CMD_UNSPEC,
+ HWSIM_CMD_REGISTER,
+ HWSIM_CMD_FRAME,
+ HWSIM_CMD_TX_INFO_FRAME,
+ __HWSIM_CMD_MAX,
+};
+#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)
+
+/**
+ * enum hwsim_attrs - hwsim netlink attributes
+ *
+ * @HWSIM_ATTR_UNSPEC: unspecified attribute to catch errors
+ *
+ * @HWSIM_ATTR_ADDR_RECEIVER: MAC address of the radio device that
+ * the frame is broadcasted to
+ * @HWSIM_ATTR_ADDR_TRANSMITTER: MAC address of the radio device that
+ * the frame was broadcasted from
+ * @HWSIM_ATTR_MSG_LEN: Length of the broadcasted frame
+ * @HWSIM_ATTR_MSG: Data array
+ * @HWSIM_ATTR_FLAGS: mac80211 transmission flags, used to process
+ properly the frame at user space
+ * @HWSIM_ATTR_RX_RATE: estimated rx rate index for this frame at user
+ space
+ * @HWSIM_ATTR_SIGNAL: estimated RX signal for this frame at user
+ space
+ * @HWSIM_ATTR_TX_INFO: ieee80211_tx_rate array
+ * @HWSIM_ATTR_CB_SKB: sk_buff custom buffer of the broadcasted frame
+ * @__HWSIM_ATTR_MAX: enum limit
+ */
+
+
+enum {
+ HWSIM_ATTR_UNSPEC,
+ HWSIM_ATTR_ADDR_RECEIVER,
+ HWSIM_ATTR_ADDR_TRANSMITTER,
+ HWSIM_ATTR_MSG_LEN,
+ HWSIM_ATTR_MSG,
+ HWSIM_ATTR_FLAGS,
+ HWSIM_ATTR_RX_RATE,
+ HWSIM_ATTR_SIGNAL,
+ HWSIM_ATTR_TX_INFO,
+ HWSIM_ATTR_CB_SKB,
+ __HWSIM_ATTR_MAX,
+};
+#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
+
+static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
+ [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_MSG_LEN] = { .type = NLA_U32 },
+ [HWSIM_ATTR_MSG] = { .type = NLA_STRING },
+ [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 },
+ [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32},
+ [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 },
+ [HWSIM_ATTR_TX_INFO] = { .type = NLA_UNSPEC,
+ .len = IEEE80211_TX_MAX_RATES*sizeof(
+ struct ieee80211_tx_rate)},
+ [HWSIM_ATTR_CB_SKB] = { .type = NLA_UNSPEC, .len = 48*sizeof(char) },
+};
+
+#define VERSION_NR 1
+
+static struct genl_family hwsim_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = "HWSIM",
+ .version = VERSION_NR,
+ .maxattr = HWSIM_ATTR_MAX,
+};
--
1.7.0.4



2011-04-28 18:45:49

by John W. Linville

[permalink] [raw]
Subject: Re: [PATCH v2] mac80211_hwsim driver support userspace frame tx/rx

On Fri, Apr 22, 2011 at 09:31:43PM +0200, Javier Lopez wrote:
> This is an updated patch that solve the issues found in the previous
> patch, it adds frame exchange capabilities with userspace to
> mac80211_hwsim driver. Allowing user space applications to decide if a
> frame should be dropped or not.
>
> Frame exchange between kernel and userspace is done through a generic
> netlink communication protocol, a new family called HWSIM is proposed
> in order to communicate with userspace applications. This family

> +static struct genl_family hwsim_genl_family = {
> + .id = GENL_ID_GENERATE,
> + .hdrsize = 0,
> + .name = "HWSIM",
> + .version = VERSION_NR,
> + .maxattr = HWSIM_ATTR_MAX,
> +};

I don't have any big objections to this patch. But I think that
"HWSIM" is not a very good name. Most/all of the other genl_family
definitions seem to use a name specific to their function. While
"HWSIM" is somewhat descriptive, it seems prone to confusion. I would
prefer to see a name like "mac80211_hwsim".

John

P.S. Sorry to "bikeshed" this...
--
John W. Linville Someday the world will need a hero, and you
[email protected] might be all we have. Be ready.

2011-04-19 02:19:26

by Javier Cardona

[permalink] [raw]
Subject: Re: [PATCH] mac80211_hwsim driver support userspace frame tx/rx

On Mon, Apr 18, 2011 at 1:45 PM, Javier Lopez <[email protected]> wrote:
> This patch adds the capability to hwsim to send traffic via userspace.
>
> Frame exchange between kernel and userspace is done through a generic
> netlink communication protocol, a new family called HWSIM is proposed
> in order to communicate with userspace applications. This family
> contains three basic commands; HWSIM_CMD_REGISTER which is used to
> register a userspace application with this driver, HWSIM_CMD_FRAME to
> exchange frames from kernel to user and vice-versa, and finally
> HWSIM_CMD_TX_INFO_FRAME which returns to the kernel all the information
> about a frame transmission over the medium, number of tries, rates,
> ack signal and so on.
>
> How it works:
>
> Once the driver is loaded, it registers the HWSIM generic netlink
> family. In the absence of a daemon the driver implements a perfect
> wireless medium in the kernel as it did in the past. Once a daemon a
> registers itself via HWSIM_CMD_REGISTER, the kernel module stores the
> daemon PID and from this moment all frames will be sent to the daemon.
>
> The userspace application will be in charge of process/forward all
> frames broadcast by any radio using the mac80211_hwsim driver. If the
> user application is stopped the module will switch back to in-kernel
> perfect channel simulation. This transmission of kernel to user space
> is done through the HWSIM_CMD_FRAME command.
>
> The userspace application once sends the HWSIM_CMD_REGISTER, waits
> for incoming messages from kernel, for each HWSIM_CMD_FRAME command
> received, the application will try to broadcast it, cloning the frame
> to all known mac80211_hwsim radios. The daemon may decide to send or
> drop this frame. In the case of sending a frame back to kernel, the
> daemon will create a new HWSIM_CMD_FRAME command, fill the necessary
> attributes and send it back. In the case of a frame drop, the example
> userspace daemon retransmits frames according to the ieee80211_tx_info
> values received from the kernel.
>
> After each frame complete transmission a HWSIM_CMD_TX_INFO command
> may be sent to the kernel to inform about the tx status, this command
> contains information such as number of tries, rates, ack'ed or not
> ack'ed, ack_signal, etc.
>
> If you want to test the actual implementation of the wireless medium
> daemon (wmediumd) and test this patch, just get it from GitHub:
>
> - Last version tarball: https://github.com/jlopex/cozybit/tarball/master
> - Or visiting my github tree: https://github.com/jlopex/cozybit/tree
>
> Best regards,
>
> Signed-off-by: Javier Lopez <[email protected]>
> Signed-off-by: Javier Cardona <[email protected]>

My apologies Javier, but after my first review of your patch I noticed
other issues. See below.

> ---
> drivers/net/wireless/mac80211_hwsim.c | 378 ++++++++++++++++++++++++++++++++-
> drivers/net/wireless/mac80211_hwsim.h | 123 +++++++++++
> 2 files changed, 495 insertions(+), 6 deletions(-)
> create mode 100644 drivers/net/wireless/mac80211_hwsim.h
>
> diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
> index f4f4257..0be0ad7 100644
> --- a/drivers/net/wireless/mac80211_hwsim.c
> +++ b/drivers/net/wireless/mac80211_hwsim.c
> @@ -1,6 +1,7 @@
> /*
> * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
> * Copyright (c) 2008, Jouni Malinen <[email protected]>
> + * Copyright (c) 2011, Javier Lopez <[email protected]>
> *
> * This program is free software; you can redistribute it and/or modify
> * it under the terms of the GNU General Public License version 2 as
> @@ -25,11 +26,15 @@
> #include <linux/rtnetlink.h>
> #include <linux/etherdevice.h>
> #include <linux/debugfs.h>
> +#include <net/genetlink.h>
> +#include "mac80211_hwsim.h"
>
> MODULE_AUTHOR("Jouni Malinen");
> MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211");
> MODULE_LICENSE("GPL");
>
> +static atomic_t wmediumd_pid;
> +
> static int radios = 2;
> module_param(radios, int, 0444);
> MODULE_PARM_DESC(radios, "Number of simulated radios");
> @@ -479,20 +484,92 @@ static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
> }
>
>
> +static int hwsim_frame_send_nl(struct mac_address *src,
> + struct sk_buff *my_skb, int _pid)
> +{
> +
> + struct ieee80211_tx_info *txi;
> + struct sk_buff *skb;
> + void *msg_head;
> + int rc;
> +
> + skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
> + msg_head = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
> + HWSIM_CMD_FRAME);
> + if (msg_head == NULL) {
> + printk(KERN_DEBUG "mac80211_hwsim: problem with msg_head\n");
> + goto out;
> + }
> +
> + rc = nla_put(skb, HWSIM_ATTR_ADDR_TRANSMITTER,
> + sizeof(struct mac_address), src);
> +
> + rc = nla_put_u32(skb, HWSIM_ATTR_MSG_LEN, my_skb->len);

Use nla_len instead

> +
> + rc = nla_put(skb, HWSIM_ATTR_MSG, my_skb->len, my_skb->data);
> + if (rc != 0) {
> + printk(KERN_DEBUG "mac80211_hwsim: "
> + "error filling msg payload\n");
> + goto out;
> + }
> +
> + /* We get a copy of the control buffer for this tx*/
> + rc = nla_put(skb, HWSIM_ATTR_CB_SKB, sizeof(my_skb->cb),
> + my_skb->cb);
> +
> + txi = IEEE80211_SKB_CB(my_skb);
> +
> + /* We get the flags for this transmission, wmediumd maybe
> + changes its behaviour depending on the flags */
> + rc = nla_put_u32(skb, HWSIM_ATTR_FLAGS, txi->flags);
> + /* We get the tx control (rate and retries) info*/
> + rc = nla_put(skb, HWSIM_ATTR_TX_INFO,
> + sizeof(struct ieee80211_tx_rate)*IEEE80211_TX_MAX_RATES,
> + txi->control.rates);

Aren't you sending these already inside the control buffer? If so, why send
them twice?

> +
> + genlmsg_end(skb, msg_head);
> + rc = genlmsg_unicast(&init_net, skb, _pid);
> + if (rc != 0) {
> + printk(KERN_DEBUG "mac80211_hwsim: wmediumd not responding "
> + "at PID:%d, switching to no wmediumd mode.\n", _pid);
> + atomic_set(&wmediumd_pid, 0);
> + }
> +
> + return 0;
> +
> +out:
> + printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
> + return -1;
> +}
> +
> static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
> struct sk_buff *skb)
> {
> struct mac80211_hwsim_data *data = hw->priv, *data2;
> - bool ack = false;
> struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
> struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> struct ieee80211_rx_status rx_status;
>
> + bool ack = false;
> +
> if (data->idle) {
> wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
> return false;
> }
>
> + if (data->ps != PS_DISABLED)
> + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
> +
> + /* wmediumd mode */
> + if (atomic_read(&wmediumd_pid)) {
> + hwsim_frame_send_nl((struct mac_address *)
> + &data->addresses[1].addr, skb,
> + atomic_read(&wmediumd_pid));
> + return true;
> + }
> +
> + /* NO wmediumd, normal mac80211_hwsim behaviour*/
> +
> memset(&rx_status, 0, sizeof(rx_status));
> /* TODO: set mactime */
> rx_status.freq = data->channel->center_freq;
> @@ -501,9 +578,6 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
> /* TODO: simulate real signal strength (and optional packet loss) */
> rx_status.signal = data->power_level - 50;
>
> - if (data->ps != PS_DISABLED)
> - hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
> -
> /* release the skb's source info */
> skb_orphan(skb);
> skb_dst_drop(skb);
> @@ -530,9 +604,10 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
> if (nskb == NULL)
> continue;
>
> + memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
> +
> if (mac80211_hwsim_addr_match(data2, hdr->addr1))
> ack = true;
> - memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
> ieee80211_rx_irqsafe(data2->hw, nskb);
> }
> spin_unlock(&hwsim_radio_lock);
> @@ -540,12 +615,12 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
> return ack;
> }
>
> -
> static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
> {
> bool ack;
> struct ieee80211_tx_info *txi;
>
> +
> mac80211_hwsim_monitor_rx(hw, skb);
>
> if (skb->len < 10) {
> @@ -554,6 +629,13 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
> return;
> }
>
> + /* wmediumd mode*/
> + if (atomic_read(&wmediumd_pid)) {
> + mac80211_hwsim_tx_frame(hw, skb);
> + return;
> + }
> +
> + /* NO wmediumd, normal mac80211_hwsim behaviour*/
> ack = mac80211_hwsim_tx_frame(hw, skb);
> if (ack && skb->len >= 16) {
> struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
> @@ -1244,6 +1326,276 @@ static int hwsim_fops_group_write(void *dat, u64 val)
> return 0;
> }
>
> +
> +struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(
> + struct mac_address *addr)
> +{
> + struct mac80211_hwsim_data *data;
> + bool _found = false;
> +
> + spin_lock(&hwsim_radio_lock);
> + list_for_each_entry(data, &hwsim_radios, list) {
> + if (memcmp(data->addresses[1].addr, addr,
> + sizeof(struct mac_address)) == 0) {
> + _found = true;
> + break;
> + }
> + }
> + spin_unlock(&hwsim_radio_lock);
> +
> + if (!_found) {
> + printk(KERN_DEBUG "mac80211_hwsim: invalid radio ID\n");
> + return NULL;
> + }
> +
> + return data;
> +
> +
> +}
> +
> +static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
> + struct genl_info *info)
> +{
> +
> + struct ieee80211_hdr *hdr;
> + struct mac80211_hwsim_data *data2;
> + struct ieee80211_tx_info *txi;
> + struct ieee80211_tx_rate *tx_attempts;
> + char *cb;
> +
> + int i;
> +
> + struct mac_address *dst = (struct mac_address *)nla_data(
> + info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]);
> + int frame_data_len = nla_get_u32(info->attrs[HWSIM_ATTR_MSG_LEN]);
> + char *frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_MSG]);
> + int flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]);
> +
> + /* Allocate new skb here */
> + struct sk_buff *skb = alloc_skb(IEEE80211_MAX_DATA_LEN, GFP_KERNEL);

Again, skb may be null here.

> +
> + if (frame_data_len <= IEEE80211_MAX_DATA_LEN) {
> + /* Copy the data */
> + memcpy(skb_put(skb, frame_data_len), frame_data,
> + frame_data_len);
> + } else
> + goto out;
> +
> + data2 = get_hwsim_data_ref_from_addr(dst);
> +
> + if (data2 == NULL)
> + goto out;
> +
> + printk(KERN_DEBUG "mac80211_hwsim: TX_INFO received\n");
> +
> + /*Tx info received because the frame was acked on user space,
> + so we get all the necessary info: tx attempts and skb control buffer*/
> +
> + tx_attempts = (struct ieee80211_tx_rate *)nla_data(
> + info->attrs[HWSIM_ATTR_TX_INFO]);
> +
> + /* ieee80211_tx_status() does not dereference anything from the
> + ieee80211_tx_info structure included in this cb, so it is safe
> + to get whatever we get from userspace and copy it here. */
> +
> + cb = (char *)nla_data(info->attrs[HWSIM_ATTR_CB_SKB]);

You should probably check that nla_len of CB == sizeof(skb->cb)

> + memcpy(skb->cb, cb, sizeof(skb->cb));
> +
> + /* now send back TX status */
> + txi = IEEE80211_SKB_CB(skb);
> +
> + if (txi->control.vif)
> + hwsim_check_magic(txi->control.vif);
> + if (txi->control.sta)
> + hwsim_check_sta_magic(txi->control.sta);
> +
> + ieee80211_tx_info_clear_status(txi);
> + txi->flags = flags;
> +
> + for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
> + txi->status.rates[i].idx = tx_attempts[i].idx;
> + txi->status.rates[i].count = tx_attempts[i].count;
> + txi->status.rates[i].flags = tx_attempts[i].flags;
> + }
> +
> + /* unknown values that must be filled in:
> + txi->status.ampdu_ack_len = 驴?驴驴?驴?驴;
> + txi->status.ampdu_ack_map = 驴?驴?;
> + */

These show as the Chinese character for 'donkey' on my display, probably not
what you intended. Anyway, why adding that commented out code?

> +
> + txi->status.ack_signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
> +
> + if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) &&
> + (txi->flags & IEEE80211_TX_STAT_ACK)) {
> + if (skb->len >= 16) {
> + hdr = (struct ieee80211_hdr *) skb->data;
> + mac80211_hwsim_monitor_ack(data2->hw, hdr->addr2);
> + }
> + }
> + ieee80211_tx_status_irqsafe(data2->hw, skb);
> + return 0;
> +
> +out:
> + kfree_skb(skb);
> + printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
> + return -1;
> +
> +}
> +
> +static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
> + struct genl_info *info)
> +{
> +
> + struct mac80211_hwsim_data *data2;
> + struct ieee80211_rx_status rx_status;
> +
> + struct mac_address *dst = (struct mac_address *)nla_data(
> + info->attrs[HWSIM_ATTR_ADDR_RECEIVER]);
> +
> + int frame_data_len = nla_get_u32(info->attrs[HWSIM_ATTR_MSG_LEN]);

> + char* frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_MSG]);
> +
> + /* Allocate new skb here */
> + struct sk_buff *skb = alloc_skb(IEEE80211_MAX_DATA_LEN, GFP_KERNEL);

Should handle no-memory situations here.

> +
> + if (frame_data_len <= IEEE80211_MAX_DATA_LEN) {
> + /* Copy the data */
> + memcpy(skb_put(skb, frame_data_len), frame_data,
> + frame_data_len);
> + } else
> + goto out;
> +
> + data2 = get_hwsim_data_ref_from_addr(dst);
> +
> + if (data2 == NULL)
> + goto out;
> +
> + /*A frame is received from user space*/
> + printk(KERN_DEBUG "mac80211_hwsim: CLONED FRAME received\n");

This and all other printks in the normal execution path should
probably be removed.

> + memset(&rx_status, 0, sizeof(rx_status));
> + /* TODO: set mactime */
> + rx_status.freq = data2->channel->center_freq;
> + rx_status.band = data2->channel->band;
> + rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
> + rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
> +
> + memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
> + ieee80211_rx_irqsafe(data2->hw, skb);
> +
> + return 0;
> +out:
> + kfree_skb(skb);
> + printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
> + return -1;
> +}
> +
> +static int hwsim_register_received_nl(struct sk_buff *skb_2,
> + struct genl_info *info)
> +{
> + if (info == NULL)
> + goto out;
> +
> + atomic_set(&wmediumd_pid, info->snd_pid);
> +
> + printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, "
> + "switching to wmediumd mode with pid %d\n", info->snd_pid);
> +
> + return 0;
> +
> +out:
> + printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
> + return -1;
> +}
> +
> +/* Generic Netlink operations array */
> +static struct genl_ops hwsim_ops[] = {
> + {
> + .cmd = HWSIM_CMD_REGISTER,
> + .flags = 0,
> + .policy = hwsim_genl_policy,
> + .doit = hwsim_register_received_nl,
> + .dumpit = NULL,
> + },
> + {
> + .cmd = HWSIM_CMD_FRAME,
> + .flags = 0,
> + .policy = hwsim_genl_policy,
> + .doit = hwsim_cloned_frame_received_nl,
> + .dumpit = NULL,
> + },
> + {
> + .cmd = HWSIM_CMD_TX_INFO_FRAME,
> + .flags = 0,
> + .policy = hwsim_genl_policy,
> + .doit = hwsim_tx_info_frame_received_nl,
> + .dumpit = NULL,
> + },
> +};
> +
> +static int hwsim_init_netlink(void)
> +{
> + int rc;
> + printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
> +
> + atomic_set(&wmediumd_pid, 0);
> +
> + rc = genl_register_family(&hwsim_genl_family);
> + if (rc != 0)
> + goto failure;
> + rc = genl_register_ops(&hwsim_genl_family, &hwsim_ops[0]);
> + if (rc != 0) {
> + printk(KERN_DEBUG "mac80211_hwsim: register ops: %i\n", rc);
> + genl_unregister_family(&hwsim_genl_family);
> + goto failure;
> + }
> + rc = genl_register_ops(&hwsim_genl_family, &hwsim_ops[1]);
> + if (rc != 0) {
> + printk(KERN_DEBUG "mac80211_hwsim: register ops: %i\n", rc);
> + genl_unregister_family(&hwsim_genl_family);
> + goto failure;
> + }
> + rc = genl_register_ops(&hwsim_genl_family, &hwsim_ops[2]);
> + if (rc != 0) {
> + printk(KERN_DEBUG "mac80211_hwsim: register ops: %i\n", rc);
> + genl_unregister_family(&hwsim_genl_family);
> + goto failure;
> + }
> + return 0;
> +
> +failure:
> + printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
> + return -1;
> +}
> +
> +static void hwsim_exit_netlink(void)
> +{
> + int ret;
> +
> + printk(KERN_INFO "mac80211_hwsim: closing netlink\n");
> + /*unregister the functions*/
> +
> + ret = genl_unregister_ops(&hwsim_genl_family, &hwsim_ops[0]);
> + if (ret != 0) {
> + printk(KERN_DEBUG "mac80211_hwsim: unregister ops: %i\n", ret);
> + return;
> + }
> + ret = genl_unregister_ops(&hwsim_genl_family, &hwsim_ops[1]);
> + if (ret != 0) {
> + printk(KERN_DEBUG "mac80211_hwsim: unregister ops: %i\n", ret);
> + return;
> + }
> + ret = genl_unregister_ops(&hwsim_genl_family, &hwsim_ops[2]);
> + if (ret != 0) {
> + printk(KERN_DEBUG "mac80211_hwsim: unregister ops: %i\n", ret);
> + return;
> + }
> + ret = genl_unregister_family(&hwsim_genl_family);
> + if (ret != 0)
> + printk(KERN_DEBUG "mac80211_hwsim: "
> + "unregister family %i\n", ret);
> +}
> +
> +
> DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group,
> hwsim_fops_group_read, hwsim_fops_group_write,
> "%llx\n");
> @@ -1379,6 +1731,10 @@ static int __init init_mac80211_hwsim(void)
> data->group = 1;
> mutex_init(&data->mutex);
>
> + /* Enable frame retransmission mechanism for a lossy channel */
> + hw->max_rates = 4;
> + hw->max_rate_tries = 11;
> +
> /* Work to be done prior to ieee80211_register_hw() */
> switch (regtest) {
> case HWSIM_REGTEST_DISABLED:
> @@ -1528,8 +1884,16 @@ static int __init init_mac80211_hwsim(void)
>
> rtnl_unlock();
>
> + err = hwsim_init_netlink();
> + if (err < 0)
> + goto failed_nl;
> +
> return 0;
>
> +failed_nl:
> + printk(KERN_DEBUG "mac80211_hwsim: failed initializing netlink\n");
> + return err;
> +
> failed_mon:
> rtnl_unlock();
> free_netdev(hwsim_mon);
> @@ -1550,6 +1914,8 @@ static void __exit exit_mac80211_hwsim(void)
> {
> printk(KERN_DEBUG "mac80211_hwsim: unregister radios\n");
>
> + hwsim_exit_netlink();
> +
> mac80211_hwsim_free();
> unregister_netdev(hwsim_mon);
> }
> diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h
> new file mode 100644
> index 0000000..be37f44
> --- /dev/null
> +++ b/drivers/net/wireless/mac80211_hwsim.h
> @@ -0,0 +1,123 @@
> +/*
> + * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
> + * Copyright (c) 2008, Jouni Malinen <[email protected]>
> + * Copyright (c) 2011, Javier Lopez <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <net/mac80211.h>
> +
> +/**
> + * DOC: Frame transmission/registration support
> + *
> + * Frame transmission and registration support exists to allow userspace
> + * entities such as wmediumd to receive and process all broadcasted
> + * frames from a mac80211_hwsim radio device.
> + *
> + * This allow user space applications to decide if the frame should be
> + * dropped or not and implement a wireless medium simulator at user space.
> + *
> + * Registration is done by sending a register message to the driver and
> + * will be automatically unregistered if the user application doesn't
> + * responds to sent frames.
> + * Once registered the user application has to take responsibility of
> + * broadcasting the frames to all listening mac80211_hwsim radio
> + * interfaces.
> + *
> + * For more technical details, see the corresponding command descriptions
> + * below.
> + */
> +
> +/**
> + * enum hwsim_commands - supported hwsim commands
> + *
> + * @HWSIM_CMD_UNSPEC: unspecified command to catch errors
> + *
> + * @HWSIM_CMD_REGISTER: request to register and received all broadcasted
> + * frames by any mac80211_hwsim radio device.
> + * @HWSIM_CMD_FRAME: send/receive a broadcasted frame from/to kernel/user
> + * space, uses:
> + * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER,
> + * %HWSIM_ATTR_MSG, %HWSIM_ATTR_MSG_LEN, %HWSIM_ATTR_FLAGS,
> + * %HWSIM_ATTR_RX_RATE, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_CB_SKB
> + * @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to
> + * kernel, uses:
> + * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_MSG, %HWSIM_ATTR_MSG_LEN,
> + * %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL
> + * @__HWSIM_CMD_MAX: enum limit
> + */
> +enum {
> + HWSIM_CMD_UNSPEC,
> + HWSIM_CMD_REGISTER,
> + HWSIM_CMD_FRAME,
> + HWSIM_CMD_TX_INFO_FRAME,
> + __HWSIM_CMD_MAX,
> +};
> +#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)
> +
> +/**
> + * enum hwsim_attrs - hwsim netlink attributes
> + *
> + * @HWSIM_ATTR_UNSPEC: unspecified attribute to catch errors
> + *
> + * @HWSIM_ATTR_ADDR_RECEIVER: MAC address of the radio device that
> + * the frame is broadcasted to
> + * @HWSIM_ATTR_ADDR_TRANSMITTER: MAC address of the radio device that
> + * the frame was broadcasted from
> + * @HWSIM_ATTR_MSG_LEN: Length of the broadcasted frame
> + * @HWSIM_ATTR_MSG: Data array
> + * @HWSIM_ATTR_FLAGS: mac80211 transmission flags, used to process
> + properly the frame at user space
> + * @HWSIM_ATTR_RX_RATE: estimated rx rate index for this frame at user
> + space
> + * @HWSIM_ATTR_SIGNAL: estimated RX signal for this frame at user
> + space
> + * @HWSIM_ATTR_TX_INFO: ieee80211_tx_rate array
> + * @HWSIM_ATTR_CB_SKB: sk_buff custom buffer of the broadcasted frame
> + * @__HWSIM_ATTR_MAX: enum limit
> + */
> +
> +
> +enum {
> + HWSIM_ATTR_UNSPEC,
> + HWSIM_ATTR_ADDR_RECEIVER,
> + HWSIM_ATTR_ADDR_TRANSMITTER,
> + HWSIM_ATTR_MSG_LEN,
> + HWSIM_ATTR_MSG,
> + HWSIM_ATTR_FLAGS,
> + HWSIM_ATTR_RX_RATE,
> + HWSIM_ATTR_SIGNAL,
> + HWSIM_ATTR_TX_INFO,
> + HWSIM_ATTR_CB_SKB,
> + __HWSIM_ATTR_MAX,
> +};
> +#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
> +
> +static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
> + [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC,
> + .len = 6*sizeof(u8) },
> + [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC,
> + .len = 6*sizeof(u8) },
> + [HWSIM_ATTR_MSG_LEN] = { .type = NLA_U32 },

You don't need this, use nla_len instead. See nl80211.c for examples.

> + [HWSIM_ATTR_MSG] = { .type = NLA_STRING },

To me, FRAME would be more intuitive than MSG.

> + [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 },
> + [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32},
> + [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 },
> + [HWSIM_ATTR_TX_INFO] = { .type = NLA_UNSPEC,
> + .len = IEEE80211_TX_MAX_RATES*sizeof(
> + struct ieee80211_tx_rate)},
> + [HWSIM_ATTR_CB_SKB] = { .type = NLA_UNSPEC, .len = 48*sizeof(char) },
> +};
> +
> +#define VERSION_NR 1
> +
> +static struct genl_family hwsim_genl_family = {
> + .id = GENL_ID_GENERATE,
> + .hdrsize = 0,
> + .name = "HWSIM",
> + .version = VERSION_NR,
> + .maxattr = HWSIM_ATTR_MAX,
> +};
> --
> 1.7.0.4
>
>



--
Javier Cardona
cozybit Inc.
http://www.cozybit.com

2011-04-22 19:31:58

by Javier López

[permalink] [raw]
Subject: [PATCH v2] mac80211_hwsim driver support userspace frame tx/rx

This is an updated patch that solve the issues found in the previous
patch, it adds frame exchange capabilities with userspace to
mac80211_hwsim driver. Allowing user space applications to decide if a
frame should be dropped or not.

Frame exchange between kernel and userspace is done through a generic
netlink communication protocol, a new family called HWSIM is proposed
in order to communicate with userspace applications. This family
contains three basic commands; HWSIM_CMD_REGISTER which is used to
register a userspace application with this driver, HWSIM_CMD_FRAME to
exchange frames from kernel to user and vice-versa, and finally
HWSIM_CMD_TX_INFO_FRAME which returns to the kernel all the information
about a frame transmission over the medium, number of tries, rates,
ack signal and so on.

How it works:

Once the driver is loaded, it registers the HWSIM generic netlink
family. In the absence of a daemon the driver implements a perfect
wireless medium in the kernel as it did in the past. Once a daemon a
registers itself via HWSIM_CMD_REGISTER, the kernel module stores the
daemon PID and from this moment all frames will be sent to the daemon.

The userspace application will be in charge of process/forward all
frames broadcast by any radio using the mac80211_hwsim driver. If the
user application is stopped the module will switch back to in-kernel
perfect channel simulation. This transmission of kernel to user space
is done through the HWSIM_CMD_FRAME command.

The userspace application once sends the HWSIM_CMD_REGISTER, waits
for incoming messages from kernel, for each HWSIM_CMD_FRAME command
received, the application will try to broadcast it, cloning the frame
to all known mac80211_hwsim radios. The daemon may decide to send or
drop this frame. In the case of sending a frame back to kernel, the
daemon will create a new HWSIM_CMD_FRAME command, fill the necessary
attributes and send it back. In the case of a frame drop, the example
userspace daemon retransmits frames according to the ieee80211_tx_info
values received from the kernel.

After each frame complete transmission a HWSIM_CMD_TX_INFO command
may be sent to the kernel to inform about the tx status, this command
contains information such as number of tries, rates, ack'ed or not
ack'ed, ack_signal, etc.

If you want to test the actual implementation of the wireless medium
daemon (wmediumd) and test this patch, just get it from GitHub:

- Last version tarball: https://github.com/jlopex/cozybit/tarball/master
- Or visiting my github tree: https://github.com/jlopex/cozybit/tree

Signed-off-by: Javier Lopez <[email protected]>
---
drivers/net/wireless/mac80211_hwsim.c | 388 ++++++++++++++++++++++++++++++++-
drivers/net/wireless/mac80211_hwsim.h | 121 ++++++++++
2 files changed, 503 insertions(+), 6 deletions(-)
create mode 100644 drivers/net/wireless/mac80211_hwsim.h

diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index f4f4257..abb9444 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -1,6 +1,7 @@
/*
* mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
* Copyright (c) 2008, Jouni Malinen <[email protected]>
+ * Copyright (c) 2011, Javier Lopez <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -25,11 +26,15 @@
#include <linux/rtnetlink.h>
#include <linux/etherdevice.h>
#include <linux/debugfs.h>
+#include <net/genetlink.h>
+#include "mac80211_hwsim.h"

MODULE_AUTHOR("Jouni Malinen");
MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211");
MODULE_LICENSE("GPL");

+static atomic_t wmediumd_pid;
+
static int radios = 2;
module_param(radios, int, 0444);
MODULE_PARM_DESC(radios, "Number of simulated radios");
@@ -479,20 +484,95 @@ static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
}


+static int hwsim_frame_send_nl(struct mac_address *src,
+ struct sk_buff *my_skb, int _pid)
+{
+
+ struct ieee80211_tx_info *txi;
+ struct sk_buff *skb;
+ void *msg_head;
+ int rc;
+
+ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (skb == NULL) {
+ printk(KERN_DEBUG "mac80211_hwsim: problem allocating skb\n");
+ goto out;
+ }
+
+ msg_head = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
+ HWSIM_CMD_FRAME);
+ if (msg_head == NULL) {
+ printk(KERN_DEBUG "mac80211_hwsim: problem with msg_head\n");
+ goto out;
+ }
+
+ rc = nla_put(skb, HWSIM_ATTR_ADDR_TRANSMITTER,
+ sizeof(struct mac_address), src);
+ /* We get the skb->data */
+ rc = nla_put(skb, HWSIM_ATTR_FRAME, my_skb->len, my_skb->data);
+ /* We get a copy of the control buffer for this tx*/
+ rc = nla_put(skb, HWSIM_ATTR_CB_SKB, sizeof(my_skb->cb),
+ my_skb->cb);
+
+ txi = IEEE80211_SKB_CB(my_skb);
+
+ /* We get the flags for this transmission, wmediumd maybe
+ changes its behaviour depending on the flags */
+ rc = nla_put_u32(skb, HWSIM_ATTR_FLAGS, txi->flags);
+ /* We get the tx control (rate and retries) info*/
+ rc = nla_put(skb, HWSIM_ATTR_TX_INFO,
+ sizeof(struct ieee80211_tx_rate)*IEEE80211_TX_MAX_RATES,
+ txi->control.rates);
+
+ if (rc != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: "
+ "error filling msg payload\n");
+ goto out;
+ }
+
+ genlmsg_end(skb, msg_head);
+ rc = genlmsg_unicast(&init_net, skb, _pid);
+ if (rc != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: wmediumd not responding "
+ "at PID:%d, switching to no wmediumd mode.\n", _pid);
+ atomic_set(&wmediumd_pid, 0);
+ }
+
+ return 0;
+
+out:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+}
+
static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
struct sk_buff *skb)
{
struct mac80211_hwsim_data *data = hw->priv, *data2;
- bool ack = false;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_rx_status rx_status;

+ bool ack = false;
+
if (data->idle) {
wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
return false;
}

+ if (data->ps != PS_DISABLED)
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+
+ /* wmediumd mode */
+ if (atomic_read(&wmediumd_pid)) {
+ hwsim_frame_send_nl((struct mac_address *)
+ &data->addresses[1].addr, skb,
+ atomic_read(&wmediumd_pid));
+ return true;
+ }
+
+ /* NO wmediumd, normal mac80211_hwsim behaviour*/
+
memset(&rx_status, 0, sizeof(rx_status));
/* TODO: set mactime */
rx_status.freq = data->channel->center_freq;
@@ -501,9 +581,6 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
/* TODO: simulate real signal strength (and optional packet loss) */
rx_status.signal = data->power_level - 50;

- if (data->ps != PS_DISABLED)
- hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
-
/* release the skb's source info */
skb_orphan(skb);
skb_dst_drop(skb);
@@ -530,9 +607,10 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
if (nskb == NULL)
continue;

+ memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
+
if (mac80211_hwsim_addr_match(data2, hdr->addr1))
ack = true;
- memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
ieee80211_rx_irqsafe(data2->hw, nskb);
}
spin_unlock(&hwsim_radio_lock);
@@ -540,12 +618,12 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
return ack;
}

-
static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
bool ack;
struct ieee80211_tx_info *txi;

+
mac80211_hwsim_monitor_rx(hw, skb);

if (skb->len < 10) {
@@ -554,6 +632,13 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
return;
}

+ /* wmediumd mode*/
+ if (atomic_read(&wmediumd_pid)) {
+ mac80211_hwsim_tx_frame(hw, skb);
+ return;
+ }
+
+ /* NO wmediumd, normal mac80211_hwsim behaviour*/
ack = mac80211_hwsim_tx_frame(hw, skb);
if (ack && skb->len >= 16) {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
@@ -1244,6 +1329,283 @@ static int hwsim_fops_group_write(void *dat, u64 val)
return 0;
}

+
+struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(
+ struct mac_address *addr)
+{
+ struct mac80211_hwsim_data *data;
+ bool _found = false;
+
+ spin_lock(&hwsim_radio_lock);
+ list_for_each_entry(data, &hwsim_radios, list) {
+ if (memcmp(data->addresses[1].addr, addr,
+ sizeof(struct mac_address)) == 0) {
+ _found = true;
+ break;
+ }
+ }
+ spin_unlock(&hwsim_radio_lock);
+
+ if (!_found) {
+ printk(KERN_DEBUG "mac80211_hwsim: invalid radio ID\n");
+ return NULL;
+ }
+
+ return data;
+
+
+}
+
+static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct ieee80211_hdr *hdr;
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_tx_info *txi;
+ struct ieee80211_tx_rate *tx_attempts;
+ char *cb;
+
+ int i;
+
+ struct mac_address *dst = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]);
+ int frame_data_len = nla_len(info->attrs[HWSIM_ATTR_FRAME]);
+ char *frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_FRAME]);
+ int flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]);
+
+ /* Allocate new skb here */
+ struct sk_buff *skb = alloc_skb(IEEE80211_MAX_DATA_LEN, GFP_KERNEL);
+ if (skb == NULL)
+ goto out;
+
+ if (frame_data_len <= IEEE80211_MAX_DATA_LEN) {
+ /* Copy the data */
+ memcpy(skb_put(skb, frame_data_len), frame_data,
+ frame_data_len);
+ } else
+ goto out;
+
+ data2 = get_hwsim_data_ref_from_addr(dst);
+
+ if (data2 == NULL)
+ goto out;
+
+ /*Tx info received because the frame was broadcasted on user space,
+ so we get all the necessary info: tx attempts and skb control buffer*/
+
+ tx_attempts = (struct ieee80211_tx_rate *)nla_data(
+ info->attrs[HWSIM_ATTR_TX_INFO]);
+
+ if (tx_attempts == NULL)
+ goto out;
+
+ /* ieee80211_tx_status() does not dereference anything from the
+ ieee80211_tx_info structure included in this cb, so it is safe
+ to get whatever we get from userspace and copy it here. */
+
+ /* check size of received custom buffer */
+ if (nla_len(info->attrs[HWSIM_ATTR_CB_SKB]) != sizeof(skb->cb)) {
+ printk(KERN_DEBUG "mac80211_hwsim: not valid cb received\n");
+ goto out;
+ }
+
+ /* ieee80211_tx_status() does not dereference anything from the
+ ieee80211_tx_info structure included in this cb, so it is safe
+ to get whatever we get from userspace and copy it here. */
+
+ cb = (char *)nla_data(info->attrs[HWSIM_ATTR_CB_SKB]);
+ memcpy(skb->cb, cb, sizeof(skb->cb));
+
+ /* now send back TX status */
+ txi = IEEE80211_SKB_CB(skb);
+
+ if (txi->control.vif)
+ hwsim_check_magic(txi->control.vif);
+ if (txi->control.sta)
+ hwsim_check_sta_magic(txi->control.sta);
+
+ ieee80211_tx_info_clear_status(txi);
+ txi->flags = flags;
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ txi->status.rates[i].idx = tx_attempts[i].idx;
+ txi->status.rates[i].count = tx_attempts[i].count;
+ txi->status.rates[i].flags = tx_attempts[i].flags;
+ }
+
+ txi->status.ack_signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) &&
+ (txi->flags & IEEE80211_TX_STAT_ACK)) {
+ if (skb->len >= 16) {
+ hdr = (struct ieee80211_hdr *) skb->data;
+ mac80211_hwsim_monitor_ack(data2->hw, hdr->addr2);
+ }
+ }
+ ieee80211_tx_status_irqsafe(data2->hw, skb);
+ return 0;
+
+out:
+ kfree_skb(skb);
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+
+}
+
+static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_rx_status rx_status;
+
+ struct mac_address *dst = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_RECEIVER]);
+
+ int frame_data_len = nla_len(info->attrs[HWSIM_ATTR_FRAME]);
+ char* frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_FRAME]);
+ /* Allocate new skb here */
+ struct sk_buff *skb = alloc_skb(IEEE80211_MAX_DATA_LEN, GFP_KERNEL);
+ if (skb == NULL)
+ goto out;
+
+ if (frame_data_len <= IEEE80211_MAX_DATA_LEN) {
+ /* Copy the data */
+ memcpy(skb_put(skb, frame_data_len), frame_data,
+ frame_data_len);
+ } else
+ goto out;
+
+ data2 = get_hwsim_data_ref_from_addr(dst);
+
+ if (data2 == NULL)
+ goto out;
+
+ /*A frame is received from user space*/
+ memset(&rx_status, 0, sizeof(rx_status));
+ rx_status.freq = data2->channel->center_freq;
+ rx_status.band = data2->channel->band;
+ rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
+ rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
+ ieee80211_rx_irqsafe(data2->hw, skb);
+
+ return 0;
+out:
+ kfree_skb(skb);
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+}
+
+static int hwsim_register_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+ if (info == NULL)
+ goto out;
+
+ atomic_set(&wmediumd_pid, info->snd_pid);
+
+ printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, "
+ "switching to wmediumd mode with pid %d\n", info->snd_pid);
+
+ return 0;
+
+out:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+}
+
+/* Generic Netlink operations array */
+static struct genl_ops hwsim_ops[] = {
+ {
+ .cmd = HWSIM_CMD_REGISTER,
+ .flags = 0,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_register_received_nl,
+ .dumpit = NULL,
+ },
+ {
+ .cmd = HWSIM_CMD_FRAME,
+ .flags = 0,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_cloned_frame_received_nl,
+ .dumpit = NULL,
+ },
+ {
+ .cmd = HWSIM_CMD_TX_INFO_FRAME,
+ .flags = 0,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_tx_info_frame_received_nl,
+ .dumpit = NULL,
+ },
+};
+
+static int hwsim_init_netlink(void)
+{
+ int rc;
+ printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
+
+ atomic_set(&wmediumd_pid, 0);
+
+ rc = genl_register_family(&hwsim_genl_family);
+ if (rc != 0)
+ goto failure;
+ rc = genl_register_ops(&hwsim_genl_family, &hwsim_ops[0]);
+ if (rc != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: register ops: %i\n", rc);
+ genl_unregister_family(&hwsim_genl_family);
+ goto failure;
+ }
+ rc = genl_register_ops(&hwsim_genl_family, &hwsim_ops[1]);
+ if (rc != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: register ops: %i\n", rc);
+ genl_unregister_family(&hwsim_genl_family);
+ goto failure;
+ }
+ rc = genl_register_ops(&hwsim_genl_family, &hwsim_ops[2]);
+ if (rc != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: register ops: %i\n", rc);
+ genl_unregister_family(&hwsim_genl_family);
+ goto failure;
+ }
+ return 0;
+
+failure:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+}
+
+static void hwsim_exit_netlink(void)
+{
+ int ret;
+
+ printk(KERN_INFO "mac80211_hwsim: closing netlink\n");
+ /*unregister the functions*/
+
+ ret = genl_unregister_ops(&hwsim_genl_family, &hwsim_ops[0]);
+ if (ret != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: unregister ops: %i\n", ret);
+ return;
+ }
+ ret = genl_unregister_ops(&hwsim_genl_family, &hwsim_ops[1]);
+ if (ret != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: unregister ops: %i\n", ret);
+ return;
+ }
+ ret = genl_unregister_ops(&hwsim_genl_family, &hwsim_ops[2]);
+ if (ret != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: unregister ops: %i\n", ret);
+ return;
+ }
+ ret = genl_unregister_family(&hwsim_genl_family);
+ if (ret != 0)
+ printk(KERN_DEBUG "mac80211_hwsim: "
+ "unregister family %i\n", ret);
+}
+
+
DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group,
hwsim_fops_group_read, hwsim_fops_group_write,
"%llx\n");
@@ -1379,6 +1741,10 @@ static int __init init_mac80211_hwsim(void)
data->group = 1;
mutex_init(&data->mutex);

+ /* Enable frame retransmission mechanism for a lossy channel */
+ hw->max_rates = 4;
+ hw->max_rate_tries = 11;
+
/* Work to be done prior to ieee80211_register_hw() */
switch (regtest) {
case HWSIM_REGTEST_DISABLED:
@@ -1528,8 +1894,16 @@ static int __init init_mac80211_hwsim(void)

rtnl_unlock();

+ err = hwsim_init_netlink();
+ if (err < 0)
+ goto failed_nl;
+
return 0;

+failed_nl:
+ printk(KERN_DEBUG "mac80211_hwsim: failed initializing netlink\n");
+ return err;
+
failed_mon:
rtnl_unlock();
free_netdev(hwsim_mon);
@@ -1550,6 +1924,8 @@ static void __exit exit_mac80211_hwsim(void)
{
printk(KERN_DEBUG "mac80211_hwsim: unregister radios\n");

+ hwsim_exit_netlink();
+
mac80211_hwsim_free();
unregister_netdev(hwsim_mon);
}
diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h
new file mode 100644
index 0000000..f1460e8
--- /dev/null
+++ b/drivers/net/wireless/mac80211_hwsim.h
@@ -0,0 +1,121 @@
+/*
+ * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
+ * Copyright (c) 2008, Jouni Malinen <[email protected]>
+ * Copyright (c) 2011, Javier Lopez <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <net/mac80211.h>
+
+/**
+ * DOC: Frame transmission/registration support
+ *
+ * Frame transmission and registration support exists to allow userspace
+ * entities such as wmediumd to receive and process all broadcasted
+ * frames from a mac80211_hwsim radio device.
+ *
+ * This allow user space applications to decide if the frame should be
+ * dropped or not and implement a wireless medium simulator at user space.
+ *
+ * Registration is done by sending a register message to the driver and
+ * will be automatically unregistered if the user application doesn't
+ * responds to sent frames.
+ * Once registered the user application has to take responsibility of
+ * broadcasting the frames to all listening mac80211_hwsim radio
+ * interfaces.
+ *
+ * For more technical details, see the corresponding command descriptions
+ * below.
+ */
+
+/**
+ * enum hwsim_commands - supported hwsim commands
+ *
+ * @HWSIM_CMD_UNSPEC: unspecified command to catch errors
+ *
+ * @HWSIM_CMD_REGISTER: request to register and received all broadcasted
+ * frames by any mac80211_hwsim radio device.
+ * @HWSIM_CMD_FRAME: send/receive a broadcasted frame from/to kernel/user
+ * space, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER,
+ * %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE,
+ * %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_CB_SKB
+ * @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to
+ * kernel, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS,
+ * %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL
+ * @__HWSIM_CMD_MAX: enum limit
+ */
+enum {
+ HWSIM_CMD_UNSPEC,
+ HWSIM_CMD_REGISTER,
+ HWSIM_CMD_FRAME,
+ HWSIM_CMD_TX_INFO_FRAME,
+ __HWSIM_CMD_MAX,
+};
+#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)
+
+/**
+ * enum hwsim_attrs - hwsim netlink attributes
+ *
+ * @HWSIM_ATTR_UNSPEC: unspecified attribute to catch errors
+ *
+ * @HWSIM_ATTR_ADDR_RECEIVER: MAC address of the radio device that
+ * the frame is broadcasted to
+ * @HWSIM_ATTR_ADDR_TRANSMITTER: MAC address of the radio device that
+ * the frame was broadcasted from
+ * @HWSIM_ATTR_FRAME: Data array
+ * @HWSIM_ATTR_FLAGS: mac80211 transmission flags, used to process
+ properly the frame at user space
+ * @HWSIM_ATTR_RX_RATE: estimated rx rate index for this frame at user
+ space
+ * @HWSIM_ATTR_SIGNAL: estimated RX signal for this frame at user
+ space
+ * @HWSIM_ATTR_TX_INFO: ieee80211_tx_rate array
+ * @HWSIM_ATTR_CB_SKB: sk_buff custom buffer of the broadcasted frame
+ * @__HWSIM_ATTR_MAX: enum limit
+ */
+
+
+enum {
+ HWSIM_ATTR_UNSPEC,
+ HWSIM_ATTR_ADDR_RECEIVER,
+ HWSIM_ATTR_ADDR_TRANSMITTER,
+ HWSIM_ATTR_FRAME,
+ HWSIM_ATTR_FLAGS,
+ HWSIM_ATTR_RX_RATE,
+ HWSIM_ATTR_SIGNAL,
+ HWSIM_ATTR_TX_INFO,
+ HWSIM_ATTR_CB_SKB,
+ __HWSIM_ATTR_MAX,
+};
+#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
+
+static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
+ [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_FRAME] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+ [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 },
+ [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 },
+ [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 },
+ [HWSIM_ATTR_TX_INFO] = { .type = NLA_UNSPEC,
+ .len = IEEE80211_TX_MAX_RATES*sizeof(
+ struct ieee80211_tx_rate)},
+ [HWSIM_ATTR_CB_SKB] = { .type = NLA_BINARY, .len = 48*sizeof(char) },
+};
+
+#define VERSION_NR 1
+
+static struct genl_family hwsim_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = "HWSIM",
+ .version = VERSION_NR,
+ .maxattr = HWSIM_ATTR_MAX,
+};
--
1.7.0.4


2011-05-25 19:03:46

by Javier López

[permalink] [raw]
Subject: [PATCH v6] mac80211_hwsim driver support userspace frame tx/rx

This patch adds to mac80211_hwsim the capability to send traffic via
userspace.

Frame exchange between kernel and user spaces is done through generic
netlink communication protocol. A new generic netlink family
MAC80211_HWSIM is proposed, this family contains three basic commands
HWSIM_CMD_REGISTER, which is the command used to register a new
traffic listener, HWSIM_CMD_FRAME, to exchange the frames from kernel
to user and vice-versa, and HWSIM_CMD_TX_INFO_FRAME which returns
from user all the information about retransmissions, rates, rx signal,
and so on.

How it works:

Once the driver is loaded the MAC80211_HWSIM family will be registered.
In the absence of userspace daemon, the driver itselfs implements a
perfect wireless medium as it did in the past. When a daemon sends a
HWSIM_CMD_REGISTER command, the module stores the application PID, and
from this moment all frames will be sent to the registered daemon.

The user space application will be in charge of process/forward all
frames broadcast by any mac80211_hwsim radio. If the user application
is stopped, the kernel module will detect the release of the socket
and it will switch back to in-kernel perfect channel simulation.

The userspace daemon must be waiting for incoming HWSIM_CMD_FRAME
commands sent from kernel, for each HWSIM_CMD_FRAME command the
application will try to broadcast this frame to all mac80211_hwsim
radios, however the application may decide to forward/drop this frame.
In the case of forwarding the frame, a new HWSIM_CMD_FRAME command will
be created, all necessary attributes will be populated and the frame
will be sent back to the kernel.

Also after the frame broadcast phase, a HWSIM_CMD_TX_INFO_FRAME
command will be sent from userspace to kernel, this command contains
all the information regarding the transmission, such as number of
tries, rates, ack signal, etc.

You can find the actual implementation of wireless mediumd daemon
(wmediumd) at:

* Last version tarball: https://github.com/jlopex/cozybit/tarball/master
* Or visiting my github tree: https://github.com/jlopex/cozybit/tree

Signed-off-by: Javier Lopez <[email protected]>
---
Thanks to Johaness Berg, Javier Cardona and John Linville for their
effort, reviews and patience with me.

v1: Initial version
v2: Fixed memory check issues, added netlink attributes checks, erased
extra printks
v3: Changed HWSIM generic netlink to MAC80211_HWSIM generic netlink family,
solved a lock issue
v4: Added netlink notifier in order to detect user space socket release,
more accurate memory allocation, added skb_queue per radio to hold last
exchanged frames with userspace
v5: Avoided the use of ieee80211 data structures, changed the cookie netlink
attribute to U64, solved memory leak in no-wmediumd mode, erased extra
debug messages, added netlink attributes check per each command, nla_len
for all data structures sent over netlink, solved a bug with interfaces
not completely configured
v6: Added patch description and changelog

drivers/net/wireless/mac80211_hwsim.c | 430 ++++++++++++++++++++++++++++++++-
drivers/net/wireless/mac80211_hwsim.h | 150 ++++++++++++
2 files changed, 569 insertions(+), 11 deletions(-)
create mode 100644 drivers/net/wireless/mac80211_hwsim.h

diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index f4f4257..dfbbda3 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -1,6 +1,7 @@
/*
* mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
* Copyright (c) 2008, Jouni Malinen <[email protected]>
+ * Copyright (c) 2011, Javier Lopez <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -25,11 +26,17 @@
#include <linux/rtnetlink.h>
#include <linux/etherdevice.h>
#include <linux/debugfs.h>
+#include <net/genetlink.h>
+#include "mac80211_hwsim.h"
+
+#define WARN_QUEUE 100
+#define MAX_QUEUE 200

MODULE_AUTHOR("Jouni Malinen");
MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211");
MODULE_LICENSE("GPL");

+int wmediumd_pid;
static int radios = 2;
module_param(radios, int, 0444);
MODULE_PARM_DESC(radios, "Number of simulated radios");
@@ -302,6 +309,7 @@ struct mac80211_hwsim_data {
struct dentry *debugfs;
struct dentry *debugfs_ps;

+ struct sk_buff_head pending; /* packets pending */
/*
* Only radios in the same group can communicate together (the
* channel has to match too). Each bit represents a group. A
@@ -322,6 +330,14 @@ struct hwsim_radiotap_hdr {
__le16 rt_chbitmask;
} __packed;

+/* MAC80211_HWSIM netlinf family */
+static struct genl_family hwsim_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = "MAC80211_HWSIM",
+ .version = VERSION_NR,
+ .maxattr = HWSIM_ATTR_MAX,
+};

static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb,
struct net_device *dev)
@@ -478,9 +494,89 @@ static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
return md.ret;
}

+static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
+ struct sk_buff *my_skb)
+{
+ struct sk_buff *skb;
+ struct mac80211_hwsim_data *data = hw->priv;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) my_skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(my_skb);
+ void *msg_head;
+ unsigned int hwsim_flags = 0;
+ int i;
+ struct hwsim_tx_rate tx_attempts[IEEE80211_TX_MAX_RATES];

-static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
- struct sk_buff *skb)
+ if (data->idle) {
+ wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
+ dev_kfree_skb(my_skb);
+ return;
+ }
+
+ if (data->ps != PS_DISABLED)
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+
+ /* If the queue contains MAX_QUEUE skb's drop some */
+ if (skb_queue_len(&data->pending) >= MAX_QUEUE) {
+ /* Droping until WARN_QUEUE level */
+ while (skb_queue_len(&data->pending) >= WARN_QUEUE)
+ skb_dequeue(&data->pending);
+ }
+
+ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (skb == NULL)
+ goto nla_put_failure;
+
+ msg_head = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
+ HWSIM_CMD_FRAME);
+ if (msg_head == NULL) {
+ printk(KERN_DEBUG "mac80211_hwsim: problem with msg_head\n");
+ goto nla_put_failure;
+ }
+
+ NLA_PUT(skb, HWSIM_ATTR_ADDR_TRANSMITTER,
+ sizeof(struct mac_address), data->addresses[1].addr);
+
+ /* We get the skb->data */
+ NLA_PUT(skb, HWSIM_ATTR_FRAME, my_skb->len, my_skb->data);
+
+ /* We get the flags for this transmission, and we translate them to
+ wmediumd flags */
+
+ if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)
+ hwsim_flags |= HWSIM_TX_CTL_REQ_TX_STATUS;
+
+ if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+ hwsim_flags |= HWSIM_TX_CTL_NO_ACK;
+
+ NLA_PUT_U32(skb, HWSIM_ATTR_FLAGS, hwsim_flags);
+
+ /* We get the tx control (rate and retries) info*/
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ tx_attempts[i].idx = info->status.rates[i].idx;
+ tx_attempts[i].count = info->status.rates[i].count;
+ }
+
+ NLA_PUT(skb, HWSIM_ATTR_TX_INFO,
+ sizeof(struct hwsim_tx_rate)*IEEE80211_TX_MAX_RATES,
+ tx_attempts);
+
+ /* We create a cookie to identify this skb */
+ NLA_PUT_U64(skb, HWSIM_ATTR_COOKIE, (unsigned long) my_skb);
+
+ genlmsg_end(skb, msg_head);
+ genlmsg_unicast(&init_net, skb, wmediumd_pid);
+
+ /* Enqueue the packet */
+ skb_queue_tail(&data->pending, my_skb);
+ return;
+
+nla_put_failure:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+}
+
+static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
+ struct sk_buff *skb)
{
struct mac80211_hwsim_data *data = hw->priv, *data2;
bool ack = false;
@@ -540,6 +636,15 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
return ack;
}

+static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
+ struct sk_buff *skb)
+{
+ if (wmediumd_pid) {
+ mac80211_hwsim_tx_frame_nl(hw, skb);
+ return true;
+ } else
+ return mac80211_hwsim_tx_frame_no_nl(hw, skb);
+}

static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
@@ -555,6 +660,12 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
}

ack = mac80211_hwsim_tx_frame(hw, skb);
+
+ /* wmediumd mode check */
+ if (wmediumd_pid)
+ return;
+
+ /* NO wmediumd detected, perfect medium simulation */
if (ack && skb->len >= 16) {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
mac80211_hwsim_monitor_ack(hw, hdr->addr2);
@@ -650,7 +761,8 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,

mac80211_hwsim_monitor_rx(hw, skb);
mac80211_hwsim_tx_frame(hw, skb);
- dev_kfree_skb(skb);
+ if (!wmediumd_pid)
+ dev_kfree_skb(skb);
}


@@ -966,12 +1078,7 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,

static void mac80211_hwsim_flush(struct ieee80211_hw *hw, bool drop)
{
- /*
- * In this special case, there's nothing we need to
- * do because hwsim does transmission synchronously.
- * In the future, when it does transmissions via
- * userspace, we may need to do something.
- */
+ /* Not implemented, queues only on kernel side */
}

struct hw_scan_done {
@@ -1139,7 +1246,8 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
memcpy(pspoll->ta, mac, ETH_ALEN);
if (!mac80211_hwsim_tx_frame(data->hw, skb))
printk(KERN_DEBUG "%s: PS-Poll frame not ack'ed\n", __func__);
- dev_kfree_skb(skb);
+ if (!wmediumd_pid)
+ dev_kfree_skb(skb);
}


@@ -1170,7 +1278,8 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
memcpy(hdr->addr3, vp->bssid, ETH_ALEN);
if (!mac80211_hwsim_tx_frame(data->hw, skb))
printk(KERN_DEBUG "%s: nullfunc frame not ack'ed\n", __func__);
- dev_kfree_skb(skb);
+ if (!wmediumd_pid)
+ dev_kfree_skb(skb);
}


@@ -1248,6 +1357,290 @@ DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group,
hwsim_fops_group_read, hwsim_fops_group_write,
"%llx\n");

+struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(
+ struct mac_address *addr)
+{
+ struct mac80211_hwsim_data *data;
+ bool _found = false;
+
+ spin_lock_bh(&hwsim_radio_lock);
+ list_for_each_entry(data, &hwsim_radios, list) {
+ if (memcmp(data->addresses[1].addr, addr,
+ sizeof(struct mac_address)) == 0) {
+ _found = true;
+ break;
+ }
+ }
+ spin_unlock_bh(&hwsim_radio_lock);
+
+ if (!_found)
+ return NULL;
+
+ return data;
+}
+
+static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct ieee80211_hdr *hdr;
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_tx_info *txi;
+ struct hwsim_tx_rate *tx_attempts;
+ struct sk_buff __user *ret_skb;
+ struct sk_buff *skb, *tmp;
+ struct mac_address *src;
+ unsigned int hwsim_flags;
+
+ int i;
+ bool found = false;
+
+ if (!info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER] ||
+ !info->attrs[HWSIM_ATTR_FLAGS] ||
+ !info->attrs[HWSIM_ATTR_COOKIE] ||
+ !info->attrs[HWSIM_ATTR_TX_INFO])
+ goto out;
+
+ if (nla_len(info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]) !=
+ sizeof(struct mac_address))
+ goto out;
+
+ src = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]);
+ hwsim_flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]);
+
+ ret_skb = (struct sk_buff __user *)
+ (unsigned long) nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]);
+
+ data2 = get_hwsim_data_ref_from_addr(src);
+
+ if (data2 == NULL)
+ goto out;
+
+ /* look for the skb matching the cookie passed back from user */
+ skb_queue_walk_safe(&data2->pending, skb, tmp) {
+ if (skb == ret_skb) {
+ skb_unlink(skb, &data2->pending);
+ found = true;
+ break;
+ }
+ }
+
+ /* not found */
+ if (!found)
+ goto out;
+
+ /* Tx info received because the frame was broadcasted on user space,
+ so we get all the necessary info: tx attempts and skb control buff */
+
+ if (nla_len(info->attrs[HWSIM_ATTR_TX_INFO]) !=
+ sizeof(struct hwsim_tx_rate)*IEEE80211_TX_MAX_RATES)
+ goto out;
+
+ tx_attempts = (struct hwsim_tx_rate *)nla_data(
+ info->attrs[HWSIM_ATTR_TX_INFO]);
+
+ /* now send back TX status */
+ txi = IEEE80211_SKB_CB(skb);
+
+ if (txi->control.vif)
+ hwsim_check_magic(txi->control.vif);
+ if (txi->control.sta)
+ hwsim_check_sta_magic(txi->control.sta);
+
+ ieee80211_tx_info_clear_status(txi);
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ txi->status.rates[i].idx = tx_attempts[i].idx;
+ txi->status.rates[i].count = tx_attempts[i].count;
+ /*txi->status.rates[i].flags = 0;*/
+ }
+
+ txi->status.ack_signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ if (!(hwsim_flags & HWSIM_TX_CTL_NO_ACK) &&
+ (hwsim_flags & HWSIM_TX_STAT_ACK)) {
+ if (skb->len >= 16) {
+ hdr = (struct ieee80211_hdr *) skb->data;
+ mac80211_hwsim_monitor_ack(data2->hw, hdr->addr2);
+ }
+ }
+ ieee80211_tx_status_irqsafe(data2->hw, skb);
+ return 0;
+out:
+ return -1;
+
+}
+
+static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_rx_status rx_status;
+ struct mac_address *dst;
+ int frame_data_len;
+ char *frame_data;
+ struct sk_buff *skb = NULL;
+
+ if (!info->attrs[HWSIM_ATTR_ADDR_RECEIVER] ||
+ !info->attrs[HWSIM_ATTR_FRAME] ||
+ !info->attrs[HWSIM_ATTR_RX_RATE] ||
+ !info->attrs[HWSIM_ATTR_SIGNAL])
+ goto out;
+
+ if (nla_len(info->attrs[HWSIM_ATTR_ADDR_RECEIVER]) !=
+ sizeof(struct mac_address))
+ goto out;
+
+ dst = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_RECEIVER]);
+
+ frame_data_len = nla_len(info->attrs[HWSIM_ATTR_FRAME]);
+ frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_FRAME]);
+
+ /* Allocate new skb here */
+ skb = alloc_skb(frame_data_len, GFP_KERNEL);
+ if (skb == NULL)
+ goto err;
+
+ if (frame_data_len <= IEEE80211_MAX_DATA_LEN) {
+ /* Copy the data */
+ memcpy(skb_put(skb, frame_data_len), frame_data,
+ frame_data_len);
+ } else
+ goto err;
+
+ data2 = get_hwsim_data_ref_from_addr(dst);
+
+ if (data2 == NULL)
+ goto out;
+
+ /* check if radio is configured properly */
+
+ if (data2->idle || !data2->started || !data2->channel)
+ goto out;
+
+ /*A frame is received from user space*/
+ memset(&rx_status, 0, sizeof(rx_status));
+ rx_status.freq = data2->channel->center_freq;
+ rx_status.band = data2->channel->band;
+ rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
+ rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
+ ieee80211_rx_irqsafe(data2->hw, skb);
+
+ return 0;
+err:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ goto out;
+out:
+ dev_kfree_skb(skb);
+ return -1;
+}
+
+static int hwsim_register_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+ if (info == NULL)
+ goto out;
+
+ wmediumd_pid = info->snd_pid;
+
+ printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, "
+ "switching to wmediumd mode with pid %d\n", info->snd_pid);
+
+ return 0;
+out:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+}
+
+/* Generic Netlink operations array */
+static struct genl_ops hwsim_ops[] = {
+ {
+ .cmd = HWSIM_CMD_REGISTER,
+ .flags = 0,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_register_received_nl,
+ .dumpit = NULL,
+ },
+ {
+ .cmd = HWSIM_CMD_FRAME,
+ .flags = 0,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_cloned_frame_received_nl,
+ .dumpit = NULL,
+ },
+ {
+ .cmd = HWSIM_CMD_TX_INFO_FRAME,
+ .flags = 0,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_tx_info_frame_received_nl,
+ .dumpit = NULL,
+ },
+};
+
+static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
+ unsigned long state,
+ void *_notify)
+{
+ struct netlink_notify *notify = _notify;
+
+ if (state != NETLINK_URELEASE)
+ return NOTIFY_DONE;
+
+ if (notify->pid == wmediumd_pid) {
+ printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink"
+ " socket, switching to perfect channel medium\n");
+ wmediumd_pid = 0;
+ }
+ return NOTIFY_DONE;
+
+}
+
+static struct notifier_block hwsim_netlink_notifier = {
+ .notifier_call = mac80211_hwsim_netlink_notify,
+};
+
+static int hwsim_init_netlink(void)
+{
+ int rc;
+ printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
+
+ wmediumd_pid = 0;
+
+ rc = genl_register_family_with_ops(&hwsim_genl_family,
+ hwsim_ops, ARRAY_SIZE(hwsim_ops));
+ if (rc)
+ goto failure;
+
+ rc = netlink_register_notifier(&hwsim_netlink_notifier);
+ if (rc)
+ goto failure;
+
+ return 0;
+
+failure:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+}
+
+static void hwsim_exit_netlink(void)
+{
+ int ret;
+
+ printk(KERN_INFO "mac80211_hwsim: closing netlink\n");
+ /* unregister the notifier */
+ netlink_unregister_notifier(&hwsim_netlink_notifier);
+ /* unregister the family */
+ ret = genl_unregister_family(&hwsim_genl_family);
+ if (ret)
+ printk(KERN_DEBUG "mac80211_hwsim: "
+ "unregister family %i\n", ret);
+}
+
static int __init init_mac80211_hwsim(void)
{
int i, err = 0;
@@ -1298,6 +1691,7 @@ static int __init init_mac80211_hwsim(void)
goto failed_drvdata;
}
data->dev->driver = &mac80211_hwsim_driver;
+ skb_queue_head_init(&data->pending);

SET_IEEE80211_DEV(hw, data->dev);
addr[3] = i >> 8;
@@ -1379,6 +1773,10 @@ static int __init init_mac80211_hwsim(void)
data->group = 1;
mutex_init(&data->mutex);

+ /* Enable frame retransmissions for lossy channels */
+ hw->max_rates = 4;
+ hw->max_rate_tries = 11;
+
/* Work to be done prior to ieee80211_register_hw() */
switch (regtest) {
case HWSIM_REGTEST_DISABLED:
@@ -1528,8 +1926,16 @@ static int __init init_mac80211_hwsim(void)

rtnl_unlock();

+ err = hwsim_init_netlink();
+ if (err < 0)
+ goto failed_nl;
+
return 0;

+failed_nl:
+ printk(KERN_DEBUG "mac_80211_hwsim: failed initializing netlink\n");
+ return err;
+
failed_mon:
rtnl_unlock();
free_netdev(hwsim_mon);
@@ -1550,6 +1956,8 @@ static void __exit exit_mac80211_hwsim(void)
{
printk(KERN_DEBUG "mac80211_hwsim: unregister radios\n");

+ hwsim_exit_netlink();
+
mac80211_hwsim_free();
unregister_netdev(hwsim_mon);
}
diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h
new file mode 100644
index 0000000..d82287c
--- /dev/null
+++ b/drivers/net/wireless/mac80211_hwsim.h
@@ -0,0 +1,150 @@
+/*
+ * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
+ * Copyright (c) 2008, Jouni Malinen <[email protected]>
+ * Copyright (c) 2011, Javier Lopez <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <net/mac80211.h>
+
+/**
+ * enum hwsim_tx_control_flags - flags to describe transmission info/status
+ *
+ * These flags are used to give the wmediumd extra information in order to
+ * modify its behavior for each frame
+ *
+ * @HWSIM_TX_CTL_REQ_TX_STATUS: require TX status callback for this frame.
+ * @HWSIM_TX_CTL_NO_ACK: tell the wmediumd not to wait for an ack
+ * @HWSIM_TX_STAT_ACK: Frame was acknowledged
+ *
+ */
+enum hwsim_tx_control_flags {
+ HWSIM_TX_CTL_REQ_TX_STATUS = BIT(0),
+ HWSIM_TX_CTL_NO_ACK = BIT(1),
+ HWSIM_TX_STAT_ACK = BIT(2),
+};
+
+/**
+ * DOC: Frame transmission/registration support
+ *
+ * Frame transmission and registration support exists to allow userspace
+ * entities such as wmediumd to receive and process all broadcasted
+ * frames from a mac80211_hwsim radio device.
+ *
+ * This allow user space applications to decide if the frame should be
+ * dropped or not and implement a wireless medium simulator at user space.
+ *
+ * Registration is done by sending a register message to the driver and
+ * will be automatically unregistered if the user application doesn't
+ * responds to sent frames.
+ * Once registered the user application has to take responsibility of
+ * broadcasting the frames to all listening mac80211_hwsim radio
+ * interfaces.
+ *
+ * For more technical details, see the corresponding command descriptions
+ * below.
+ */
+
+/**
+ * enum hwsim_commands - supported hwsim commands
+ *
+ * @HWSIM_CMD_UNSPEC: unspecified command to catch errors
+ *
+ * @HWSIM_CMD_REGISTER: request to register and received all broadcasted
+ * frames by any mac80211_hwsim radio device.
+ * @HWSIM_CMD_FRAME: send/receive a broadcasted frame from/to kernel/user
+ * space, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER,
+ * %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE,
+ * %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
+ * @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to
+ * kernel, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS,
+ * %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
+ * @__HWSIM_CMD_MAX: enum limit
+ */
+enum {
+ HWSIM_CMD_UNSPEC,
+ HWSIM_CMD_REGISTER,
+ HWSIM_CMD_FRAME,
+ HWSIM_CMD_TX_INFO_FRAME,
+ __HWSIM_CMD_MAX,
+};
+#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)
+
+/**
+ * enum hwsim_attrs - hwsim netlink attributes
+ *
+ * @HWSIM_ATTR_UNSPEC: unspecified attribute to catch errors
+ *
+ * @HWSIM_ATTR_ADDR_RECEIVER: MAC address of the radio device that
+ * the frame is broadcasted to
+ * @HWSIM_ATTR_ADDR_TRANSMITTER: MAC address of the radio device that
+ * the frame was broadcasted from
+ * @HWSIM_ATTR_FRAME: Data array
+ * @HWSIM_ATTR_FLAGS: mac80211 transmission flags, used to process
+ properly the frame at user space
+ * @HWSIM_ATTR_RX_RATE: estimated rx rate index for this frame at user
+ space
+ * @HWSIM_ATTR_SIGNAL: estimated RX signal for this frame at user
+ space
+ * @HWSIM_ATTR_TX_INFO: ieee80211_tx_rate array
+ * @HWSIM_ATTR_COOKIE: sk_buff cookie to identify the frame
+ * @__HWSIM_ATTR_MAX: enum limit
+ */
+
+
+enum {
+ HWSIM_ATTR_UNSPEC,
+ HWSIM_ATTR_ADDR_RECEIVER,
+ HWSIM_ATTR_ADDR_TRANSMITTER,
+ HWSIM_ATTR_FRAME,
+ HWSIM_ATTR_FLAGS,
+ HWSIM_ATTR_RX_RATE,
+ HWSIM_ATTR_SIGNAL,
+ HWSIM_ATTR_TX_INFO,
+ HWSIM_ATTR_COOKIE,
+ __HWSIM_ATTR_MAX,
+};
+#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
+
+/**
+ * struct hwsim_tx_rate - rate selection/status
+ *
+ * @idx: rate index to attempt to send with
+ * @count: number of tries in this rate before going to the next rate
+ *
+ * A value of -1 for @idx indicates an invalid rate and, if used
+ * in an array of retry rates, that no more rates should be tried.
+ *
+ * When used for transmit status reporting, the driver should
+ * always report the rate and number of retries used.
+ *
+ */
+struct hwsim_tx_rate {
+ s8 idx;
+ u8 count;
+} __packed;
+
+static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
+ [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_FRAME] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+ [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 },
+ [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 },
+ [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 },
+ [HWSIM_ATTR_TX_INFO] = { .type = NLA_UNSPEC,
+ .len = IEEE80211_TX_MAX_RATES*sizeof(
+ struct hwsim_tx_rate)},
+ [HWSIM_ATTR_COOKIE] = { .type = NLA_U64 },
+};
+
+
+
+#define VERSION_NR 1
--
1.7.0.4


2011-05-18 00:09:25

by Javier Cardona

[permalink] [raw]
Subject: Re: [PATCH v4] mac80211_hwsim driver support userspace frame tx/rx

John,

We've confirmed that this works as documented and makes hwsim very
useful for testing the wireless stack over lossy channels. If no one
has major objections, can you consider it for inclusion?

Cheers,

Javier


On Wed, May 11, 2011 at 2:32 PM, Javier Lopez <[email protected]> wrote:
> Hi all,
>
> Thanks for all your previous comments Johannes, I tried to implement all the solutions you proposed me, they were very helpful.
>
> In this new version of the patch I avoid the exchange of the Custom Buffer with the userspace. Instead of this I enqueue all pending skb's, for each pending skb a cookie is created and attached to the HWSIM_CMD_FRAME and HWSIM_CMD_TX_INFO.
>
> Any new comments from this patch?
>
> Thanks a lot,
>
> Feel free to download the last version of the wmediumd tool from GitHub:
>
> - Last version tarball: https://github.com/jlopex/cozybit/tarball/master
> - Or visiting my github tree: https://github.com/jlopex/cozybit/tree
>
> Javi
>
> Signed-off-by: Javier Lopez <[email protected]>
> ---
> ?drivers/net/wireless/mac80211_hwsim.c | ?401 +++++++++++++++++++++++++++++++--
> ?drivers/net/wireless/mac80211_hwsim.h | ?114 ++++++++++
> ?2 files changed, 499 insertions(+), 16 deletions(-)
> ?create mode 100644 drivers/net/wireless/mac80211_hwsim.h
>
> diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
> index f4f4257..fdb8a00 100644
> --- a/drivers/net/wireless/mac80211_hwsim.c
> +++ b/drivers/net/wireless/mac80211_hwsim.c
> @@ -1,6 +1,7 @@
> ?/*
> ?* mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
> ?* Copyright (c) 2008, Jouni Malinen <[email protected]>
> + * Copyright (c) 2011, Javier Lopez <[email protected]>
> ?*
> ?* This program is free software; you can redistribute it and/or modify
> ?* it under the terms of the GNU General Public License version 2 as
> @@ -25,11 +26,18 @@
> ?#include <linux/rtnetlink.h>
> ?#include <linux/etherdevice.h>
> ?#include <linux/debugfs.h>
> +#include <net/genetlink.h>
> +#include "mac80211_hwsim.h"
> +
> +#define WARN_QUEUE 100
> +#define MAX_QUEUE 200
>
> ?MODULE_AUTHOR("Jouni Malinen");
> ?MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211");
> ?MODULE_LICENSE("GPL");
>
> +static atomic_t wmediumd_pid;
> +
> ?static int radios = 2;
> ?module_param(radios, int, 0444);
> ?MODULE_PARM_DESC(radios, "Number of simulated radios");
> @@ -302,6 +310,8 @@ struct mac80211_hwsim_data {
> ? ? ? ?struct dentry *debugfs;
> ? ? ? ?struct dentry *debugfs_ps;
>
> + ? ? ? struct sk_buff_head pending; ? ?/* packets pending */
> +
> ? ? ? ?/*
> ? ? ? ? * Only radios in the same group can communicate together (the
> ? ? ? ? * channel has to match too). Each bit represents a group. A
> @@ -322,6 +332,14 @@ struct hwsim_radiotap_hdr {
> ? ? ? ?__le16 rt_chbitmask;
> ?} __packed;
>
> +/* MAC80211_HWSIM netlink family */
> +static struct genl_family hwsim_genl_family = {
> + ? ? ? .id = GENL_ID_GENERATE,
> + ? ? ? .hdrsize = 0,
> + ? ? ? .name = "MAC80211_HWSIM",
> + ? ? ? .version = VERSION_NR,
> + ? ? ? .maxattr = HWSIM_ATTR_MAX,
> +};
>
> ?static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb,
> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct net_device *dev)
> @@ -478,21 +496,90 @@ static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
> ? ? ? ?return md.ret;
> ?}
>
> +static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct sk_buff *my_skb)
> +{
> + ? ? ? struct sk_buff *skb;
> + ? ? ? struct mac80211_hwsim_data *data = hw->priv;
> + ? ? ? struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) my_skb->data;
> + ? ? ? struct ieee80211_tx_info *info = IEEE80211_SKB_CB(my_skb);
> + ? ? ? void *msg_head;
>
> -static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
> - ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct sk_buff *skb)
> + ? ? ? if (data->idle) {
> + ? ? ? ? ? ? ? wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
> + ? ? ? ? ? ? ? dev_kfree_skb(my_skb);
> + ? ? ? ? ? ? ? return;
> + ? ? ? }
> +
> + ? ? ? if (data->ps != PS_DISABLED)
> + ? ? ? ? ? ? ? hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
> +
> + ? ? ? /* If the queue contains MAX_QUEUE skb's drop some */
> + ? ? ? if (skb_queue_len(&data->pending) >= MAX_QUEUE) {
> +
> + ? ? ? ? ? ? ? printk(KERN_DEBUG "mac80211_hwsim: queues at maximum level, "
> + ? ? ? ? ? ? ? ? ? ? ? "droping some old frames\n");
> + ? ? ? ? ? ? ? while (skb_queue_len(&data->pending) >= WARN_QUEUE)
> + ? ? ? ? ? ? ? ? ? ? ? skb_dequeue(&data->pending);
> + ? ? ? }
> +
> + ? ? ? skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
> + ? ? ? if (skb == NULL)
> + ? ? ? ? ? ? ? goto nla_put_failure;
> +
> + ? ? ? msg_head = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?HWSIM_CMD_FRAME);
> + ? ? ? if (msg_head == NULL) {
> + ? ? ? ? ? ? ? printk(KERN_DEBUG "mac80211_hwsim: problem with msg_head\n");
> + ? ? ? ? ? ? ? goto nla_put_failure;
> + ? ? ? }
> +
> + ? ? ? NLA_PUT(skb, HWSIM_ATTR_ADDR_TRANSMITTER,
> + ? ? ? ? ? ? ? ? ? ?sizeof(struct mac_address), data->addresses[1].addr);
> +
> + ? ? ? /* We get the skb->data */
> + ? ? ? NLA_PUT(skb, HWSIM_ATTR_FRAME, my_skb->len, my_skb->data);
> +
> + ? ? ? /* We get the flags for this transmission, wmediumd maybe
> + ? ? ? ? ?changes its behaviour depending on the flags */
> + ? ? ? NLA_PUT_U32(skb, HWSIM_ATTR_FLAGS, info->flags);
> +
> + ? ? ? /* We get the tx control (rate and retries) info*/
> + ? ? ? NLA_PUT(skb, HWSIM_ATTR_TX_INFO,
> + ? ? ? ? ? ? ? ? ? ?sizeof(struct ieee80211_tx_rate)*IEEE80211_TX_MAX_RATES,
> + ? ? ? ? ? ? ? ? ? ?info->control.rates);
> + ? ? ? /* We create a cookie to identify this skb */
> + ? ? ? NLA_PUT_U32(skb, HWSIM_ATTR_COOKIE, (unsigned long) my_skb);
> +
> + ? ? ? genlmsg_end(skb, msg_head);
> + ? ? ? genlmsg_unicast(&init_net, skb, atomic_read(&wmediumd_pid));
> +
> + ? ? ? /* Enqueue the packet */
> + ? ? ? skb_queue_tail(&data->pending, my_skb);
> + ? ? ? return;
> +
> +nla_put_failure:
> + ? ? ? printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
> +}
> +
> +static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct sk_buff *skb)
> ?{
> ? ? ? ?struct mac80211_hwsim_data *data = hw->priv, *data2;
> - ? ? ? bool ack = false;
> ? ? ? ?struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
> ? ? ? ?struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> ? ? ? ?struct ieee80211_rx_status rx_status;
>
> + ? ? ? bool ack = false;
> +
> ? ? ? ?if (data->idle) {
> ? ? ? ? ? ? ? ?wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
> ? ? ? ? ? ? ? ?return false;
> ? ? ? ?}
>
> + ? ? ? if (data->ps != PS_DISABLED)
> + ? ? ? ? ? ? ? hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
> +
> ? ? ? ?memset(&rx_status, 0, sizeof(rx_status));
> ? ? ? ?/* TODO: set mactime */
> ? ? ? ?rx_status.freq = data->channel->center_freq;
> @@ -501,9 +588,6 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
> ? ? ? ?/* TODO: simulate real signal strength (and optional packet loss) */
> ? ? ? ?rx_status.signal = data->power_level - 50;
>
> - ? ? ? if (data->ps != PS_DISABLED)
> - ? ? ? ? ? ? ? hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
> -
> ? ? ? ?/* release the skb's source info */
> ? ? ? ?skb_orphan(skb);
> ? ? ? ?skb_dst_drop(skb);
> @@ -530,9 +614,10 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
> ? ? ? ? ? ? ? ?if (nskb == NULL)
> ? ? ? ? ? ? ? ? ? ? ? ?continue;
>
> + ? ? ? ? ? ? ? memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
> +
> ? ? ? ? ? ? ? ?if (mac80211_hwsim_addr_match(data2, hdr->addr1))
> ? ? ? ? ? ? ? ? ? ? ? ?ack = true;
> - ? ? ? ? ? ? ? memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
> ? ? ? ? ? ? ? ?ieee80211_rx_irqsafe(data2->hw, nskb);
> ? ? ? ?}
> ? ? ? ?spin_unlock(&hwsim_radio_lock);
> @@ -540,6 +625,15 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
> ? ? ? ?return ack;
> ?}
>
> +static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct sk_buff *skb)
> +{
> + ? ? ? if (atomic_read(&wmediumd_pid)) {
> + ? ? ? ? ? ? ? mac80211_hwsim_tx_frame_nl(hw, skb);
> + ? ? ? ? ? ? ? return true;
> + ? ? ? } else
> + ? ? ? ? ? ? ? return mac80211_hwsim_tx_frame_no_nl(hw, skb);
> +}
>
> ?static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
> ?{
> @@ -555,6 +649,12 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
> ? ? ? ?}
>
> ? ? ? ?ack = mac80211_hwsim_tx_frame(hw, skb);
> +
> + ? ? ? /* wmediumd mode*/
> + ? ? ? if (atomic_read(&wmediumd_pid))
> + ? ? ? ? ? ? ? return;
> +
> + ? ? ? /* NO wmediumd, normal mac80211_hwsim behaviour*/
> ? ? ? ?if (ack && skb->len >= 16) {
> ? ? ? ? ? ? ? ?struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
> ? ? ? ? ? ? ? ?mac80211_hwsim_monitor_ack(hw, hdr->addr2);
> @@ -571,6 +671,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
> ? ? ? ?if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) && ack)
> ? ? ? ? ? ? ? ?txi->flags |= IEEE80211_TX_STAT_ACK;
> ? ? ? ?ieee80211_tx_status_irqsafe(hw, skb);
> +
> ?}
>
>
> @@ -650,7 +751,6 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
>
> ? ? ? ?mac80211_hwsim_monitor_rx(hw, skb);
> ? ? ? ?mac80211_hwsim_tx_frame(hw, skb);
> - ? ? ? dev_kfree_skb(skb);
> ?}
>
>
> @@ -966,12 +1066,9 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,
>
> ?static void mac80211_hwsim_flush(struct ieee80211_hw *hw, bool drop)
> ?{
> - ? ? ? /*
> - ? ? ? ?* In this special case, there's nothing we need to
> - ? ? ? ?* do because hwsim does transmission synchronously.
> - ? ? ? ?* In the future, when it does transmissions via
> - ? ? ? ?* userspace, we may need to do something.
> - ? ? ? ?*/
> + ? ? ? /* Let's purge the pending queue */
> + ? ? ? struct mac80211_hwsim_data *data = hw->priv;
> + ? ? ? skb_queue_purge(&data->pending);
> ?}
>
> ?struct hw_scan_done {
> @@ -1139,7 +1236,6 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
> ? ? ? ?memcpy(pspoll->ta, mac, ETH_ALEN);
> ? ? ? ?if (!mac80211_hwsim_tx_frame(data->hw, skb))
> ? ? ? ? ? ? ? ?printk(KERN_DEBUG "%s: PS-Poll frame not ack'ed\n", __func__);
> - ? ? ? dev_kfree_skb(skb);
> ?}
>
>
> @@ -1170,7 +1266,6 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
> ? ? ? ?memcpy(hdr->addr3, vp->bssid, ETH_ALEN);
> ? ? ? ?if (!mac80211_hwsim_tx_frame(data->hw, skb))
> ? ? ? ? ? ? ? ?printk(KERN_DEBUG "%s: nullfunc frame not ack'ed\n", __func__);
> - ? ? ? dev_kfree_skb(skb);
> ?}
>
>
> @@ -1244,6 +1339,264 @@ static int hwsim_fops_group_write(void *dat, u64 val)
> ? ? ? ?return 0;
> ?}
>
> +
> +struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct mac_address *addr)
> +{
> + ? ? ? struct mac80211_hwsim_data *data;
> + ? ? ? bool _found = false;
> +
> + ? ? ? spin_lock_bh(&hwsim_radio_lock);
> + ? ? ? list_for_each_entry(data, &hwsim_radios, list) {
> + ? ? ? ? ? ? ? if (memcmp(data->addresses[1].addr, addr,
> + ? ? ? ? ? ? ? ? ? ? ? ? sizeof(struct mac_address)) == 0) {
> + ? ? ? ? ? ? ? ? ? ? ? _found = true;
> + ? ? ? ? ? ? ? ? ? ? ? break;
> + ? ? ? ? ? ? ? }
> + ? ? ? }
> + ? ? ? spin_unlock_bh(&hwsim_radio_lock);
> +
> + ? ? ? if (!_found) {
> + ? ? ? ? ? ? ? printk(KERN_DEBUG "mac80211_hwsim: invalid radio ID\n");
> + ? ? ? ? ? ? ? return NULL;
> + ? ? ? }
> +
> + ? ? ? return data;
> +}
> +
> +static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct genl_info *info)
> +{
> +
> + ? ? ? struct ieee80211_hdr *hdr;
> + ? ? ? struct mac80211_hwsim_data *data2;
> + ? ? ? struct ieee80211_tx_info *txi;
> + ? ? ? struct ieee80211_tx_rate *tx_attempts;
> + ? ? ? struct sk_buff __user *ret_skb;
> + ? ? ? struct sk_buff *skb = NULL, *tmp;
> +
> + ? ? ? int i;
> + ? ? ? bool found = false;
> +
> + ? ? ? struct mac_address *dst = (struct mac_address *)nla_data(
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]);
> + ? ? ? int flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]);
> +
> + ? ? ? ret_skb = (struct sk_buff __user *)
> + ? ? ? ? ? ? ? ? (unsigned long) nla_get_u32(info->attrs[HWSIM_ATTR_COOKIE]);
> +
> + ? ? ? data2 = get_hwsim_data_ref_from_addr(dst);
> +
> + ? ? ? if (data2 == NULL)
> + ? ? ? ? ? ? ? goto out;
> +
> + ? ? ? /* look for the skb matching the cookie passed back from user */
> + ? ? ? skb_queue_walk_safe(&data2->pending, skb, tmp) {
> + ? ? ? ? ? ? ? if (skb == ret_skb) {
> + ? ? ? ? ? ? ? ? ? ? ? skb_unlink(skb, &data2->pending);
> + ? ? ? ? ? ? ? ? ? ? ? found = true;
> + ? ? ? ? ? ? ? ? ? ? ? break;
> + ? ? ? ? ? ? ? }
> + ? ? ? }
> +
> + ? ? ? /* not found */
> + ? ? ? if (!found)
> + ? ? ? ? ? ? ? return -1;
> +
> + ? ? ? /* Tx info received because the frame was broadcasted on user space,
> + ? ? ? ?so we get all the necessary info: tx attempts and skb control buff */
> +
> + ? ? ? tx_attempts = (struct ieee80211_tx_rate *)nla_data(
> + ? ? ? ? ? ? ? ? ? ? ?info->attrs[HWSIM_ATTR_TX_INFO]);
> +
> + ? ? ? if (tx_attempts == NULL)
> + ? ? ? ? ? ? ? goto out;
> +
> + ? ? ? /* now send back TX status */
> + ? ? ? txi = IEEE80211_SKB_CB(skb);
> +
> + ? ? ? if (txi->control.vif)
> + ? ? ? ? ? ? ? hwsim_check_magic(txi->control.vif);
> + ? ? ? if (txi->control.sta)
> + ? ? ? ? ? ? ? hwsim_check_sta_magic(txi->control.sta);
> +
> + ? ? ? ieee80211_tx_info_clear_status(txi);
> + ? ? ? txi->flags = flags;
> +
> + ? ? ? for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
> + ? ? ? ? ? ? ? txi->status.rates[i].idx = tx_attempts[i].idx;
> + ? ? ? ? ? ? ? txi->status.rates[i].count = tx_attempts[i].count;
> + ? ? ? ? ? ? ? txi->status.rates[i].flags = tx_attempts[i].flags;
> + ? ? ? }
> +
> + ? ? ? txi->status.ack_signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
> +
> + ? ? ? if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) &&
> + ? ? ? ? ?(txi->flags & IEEE80211_TX_STAT_ACK)) {
> + ? ? ? ? ? ? ? if (skb->len >= 16) {
> + ? ? ? ? ? ? ? ? ? ? ? hdr = (struct ieee80211_hdr *) skb->data;
> + ? ? ? ? ? ? ? ? ? ? ? mac80211_hwsim_monitor_ack(data2->hw, hdr->addr2);
> + ? ? ? ? ? ? ? }
> + ? ? ? }
> + ? ? ? ieee80211_tx_status_irqsafe(data2->hw, skb);
> +
> + ? ? ? return 0;
> +
> +out:
> + ? ? ? printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
> + ? ? ? return -1;
> +
> +}
> +
> +static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct genl_info *info)
> +{
> +
> + ? ? ? struct mac80211_hwsim_data *data2;
> + ? ? ? struct ieee80211_rx_status rx_status;
> +
> + ? ? ? struct mac_address *dst = (struct mac_address *)nla_data(
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?info->attrs[HWSIM_ATTR_ADDR_RECEIVER]);
> +
> + ? ? ? int frame_data_len = nla_len(info->attrs[HWSIM_ATTR_FRAME]);
> + ? ? ? char* frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_FRAME]);
> + ? ? ? /* Allocate new skb here */
> + ? ? ? struct sk_buff *skb = alloc_skb(frame_data_len, GFP_KERNEL);
> + ? ? ? if (skb == NULL)
> + ? ? ? ? ? ? ? goto out;
> +
> + ? ? ? if (frame_data_len <= IEEE80211_MAX_DATA_LEN) {
> + ? ? ? ? ? ? ? /* Copy the data */
> + ? ? ? ? ? ? ? memcpy(skb_put(skb, frame_data_len), frame_data,
> + ? ? ? ? ? ? ? ? ? ? ?frame_data_len);
> + ? ? ? } else
> + ? ? ? ? ? ? ? goto out;
> +
> + ? ? ? data2 = get_hwsim_data_ref_from_addr(dst);
> +
> + ? ? ? if (data2 == NULL)
> + ? ? ? ? ? ? ? goto out;
> +
> + ? ? ? /*A frame is received from user space*/
> + ? ? ? memset(&rx_status, 0, sizeof(rx_status));
> + ? ? ? rx_status.freq = data2->channel->center_freq;
> + ? ? ? rx_status.band = data2->channel->band;
> + ? ? ? rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
> + ? ? ? rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
> +
> + ? ? ? memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
> + ? ? ? ieee80211_rx_irqsafe(data2->hw, skb);
> +
> + ? ? ? return 0;
> +out:
> + ? ? ? kfree_skb(skb);
> + ? ? ? printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
> + ? ? ? return -1;
> +}
> +
> +static int hwsim_register_received_nl(struct sk_buff *skb_2,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct genl_info *info)
> +{
> + ? ? ? if (info == NULL)
> + ? ? ? ? ? ? ? goto out;
> +
> + ? ? ? atomic_set(&wmediumd_pid, info->snd_pid);
> +
> + ? ? ? printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, "
> + ? ? ? ? ? ? ?"switching to wmediumd mode with pid %d\n", info->snd_pid);
> +
> + ? ? ? return 0;
> +
> +out:
> + ? ? ? printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
> + ? ? ? return -1;
> +}
> +
> +/* Generic Netlink operations array */
> +static struct genl_ops hwsim_ops[] = {
> + ? ? ? {
> + ? ? ? ? ? ? ? .cmd = HWSIM_CMD_REGISTER,
> + ? ? ? ? ? ? ? .flags = 0,
> + ? ? ? ? ? ? ? .policy = hwsim_genl_policy,
> + ? ? ? ? ? ? ? .doit = hwsim_register_received_nl,
> + ? ? ? ? ? ? ? .dumpit = NULL,
> + ? ? ? },
> + ? ? ? {
> + ? ? ? ? ? ? ? .cmd = HWSIM_CMD_FRAME,
> + ? ? ? ? ? ? ? .flags = 0,
> + ? ? ? ? ? ? ? .policy = hwsim_genl_policy,
> + ? ? ? ? ? ? ? .doit = hwsim_cloned_frame_received_nl,
> + ? ? ? ? ? ? ? .dumpit = NULL,
> + ? ? ? },
> + ? ? ? {
> + ? ? ? ? ? ? ? .cmd = HWSIM_CMD_TX_INFO_FRAME,
> + ? ? ? ? ? ? ? .flags = 0,
> + ? ? ? ? ? ? ? .policy = hwsim_genl_policy,
> + ? ? ? ? ? ? ? .doit = hwsim_tx_info_frame_received_nl,
> + ? ? ? ? ? ? ? .dumpit = NULL,
> + ? ? ? },
> +};
> +
> +static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?unsigned long state,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?void *_notify)
> +{
> + ? ? ? struct netlink_notify *notify = _notify;
> +
> + ? ? ? if (state != NETLINK_URELEASE)
> + ? ? ? ? ? ? ? return NOTIFY_DONE;
> +
> + ? ? ? if (notify->pid == atomic_read(&wmediumd_pid)) {
> + ? ? ? ? ? ? ? printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink"
> + ? ? ? ? ? ? ? ? ? ? ?" socket, switching to perfect channel medium\n");
> + ? ? ? ? ? ? ? atomic_set(&wmediumd_pid, 0);
> + ? ? ? }
> + ? ? ? return NOTIFY_DONE;
> +
> +}
> +
> +static struct notifier_block hwsim_netlink_notifier = {
> + ? ? ? .notifier_call = mac80211_hwsim_netlink_notify,
> +};
> +
> +static int hwsim_init_netlink(void)
> +{
> + ? ? ? int rc;
> + ? ? ? printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
> +
> + ? ? ? atomic_set(&wmediumd_pid, 0);
> +
> + ? ? ? rc = genl_register_family_with_ops(&hwsim_genl_family,
> + ? ? ? ? ? ? ? hwsim_ops, ARRAY_SIZE(hwsim_ops));
> + ? ? ? if (rc)
> + ? ? ? ? ? ? ? goto failure;
> +
> + ? ? ? rc = netlink_register_notifier(&hwsim_netlink_notifier);
> + ? ? ? if (rc)
> + ? ? ? ? ? ? ? goto failure;
> +
> + ? ? ? return 0;
> +
> +failure:
> + ? ? ? printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
> + ? ? ? return -1;
> +}
> +
> +static void hwsim_exit_netlink(void)
> +{
> + ? ? ? int ret;
> +
> + ? ? ? printk(KERN_INFO "mac80211_hwsim: closing netlink\n");
> + ? ? ? /* unregister the notifier */
> + ? ? ? netlink_unregister_notifier(&hwsim_netlink_notifier);
> + ? ? ? /* unregister the family */
> + ? ? ? ret = genl_unregister_family(&hwsim_genl_family);
> + ? ? ? if (ret)
> + ? ? ? ? ? ? ? printk(KERN_DEBUG "mac80211_hwsim: "
> + ? ? ? ? ? ? ? ? ? ? ?"unregister family %i\n", ret);
> +}
> +
> +
> ?DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group,
> ? ? ? ? ? ? ? ? ? ? ? ?hwsim_fops_group_read, hwsim_fops_group_write,
> ? ? ? ? ? ? ? ? ? ? ? ?"%llx\n");
> @@ -1299,6 +1652,8 @@ static int __init init_mac80211_hwsim(void)
> ? ? ? ? ? ? ? ?}
> ? ? ? ? ? ? ? ?data->dev->driver = &mac80211_hwsim_driver;
>
> + ? ? ? ? ? ? ? skb_queue_head_init(&data->pending);
> +
> ? ? ? ? ? ? ? ?SET_IEEE80211_DEV(hw, data->dev);
> ? ? ? ? ? ? ? ?addr[3] = i >> 8;
> ? ? ? ? ? ? ? ?addr[4] = i;
> @@ -1379,6 +1734,10 @@ static int __init init_mac80211_hwsim(void)
> ? ? ? ? ? ? ? ?data->group = 1;
> ? ? ? ? ? ? ? ?mutex_init(&data->mutex);
>
> + ? ? ? ? ? ? ? /* Enable frame retransmission mechanism for a lossy channel */
> + ? ? ? ? ? ? ? hw->max_rates = 4;
> + ? ? ? ? ? ? ? hw->max_rate_tries = 11;
> +
> ? ? ? ? ? ? ? ?/* Work to be done prior to ieee80211_register_hw() */
> ? ? ? ? ? ? ? ?switch (regtest) {
> ? ? ? ? ? ? ? ?case HWSIM_REGTEST_DISABLED:
> @@ -1528,8 +1887,16 @@ static int __init init_mac80211_hwsim(void)
>
> ? ? ? ?rtnl_unlock();
>
> + ? ? ? err = hwsim_init_netlink();
> + ? ? ? if (err < 0)
> + ? ? ? ? ? ? ? goto failed_nl;
> +
> ? ? ? ?return 0;
>
> +failed_nl:
> + ? ? ? printk(KERN_DEBUG "mac80211_hwsim: failed initializing netlink\n");
> + ? ? ? return err;
> +
> ?failed_mon:
> ? ? ? ?rtnl_unlock();
> ? ? ? ?free_netdev(hwsim_mon);
> @@ -1550,6 +1917,8 @@ static void __exit exit_mac80211_hwsim(void)
> ?{
> ? ? ? ?printk(KERN_DEBUG "mac80211_hwsim: unregister radios\n");
>
> + ? ? ? hwsim_exit_netlink();
> +
> ? ? ? ?mac80211_hwsim_free();
> ? ? ? ?unregister_netdev(hwsim_mon);
> ?}
> diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h
> new file mode 100644
> index 0000000..803786e
> --- /dev/null
> +++ b/drivers/net/wireless/mac80211_hwsim.h
> @@ -0,0 +1,114 @@
> +/*
> + * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
> + * Copyright (c) 2008, Jouni Malinen <[email protected]>
> + * Copyright (c) 2011, Javier Lopez <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <net/mac80211.h>
> +
> +/**
> + * DOC: Frame transmission/registration support
> + *
> + * Frame transmission and registration support exists to allow userspace
> + * entities such as wmediumd to receive and process all broadcasted
> + * frames from a mac80211_hwsim radio device.
> + *
> + * This allow user space applications to decide if the frame should be
> + * dropped or not and implement a wireless medium simulator at user space.
> + *
> + * Registration is done by sending a register message to the driver and
> + * will be automatically unregistered if the user application doesn't
> + * responds to sent frames.
> + * Once registered the user application has to take responsibility of
> + * broadcasting the frames to all listening mac80211_hwsim radio
> + * interfaces.
> + *
> + * For more technical details, see the corresponding command descriptions
> + * below.
> + */
> +
> +/**
> + * enum hwsim_commands - supported hwsim commands
> + *
> + * @HWSIM_CMD_UNSPEC: unspecified command to catch errors
> + *
> + * @HWSIM_CMD_REGISTER: request to register and received all broadcasted
> + * ? ? frames by any mac80211_hwsim radio device.
> + * @HWSIM_CMD_FRAME: send/receive a broadcasted frame from/to kernel/user
> + * space, uses:
> + * ? ? %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER,
> + * ? ? %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE,
> + * ? ? %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
> + * @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to
> + * kernel, uses:
> + * ? ? %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS,
> + * ? ? %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
> + * @__HWSIM_CMD_MAX: enum limit
> + */
> +enum {
> + ? ? ? HWSIM_CMD_UNSPEC,
> + ? ? ? HWSIM_CMD_REGISTER,
> + ? ? ? HWSIM_CMD_FRAME,
> + ? ? ? HWSIM_CMD_TX_INFO_FRAME,
> + ? ? ? __HWSIM_CMD_MAX,
> +};
> +#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)
> +
> +/**
> + * enum hwsim_attrs - hwsim netlink attributes
> + *
> + * @HWSIM_ATTR_UNSPEC: unspecified attribute to catch errors
> + *
> + * @HWSIM_ATTR_ADDR_RECEIVER: MAC address of the radio device that
> + * ? ? the frame is broadcasted to
> + * @HWSIM_ATTR_ADDR_TRANSMITTER: MAC address of the radio device that
> + * ? ? the frame was broadcasted from
> + * @HWSIM_ATTR_FRAME: Data array
> + * @HWSIM_ATTR_FLAGS: mac80211 transmission flags, used to process
> + ? ? ? properly the frame at user space
> + * @HWSIM_ATTR_RX_RATE: estimated rx rate index for this frame at user
> + ? ? ? space
> + * @HWSIM_ATTR_SIGNAL: estimated RX signal for this frame at user
> + ? ? ? space
> + * @HWSIM_ATTR_TX_INFO: ieee80211_tx_rate array
> + * @HWSIM_ATTR_COOKIE: sk_buff cookie to identify the frame
> + * @__HWSIM_ATTR_MAX: enum limit
> + */
> +
> +
> +enum {
> + ? ? ? HWSIM_ATTR_UNSPEC,
> + ? ? ? HWSIM_ATTR_ADDR_RECEIVER,
> + ? ? ? HWSIM_ATTR_ADDR_TRANSMITTER,
> + ? ? ? HWSIM_ATTR_FRAME,
> + ? ? ? HWSIM_ATTR_FLAGS,
> + ? ? ? HWSIM_ATTR_RX_RATE,
> + ? ? ? HWSIM_ATTR_SIGNAL,
> + ? ? ? HWSIM_ATTR_TX_INFO,
> + ? ? ? HWSIM_ATTR_COOKIE,
> + ? ? ? __HWSIM_ATTR_MAX,
> +};
> +#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
> +
> +static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
> + ? ? ? [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?.len = 6*sizeof(u8) },
> + ? ? ? [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? .len = 6*sizeof(u8) },
> + ? ? ? [HWSIM_ATTR_FRAME] = { .type = NLA_BINARY,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?.len = IEEE80211_MAX_DATA_LEN },
> + ? ? ? [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 },
> + ? ? ? [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 },
> + ? ? ? [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 },
> + ? ? ? [HWSIM_ATTR_TX_INFO] = { .type = NLA_UNSPEC,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?.len = IEEE80211_TX_MAX_RATES*sizeof(
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct ieee80211_tx_rate)},
> + ? ? ? [HWSIM_ATTR_COOKIE] = { .type = NLA_U32 },
> +};
> +
> +#define VERSION_NR 1
> +
> --
> 1.7.0.4
>
>



--
Javier Cardona
cozybit Inc.
http://www.cozybit.com

2011-05-01 19:29:58

by Javier López

[permalink] [raw]
Subject: [PATCH v3] mac80211_hwsim driver support userspace frame tx/rx

Thanks for your review John, this a new version of the patch.

I solved the issue of the name of the new netlink family, I agree with you
it could drive to confusion, now it's changed to MAC80211_HWSIM, there was
a locking problem and now is solved too.

Any comments?

If any reviewer want to use the actual implementation of the wireless medium
daemon (wmediumd) to test this patch, it's available at GitHub:

- Last version tarball: https://github.com/jlopex/cozybit/tarball/master
- Or visiting my github tree: https://github.com/jlopex/cozybit/tree

Javi

Signed-off-by: Javier Lopez <[email protected]>
---
drivers/net/wireless/mac80211_hwsim.c | 387 ++++++++++++++++++++++++++++++++-
drivers/net/wireless/mac80211_hwsim.h | 121 ++++++++++
2 files changed, 502 insertions(+), 6 deletions(-)
create mode 100644 drivers/net/wireless/mac80211_hwsim.h

diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index f4f4257..4a32328 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -1,6 +1,7 @@
/*
* mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
* Copyright (c) 2008, Jouni Malinen <[email protected]>
+ * Copyright (c) 2011, Javier Lopez <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -25,11 +26,15 @@
#include <linux/rtnetlink.h>
#include <linux/etherdevice.h>
#include <linux/debugfs.h>
+#include <net/genetlink.h>
+#include "mac80211_hwsim.h"

MODULE_AUTHOR("Jouni Malinen");
MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211");
MODULE_LICENSE("GPL");

+static atomic_t wmediumd_pid;
+
static int radios = 2;
module_param(radios, int, 0444);
MODULE_PARM_DESC(radios, "Number of simulated radios");
@@ -479,20 +484,96 @@ static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
}


+static int hwsim_frame_send_nl(struct mac_address *src,
+ struct sk_buff *my_skb, int _pid)
+{
+
+ struct ieee80211_tx_info *txi;
+ struct sk_buff *skb;
+ void *msg_head;
+ int rc;
+
+ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (skb == NULL) {
+ printk(KERN_DEBUG "mac80211_hwsim: problem allocating skb\n");
+ goto out;
+ }
+
+ msg_head = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
+ HWSIM_CMD_FRAME);
+ if (msg_head == NULL) {
+ printk(KERN_DEBUG "mac80211_hwsim: problem with msg_head\n");
+ goto out;
+ }
+
+ rc = nla_put(skb, HWSIM_ATTR_ADDR_TRANSMITTER,
+ sizeof(struct mac_address), src);
+ /* We get the skb->data */
+ rc = nla_put(skb, HWSIM_ATTR_FRAME, my_skb->len, my_skb->data);
+ /* We get a copy of the control buffer for this tx*/
+ rc = nla_put(skb, HWSIM_ATTR_CB_SKB, sizeof(my_skb->cb),
+ my_skb->cb);
+
+ txi = IEEE80211_SKB_CB(my_skb);
+
+ /* We get the flags for this transmission, wmediumd maybe
+ changes its behaviour depending on the flags */
+ rc = nla_put_u32(skb, HWSIM_ATTR_FLAGS, txi->flags);
+ /* We get the tx control (rate and retries) info*/
+ rc = nla_put(skb, HWSIM_ATTR_TX_INFO,
+ sizeof(struct ieee80211_tx_rate)*IEEE80211_TX_MAX_RATES,
+ txi->control.rates);
+
+ if (rc != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: "
+ "error filling msg payload\n");
+ goto out;
+ }
+
+ genlmsg_end(skb, msg_head);
+ rc = genlmsg_unicast(&init_net, skb, _pid);
+ if (rc != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: wmediumd not responding "
+ "at PID:%d, switching to no wmediumd mode.\n", _pid);
+ atomic_set(&wmediumd_pid, 0);
+ return -1;
+ }
+ return 0;
+
+out:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+}
+
static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
struct sk_buff *skb)
{
struct mac80211_hwsim_data *data = hw->priv, *data2;
- bool ack = false;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_rx_status rx_status;

+ bool ack = false;
+
if (data->idle) {
wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
return false;
}

+ if (data->ps != PS_DISABLED)
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+
+ /* wmediumd mode */
+ if (atomic_read(&wmediumd_pid)) {
+ /* If frame is correctly send through netlink return true*/
+ if (hwsim_frame_send_nl((struct mac_address *)
+ &data->addresses[1].addr, skb,
+ atomic_read(&wmediumd_pid)) == 0)
+ return true;
+ }
+
+ /* NO wmediumd, normal mac80211_hwsim behaviour*/
+
memset(&rx_status, 0, sizeof(rx_status));
/* TODO: set mactime */
rx_status.freq = data->channel->center_freq;
@@ -501,9 +582,6 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
/* TODO: simulate real signal strength (and optional packet loss) */
rx_status.signal = data->power_level - 50;

- if (data->ps != PS_DISABLED)
- hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
-
/* release the skb's source info */
skb_orphan(skb);
skb_dst_drop(skb);
@@ -530,9 +608,10 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
if (nskb == NULL)
continue;

+ memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
+
if (mac80211_hwsim_addr_match(data2, hdr->addr1))
ack = true;
- memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
ieee80211_rx_irqsafe(data2->hw, nskb);
}
spin_unlock(&hwsim_radio_lock);
@@ -540,12 +619,12 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
return ack;
}

-
static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
bool ack;
struct ieee80211_tx_info *txi;

+
mac80211_hwsim_monitor_rx(hw, skb);

if (skb->len < 10) {
@@ -555,6 +634,11 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
}

ack = mac80211_hwsim_tx_frame(hw, skb);
+ /* wmediumd mode*/
+ if (atomic_read(&wmediumd_pid))
+ return;
+
+ /* NO wmediumd, normal mac80211_hwsim behaviour*/
if (ack && skb->len >= 16) {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
mac80211_hwsim_monitor_ack(hw, hdr->addr2);
@@ -1244,6 +1328,283 @@ static int hwsim_fops_group_write(void *dat, u64 val)
return 0;
}

+
+struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(
+ struct mac_address *addr)
+{
+ struct mac80211_hwsim_data *data;
+ bool _found = false;
+
+ spin_lock_bh(&hwsim_radio_lock);
+ list_for_each_entry(data, &hwsim_radios, list) {
+ if (memcmp(data->addresses[1].addr, addr,
+ sizeof(struct mac_address)) == 0) {
+ _found = true;
+ break;
+ }
+ }
+ spin_unlock_bh(&hwsim_radio_lock);
+
+ if (!_found) {
+ printk(KERN_DEBUG "mac80211_hwsim: invalid radio ID\n");
+ return NULL;
+ }
+
+ return data;
+
+
+}
+
+static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct ieee80211_hdr *hdr;
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_tx_info *txi;
+ struct ieee80211_tx_rate *tx_attempts;
+ char *cb;
+
+ int i;
+
+ struct mac_address *dst = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]);
+ int frame_data_len = nla_len(info->attrs[HWSIM_ATTR_FRAME]);
+ char *frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_FRAME]);
+ int flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]);
+
+ /* Allocate new skb here */
+ struct sk_buff *skb = alloc_skb(IEEE80211_MAX_DATA_LEN, GFP_KERNEL);
+ if (skb == NULL)
+ goto out;
+
+ if (frame_data_len <= IEEE80211_MAX_DATA_LEN) {
+ /* Copy the data */
+ memcpy(skb_put(skb, frame_data_len), frame_data,
+ frame_data_len);
+ } else
+ goto out;
+
+ data2 = get_hwsim_data_ref_from_addr(dst);
+
+ if (data2 == NULL)
+ goto out;
+
+ /*Tx info received because the frame was broadcasted on user space,
+ so we get all the necessary info: tx attempts and skb control buffer*/
+
+ tx_attempts = (struct ieee80211_tx_rate *)nla_data(
+ info->attrs[HWSIM_ATTR_TX_INFO]);
+
+ if (tx_attempts == NULL)
+ goto out;
+
+ /* ieee80211_tx_status() does not dereference anything from the
+ ieee80211_tx_info structure included in this cb, so it is safe
+ to get whatever we get from userspace and copy it here. */
+
+ /* check size of received custom buffer */
+ if (nla_len(info->attrs[HWSIM_ATTR_CB_SKB]) != sizeof(skb->cb)) {
+ printk(KERN_DEBUG "mac80211_hwsim: not valid cb received\n");
+ goto out;
+ }
+
+ /* ieee80211_tx_status() does not dereference anything from the
+ ieee80211_tx_info structure included in this cb, so it is safe
+ to get whatever we get from userspace and copy it here. */
+
+ cb = (char *)nla_data(info->attrs[HWSIM_ATTR_CB_SKB]);
+ memcpy(skb->cb, cb, sizeof(skb->cb));
+
+ /* now send back TX status */
+ txi = IEEE80211_SKB_CB(skb);
+
+ if (txi->control.vif)
+ hwsim_check_magic(txi->control.vif);
+ if (txi->control.sta)
+ hwsim_check_sta_magic(txi->control.sta);
+
+ ieee80211_tx_info_clear_status(txi);
+ txi->flags = flags;
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ txi->status.rates[i].idx = tx_attempts[i].idx;
+ txi->status.rates[i].count = tx_attempts[i].count;
+ txi->status.rates[i].flags = tx_attempts[i].flags;
+ }
+
+ txi->status.ack_signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) &&
+ (txi->flags & IEEE80211_TX_STAT_ACK)) {
+ if (skb->len >= 16) {
+ hdr = (struct ieee80211_hdr *) skb->data;
+ mac80211_hwsim_monitor_ack(data2->hw, hdr->addr2);
+ }
+ }
+ ieee80211_tx_status_irqsafe(data2->hw, skb);
+ return 0;
+
+out:
+ kfree_skb(skb);
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+
+}
+
+static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_rx_status rx_status;
+
+ struct mac_address *dst = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_RECEIVER]);
+
+ int frame_data_len = nla_len(info->attrs[HWSIM_ATTR_FRAME]);
+ char* frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_FRAME]);
+ /* Allocate new skb here */
+ struct sk_buff *skb = alloc_skb(IEEE80211_MAX_DATA_LEN, GFP_KERNEL);
+ if (skb == NULL)
+ goto out;
+
+ if (frame_data_len <= IEEE80211_MAX_DATA_LEN) {
+ /* Copy the data */
+ memcpy(skb_put(skb, frame_data_len), frame_data,
+ frame_data_len);
+ } else
+ goto out;
+
+ data2 = get_hwsim_data_ref_from_addr(dst);
+
+ if (data2 == NULL)
+ goto out;
+
+ /*A frame is received from user space*/
+ memset(&rx_status, 0, sizeof(rx_status));
+ rx_status.freq = data2->channel->center_freq;
+ rx_status.band = data2->channel->band;
+ rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
+ rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
+ ieee80211_rx_irqsafe(data2->hw, skb);
+
+ return 0;
+out:
+ kfree_skb(skb);
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+}
+
+static int hwsim_register_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+ if (info == NULL)
+ goto out;
+
+ atomic_set(&wmediumd_pid, info->snd_pid);
+
+ printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, "
+ "switching to wmediumd mode with pid %d\n", info->snd_pid);
+
+ return 0;
+
+out:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+}
+
+/* Generic Netlink operations array */
+static struct genl_ops hwsim_ops[] = {
+ {
+ .cmd = HWSIM_CMD_REGISTER,
+ .flags = 0,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_register_received_nl,
+ .dumpit = NULL,
+ },
+ {
+ .cmd = HWSIM_CMD_FRAME,
+ .flags = 0,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_cloned_frame_received_nl,
+ .dumpit = NULL,
+ },
+ {
+ .cmd = HWSIM_CMD_TX_INFO_FRAME,
+ .flags = 0,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_tx_info_frame_received_nl,
+ .dumpit = NULL,
+ },
+};
+
+static int hwsim_init_netlink(void)
+{
+ int rc;
+ printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
+
+ atomic_set(&wmediumd_pid, 0);
+
+ rc = genl_register_family(&hwsim_genl_family);
+ if (rc != 0)
+ goto failure;
+ rc = genl_register_ops(&hwsim_genl_family, &hwsim_ops[0]);
+ if (rc != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: register ops: %i\n", rc);
+ genl_unregister_family(&hwsim_genl_family);
+ goto failure;
+ }
+ rc = genl_register_ops(&hwsim_genl_family, &hwsim_ops[1]);
+ if (rc != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: register ops: %i\n", rc);
+ genl_unregister_family(&hwsim_genl_family);
+ goto failure;
+ }
+ rc = genl_register_ops(&hwsim_genl_family, &hwsim_ops[2]);
+ if (rc != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: register ops: %i\n", rc);
+ genl_unregister_family(&hwsim_genl_family);
+ goto failure;
+ }
+ return 0;
+
+failure:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+}
+
+static void hwsim_exit_netlink(void)
+{
+ int ret;
+
+ printk(KERN_INFO "mac80211_hwsim: closing netlink\n");
+ /*unregister the functions*/
+
+ ret = genl_unregister_ops(&hwsim_genl_family, &hwsim_ops[0]);
+ if (ret != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: unregister ops: %i\n", ret);
+ return;
+ }
+ ret = genl_unregister_ops(&hwsim_genl_family, &hwsim_ops[1]);
+ if (ret != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: unregister ops: %i\n", ret);
+ return;
+ }
+ ret = genl_unregister_ops(&hwsim_genl_family, &hwsim_ops[2]);
+ if (ret != 0) {
+ printk(KERN_DEBUG "mac80211_hwsim: unregister ops: %i\n", ret);
+ return;
+ }
+ ret = genl_unregister_family(&hwsim_genl_family);
+ if (ret != 0)
+ printk(KERN_DEBUG "mac80211_hwsim: "
+ "unregister family %i\n", ret);
+}
+
+
DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group,
hwsim_fops_group_read, hwsim_fops_group_write,
"%llx\n");
@@ -1379,6 +1740,10 @@ static int __init init_mac80211_hwsim(void)
data->group = 1;
mutex_init(&data->mutex);

+ /* Enable frame retransmission mechanism for a lossy channel */
+ hw->max_rates = 4;
+ hw->max_rate_tries = 11;
+
/* Work to be done prior to ieee80211_register_hw() */
switch (regtest) {
case HWSIM_REGTEST_DISABLED:
@@ -1528,8 +1893,16 @@ static int __init init_mac80211_hwsim(void)

rtnl_unlock();

+ err = hwsim_init_netlink();
+ if (err < 0)
+ goto failed_nl;
+
return 0;

+failed_nl:
+ printk(KERN_DEBUG "mac80211_hwsim: failed initializing netlink\n");
+ return err;
+
failed_mon:
rtnl_unlock();
free_netdev(hwsim_mon);
@@ -1550,6 +1923,8 @@ static void __exit exit_mac80211_hwsim(void)
{
printk(KERN_DEBUG "mac80211_hwsim: unregister radios\n");

+ hwsim_exit_netlink();
+
mac80211_hwsim_free();
unregister_netdev(hwsim_mon);
}
diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h
new file mode 100644
index 0000000..c0411d5
--- /dev/null
+++ b/drivers/net/wireless/mac80211_hwsim.h
@@ -0,0 +1,121 @@
+/*
+ * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
+ * Copyright (c) 2008, Jouni Malinen <[email protected]>
+ * Copyright (c) 2011, Javier Lopez <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <net/mac80211.h>
+
+/**
+ * DOC: Frame transmission/registration support
+ *
+ * Frame transmission and registration support exists to allow userspace
+ * entities such as wmediumd to receive and process all broadcasted
+ * frames from a mac80211_hwsim radio device.
+ *
+ * This allow user space applications to decide if the frame should be
+ * dropped or not and implement a wireless medium simulator at user space.
+ *
+ * Registration is done by sending a register message to the driver and
+ * will be automatically unregistered if the user application doesn't
+ * responds to sent frames.
+ * Once registered the user application has to take responsibility of
+ * broadcasting the frames to all listening mac80211_hwsim radio
+ * interfaces.
+ *
+ * For more technical details, see the corresponding command descriptions
+ * below.
+ */
+
+/**
+ * enum hwsim_commands - supported hwsim commands
+ *
+ * @HWSIM_CMD_UNSPEC: unspecified command to catch errors
+ *
+ * @HWSIM_CMD_REGISTER: request to register and received all broadcasted
+ * frames by any mac80211_hwsim radio device.
+ * @HWSIM_CMD_FRAME: send/receive a broadcasted frame from/to kernel/user
+ * space, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER,
+ * %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE,
+ * %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_CB_SKB
+ * @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to
+ * kernel, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS,
+ * %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL
+ * @__HWSIM_CMD_MAX: enum limit
+ */
+enum {
+ HWSIM_CMD_UNSPEC,
+ HWSIM_CMD_REGISTER,
+ HWSIM_CMD_FRAME,
+ HWSIM_CMD_TX_INFO_FRAME,
+ __HWSIM_CMD_MAX,
+};
+#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)
+
+/**
+ * enum hwsim_attrs - hwsim netlink attributes
+ *
+ * @HWSIM_ATTR_UNSPEC: unspecified attribute to catch errors
+ *
+ * @HWSIM_ATTR_ADDR_RECEIVER: MAC address of the radio device that
+ * the frame is broadcasted to
+ * @HWSIM_ATTR_ADDR_TRANSMITTER: MAC address of the radio device that
+ * the frame was broadcasted from
+ * @HWSIM_ATTR_FRAME: Data array
+ * @HWSIM_ATTR_FLAGS: mac80211 transmission flags, used to process
+ properly the frame at user space
+ * @HWSIM_ATTR_RX_RATE: estimated rx rate index for this frame at user
+ space
+ * @HWSIM_ATTR_SIGNAL: estimated RX signal for this frame at user
+ space
+ * @HWSIM_ATTR_TX_INFO: ieee80211_tx_rate array
+ * @HWSIM_ATTR_CB_SKB: sk_buff custom buffer of the broadcasted frame
+ * @__HWSIM_ATTR_MAX: enum limit
+ */
+
+
+enum {
+ HWSIM_ATTR_UNSPEC,
+ HWSIM_ATTR_ADDR_RECEIVER,
+ HWSIM_ATTR_ADDR_TRANSMITTER,
+ HWSIM_ATTR_FRAME,
+ HWSIM_ATTR_FLAGS,
+ HWSIM_ATTR_RX_RATE,
+ HWSIM_ATTR_SIGNAL,
+ HWSIM_ATTR_TX_INFO,
+ HWSIM_ATTR_CB_SKB,
+ __HWSIM_ATTR_MAX,
+};
+#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
+
+static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
+ [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_FRAME] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+ [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 },
+ [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 },
+ [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 },
+ [HWSIM_ATTR_TX_INFO] = { .type = NLA_UNSPEC,
+ .len = IEEE80211_TX_MAX_RATES*sizeof(
+ struct ieee80211_tx_rate)},
+ [HWSIM_ATTR_CB_SKB] = { .type = NLA_BINARY, .len = 48*sizeof(char) },
+};
+
+#define VERSION_NR 1
+
+static struct genl_family hwsim_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = "MAC80211_HWSIM",
+ .version = VERSION_NR,
+ .maxattr = HWSIM_ATTR_MAX,
+};
--
1.7.0.4


2011-05-26 03:51:38

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v6] mac80211_hwsim driver support userspace frame tx/rx

On Wed, 2011-05-25 at 21:03 +0200, Javier Lopez wrote:

> +static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
> + struct sk_buff *skb)
> +{
> + if (wmediumd_pid) {
> + mac80211_hwsim_tx_frame_nl(hw, skb);
> + return true;
> + } else
> + return mac80211_hwsim_tx_frame_no_nl(hw, skb);
> +}

This is racy. That wouldn't be an issue at all, but you also have

> mac80211_hwsim_tx_frame(hw, skb);
> - dev_kfree_skb(skb);
> + if (!wmediumd_pid)
> + dev_kfree_skb(skb);

so that a race could lead to a double-free or a leak (not sure which
one, maybe both depending on which way it races). I suppose _tx_frame()
should just always consume the SKB so you have only one check. And maybe
use ACCESS_ONCE() or something so the compiler doesn't play tricks.

> + if (nla_len(info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]) !=
> + sizeof(struct mac_address))
> + goto out;

The policy should catch this. I think I may have pointed you to this
erroneously, possibly because I missed the policy (don't remember seeing
it before) -- should the policy really be in a header file? Normally
static variables shouldn't really be since they'd get duplicated, though
I must admit in this special case it seems very unlikely that two files
would include the header.

> +out:
> + return -1;

This is -EPERM ("Permission denied") going to userspace, almost
certainly not what you want.

> + dev_kfree_skb(skb);
> + return -1;

Ditto, same in a few other places later too.

> +/* Generic Netlink operations array */
> +static struct genl_ops hwsim_ops[] = {
> + {
> + .cmd = HWSIM_CMD_REGISTER,
> + .flags = 0,
> + .policy = hwsim_genl_policy,
> + .doit = hwsim_register_received_nl,
> + .dumpit = NULL,

No need for explicit 0/NULL initialisation, just FYI (I don't really
mind in this case, doesn't seem excessive). However, are you sure we
should allow any user to run this? While it should be safe, do we want
to risk it? More of a philosophical question I guess.

> +failure:
> + printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
> + return -1;

Same here with the error code, this could be propagated as "permission
denied" while loading the module which would be rather odd.

> +
> +
> +#define VERSION_NR 1

Did I miss a use of the version number somewhere? Seems kinda pointless
since if it's really totally incompatible the only reasonable way would
be to renumber the commands or rename the genl family.

johannes


2011-05-01 21:35:53

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v3] mac80211_hwsim driver support userspace frame tx/rx

Hey,

I never got around to looking at this patch before, sorry. Some
comments.

> +static int hwsim_frame_send_nl(struct mac_address *src,
> + struct sk_buff *my_skb, int _pid)
> +{
> +
> + struct ieee80211_tx_info *txi;
> + struct sk_buff *skb;
> + void *msg_head;
> + int rc;
> +
> + skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);

This would be better with a proper size, if Felix actually adds a-MSDU
support then SKBs might become larger than GOODSIZE. Problem is I'm not
exactly sure how to calculate the right size, my_skb->size + some small
constant (20 or so?) should do though.

> + if (skb == NULL) {
> + printk(KERN_DEBUG "mac80211_hwsim: problem allocating skb\n");
> + goto out;
> + }

That might be the least of your worries when the system is under memory
pressure, I think you should remove this message.

> + /* We get a copy of the control buffer for this tx*/
> + rc = nla_put(skb, HWSIM_ATTR_CB_SKB, sizeof(my_skb->cb),
> + my_skb->cb);

Do we really want to expose the raw information here? It contains
pointers like the station pointer, which is kinda useless, no?

> + /* We get the flags for this transmission, wmediumd maybe
> + changes its behaviour depending on the flags */
> + rc = nla_put_u32(skb, HWSIM_ATTR_FLAGS, txi->flags);
> + /* We get the tx control (rate and retries) info*/
> + rc = nla_put(skb, HWSIM_ATTR_TX_INFO,

You're losing the first "rc". Why not use NLA_PUT_U32 which contains the
goto nla_put_failure?

> + sizeof(struct ieee80211_tx_rate)*IEEE80211_TX_MAX_RATES,
> + txi->control.rates);

Why do you also expose this separately when it's already part of the CB?
Still I'm not sure we should expose such APIs, Felix is working on
changing this, do you really want to update wmediumd all the time? How
would you version that to detect which version to use?

> + genlmsg_end(skb, msg_head);
> + rc = genlmsg_unicast(&init_net, skb, _pid);
> + if (rc != 0) {
> + printk(KERN_DEBUG "mac80211_hwsim: wmediumd not responding "
> + "at PID:%d, switching to no wmediumd mode.\n", _pid);
> + atomic_set(&wmediumd_pid, 0);
> + return -1;

That actually works? (but see below)

> static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
> struct sk_buff *skb)
> {
> struct mac80211_hwsim_data *data = hw->priv, *data2;
> - bool ack = false;
> struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
> struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
> struct ieee80211_rx_status rx_status;
>
> + bool ack = false;
> +
> if (data->idle) {
> wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
> return false;
> }
>
> + if (data->ps != PS_DISABLED)
> + hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
> +
> + /* wmediumd mode */
> + if (atomic_read(&wmediumd_pid)) {
> + /* If frame is correctly send through netlink return true*/
> + if (hwsim_frame_send_nl((struct mac_address *)
> + &data->addresses[1].addr, skb,
> + atomic_read(&wmediumd_pid)) == 0)
> + return true;

This doesn't make sense. Why would you assume an ACK if you handed it
off to userspace? Wouldn't wmediumd get to decide whether an ACK was
given? Or did you change the return semantics of this function?

> @@ -555,6 +634,11 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
> }
>
> ack = mac80211_hwsim_tx_frame(hw, skb);
> + /* wmediumd mode*/
> + if (atomic_read(&wmediumd_pid))
> + return;

I guess you did. Would it make sense to just move the idle stuff into
this function, and handle the wmediumd mode separately completely?

> @@ -1244,6 +1328,283 @@ static int hwsim_fops_group_write(void *dat, u64 val)
> return 0;
> }
>
> +
> +struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(
> + struct mac_address *addr)
> +{

> + if (!_found) {
> + printk(KERN_DEBUG "mac80211_hwsim: invalid radio ID\n");

I'd prefer not having messages that userspace can trigger, even if it
needs to be privileged.

> + int frame_data_len = nla_len(info->attrs[HWSIM_ATTR_FRAME]);
> + char *frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_FRAME]);
> + int flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]);
> +
> + /* Allocate new skb here */
> + struct sk_buff *skb = alloc_skb(IEEE80211_MAX_DATA_LEN, GFP_KERNEL);

Why not allocate the right size?

> + if (skb == NULL)
> + goto out;
> +
> + if (frame_data_len <= IEEE80211_MAX_DATA_LEN) {

Then you don't even need the check. Oh crap, I just realised that in
some corner cases in mac80211 I'm now making the assumption that the
same SKB is used. I guess that's wrong, something we should discuss,
should we make that assumption (and change this stuff here)? But I guess
we'd better remove that assumption, I'll take a look.

> + if (data2 == NULL)
> + goto out;
> +
> + /*Tx info received because the frame was broadcasted on user space,
> + so we get all the necessary info: tx attempts and skb control buffer*/

Comment style?

> + tx_attempts = (struct ieee80211_tx_rate *)nla_data(
> + info->attrs[HWSIM_ATTR_TX_INFO]);
> +
> + if (tx_attempts == NULL)
> + goto out;
> +
> + /* ieee80211_tx_status() does not dereference anything from the
> + ieee80211_tx_info structure included in this cb, so it is safe
> + to get whatever we get from userspace and copy it here. */

Ditto. This seems a bit odd though, while we do that right now I
wouldn't necessarily trust that for a userspace API.

In fact, why don't we just hang on to the SKB? Then we just need to pass
a cookie to userspace, and then free release the skb. If userspace
doesn't respond with the cookie, we'd be able to "leak" memory, but we
can require userspace returns some status before too many frames build
up maybe?

> + /* ieee80211_tx_status() does not dereference anything from the
> + ieee80211_tx_info structure included in this cb, so it is safe
> + to get whatever we get from userspace and copy it here. */
> +
> + cb = (char *)nla_data(info->attrs[HWSIM_ATTR_CB_SKB]);
> + memcpy(skb->cb, cb, sizeof(skb->cb));
> +
> + /* now send back TX status */
> + txi = IEEE80211_SKB_CB(skb);
> +
> + if (txi->control.vif)
> + hwsim_check_magic(txi->control.vif);
> + if (txi->control.sta)
> + hwsim_check_sta_magic(txi->control.sta);

Err, this dereferences them after you just said it wouldn't be safe.
That's really not a good idea now that you've copied it from userspace.

> +static int hwsim_register_received_nl(struct sk_buff *skb_2,
> + struct genl_info *info)
> +{
> + if (info == NULL)
> + goto out;
> +
> + atomic_set(&wmediumd_pid, info->snd_pid);
> +
> + printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, "
> + "switching to wmediumd mode with pid %d\n", info->snd_pid);
> +
> + return 0;
> +
> +out:
> + printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
> + return -1;
> +}
> +
> +/* Generic Netlink operations array */
> +static struct genl_ops hwsim_ops[] = {
> + {
> + .cmd = HWSIM_CMD_REGISTER,
> + .flags = 0,
> + .policy = hwsim_genl_policy,
> + .doit = hwsim_register_received_nl,
> + .dumpit = NULL,
> + },
> + {
> + .cmd = HWSIM_CMD_FRAME,
> + .flags = 0,
> + .policy = hwsim_genl_policy,
> + .doit = hwsim_cloned_frame_received_nl,
> + .dumpit = NULL,
> + },
> + {
> + .cmd = HWSIM_CMD_TX_INFO_FRAME,
> + .flags = 0,
> + .policy = hwsim_genl_policy,
> + .doit = hwsim_tx_info_frame_received_nl,
> + .dumpit = NULL,
> + },

Oh, and in fact, you aren't even requiring it to be privileged. That's a
problem waiting to happen if you keep the userspace API as it is.

> +static int hwsim_init_netlink(void)
> +{
> + int rc;
> + printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
> +
> + atomic_set(&wmediumd_pid, 0);
> +
> + rc = genl_register_family(&hwsim_genl_family);
> + if (rc != 0)
> + goto failure;
> + rc = genl_register_ops(&hwsim_genl_family, &hwsim_ops[0]);

This would be easier with genl_register_family_with_ops().

> + ret = genl_unregister_ops(&hwsim_genl_family, &hwsim_ops[2]);
> + if (ret != 0) {
> + printk(KERN_DEBUG "mac80211_hwsim: unregister ops: %i\n", ret);
> + return;
> + }
> + ret = genl_unregister_family(&hwsim_genl_family);

Unregistering the ops isn't necessary, they go with the family.

> + * Registration is done by sending a register message to the driver and
> + * will be automatically unregistered if the user application doesn't
> + * responds to sent frames.

I think that logic should be replaced by using a netlink notifier that
tells you when the application closed the socket. We implement this for
some things in nl80211, see nl80211_netlink_notify().

> +static struct genl_family hwsim_genl_family = {
> + .id = GENL_ID_GENERATE,
> + .hdrsize = 0,
> + .name = "MAC80211_HWSIM",
> + .version = VERSION_NR,
> + .maxattr = HWSIM_ATTR_MAX,
> +};

This shouldn't be in a header file.



I think the API could use with some more thought. I'd much rather
translate the skb CB info into something userspace can parse, and
implement an skb status queue of outstanding SKBs that userspace needs
to give a status for. Use the pointer as a cookie, but don't dereference
it blindly (walk the SKB list when userspace gives a cookie and check
it's correct). When the skb queue gets too full, drop some, or so. Only
need up to four outstanding SKBs for each device anyway, I think? Maybe
not even that, not sure if/how QoS is relevant.

johannes


2011-05-11 21:33:19

by Javier López

[permalink] [raw]
Subject: [PATCH v4] mac80211_hwsim driver support userspace frame tx/rx

Hi all,

Thanks for all your previous comments Johannes, I tried to implement all the solutions you proposed me, they were very helpful.

In this new version of the patch I avoid the exchange of the Custom Buffer with the userspace. Instead of this I enqueue all pending skb's, for each pending skb a cookie is created and attached to the HWSIM_CMD_FRAME and HWSIM_CMD_TX_INFO.

Any new comments from this patch?

Thanks a lot,

Feel free to download the last version of the wmediumd tool from GitHub:

- Last version tarball: https://github.com/jlopex/cozybit/tarball/master
- Or visiting my github tree: https://github.com/jlopex/cozybit/tree

Javi

Signed-off-by: Javier Lopez <[email protected]>
---
drivers/net/wireless/mac80211_hwsim.c | 401 +++++++++++++++++++++++++++++++--
drivers/net/wireless/mac80211_hwsim.h | 114 ++++++++++
2 files changed, 499 insertions(+), 16 deletions(-)
create mode 100644 drivers/net/wireless/mac80211_hwsim.h

diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index f4f4257..fdb8a00 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -1,6 +1,7 @@
/*
* mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
* Copyright (c) 2008, Jouni Malinen <[email protected]>
+ * Copyright (c) 2011, Javier Lopez <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -25,11 +26,18 @@
#include <linux/rtnetlink.h>
#include <linux/etherdevice.h>
#include <linux/debugfs.h>
+#include <net/genetlink.h>
+#include "mac80211_hwsim.h"
+
+#define WARN_QUEUE 100
+#define MAX_QUEUE 200

MODULE_AUTHOR("Jouni Malinen");
MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211");
MODULE_LICENSE("GPL");

+static atomic_t wmediumd_pid;
+
static int radios = 2;
module_param(radios, int, 0444);
MODULE_PARM_DESC(radios, "Number of simulated radios");
@@ -302,6 +310,8 @@ struct mac80211_hwsim_data {
struct dentry *debugfs;
struct dentry *debugfs_ps;

+ struct sk_buff_head pending; /* packets pending */
+
/*
* Only radios in the same group can communicate together (the
* channel has to match too). Each bit represents a group. A
@@ -322,6 +332,14 @@ struct hwsim_radiotap_hdr {
__le16 rt_chbitmask;
} __packed;

+/* MAC80211_HWSIM netlink family */
+static struct genl_family hwsim_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = "MAC80211_HWSIM",
+ .version = VERSION_NR,
+ .maxattr = HWSIM_ATTR_MAX,
+};

static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb,
struct net_device *dev)
@@ -478,21 +496,90 @@ static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
return md.ret;
}

+static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
+ struct sk_buff *my_skb)
+{
+ struct sk_buff *skb;
+ struct mac80211_hwsim_data *data = hw->priv;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) my_skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(my_skb);
+ void *msg_head;

-static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
- struct sk_buff *skb)
+ if (data->idle) {
+ wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
+ dev_kfree_skb(my_skb);
+ return;
+ }
+
+ if (data->ps != PS_DISABLED)
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+
+ /* If the queue contains MAX_QUEUE skb's drop some */
+ if (skb_queue_len(&data->pending) >= MAX_QUEUE) {
+
+ printk(KERN_DEBUG "mac80211_hwsim: queues at maximum level, "
+ "droping some old frames\n");
+ while (skb_queue_len(&data->pending) >= WARN_QUEUE)
+ skb_dequeue(&data->pending);
+ }
+
+ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (skb == NULL)
+ goto nla_put_failure;
+
+ msg_head = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
+ HWSIM_CMD_FRAME);
+ if (msg_head == NULL) {
+ printk(KERN_DEBUG "mac80211_hwsim: problem with msg_head\n");
+ goto nla_put_failure;
+ }
+
+ NLA_PUT(skb, HWSIM_ATTR_ADDR_TRANSMITTER,
+ sizeof(struct mac_address), data->addresses[1].addr);
+
+ /* We get the skb->data */
+ NLA_PUT(skb, HWSIM_ATTR_FRAME, my_skb->len, my_skb->data);
+
+ /* We get the flags for this transmission, wmediumd maybe
+ changes its behaviour depending on the flags */
+ NLA_PUT_U32(skb, HWSIM_ATTR_FLAGS, info->flags);
+
+ /* We get the tx control (rate and retries) info*/
+ NLA_PUT(skb, HWSIM_ATTR_TX_INFO,
+ sizeof(struct ieee80211_tx_rate)*IEEE80211_TX_MAX_RATES,
+ info->control.rates);
+ /* We create a cookie to identify this skb */
+ NLA_PUT_U32(skb, HWSIM_ATTR_COOKIE, (unsigned long) my_skb);
+
+ genlmsg_end(skb, msg_head);
+ genlmsg_unicast(&init_net, skb, atomic_read(&wmediumd_pid));
+
+ /* Enqueue the packet */
+ skb_queue_tail(&data->pending, my_skb);
+ return;
+
+nla_put_failure:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+}
+
+static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
+ struct sk_buff *skb)
{
struct mac80211_hwsim_data *data = hw->priv, *data2;
- bool ack = false;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
struct ieee80211_rx_status rx_status;

+ bool ack = false;
+
if (data->idle) {
wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
return false;
}

+ if (data->ps != PS_DISABLED)
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+
memset(&rx_status, 0, sizeof(rx_status));
/* TODO: set mactime */
rx_status.freq = data->channel->center_freq;
@@ -501,9 +588,6 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
/* TODO: simulate real signal strength (and optional packet loss) */
rx_status.signal = data->power_level - 50;

- if (data->ps != PS_DISABLED)
- hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
-
/* release the skb's source info */
skb_orphan(skb);
skb_dst_drop(skb);
@@ -530,9 +614,10 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
if (nskb == NULL)
continue;

+ memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
+
if (mac80211_hwsim_addr_match(data2, hdr->addr1))
ack = true;
- memcpy(IEEE80211_SKB_RXCB(nskb), &rx_status, sizeof(rx_status));
ieee80211_rx_irqsafe(data2->hw, nskb);
}
spin_unlock(&hwsim_radio_lock);
@@ -540,6 +625,15 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
return ack;
}

+static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
+ struct sk_buff *skb)
+{
+ if (atomic_read(&wmediumd_pid)) {
+ mac80211_hwsim_tx_frame_nl(hw, skb);
+ return true;
+ } else
+ return mac80211_hwsim_tx_frame_no_nl(hw, skb);
+}

static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
@@ -555,6 +649,12 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
}

ack = mac80211_hwsim_tx_frame(hw, skb);
+
+ /* wmediumd mode*/
+ if (atomic_read(&wmediumd_pid))
+ return;
+
+ /* NO wmediumd, normal mac80211_hwsim behaviour*/
if (ack && skb->len >= 16) {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
mac80211_hwsim_monitor_ack(hw, hdr->addr2);
@@ -571,6 +671,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) && ack)
txi->flags |= IEEE80211_TX_STAT_ACK;
ieee80211_tx_status_irqsafe(hw, skb);
+
}


@@ -650,7 +751,6 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,

mac80211_hwsim_monitor_rx(hw, skb);
mac80211_hwsim_tx_frame(hw, skb);
- dev_kfree_skb(skb);
}


@@ -966,12 +1066,9 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,

static void mac80211_hwsim_flush(struct ieee80211_hw *hw, bool drop)
{
- /*
- * In this special case, there's nothing we need to
- * do because hwsim does transmission synchronously.
- * In the future, when it does transmissions via
- * userspace, we may need to do something.
- */
+ /* Let's purge the pending queue */
+ struct mac80211_hwsim_data *data = hw->priv;
+ skb_queue_purge(&data->pending);
}

struct hw_scan_done {
@@ -1139,7 +1236,6 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
memcpy(pspoll->ta, mac, ETH_ALEN);
if (!mac80211_hwsim_tx_frame(data->hw, skb))
printk(KERN_DEBUG "%s: PS-Poll frame not ack'ed\n", __func__);
- dev_kfree_skb(skb);
}


@@ -1170,7 +1266,6 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
memcpy(hdr->addr3, vp->bssid, ETH_ALEN);
if (!mac80211_hwsim_tx_frame(data->hw, skb))
printk(KERN_DEBUG "%s: nullfunc frame not ack'ed\n", __func__);
- dev_kfree_skb(skb);
}


@@ -1244,6 +1339,264 @@ static int hwsim_fops_group_write(void *dat, u64 val)
return 0;
}

+
+struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(
+ struct mac_address *addr)
+{
+ struct mac80211_hwsim_data *data;
+ bool _found = false;
+
+ spin_lock_bh(&hwsim_radio_lock);
+ list_for_each_entry(data, &hwsim_radios, list) {
+ if (memcmp(data->addresses[1].addr, addr,
+ sizeof(struct mac_address)) == 0) {
+ _found = true;
+ break;
+ }
+ }
+ spin_unlock_bh(&hwsim_radio_lock);
+
+ if (!_found) {
+ printk(KERN_DEBUG "mac80211_hwsim: invalid radio ID\n");
+ return NULL;
+ }
+
+ return data;
+}
+
+static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct ieee80211_hdr *hdr;
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_tx_info *txi;
+ struct ieee80211_tx_rate *tx_attempts;
+ struct sk_buff __user *ret_skb;
+ struct sk_buff *skb = NULL, *tmp;
+
+ int i;
+ bool found = false;
+
+ struct mac_address *dst = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]);
+ int flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]);
+
+ ret_skb = (struct sk_buff __user *)
+ (unsigned long) nla_get_u32(info->attrs[HWSIM_ATTR_COOKIE]);
+
+ data2 = get_hwsim_data_ref_from_addr(dst);
+
+ if (data2 == NULL)
+ goto out;
+
+ /* look for the skb matching the cookie passed back from user */
+ skb_queue_walk_safe(&data2->pending, skb, tmp) {
+ if (skb == ret_skb) {
+ skb_unlink(skb, &data2->pending);
+ found = true;
+ break;
+ }
+ }
+
+ /* not found */
+ if (!found)
+ return -1;
+
+ /* Tx info received because the frame was broadcasted on user space,
+ so we get all the necessary info: tx attempts and skb control buff */
+
+ tx_attempts = (struct ieee80211_tx_rate *)nla_data(
+ info->attrs[HWSIM_ATTR_TX_INFO]);
+
+ if (tx_attempts == NULL)
+ goto out;
+
+ /* now send back TX status */
+ txi = IEEE80211_SKB_CB(skb);
+
+ if (txi->control.vif)
+ hwsim_check_magic(txi->control.vif);
+ if (txi->control.sta)
+ hwsim_check_sta_magic(txi->control.sta);
+
+ ieee80211_tx_info_clear_status(txi);
+ txi->flags = flags;
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ txi->status.rates[i].idx = tx_attempts[i].idx;
+ txi->status.rates[i].count = tx_attempts[i].count;
+ txi->status.rates[i].flags = tx_attempts[i].flags;
+ }
+
+ txi->status.ack_signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) &&
+ (txi->flags & IEEE80211_TX_STAT_ACK)) {
+ if (skb->len >= 16) {
+ hdr = (struct ieee80211_hdr *) skb->data;
+ mac80211_hwsim_monitor_ack(data2->hw, hdr->addr2);
+ }
+ }
+ ieee80211_tx_status_irqsafe(data2->hw, skb);
+
+ return 0;
+
+out:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+
+}
+
+static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_rx_status rx_status;
+
+ struct mac_address *dst = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_RECEIVER]);
+
+ int frame_data_len = nla_len(info->attrs[HWSIM_ATTR_FRAME]);
+ char* frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_FRAME]);
+ /* Allocate new skb here */
+ struct sk_buff *skb = alloc_skb(frame_data_len, GFP_KERNEL);
+ if (skb == NULL)
+ goto out;
+
+ if (frame_data_len <= IEEE80211_MAX_DATA_LEN) {
+ /* Copy the data */
+ memcpy(skb_put(skb, frame_data_len), frame_data,
+ frame_data_len);
+ } else
+ goto out;
+
+ data2 = get_hwsim_data_ref_from_addr(dst);
+
+ if (data2 == NULL)
+ goto out;
+
+ /*A frame is received from user space*/
+ memset(&rx_status, 0, sizeof(rx_status));
+ rx_status.freq = data2->channel->center_freq;
+ rx_status.band = data2->channel->band;
+ rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
+ rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
+ ieee80211_rx_irqsafe(data2->hw, skb);
+
+ return 0;
+out:
+ kfree_skb(skb);
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+}
+
+static int hwsim_register_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+ if (info == NULL)
+ goto out;
+
+ atomic_set(&wmediumd_pid, info->snd_pid);
+
+ printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, "
+ "switching to wmediumd mode with pid %d\n", info->snd_pid);
+
+ return 0;
+
+out:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+}
+
+/* Generic Netlink operations array */
+static struct genl_ops hwsim_ops[] = {
+ {
+ .cmd = HWSIM_CMD_REGISTER,
+ .flags = 0,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_register_received_nl,
+ .dumpit = NULL,
+ },
+ {
+ .cmd = HWSIM_CMD_FRAME,
+ .flags = 0,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_cloned_frame_received_nl,
+ .dumpit = NULL,
+ },
+ {
+ .cmd = HWSIM_CMD_TX_INFO_FRAME,
+ .flags = 0,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_tx_info_frame_received_nl,
+ .dumpit = NULL,
+ },
+};
+
+static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
+ unsigned long state,
+ void *_notify)
+{
+ struct netlink_notify *notify = _notify;
+
+ if (state != NETLINK_URELEASE)
+ return NOTIFY_DONE;
+
+ if (notify->pid == atomic_read(&wmediumd_pid)) {
+ printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink"
+ " socket, switching to perfect channel medium\n");
+ atomic_set(&wmediumd_pid, 0);
+ }
+ return NOTIFY_DONE;
+
+}
+
+static struct notifier_block hwsim_netlink_notifier = {
+ .notifier_call = mac80211_hwsim_netlink_notify,
+};
+
+static int hwsim_init_netlink(void)
+{
+ int rc;
+ printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
+
+ atomic_set(&wmediumd_pid, 0);
+
+ rc = genl_register_family_with_ops(&hwsim_genl_family,
+ hwsim_ops, ARRAY_SIZE(hwsim_ops));
+ if (rc)
+ goto failure;
+
+ rc = netlink_register_notifier(&hwsim_netlink_notifier);
+ if (rc)
+ goto failure;
+
+ return 0;
+
+failure:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+}
+
+static void hwsim_exit_netlink(void)
+{
+ int ret;
+
+ printk(KERN_INFO "mac80211_hwsim: closing netlink\n");
+ /* unregister the notifier */
+ netlink_unregister_notifier(&hwsim_netlink_notifier);
+ /* unregister the family */
+ ret = genl_unregister_family(&hwsim_genl_family);
+ if (ret)
+ printk(KERN_DEBUG "mac80211_hwsim: "
+ "unregister family %i\n", ret);
+}
+
+
DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group,
hwsim_fops_group_read, hwsim_fops_group_write,
"%llx\n");
@@ -1299,6 +1652,8 @@ static int __init init_mac80211_hwsim(void)
}
data->dev->driver = &mac80211_hwsim_driver;

+ skb_queue_head_init(&data->pending);
+
SET_IEEE80211_DEV(hw, data->dev);
addr[3] = i >> 8;
addr[4] = i;
@@ -1379,6 +1734,10 @@ static int __init init_mac80211_hwsim(void)
data->group = 1;
mutex_init(&data->mutex);

+ /* Enable frame retransmission mechanism for a lossy channel */
+ hw->max_rates = 4;
+ hw->max_rate_tries = 11;
+
/* Work to be done prior to ieee80211_register_hw() */
switch (regtest) {
case HWSIM_REGTEST_DISABLED:
@@ -1528,8 +1887,16 @@ static int __init init_mac80211_hwsim(void)

rtnl_unlock();

+ err = hwsim_init_netlink();
+ if (err < 0)
+ goto failed_nl;
+
return 0;

+failed_nl:
+ printk(KERN_DEBUG "mac80211_hwsim: failed initializing netlink\n");
+ return err;
+
failed_mon:
rtnl_unlock();
free_netdev(hwsim_mon);
@@ -1550,6 +1917,8 @@ static void __exit exit_mac80211_hwsim(void)
{
printk(KERN_DEBUG "mac80211_hwsim: unregister radios\n");

+ hwsim_exit_netlink();
+
mac80211_hwsim_free();
unregister_netdev(hwsim_mon);
}
diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h
new file mode 100644
index 0000000..803786e
--- /dev/null
+++ b/drivers/net/wireless/mac80211_hwsim.h
@@ -0,0 +1,114 @@
+/*
+ * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
+ * Copyright (c) 2008, Jouni Malinen <[email protected]>
+ * Copyright (c) 2011, Javier Lopez <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <net/mac80211.h>
+
+/**
+ * DOC: Frame transmission/registration support
+ *
+ * Frame transmission and registration support exists to allow userspace
+ * entities such as wmediumd to receive and process all broadcasted
+ * frames from a mac80211_hwsim radio device.
+ *
+ * This allow user space applications to decide if the frame should be
+ * dropped or not and implement a wireless medium simulator at user space.
+ *
+ * Registration is done by sending a register message to the driver and
+ * will be automatically unregistered if the user application doesn't
+ * responds to sent frames.
+ * Once registered the user application has to take responsibility of
+ * broadcasting the frames to all listening mac80211_hwsim radio
+ * interfaces.
+ *
+ * For more technical details, see the corresponding command descriptions
+ * below.
+ */
+
+/**
+ * enum hwsim_commands - supported hwsim commands
+ *
+ * @HWSIM_CMD_UNSPEC: unspecified command to catch errors
+ *
+ * @HWSIM_CMD_REGISTER: request to register and received all broadcasted
+ * frames by any mac80211_hwsim radio device.
+ * @HWSIM_CMD_FRAME: send/receive a broadcasted frame from/to kernel/user
+ * space, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER,
+ * %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE,
+ * %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
+ * @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to
+ * kernel, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS,
+ * %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
+ * @__HWSIM_CMD_MAX: enum limit
+ */
+enum {
+ HWSIM_CMD_UNSPEC,
+ HWSIM_CMD_REGISTER,
+ HWSIM_CMD_FRAME,
+ HWSIM_CMD_TX_INFO_FRAME,
+ __HWSIM_CMD_MAX,
+};
+#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)
+
+/**
+ * enum hwsim_attrs - hwsim netlink attributes
+ *
+ * @HWSIM_ATTR_UNSPEC: unspecified attribute to catch errors
+ *
+ * @HWSIM_ATTR_ADDR_RECEIVER: MAC address of the radio device that
+ * the frame is broadcasted to
+ * @HWSIM_ATTR_ADDR_TRANSMITTER: MAC address of the radio device that
+ * the frame was broadcasted from
+ * @HWSIM_ATTR_FRAME: Data array
+ * @HWSIM_ATTR_FLAGS: mac80211 transmission flags, used to process
+ properly the frame at user space
+ * @HWSIM_ATTR_RX_RATE: estimated rx rate index for this frame at user
+ space
+ * @HWSIM_ATTR_SIGNAL: estimated RX signal for this frame at user
+ space
+ * @HWSIM_ATTR_TX_INFO: ieee80211_tx_rate array
+ * @HWSIM_ATTR_COOKIE: sk_buff cookie to identify the frame
+ * @__HWSIM_ATTR_MAX: enum limit
+ */
+
+
+enum {
+ HWSIM_ATTR_UNSPEC,
+ HWSIM_ATTR_ADDR_RECEIVER,
+ HWSIM_ATTR_ADDR_TRANSMITTER,
+ HWSIM_ATTR_FRAME,
+ HWSIM_ATTR_FLAGS,
+ HWSIM_ATTR_RX_RATE,
+ HWSIM_ATTR_SIGNAL,
+ HWSIM_ATTR_TX_INFO,
+ HWSIM_ATTR_COOKIE,
+ __HWSIM_ATTR_MAX,
+};
+#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
+
+static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
+ [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_FRAME] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+ [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 },
+ [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 },
+ [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 },
+ [HWSIM_ATTR_TX_INFO] = { .type = NLA_UNSPEC,
+ .len = IEEE80211_TX_MAX_RATES*sizeof(
+ struct ieee80211_tx_rate)},
+ [HWSIM_ATTR_COOKIE] = { .type = NLA_U32 },
+};
+
+#define VERSION_NR 1
+
--
1.7.0.4


2011-05-25 10:42:05

by Javier López

[permalink] [raw]
Subject: [PATCH v5] mac80211_hwsim driver support userspace frame tx/rx

Hello all,

This a new version from the patch, I added all the suggestions Johannes made from the previous one. Thanks!

* Solved a bug
* Avoided the use of ieee80211 kernel structures, instead of this I defined and I translate them to hwsim data structs.
* I changed the cookie netlink attribute to U64
* Frames are freed in no-wmediumd case
* Deleted some debug messages ;-)
* Netlink attribute check
* nla_len check for all data structures

I also updated the code of wmediumd to work with this new patch, like always you
can get your copy at GitHub:

* Last version tarball: https://github.com/jlopex/cozybit/tarball/master
* Or visiting my github tree: https://github.com/jlopex/cozybit/tree

Signed-off-by: Javier Lopez <[email protected]>
---
drivers/net/wireless/mac80211_hwsim.c | 430 ++++++++++++++++++++++++++++++++-
drivers/net/wireless/mac80211_hwsim.h | 150 ++++++++++++
2 files changed, 569 insertions(+), 11 deletions(-)
create mode 100644 drivers/net/wireless/mac80211_hwsim.h

diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index f4f4257..dfbbda3 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -1,6 +1,7 @@
/*
* mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
* Copyright (c) 2008, Jouni Malinen <[email protected]>
+ * Copyright (c) 2011, Javier Lopez <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -25,11 +26,17 @@
#include <linux/rtnetlink.h>
#include <linux/etherdevice.h>
#include <linux/debugfs.h>
+#include <net/genetlink.h>
+#include "mac80211_hwsim.h"
+
+#define WARN_QUEUE 100
+#define MAX_QUEUE 200

MODULE_AUTHOR("Jouni Malinen");
MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211");
MODULE_LICENSE("GPL");

+int wmediumd_pid;
static int radios = 2;
module_param(radios, int, 0444);
MODULE_PARM_DESC(radios, "Number of simulated radios");
@@ -302,6 +309,7 @@ struct mac80211_hwsim_data {
struct dentry *debugfs;
struct dentry *debugfs_ps;

+ struct sk_buff_head pending; /* packets pending */
/*
* Only radios in the same group can communicate together (the
* channel has to match too). Each bit represents a group. A
@@ -322,6 +330,14 @@ struct hwsim_radiotap_hdr {
__le16 rt_chbitmask;
} __packed;

+/* MAC80211_HWSIM netlinf family */
+static struct genl_family hwsim_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = "MAC80211_HWSIM",
+ .version = VERSION_NR,
+ .maxattr = HWSIM_ATTR_MAX,
+};

static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb,
struct net_device *dev)
@@ -478,9 +494,89 @@ static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
return md.ret;
}

+static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
+ struct sk_buff *my_skb)
+{
+ struct sk_buff *skb;
+ struct mac80211_hwsim_data *data = hw->priv;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) my_skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(my_skb);
+ void *msg_head;
+ unsigned int hwsim_flags = 0;
+ int i;
+ struct hwsim_tx_rate tx_attempts[IEEE80211_TX_MAX_RATES];

-static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
- struct sk_buff *skb)
+ if (data->idle) {
+ wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
+ dev_kfree_skb(my_skb);
+ return;
+ }
+
+ if (data->ps != PS_DISABLED)
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+
+ /* If the queue contains MAX_QUEUE skb's drop some */
+ if (skb_queue_len(&data->pending) >= MAX_QUEUE) {
+ /* Droping until WARN_QUEUE level */
+ while (skb_queue_len(&data->pending) >= WARN_QUEUE)
+ skb_dequeue(&data->pending);
+ }
+
+ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (skb == NULL)
+ goto nla_put_failure;
+
+ msg_head = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
+ HWSIM_CMD_FRAME);
+ if (msg_head == NULL) {
+ printk(KERN_DEBUG "mac80211_hwsim: problem with msg_head\n");
+ goto nla_put_failure;
+ }
+
+ NLA_PUT(skb, HWSIM_ATTR_ADDR_TRANSMITTER,
+ sizeof(struct mac_address), data->addresses[1].addr);
+
+ /* We get the skb->data */
+ NLA_PUT(skb, HWSIM_ATTR_FRAME, my_skb->len, my_skb->data);
+
+ /* We get the flags for this transmission, and we translate them to
+ wmediumd flags */
+
+ if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)
+ hwsim_flags |= HWSIM_TX_CTL_REQ_TX_STATUS;
+
+ if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+ hwsim_flags |= HWSIM_TX_CTL_NO_ACK;
+
+ NLA_PUT_U32(skb, HWSIM_ATTR_FLAGS, hwsim_flags);
+
+ /* We get the tx control (rate and retries) info*/
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ tx_attempts[i].idx = info->status.rates[i].idx;
+ tx_attempts[i].count = info->status.rates[i].count;
+ }
+
+ NLA_PUT(skb, HWSIM_ATTR_TX_INFO,
+ sizeof(struct hwsim_tx_rate)*IEEE80211_TX_MAX_RATES,
+ tx_attempts);
+
+ /* We create a cookie to identify this skb */
+ NLA_PUT_U64(skb, HWSIM_ATTR_COOKIE, (unsigned long) my_skb);
+
+ genlmsg_end(skb, msg_head);
+ genlmsg_unicast(&init_net, skb, wmediumd_pid);
+
+ /* Enqueue the packet */
+ skb_queue_tail(&data->pending, my_skb);
+ return;
+
+nla_put_failure:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+}
+
+static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
+ struct sk_buff *skb)
{
struct mac80211_hwsim_data *data = hw->priv, *data2;
bool ack = false;
@@ -540,6 +636,15 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
return ack;
}

+static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
+ struct sk_buff *skb)
+{
+ if (wmediumd_pid) {
+ mac80211_hwsim_tx_frame_nl(hw, skb);
+ return true;
+ } else
+ return mac80211_hwsim_tx_frame_no_nl(hw, skb);
+}

static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
@@ -555,6 +660,12 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
}

ack = mac80211_hwsim_tx_frame(hw, skb);
+
+ /* wmediumd mode check */
+ if (wmediumd_pid)
+ return;
+
+ /* NO wmediumd detected, perfect medium simulation */
if (ack && skb->len >= 16) {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
mac80211_hwsim_monitor_ack(hw, hdr->addr2);
@@ -650,7 +761,8 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,

mac80211_hwsim_monitor_rx(hw, skb);
mac80211_hwsim_tx_frame(hw, skb);
- dev_kfree_skb(skb);
+ if (!wmediumd_pid)
+ dev_kfree_skb(skb);
}


@@ -966,12 +1078,7 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,

static void mac80211_hwsim_flush(struct ieee80211_hw *hw, bool drop)
{
- /*
- * In this special case, there's nothing we need to
- * do because hwsim does transmission synchronously.
- * In the future, when it does transmissions via
- * userspace, we may need to do something.
- */
+ /* Not implemented, queues only on kernel side */
}

struct hw_scan_done {
@@ -1139,7 +1246,8 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
memcpy(pspoll->ta, mac, ETH_ALEN);
if (!mac80211_hwsim_tx_frame(data->hw, skb))
printk(KERN_DEBUG "%s: PS-Poll frame not ack'ed\n", __func__);
- dev_kfree_skb(skb);
+ if (!wmediumd_pid)
+ dev_kfree_skb(skb);
}


@@ -1170,7 +1278,8 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
memcpy(hdr->addr3, vp->bssid, ETH_ALEN);
if (!mac80211_hwsim_tx_frame(data->hw, skb))
printk(KERN_DEBUG "%s: nullfunc frame not ack'ed\n", __func__);
- dev_kfree_skb(skb);
+ if (!wmediumd_pid)
+ dev_kfree_skb(skb);
}


@@ -1248,6 +1357,290 @@ DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group,
hwsim_fops_group_read, hwsim_fops_group_write,
"%llx\n");

+struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(
+ struct mac_address *addr)
+{
+ struct mac80211_hwsim_data *data;
+ bool _found = false;
+
+ spin_lock_bh(&hwsim_radio_lock);
+ list_for_each_entry(data, &hwsim_radios, list) {
+ if (memcmp(data->addresses[1].addr, addr,
+ sizeof(struct mac_address)) == 0) {
+ _found = true;
+ break;
+ }
+ }
+ spin_unlock_bh(&hwsim_radio_lock);
+
+ if (!_found)
+ return NULL;
+
+ return data;
+}
+
+static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct ieee80211_hdr *hdr;
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_tx_info *txi;
+ struct hwsim_tx_rate *tx_attempts;
+ struct sk_buff __user *ret_skb;
+ struct sk_buff *skb, *tmp;
+ struct mac_address *src;
+ unsigned int hwsim_flags;
+
+ int i;
+ bool found = false;
+
+ if (!info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER] ||
+ !info->attrs[HWSIM_ATTR_FLAGS] ||
+ !info->attrs[HWSIM_ATTR_COOKIE] ||
+ !info->attrs[HWSIM_ATTR_TX_INFO])
+ goto out;
+
+ if (nla_len(info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]) !=
+ sizeof(struct mac_address))
+ goto out;
+
+ src = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]);
+ hwsim_flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]);
+
+ ret_skb = (struct sk_buff __user *)
+ (unsigned long) nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]);
+
+ data2 = get_hwsim_data_ref_from_addr(src);
+
+ if (data2 == NULL)
+ goto out;
+
+ /* look for the skb matching the cookie passed back from user */
+ skb_queue_walk_safe(&data2->pending, skb, tmp) {
+ if (skb == ret_skb) {
+ skb_unlink(skb, &data2->pending);
+ found = true;
+ break;
+ }
+ }
+
+ /* not found */
+ if (!found)
+ goto out;
+
+ /* Tx info received because the frame was broadcasted on user space,
+ so we get all the necessary info: tx attempts and skb control buff */
+
+ if (nla_len(info->attrs[HWSIM_ATTR_TX_INFO]) !=
+ sizeof(struct hwsim_tx_rate)*IEEE80211_TX_MAX_RATES)
+ goto out;
+
+ tx_attempts = (struct hwsim_tx_rate *)nla_data(
+ info->attrs[HWSIM_ATTR_TX_INFO]);
+
+ /* now send back TX status */
+ txi = IEEE80211_SKB_CB(skb);
+
+ if (txi->control.vif)
+ hwsim_check_magic(txi->control.vif);
+ if (txi->control.sta)
+ hwsim_check_sta_magic(txi->control.sta);
+
+ ieee80211_tx_info_clear_status(txi);
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ txi->status.rates[i].idx = tx_attempts[i].idx;
+ txi->status.rates[i].count = tx_attempts[i].count;
+ /*txi->status.rates[i].flags = 0;*/
+ }
+
+ txi->status.ack_signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ if (!(hwsim_flags & HWSIM_TX_CTL_NO_ACK) &&
+ (hwsim_flags & HWSIM_TX_STAT_ACK)) {
+ if (skb->len >= 16) {
+ hdr = (struct ieee80211_hdr *) skb->data;
+ mac80211_hwsim_monitor_ack(data2->hw, hdr->addr2);
+ }
+ }
+ ieee80211_tx_status_irqsafe(data2->hw, skb);
+ return 0;
+out:
+ return -1;
+
+}
+
+static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_rx_status rx_status;
+ struct mac_address *dst;
+ int frame_data_len;
+ char *frame_data;
+ struct sk_buff *skb = NULL;
+
+ if (!info->attrs[HWSIM_ATTR_ADDR_RECEIVER] ||
+ !info->attrs[HWSIM_ATTR_FRAME] ||
+ !info->attrs[HWSIM_ATTR_RX_RATE] ||
+ !info->attrs[HWSIM_ATTR_SIGNAL])
+ goto out;
+
+ if (nla_len(info->attrs[HWSIM_ATTR_ADDR_RECEIVER]) !=
+ sizeof(struct mac_address))
+ goto out;
+
+ dst = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_RECEIVER]);
+
+ frame_data_len = nla_len(info->attrs[HWSIM_ATTR_FRAME]);
+ frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_FRAME]);
+
+ /* Allocate new skb here */
+ skb = alloc_skb(frame_data_len, GFP_KERNEL);
+ if (skb == NULL)
+ goto err;
+
+ if (frame_data_len <= IEEE80211_MAX_DATA_LEN) {
+ /* Copy the data */
+ memcpy(skb_put(skb, frame_data_len), frame_data,
+ frame_data_len);
+ } else
+ goto err;
+
+ data2 = get_hwsim_data_ref_from_addr(dst);
+
+ if (data2 == NULL)
+ goto out;
+
+ /* check if radio is configured properly */
+
+ if (data2->idle || !data2->started || !data2->channel)
+ goto out;
+
+ /*A frame is received from user space*/
+ memset(&rx_status, 0, sizeof(rx_status));
+ rx_status.freq = data2->channel->center_freq;
+ rx_status.band = data2->channel->band;
+ rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
+ rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
+ ieee80211_rx_irqsafe(data2->hw, skb);
+
+ return 0;
+err:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ goto out;
+out:
+ dev_kfree_skb(skb);
+ return -1;
+}
+
+static int hwsim_register_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+ if (info == NULL)
+ goto out;
+
+ wmediumd_pid = info->snd_pid;
+
+ printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, "
+ "switching to wmediumd mode with pid %d\n", info->snd_pid);
+
+ return 0;
+out:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+}
+
+/* Generic Netlink operations array */
+static struct genl_ops hwsim_ops[] = {
+ {
+ .cmd = HWSIM_CMD_REGISTER,
+ .flags = 0,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_register_received_nl,
+ .dumpit = NULL,
+ },
+ {
+ .cmd = HWSIM_CMD_FRAME,
+ .flags = 0,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_cloned_frame_received_nl,
+ .dumpit = NULL,
+ },
+ {
+ .cmd = HWSIM_CMD_TX_INFO_FRAME,
+ .flags = 0,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_tx_info_frame_received_nl,
+ .dumpit = NULL,
+ },
+};
+
+static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
+ unsigned long state,
+ void *_notify)
+{
+ struct netlink_notify *notify = _notify;
+
+ if (state != NETLINK_URELEASE)
+ return NOTIFY_DONE;
+
+ if (notify->pid == wmediumd_pid) {
+ printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink"
+ " socket, switching to perfect channel medium\n");
+ wmediumd_pid = 0;
+ }
+ return NOTIFY_DONE;
+
+}
+
+static struct notifier_block hwsim_netlink_notifier = {
+ .notifier_call = mac80211_hwsim_netlink_notify,
+};
+
+static int hwsim_init_netlink(void)
+{
+ int rc;
+ printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
+
+ wmediumd_pid = 0;
+
+ rc = genl_register_family_with_ops(&hwsim_genl_family,
+ hwsim_ops, ARRAY_SIZE(hwsim_ops));
+ if (rc)
+ goto failure;
+
+ rc = netlink_register_notifier(&hwsim_netlink_notifier);
+ if (rc)
+ goto failure;
+
+ return 0;
+
+failure:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -1;
+}
+
+static void hwsim_exit_netlink(void)
+{
+ int ret;
+
+ printk(KERN_INFO "mac80211_hwsim: closing netlink\n");
+ /* unregister the notifier */
+ netlink_unregister_notifier(&hwsim_netlink_notifier);
+ /* unregister the family */
+ ret = genl_unregister_family(&hwsim_genl_family);
+ if (ret)
+ printk(KERN_DEBUG "mac80211_hwsim: "
+ "unregister family %i\n", ret);
+}
+
static int __init init_mac80211_hwsim(void)
{
int i, err = 0;
@@ -1298,6 +1691,7 @@ static int __init init_mac80211_hwsim(void)
goto failed_drvdata;
}
data->dev->driver = &mac80211_hwsim_driver;
+ skb_queue_head_init(&data->pending);

SET_IEEE80211_DEV(hw, data->dev);
addr[3] = i >> 8;
@@ -1379,6 +1773,10 @@ static int __init init_mac80211_hwsim(void)
data->group = 1;
mutex_init(&data->mutex);

+ /* Enable frame retransmissions for lossy channels */
+ hw->max_rates = 4;
+ hw->max_rate_tries = 11;
+
/* Work to be done prior to ieee80211_register_hw() */
switch (regtest) {
case HWSIM_REGTEST_DISABLED:
@@ -1528,8 +1926,16 @@ static int __init init_mac80211_hwsim(void)

rtnl_unlock();

+ err = hwsim_init_netlink();
+ if (err < 0)
+ goto failed_nl;
+
return 0;

+failed_nl:
+ printk(KERN_DEBUG "mac_80211_hwsim: failed initializing netlink\n");
+ return err;
+
failed_mon:
rtnl_unlock();
free_netdev(hwsim_mon);
@@ -1550,6 +1956,8 @@ static void __exit exit_mac80211_hwsim(void)
{
printk(KERN_DEBUG "mac80211_hwsim: unregister radios\n");

+ hwsim_exit_netlink();
+
mac80211_hwsim_free();
unregister_netdev(hwsim_mon);
}
diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h
new file mode 100644
index 0000000..d82287c
--- /dev/null
+++ b/drivers/net/wireless/mac80211_hwsim.h
@@ -0,0 +1,150 @@
+/*
+ * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
+ * Copyright (c) 2008, Jouni Malinen <[email protected]>
+ * Copyright (c) 2011, Javier Lopez <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <net/mac80211.h>
+
+/**
+ * enum hwsim_tx_control_flags - flags to describe transmission info/status
+ *
+ * These flags are used to give the wmediumd extra information in order to
+ * modify its behavior for each frame
+ *
+ * @HWSIM_TX_CTL_REQ_TX_STATUS: require TX status callback for this frame.
+ * @HWSIM_TX_CTL_NO_ACK: tell the wmediumd not to wait for an ack
+ * @HWSIM_TX_STAT_ACK: Frame was acknowledged
+ *
+ */
+enum hwsim_tx_control_flags {
+ HWSIM_TX_CTL_REQ_TX_STATUS = BIT(0),
+ HWSIM_TX_CTL_NO_ACK = BIT(1),
+ HWSIM_TX_STAT_ACK = BIT(2),
+};
+
+/**
+ * DOC: Frame transmission/registration support
+ *
+ * Frame transmission and registration support exists to allow userspace
+ * entities such as wmediumd to receive and process all broadcasted
+ * frames from a mac80211_hwsim radio device.
+ *
+ * This allow user space applications to decide if the frame should be
+ * dropped or not and implement a wireless medium simulator at user space.
+ *
+ * Registration is done by sending a register message to the driver and
+ * will be automatically unregistered if the user application doesn't
+ * responds to sent frames.
+ * Once registered the user application has to take responsibility of
+ * broadcasting the frames to all listening mac80211_hwsim radio
+ * interfaces.
+ *
+ * For more technical details, see the corresponding command descriptions
+ * below.
+ */
+
+/**
+ * enum hwsim_commands - supported hwsim commands
+ *
+ * @HWSIM_CMD_UNSPEC: unspecified command to catch errors
+ *
+ * @HWSIM_CMD_REGISTER: request to register and received all broadcasted
+ * frames by any mac80211_hwsim radio device.
+ * @HWSIM_CMD_FRAME: send/receive a broadcasted frame from/to kernel/user
+ * space, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER,
+ * %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE,
+ * %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
+ * @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to
+ * kernel, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS,
+ * %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
+ * @__HWSIM_CMD_MAX: enum limit
+ */
+enum {
+ HWSIM_CMD_UNSPEC,
+ HWSIM_CMD_REGISTER,
+ HWSIM_CMD_FRAME,
+ HWSIM_CMD_TX_INFO_FRAME,
+ __HWSIM_CMD_MAX,
+};
+#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)
+
+/**
+ * enum hwsim_attrs - hwsim netlink attributes
+ *
+ * @HWSIM_ATTR_UNSPEC: unspecified attribute to catch errors
+ *
+ * @HWSIM_ATTR_ADDR_RECEIVER: MAC address of the radio device that
+ * the frame is broadcasted to
+ * @HWSIM_ATTR_ADDR_TRANSMITTER: MAC address of the radio device that
+ * the frame was broadcasted from
+ * @HWSIM_ATTR_FRAME: Data array
+ * @HWSIM_ATTR_FLAGS: mac80211 transmission flags, used to process
+ properly the frame at user space
+ * @HWSIM_ATTR_RX_RATE: estimated rx rate index for this frame at user
+ space
+ * @HWSIM_ATTR_SIGNAL: estimated RX signal for this frame at user
+ space
+ * @HWSIM_ATTR_TX_INFO: ieee80211_tx_rate array
+ * @HWSIM_ATTR_COOKIE: sk_buff cookie to identify the frame
+ * @__HWSIM_ATTR_MAX: enum limit
+ */
+
+
+enum {
+ HWSIM_ATTR_UNSPEC,
+ HWSIM_ATTR_ADDR_RECEIVER,
+ HWSIM_ATTR_ADDR_TRANSMITTER,
+ HWSIM_ATTR_FRAME,
+ HWSIM_ATTR_FLAGS,
+ HWSIM_ATTR_RX_RATE,
+ HWSIM_ATTR_SIGNAL,
+ HWSIM_ATTR_TX_INFO,
+ HWSIM_ATTR_COOKIE,
+ __HWSIM_ATTR_MAX,
+};
+#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
+
+/**
+ * struct hwsim_tx_rate - rate selection/status
+ *
+ * @idx: rate index to attempt to send with
+ * @count: number of tries in this rate before going to the next rate
+ *
+ * A value of -1 for @idx indicates an invalid rate and, if used
+ * in an array of retry rates, that no more rates should be tried.
+ *
+ * When used for transmit status reporting, the driver should
+ * always report the rate and number of retries used.
+ *
+ */
+struct hwsim_tx_rate {
+ s8 idx;
+ u8 count;
+} __packed;
+
+static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
+ [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_FRAME] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+ [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 },
+ [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 },
+ [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 },
+ [HWSIM_ATTR_TX_INFO] = { .type = NLA_UNSPEC,
+ .len = IEEE80211_TX_MAX_RATES*sizeof(
+ struct hwsim_tx_rate)},
+ [HWSIM_ATTR_COOKIE] = { .type = NLA_U64 },
+};
+
+
+
+#define VERSION_NR 1
--
1.7.0.4


2011-05-31 21:29:32

by Javier López

[permalink] [raw]
Subject: [PATCH v7] mac80211_hwsim driver support userspace frame tx/rx

This patch adds to mac80211_hwsim the capability to send traffic via
userspace.

Frame exchange between kernel and user spaces is done through generic
netlink communication protocol. A new generic netlink family
MAC80211_HWSIM is proposed, this family contains three basic commands
HWSIM_CMD_REGISTER, which is the command used to register a new
traffic listener, HWSIM_CMD_FRAME, to exchange the frames from kernel
to user and vice-versa, and HWSIM_CMD_TX_INFO_FRAME which returns
from user all the information about retransmissions, rates, rx signal,
and so on.

How it works:

Once the driver is loaded the MAC80211_HWSIM family will be registered.
In the absence of userspace daemon, the driver itselfs implements a
perfect wireless medium as it did in the past. When a daemon sends a
HWSIM_CMD_REGISTER command, the module stores the application PID, and
from this moment all frames will be sent to the registered daemon.

The user space application will be in charge of process/forward all
frames broadcast by any mac80211_hwsim radio. If the user application
is stopped, the kernel module will detect the release of the socket
and it will switch back to in-kernel perfect channel simulation.

The userspace daemon must be waiting for incoming HWSIM_CMD_FRAME
commands sent from kernel, for each HWSIM_CMD_FRAME command the
application will try to broadcast this frame to all mac80211_hwsim
radios, however the application may decide to forward/drop this frame.
In the case of forwarding the frame, a new HWSIM_CMD_FRAME command will
be created, all necessary attributes will be populated and the frame
will be sent back to the kernel.

Also after the frame broadcast phase, a HWSIM_CMD_TX_INFO_FRAME
command will be sent from userspace to kernel, this command contains
all the information regarding the transmission, such as number of
tries, rates, ack signal, etc.

You can find the actual implementation of wireless mediumd daemon
(wmediumd) at:

* Last version tarball: https://github.com/jlopex/cozybit/tarball/master
* Or visiting my github tree: https://github.com/jlopex/cozybit/tree


Signed-off-by: Javier Lopez <[email protected]>
---

Thanks to Johaness Berg, Javier Cardona and John Linville for their
effort, reviews and patience with me.

Changelog:

v1: Initial version
v2: Fixed memory check issues, added netlink attributes checks, erased
extra printks
v3: Changed HWSIM generic netlink to MAC80211_HWSIM generic netlink family,
solved a lock issue
v4: Added netlink notifier in order to detect user space socket release,
more accurate memory allocation, added skb_queue per radio to hold last
exchanged frames with userspace
v5: Avoided the use of ieee80211 data structures, changed the cookie netlink
attribute to U64, solved memory leak in no-wmediumd mode, erased extra
debug messages, added netlink attributes check per each command, nla_len
for all data structures sent over netlink, solved a bug with interfaces
not completely configured
v6: Added patch description and changelog
v7: Solved double free / memory leak, moved policy to .c file, changed
mistaken returns, now superuser permit is required to use this API

drivers/net/wireless/mac80211_hwsim.c | 462 +++++++++++++++++++++++++++++++-
drivers/net/wireless/mac80211_hwsim.h | 131 ++++++++++
2 files changed, 578 insertions(+), 15 deletions(-)
create mode 100644 drivers/net/wireless/mac80211_hwsim.h

diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 9d4a40e..c4a4364 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -1,6 +1,7 @@
/*
* mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
* Copyright (c) 2008, Jouni Malinen <[email protected]>
+ * Copyright (c) 2011, Javier Lopez <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -25,11 +26,17 @@
#include <linux/rtnetlink.h>
#include <linux/etherdevice.h>
#include <linux/debugfs.h>
+#include <net/genetlink.h>
+#include "mac80211_hwsim.h"
+
+#define WARN_QUEUE 100
+#define MAX_QUEUE 200

MODULE_AUTHOR("Jouni Malinen");
MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211");
MODULE_LICENSE("GPL");

+int wmediumd_pid;
static int radios = 2;
module_param(radios, int, 0444);
MODULE_PARM_DESC(radios, "Number of simulated radios");
@@ -302,6 +309,7 @@ struct mac80211_hwsim_data {
struct dentry *debugfs;
struct dentry *debugfs_ps;

+ struct sk_buff_head pending; /* packets pending */
/*
* Only radios in the same group can communicate together (the
* channel has to match too). Each bit represents a group. A
@@ -322,6 +330,32 @@ struct hwsim_radiotap_hdr {
__le16 rt_chbitmask;
} __packed;

+/* MAC80211_HWSIM netlinf family */
+static struct genl_family hwsim_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = "MAC80211_HWSIM",
+ .version = 1,
+ .maxattr = HWSIM_ATTR_MAX,
+};
+
+/* MAC80211_HWSIM netlink policy */
+
+static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
+ [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_FRAME] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+ [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 },
+ [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 },
+ [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 },
+ [HWSIM_ATTR_TX_INFO] = { .type = NLA_UNSPEC,
+ .len = IEEE80211_TX_MAX_RATES*sizeof(
+ struct hwsim_tx_rate)},
+ [HWSIM_ATTR_COOKIE] = { .type = NLA_U64 },
+};

static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb,
struct net_device *dev)
@@ -478,9 +512,89 @@ static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
return md.ret;
}

+static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
+ struct sk_buff *my_skb,
+ int dst_pid)
+{
+ struct sk_buff *skb;
+ struct mac80211_hwsim_data *data = hw->priv;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) my_skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(my_skb);
+ void *msg_head;
+ unsigned int hwsim_flags = 0;
+ int i;
+ struct hwsim_tx_rate tx_attempts[IEEE80211_TX_MAX_RATES];
+
+ if (data->idle) {
+ wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
+ dev_kfree_skb(my_skb);
+ return;
+ }
+
+ if (data->ps != PS_DISABLED)
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+ /* If the queue contains MAX_QUEUE skb's drop some */
+ if (skb_queue_len(&data->pending) >= MAX_QUEUE) {
+ /* Droping until WARN_QUEUE level */
+ while (skb_queue_len(&data->pending) >= WARN_QUEUE)
+ skb_dequeue(&data->pending);
+ }
+
+ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (skb == NULL)
+ goto nla_put_failure;
+
+ msg_head = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
+ HWSIM_CMD_FRAME);
+ if (msg_head == NULL) {
+ printk(KERN_DEBUG "mac80211_hwsim: problem with msg_head\n");
+ goto nla_put_failure;
+ }
+
+ NLA_PUT(skb, HWSIM_ATTR_ADDR_TRANSMITTER,
+ sizeof(struct mac_address), data->addresses[1].addr);

-static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
- struct sk_buff *skb)
+ /* We get the skb->data */
+ NLA_PUT(skb, HWSIM_ATTR_FRAME, my_skb->len, my_skb->data);
+
+ /* We get the flags for this transmission, and we translate them to
+ wmediumd flags */
+
+ if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)
+ hwsim_flags |= HWSIM_TX_CTL_REQ_TX_STATUS;
+
+ if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+ hwsim_flags |= HWSIM_TX_CTL_NO_ACK;
+
+ NLA_PUT_U32(skb, HWSIM_ATTR_FLAGS, hwsim_flags);
+
+ /* We get the tx control (rate and retries) info*/
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ tx_attempts[i].idx = info->status.rates[i].idx;
+ tx_attempts[i].count = info->status.rates[i].count;
+ }
+
+ NLA_PUT(skb, HWSIM_ATTR_TX_INFO,
+ sizeof(struct hwsim_tx_rate)*IEEE80211_TX_MAX_RATES,
+ tx_attempts);
+
+ /* We create a cookie to identify this skb */
+ NLA_PUT_U64(skb, HWSIM_ATTR_COOKIE, (unsigned long) my_skb);
+
+ genlmsg_end(skb, msg_head);
+ genlmsg_unicast(&init_net, skb, dst_pid);
+
+ /* Enqueue the packet */
+ skb_queue_tail(&data->pending, my_skb);
+ return;
+
+nla_put_failure:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+}
+
+static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
+ struct sk_buff *skb)
{
struct mac80211_hwsim_data *data = hw->priv, *data2;
bool ack = false;
@@ -540,11 +654,11 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
return ack;
}

-
static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
bool ack;
struct ieee80211_tx_info *txi;
+ int _pid;

mac80211_hwsim_monitor_rx(hw, skb);

@@ -554,7 +668,15 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
return;
}

- ack = mac80211_hwsim_tx_frame(hw, skb);
+ /* wmediumd mode check */
+ _pid = wmediumd_pid;
+
+ if (_pid)
+ return mac80211_hwsim_tx_frame_nl(hw, skb, _pid);
+
+ /* NO wmediumd detected, perfect medium simulation */
+ ack = mac80211_hwsim_tx_frame_no_nl(hw, skb);
+
if (ack && skb->len >= 16) {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
mac80211_hwsim_monitor_ack(hw, hdr->addr2);
@@ -635,6 +757,7 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
struct ieee80211_hw *hw = arg;
struct sk_buff *skb;
struct ieee80211_tx_info *info;
+ int _pid;

hwsim_check_magic(vif);

@@ -649,7 +772,14 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
info = IEEE80211_SKB_CB(skb);

mac80211_hwsim_monitor_rx(hw, skb);
- mac80211_hwsim_tx_frame(hw, skb);
+
+ /* wmediumd mode check */
+ _pid = wmediumd_pid;
+
+ if (_pid)
+ return mac80211_hwsim_tx_frame_nl(hw, skb, _pid);
+
+ mac80211_hwsim_tx_frame_no_nl(hw, skb);
dev_kfree_skb(skb);
}

@@ -966,12 +1096,7 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,

static void mac80211_hwsim_flush(struct ieee80211_hw *hw, bool drop)
{
- /*
- * In this special case, there's nothing we need to
- * do because hwsim does transmission synchronously.
- * In the future, when it does transmissions via
- * userspace, we may need to do something.
- */
+ /* Not implemented, queues only on kernel side */
}

struct hw_scan_done {
@@ -1119,6 +1244,7 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
struct sk_buff *skb;
struct ieee80211_pspoll *pspoll;
+ int _pid;

if (!vp->assoc)
return;
@@ -1137,8 +1263,15 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
pspoll->aid = cpu_to_le16(0xc000 | vp->aid);
memcpy(pspoll->bssid, vp->bssid, ETH_ALEN);
memcpy(pspoll->ta, mac, ETH_ALEN);
- if (!mac80211_hwsim_tx_frame(data->hw, skb))
- printk(KERN_DEBUG "%s: PS-Poll frame not ack'ed\n", __func__);
+
+ /* wmediumd mode check */
+ _pid = wmediumd_pid;
+
+ if (_pid)
+ return mac80211_hwsim_tx_frame_nl(data->hw, skb, _pid);
+
+ if (!mac80211_hwsim_tx_frame_no_nl(data->hw, skb))
+ printk(KERN_DEBUG "%s: PS-poll frame not ack'ed\n", __func__);
dev_kfree_skb(skb);
}

@@ -1149,6 +1282,7 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
struct sk_buff *skb;
struct ieee80211_hdr *hdr;
+ bool _pid;

if (!vp->assoc)
return;
@@ -1168,7 +1302,14 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
memcpy(hdr->addr1, vp->bssid, ETH_ALEN);
memcpy(hdr->addr2, mac, ETH_ALEN);
memcpy(hdr->addr3, vp->bssid, ETH_ALEN);
- if (!mac80211_hwsim_tx_frame(data->hw, skb))
+
+ /* wmediumd mode check */
+ _pid = wmediumd_pid;
+
+ if (_pid)
+ return mac80211_hwsim_tx_frame_nl(data->hw, skb, _pid);
+
+ if (!mac80211_hwsim_tx_frame_no_nl(data->hw, skb))
printk(KERN_DEBUG "%s: nullfunc frame not ack'ed\n", __func__);
dev_kfree_skb(skb);
}
@@ -1248,6 +1389,273 @@ DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group,
hwsim_fops_group_read, hwsim_fops_group_write,
"%llx\n");

+struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(
+ struct mac_address *addr)
+{
+ struct mac80211_hwsim_data *data;
+ bool _found = false;
+
+ spin_lock_bh(&hwsim_radio_lock);
+ list_for_each_entry(data, &hwsim_radios, list) {
+ if (memcmp(data->addresses[1].addr, addr,
+ sizeof(struct mac_address)) == 0) {
+ _found = true;
+ break;
+ }
+ }
+ spin_unlock_bh(&hwsim_radio_lock);
+
+ if (!_found)
+ return NULL;
+
+ return data;
+}
+
+static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct ieee80211_hdr *hdr;
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_tx_info *txi;
+ struct hwsim_tx_rate *tx_attempts;
+ struct sk_buff __user *ret_skb;
+ struct sk_buff *skb, *tmp;
+ struct mac_address *src;
+ unsigned int hwsim_flags;
+
+ int i;
+ bool found = false;
+
+ if (!info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER] ||
+ !info->attrs[HWSIM_ATTR_FLAGS] ||
+ !info->attrs[HWSIM_ATTR_COOKIE] ||
+ !info->attrs[HWSIM_ATTR_TX_INFO])
+ goto out;
+
+ src = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]);
+ hwsim_flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]);
+
+ ret_skb = (struct sk_buff __user *)
+ (unsigned long) nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]);
+
+ data2 = get_hwsim_data_ref_from_addr(src);
+
+ if (data2 == NULL)
+ goto out;
+
+ /* look for the skb matching the cookie passed back from user */
+ skb_queue_walk_safe(&data2->pending, skb, tmp) {
+ if (skb == ret_skb) {
+ skb_unlink(skb, &data2->pending);
+ found = true;
+ break;
+ }
+ }
+
+ /* not found */
+ if (!found)
+ goto out;
+
+ /* Tx info received because the frame was broadcasted on user space,
+ so we get all the necessary info: tx attempts and skb control buff */
+
+ tx_attempts = (struct hwsim_tx_rate *)nla_data(
+ info->attrs[HWSIM_ATTR_TX_INFO]);
+
+ /* now send back TX status */
+ txi = IEEE80211_SKB_CB(skb);
+
+ if (txi->control.vif)
+ hwsim_check_magic(txi->control.vif);
+ if (txi->control.sta)
+ hwsim_check_sta_magic(txi->control.sta);
+
+ ieee80211_tx_info_clear_status(txi);
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ txi->status.rates[i].idx = tx_attempts[i].idx;
+ txi->status.rates[i].count = tx_attempts[i].count;
+ /*txi->status.rates[i].flags = 0;*/
+ }
+
+ txi->status.ack_signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ if (!(hwsim_flags & HWSIM_TX_CTL_NO_ACK) &&
+ (hwsim_flags & HWSIM_TX_STAT_ACK)) {
+ if (skb->len >= 16) {
+ hdr = (struct ieee80211_hdr *) skb->data;
+ mac80211_hwsim_monitor_ack(data2->hw, hdr->addr2);
+ }
+ }
+ ieee80211_tx_status_irqsafe(data2->hw, skb);
+ return 0;
+out:
+ return -EINVAL;
+
+}
+
+static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_rx_status rx_status;
+ struct mac_address *dst;
+ int frame_data_len;
+ char *frame_data;
+ struct sk_buff *skb = NULL;
+
+ if (!info->attrs[HWSIM_ATTR_ADDR_RECEIVER] ||
+ !info->attrs[HWSIM_ATTR_FRAME] ||
+ !info->attrs[HWSIM_ATTR_RX_RATE] ||
+ !info->attrs[HWSIM_ATTR_SIGNAL])
+ goto out;
+
+ dst = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_RECEIVER]);
+
+ frame_data_len = nla_len(info->attrs[HWSIM_ATTR_FRAME]);
+ frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_FRAME]);
+
+ /* Allocate new skb here */
+ skb = alloc_skb(frame_data_len, GFP_KERNEL);
+ if (skb == NULL)
+ goto err;
+
+ if (frame_data_len <= IEEE80211_MAX_DATA_LEN) {
+ /* Copy the data */
+ memcpy(skb_put(skb, frame_data_len), frame_data,
+ frame_data_len);
+ } else
+ goto err;
+
+ data2 = get_hwsim_data_ref_from_addr(dst);
+
+ if (data2 == NULL)
+ goto out;
+
+ /* check if radio is configured properly */
+
+ if (data2->idle || !data2->started || !data2->channel)
+ goto out;
+
+ /*A frame is received from user space*/
+ memset(&rx_status, 0, sizeof(rx_status));
+ rx_status.freq = data2->channel->center_freq;
+ rx_status.band = data2->channel->band;
+ rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
+ rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
+ ieee80211_rx_irqsafe(data2->hw, skb);
+
+ return 0;
+err:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ goto out;
+out:
+ dev_kfree_skb(skb);
+ return -EINVAL;
+}
+
+static int hwsim_register_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+ if (info == NULL)
+ goto out;
+
+ wmediumd_pid = info->snd_pid;
+
+ printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, "
+ "switching to wmediumd mode with pid %d\n", info->snd_pid);
+
+ return 0;
+out:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -EINVAL;
+}
+
+/* Generic Netlink operations array */
+static struct genl_ops hwsim_ops[] = {
+ {
+ .cmd = HWSIM_CMD_REGISTER,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_register_received_nl,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = HWSIM_CMD_FRAME,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_cloned_frame_received_nl,
+ },
+ {
+ .cmd = HWSIM_CMD_TX_INFO_FRAME,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_tx_info_frame_received_nl,
+ },
+};
+
+static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
+ unsigned long state,
+ void *_notify)
+{
+ struct netlink_notify *notify = _notify;
+
+ if (state != NETLINK_URELEASE)
+ return NOTIFY_DONE;
+
+ if (notify->pid == wmediumd_pid) {
+ printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink"
+ " socket, switching to perfect channel medium\n");
+ wmediumd_pid = 0;
+ }
+ return NOTIFY_DONE;
+
+}
+
+static struct notifier_block hwsim_netlink_notifier = {
+ .notifier_call = mac80211_hwsim_netlink_notify,
+};
+
+static int hwsim_init_netlink(void)
+{
+ int rc;
+ printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
+
+ wmediumd_pid = 0;
+
+ rc = genl_register_family_with_ops(&hwsim_genl_family,
+ hwsim_ops, ARRAY_SIZE(hwsim_ops));
+ if (rc)
+ goto failure;
+
+ rc = netlink_register_notifier(&hwsim_netlink_notifier);
+ if (rc)
+ goto failure;
+
+ return 0;
+
+failure:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -EINVAL;
+}
+
+static void hwsim_exit_netlink(void)
+{
+ int ret;
+
+ printk(KERN_INFO "mac80211_hwsim: closing netlink\n");
+ /* unregister the notifier */
+ netlink_unregister_notifier(&hwsim_netlink_notifier);
+ /* unregister the family */
+ ret = genl_unregister_family(&hwsim_genl_family);
+ if (ret)
+ printk(KERN_DEBUG "mac80211_hwsim: "
+ "unregister family %i\n", ret);
+}
+
static int __init init_mac80211_hwsim(void)
{
int i, err = 0;
@@ -1298,6 +1706,7 @@ static int __init init_mac80211_hwsim(void)
goto failed_drvdata;
}
data->dev->driver = &mac80211_hwsim_driver;
+ skb_queue_head_init(&data->pending);

SET_IEEE80211_DEV(hw, data->dev);
addr[3] = i >> 8;
@@ -1379,6 +1788,10 @@ static int __init init_mac80211_hwsim(void)
data->group = 1;
mutex_init(&data->mutex);

+ /* Enable frame retransmissions for lossy channels */
+ hw->max_rates = 4;
+ hw->max_rate_tries = 11;
+
/* Work to be done prior to ieee80211_register_hw() */
switch (regtest) {
case HWSIM_REGTEST_DISABLED:
@@ -1515,12 +1928,29 @@ static int __init init_mac80211_hwsim(void)
if (hwsim_mon == NULL)
goto failed;

- err = register_netdev(hwsim_mon);
+ rtnl_lock();
+
+ err = dev_alloc_name(hwsim_mon, hwsim_mon->name);
if (err < 0)
goto failed_mon;

+
+ err = register_netdevice(hwsim_mon);
+ if (err < 0)
+ goto failed_mon;
+
+ rtnl_unlock();
+
+ err = hwsim_init_netlink();
+ if (err < 0)
+ goto failed_nl;
+
return 0;

+failed_nl:
+ printk(KERN_DEBUG "mac_80211_hwsim: failed initializing netlink\n");
+ return err;
+
failed_mon:
rtnl_unlock();
free_netdev(hwsim_mon);
@@ -1541,6 +1971,8 @@ static void __exit exit_mac80211_hwsim(void)
{
printk(KERN_DEBUG "mac80211_hwsim: unregister radios\n");

+ hwsim_exit_netlink();
+
mac80211_hwsim_free();
unregister_netdev(hwsim_mon);
}
diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h
new file mode 100644
index 0000000..e9da5d4
--- /dev/null
+++ b/drivers/net/wireless/mac80211_hwsim.h
@@ -0,0 +1,131 @@
+/*
+ * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
+ * Copyright (c) 2008, Jouni Malinen <[email protected]>
+ * Copyright (c) 2011, Javier Lopez <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <net/mac80211.h>
+
+/**
+ * enum hwsim_tx_control_flags - flags to describe transmission info/status
+ *
+ * These flags are used to give the wmediumd extra information in order to
+ * modify its behavior for each frame
+ *
+ * @HWSIM_TX_CTL_REQ_TX_STATUS: require TX status callback for this frame.
+ * @HWSIM_TX_CTL_NO_ACK: tell the wmediumd not to wait for an ack
+ * @HWSIM_TX_STAT_ACK: Frame was acknowledged
+ *
+ */
+enum hwsim_tx_control_flags {
+ HWSIM_TX_CTL_REQ_TX_STATUS = BIT(0),
+ HWSIM_TX_CTL_NO_ACK = BIT(1),
+ HWSIM_TX_STAT_ACK = BIT(2),
+};
+
+/**
+ * DOC: Frame transmission/registration support
+ *
+ * Frame transmission and registration support exists to allow userspace
+ * entities such as wmediumd to receive and process all broadcasted
+ * frames from a mac80211_hwsim radio device.
+ *
+ * This allow user space applications to decide if the frame should be
+ * dropped or not and implement a wireless medium simulator at user space.
+ *
+ * Registration is done by sending a register message to the driver and
+ * will be automatically unregistered if the user application doesn't
+ * responds to sent frames.
+ * Once registered the user application has to take responsibility of
+ * broadcasting the frames to all listening mac80211_hwsim radio
+ * interfaces.
+ *
+ * For more technical details, see the corresponding command descriptions
+ * below.
+ */
+
+/**
+ * enum hwsim_commands - supported hwsim commands
+ *
+ * @HWSIM_CMD_UNSPEC: unspecified command to catch errors
+ *
+ * @HWSIM_CMD_REGISTER: request to register and received all broadcasted
+ * frames by any mac80211_hwsim radio device.
+ * @HWSIM_CMD_FRAME: send/receive a broadcasted frame from/to kernel/user
+ * space, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER,
+ * %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE,
+ * %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
+ * @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to
+ * kernel, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS,
+ * %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
+ * @__HWSIM_CMD_MAX: enum limit
+ */
+enum {
+ HWSIM_CMD_UNSPEC,
+ HWSIM_CMD_REGISTER,
+ HWSIM_CMD_FRAME,
+ HWSIM_CMD_TX_INFO_FRAME,
+ __HWSIM_CMD_MAX,
+};
+#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)
+
+/**
+ * enum hwsim_attrs - hwsim netlink attributes
+ *
+ * @HWSIM_ATTR_UNSPEC: unspecified attribute to catch errors
+ *
+ * @HWSIM_ATTR_ADDR_RECEIVER: MAC address of the radio device that
+ * the frame is broadcasted to
+ * @HWSIM_ATTR_ADDR_TRANSMITTER: MAC address of the radio device that
+ * the frame was broadcasted from
+ * @HWSIM_ATTR_FRAME: Data array
+ * @HWSIM_ATTR_FLAGS: mac80211 transmission flags, used to process
+ properly the frame at user space
+ * @HWSIM_ATTR_RX_RATE: estimated rx rate index for this frame at user
+ space
+ * @HWSIM_ATTR_SIGNAL: estimated RX signal for this frame at user
+ space
+ * @HWSIM_ATTR_TX_INFO: ieee80211_tx_rate array
+ * @HWSIM_ATTR_COOKIE: sk_buff cookie to identify the frame
+ * @__HWSIM_ATTR_MAX: enum limit
+ */
+
+
+enum {
+ HWSIM_ATTR_UNSPEC,
+ HWSIM_ATTR_ADDR_RECEIVER,
+ HWSIM_ATTR_ADDR_TRANSMITTER,
+ HWSIM_ATTR_FRAME,
+ HWSIM_ATTR_FLAGS,
+ HWSIM_ATTR_RX_RATE,
+ HWSIM_ATTR_SIGNAL,
+ HWSIM_ATTR_TX_INFO,
+ HWSIM_ATTR_COOKIE,
+ __HWSIM_ATTR_MAX,
+};
+#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
+
+/**
+ * struct hwsim_tx_rate - rate selection/status
+ *
+ * @idx: rate index to attempt to send with
+ * @count: number of tries in this rate before going to the next rate
+ *
+ * A value of -1 for @idx indicates an invalid rate and, if used
+ * in an array of retry rates, that no more rates should be tried.
+ *
+ * When used for transmit status reporting, the driver should
+ * always report the rate and number of retries used.
+ *
+ */
+struct hwsim_tx_rate {
+ s8 idx;
+ u8 count;
+} __packed;
+
--
1.7.0.4


2011-05-18 21:00:34

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v4] mac80211_hwsim driver support userspace frame tx/rx

On Tue, 2011-05-17 at 17:09 -0700, Javier Cardona wrote:
> John,
>
> We've confirmed that this works as documented and makes hwsim very
> useful for testing the wireless stack over lossy channels. If no one
> has major objections, can you consider it for inclusion?

I am just reading through the patch now but I don't think I'll get to it
today, would you mind waiting a few days? I haven't been able to look
earlier as I was travelling (and am in Portland, OR now)

johannes



2011-05-19 15:57:55

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v4] mac80211_hwsim driver support userspace frame tx/rx

Hi,

Sorry for the late reply, I was travelling and had so much other stuff
to do as well. I like what you did with the pending, but have some
further comments:

> + /* We get the flags for this transmission, wmediumd maybe
> + changes its behaviour depending on the flags */
> + NLA_PUT_U32(skb, HWSIM_ATTR_FLAGS, info->flags);

I'm not sure that we want to tie wmediumd to a precise version of
mac80211? This would make the mac80211 internals ABI for wmediumd rather
than API for the drivers, which I'd like to avoid. What flags does it
actually need? I think you should translate those flags into hwsim
netlink flags.

> + /* We get the tx control (rate and retries) info*/
> + NLA_PUT(skb, HWSIM_ATTR_TX_INFO,
> + sizeof(struct ieee80211_tx_rate)*IEEE80211_TX_MAX_RATES,
> + info->control.rates);

The same applies here, Felix is indeed planning to change this. I know
it's a lot of code and not very efficient, but I think you should wrap
this stuff in a real netlink attribute so it has a chance of not
breaking when mac80211 APIs change.

> + /* We create a cookie to identify this skb */
> + NLA_PUT_U32(skb, HWSIM_ATTR_COOKIE, (unsigned long) my_skb);

This needs to be a U64 so that 64-bit systems work.

> +static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
> + struct sk_buff *skb)
> +{
> + if (atomic_read(&wmediumd_pid)) {

This reminds me -- why is that thing atomic? It doesn't seem necessary
since there will be locking (rtnl) on the callbacks setting it anyway?

> @@ -571,6 +671,7 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
> if (!(txi->flags & IEEE80211_TX_CTL_NO_ACK) && ack)
> txi->flags |= IEEE80211_TX_STAT_ACK;
> ieee80211_tx_status_irqsafe(hw, skb);
> +
> }

spurious whitespace change

> @@ -650,7 +751,6 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
>
> mac80211_hwsim_monitor_rx(hw, skb);
> mac80211_hwsim_tx_frame(hw, skb);
> - dev_kfree_skb(skb);
> }

Where are frames freed in the no-wmediumd case?

> @@ -966,12 +1066,9 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,
>
> static void mac80211_hwsim_flush(struct ieee80211_hw *hw, bool drop)
> {
> - /*
> - * In this special case, there's nothing we need to
> - * do because hwsim does transmission synchronously.
> - * In the future, when it does transmissions via
> - * userspace, we may need to do something.
> - */
> + /* Let's purge the pending queue */
> + struct mac80211_hwsim_data *data = hw->priv;
> + skb_queue_purge(&data->pending);
> }

This doesn't seem right, but unless you do buffering in wmediumd you
don't really have to worry much. I guess a real implementation would
send a notification to wmediumd to flush and then wait for the queue to
become empty (with some timeout I guess).


> + if (!_found) {
> + printk(KERN_DEBUG "mac80211_hwsim: invalid radio ID\n");
> + return NULL;
> + }

I think you're generally erring a bit on the side of too much debug
printing, but since it's a debug driver I don't really care :)

> + return data;
> +}
> +
> +static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
> + struct genl_info *info)
> +{
> +
> + struct ieee80211_hdr *hdr;
> + struct mac80211_hwsim_data *data2;
> + struct ieee80211_tx_info *txi;
> + struct ieee80211_tx_rate *tx_attempts;
> + struct sk_buff __user *ret_skb;
> + struct sk_buff *skb = NULL, *tmp;
> +
> + int i;
> + bool found = false;
> +
> + struct mac_address *dst = (struct mac_address *)nla_data(
> + info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]);
> + int flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]);

Maybe I'm missing it, but I don't see where you verified that the
attributes actually exist? This might otherwise crash. Same for other
attributes too.

> + ret_skb = (struct sk_buff __user *)
> + (unsigned long) nla_get_u32(info->attrs[HWSIM_ATTR_COOKIE]);

Like this one, also u64.

> + tx_attempts = (struct ieee80211_tx_rate *)nla_data(
> + info->attrs[HWSIM_ATTR_TX_INFO]);

Again, translation between structs and netlink would be good so we don't
tie this to internal mac80211 APIs.

> + if (tx_attempts == NULL)
> + goto out;

That can never happen ... either nla_data() already crashed because the
attribute didn't exist, or tx_attempts is non-NULL. However, you need to
check nla_len().

johannes


2011-06-01 08:48:31

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v7] mac80211_hwsim driver support userspace frame tx/rx

On Wed, 2011-06-01 at 09:51 +0200, Javier López wrote:

> This file includes the necessary enums and data structures. If I
> follow the nl80211 structure, should I move the file
> "mac80211_hwsim.h" to "include/linux" and make it available to
> userspace?

I don't think that's necessary, but I don't see why you include
net/mac80211.h since the file doesn't need anything from it.

johannes


2011-05-31 22:07:01

by Javier López

[permalink] [raw]
Subject: [PATCH v7] mac80211_hwsim driver support userspace frame tx/rx

This patch adds to mac80211_hwsim the capability to send traffic via
userspace.

Frame exchange between kernel and user spaces is done through generic
netlink communication protocol. A new generic netlink family
MAC80211_HWSIM is proposed, this family contains three basic commands
HWSIM_CMD_REGISTER, which is the command used to register a new
traffic listener, HWSIM_CMD_FRAME, to exchange the frames from kernel
to user and vice-versa, and HWSIM_CMD_TX_INFO_FRAME which returns
from user all the information about retransmissions, rates, rx signal,
and so on.

How it works:

Once the driver is loaded the MAC80211_HWSIM family will be registered.
In the absence of userspace daemon, the driver itselfs implements a
perfect wireless medium as it did in the past. When a daemon sends a
HWSIM_CMD_REGISTER command, the module stores the application PID, and
from this moment all frames will be sent to the registered daemon.

The user space application will be in charge of process/forward all
frames broadcast by any mac80211_hwsim radio. If the user application
is stopped, the kernel module will detect the release of the socket
and it will switch back to in-kernel perfect channel simulation.

The userspace daemon must be waiting for incoming HWSIM_CMD_FRAME
commands sent from kernel, for each HWSIM_CMD_FRAME command the
application will try to broadcast this frame to all mac80211_hwsim
radios, however the application may decide to forward/drop this frame.
In the case of forwarding the frame, a new HWSIM_CMD_FRAME command will
be created, all necessary attributes will be populated and the frame
will be sent back to the kernel.

Also after the frame broadcast phase, a HWSIM_CMD_TX_INFO_FRAME
command will be sent from userspace to kernel, this command contains
all the information regarding the transmission, such as number of
tries, rates, ack signal, etc.

You can find the actual implementation of wireless mediumd daemon
(wmediumd) at:

* Last version tarball: https://github.com/jlopex/cozybit/tarball/master
* Or visiting my github tree: https://github.com/jlopex/cozybit/tree

Signed-off-by: Javier Lopez <[email protected]>
---
Please ignore previous [Patch v7] I sent a few minutes ago, this one
fixes a type error.

Thanks to Johaness Berg, Javier Cardona and John Linville for their
effort, reviews and patience with me.

Changelog:

v1: Initial version
v2: Fixed memory check issues, added netlink attributes checks, erased
extra printks
v3: Changed HWSIM generic netlink to MAC80211_HWSIM generic netlink family,
solved a lock issue
v4: Added netlink notifier in order to detect user space socket release,
more accurate memory allocation, added skb_queue per radio to hold last
exchanged frames with userspace
v5: Avoided the use of ieee80211 data structures, changed the cookie netlink
attribute to U64, solved memory leak in no-wmediumd mode, erased extra
debug messages, added netlink attributes check per each command, nla_len
for all data structures sent over netlink, solved a bug with interfaces
not completely configured
v6: Added patch description and changelog
v7: Solved double free / memory leak, moved policy to .c file, changed
mistaken returns, now superuser permit is required to use this API

drivers/net/wireless/mac80211_hwsim.c | 462 +++++++++++++++++++++++++++++++-
drivers/net/wireless/mac80211_hwsim.h | 131 ++++++++++
2 files changed, 578 insertions(+), 15 deletions(-)
create mode 100644 drivers/net/wireless/mac80211_hwsim.h

diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 9d4a40e..7e1fa96 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -1,6 +1,7 @@
/*
* mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
* Copyright (c) 2008, Jouni Malinen <[email protected]>
+ * Copyright (c) 2011, Javier Lopez <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -25,11 +26,17 @@
#include <linux/rtnetlink.h>
#include <linux/etherdevice.h>
#include <linux/debugfs.h>
+#include <net/genetlink.h>
+#include "mac80211_hwsim.h"
+
+#define WARN_QUEUE 100
+#define MAX_QUEUE 200

MODULE_AUTHOR("Jouni Malinen");
MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211");
MODULE_LICENSE("GPL");

+int wmediumd_pid;
static int radios = 2;
module_param(radios, int, 0444);
MODULE_PARM_DESC(radios, "Number of simulated radios");
@@ -302,6 +309,7 @@ struct mac80211_hwsim_data {
struct dentry *debugfs;
struct dentry *debugfs_ps;

+ struct sk_buff_head pending; /* packets pending */
/*
* Only radios in the same group can communicate together (the
* channel has to match too). Each bit represents a group. A
@@ -322,6 +330,32 @@ struct hwsim_radiotap_hdr {
__le16 rt_chbitmask;
} __packed;

+/* MAC80211_HWSIM netlinf family */
+static struct genl_family hwsim_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = "MAC80211_HWSIM",
+ .version = 1,
+ .maxattr = HWSIM_ATTR_MAX,
+};
+
+/* MAC80211_HWSIM netlink policy */
+
+static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
+ [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_FRAME] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+ [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 },
+ [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 },
+ [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 },
+ [HWSIM_ATTR_TX_INFO] = { .type = NLA_UNSPEC,
+ .len = IEEE80211_TX_MAX_RATES*sizeof(
+ struct hwsim_tx_rate)},
+ [HWSIM_ATTR_COOKIE] = { .type = NLA_U64 },
+};

static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb,
struct net_device *dev)
@@ -478,9 +512,89 @@ static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
return md.ret;
}

+static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
+ struct sk_buff *my_skb,
+ int dst_pid)
+{
+ struct sk_buff *skb;
+ struct mac80211_hwsim_data *data = hw->priv;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) my_skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(my_skb);
+ void *msg_head;
+ unsigned int hwsim_flags = 0;
+ int i;
+ struct hwsim_tx_rate tx_attempts[IEEE80211_TX_MAX_RATES];
+
+ if (data->idle) {
+ wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
+ dev_kfree_skb(my_skb);
+ return;
+ }
+
+ if (data->ps != PS_DISABLED)
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+ /* If the queue contains MAX_QUEUE skb's drop some */
+ if (skb_queue_len(&data->pending) >= MAX_QUEUE) {
+ /* Droping until WARN_QUEUE level */
+ while (skb_queue_len(&data->pending) >= WARN_QUEUE)
+ skb_dequeue(&data->pending);
+ }
+
+ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (skb == NULL)
+ goto nla_put_failure;
+
+ msg_head = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
+ HWSIM_CMD_FRAME);
+ if (msg_head == NULL) {
+ printk(KERN_DEBUG "mac80211_hwsim: problem with msg_head\n");
+ goto nla_put_failure;
+ }
+
+ NLA_PUT(skb, HWSIM_ATTR_ADDR_TRANSMITTER,
+ sizeof(struct mac_address), data->addresses[1].addr);

-static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
- struct sk_buff *skb)
+ /* We get the skb->data */
+ NLA_PUT(skb, HWSIM_ATTR_FRAME, my_skb->len, my_skb->data);
+
+ /* We get the flags for this transmission, and we translate them to
+ wmediumd flags */
+
+ if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)
+ hwsim_flags |= HWSIM_TX_CTL_REQ_TX_STATUS;
+
+ if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+ hwsim_flags |= HWSIM_TX_CTL_NO_ACK;
+
+ NLA_PUT_U32(skb, HWSIM_ATTR_FLAGS, hwsim_flags);
+
+ /* We get the tx control (rate and retries) info*/
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ tx_attempts[i].idx = info->status.rates[i].idx;
+ tx_attempts[i].count = info->status.rates[i].count;
+ }
+
+ NLA_PUT(skb, HWSIM_ATTR_TX_INFO,
+ sizeof(struct hwsim_tx_rate)*IEEE80211_TX_MAX_RATES,
+ tx_attempts);
+
+ /* We create a cookie to identify this skb */
+ NLA_PUT_U64(skb, HWSIM_ATTR_COOKIE, (unsigned long) my_skb);
+
+ genlmsg_end(skb, msg_head);
+ genlmsg_unicast(&init_net, skb, dst_pid);
+
+ /* Enqueue the packet */
+ skb_queue_tail(&data->pending, my_skb);
+ return;
+
+nla_put_failure:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+}
+
+static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
+ struct sk_buff *skb)
{
struct mac80211_hwsim_data *data = hw->priv, *data2;
bool ack = false;
@@ -540,11 +654,11 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
return ack;
}

-
static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
bool ack;
struct ieee80211_tx_info *txi;
+ int _pid;

mac80211_hwsim_monitor_rx(hw, skb);

@@ -554,7 +668,15 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
return;
}

- ack = mac80211_hwsim_tx_frame(hw, skb);
+ /* wmediumd mode check */
+ _pid = wmediumd_pid;
+
+ if (_pid)
+ return mac80211_hwsim_tx_frame_nl(hw, skb, _pid);
+
+ /* NO wmediumd detected, perfect medium simulation */
+ ack = mac80211_hwsim_tx_frame_no_nl(hw, skb);
+
if (ack && skb->len >= 16) {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
mac80211_hwsim_monitor_ack(hw, hdr->addr2);
@@ -635,6 +757,7 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
struct ieee80211_hw *hw = arg;
struct sk_buff *skb;
struct ieee80211_tx_info *info;
+ int _pid;

hwsim_check_magic(vif);

@@ -649,7 +772,14 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
info = IEEE80211_SKB_CB(skb);

mac80211_hwsim_monitor_rx(hw, skb);
- mac80211_hwsim_tx_frame(hw, skb);
+
+ /* wmediumd mode check */
+ _pid = wmediumd_pid;
+
+ if (_pid)
+ return mac80211_hwsim_tx_frame_nl(hw, skb, _pid);
+
+ mac80211_hwsim_tx_frame_no_nl(hw, skb);
dev_kfree_skb(skb);
}

@@ -966,12 +1096,7 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,

static void mac80211_hwsim_flush(struct ieee80211_hw *hw, bool drop)
{
- /*
- * In this special case, there's nothing we need to
- * do because hwsim does transmission synchronously.
- * In the future, when it does transmissions via
- * userspace, we may need to do something.
- */
+ /* Not implemented, queues only on kernel side */
}

struct hw_scan_done {
@@ -1119,6 +1244,7 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
struct sk_buff *skb;
struct ieee80211_pspoll *pspoll;
+ int _pid;

if (!vp->assoc)
return;
@@ -1137,8 +1263,15 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
pspoll->aid = cpu_to_le16(0xc000 | vp->aid);
memcpy(pspoll->bssid, vp->bssid, ETH_ALEN);
memcpy(pspoll->ta, mac, ETH_ALEN);
- if (!mac80211_hwsim_tx_frame(data->hw, skb))
- printk(KERN_DEBUG "%s: PS-Poll frame not ack'ed\n", __func__);
+
+ /* wmediumd mode check */
+ _pid = wmediumd_pid;
+
+ if (_pid)
+ return mac80211_hwsim_tx_frame_nl(data->hw, skb, _pid);
+
+ if (!mac80211_hwsim_tx_frame_no_nl(data->hw, skb))
+ printk(KERN_DEBUG "%s: PS-poll frame not ack'ed\n", __func__);
dev_kfree_skb(skb);
}

@@ -1149,6 +1282,7 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
struct sk_buff *skb;
struct ieee80211_hdr *hdr;
+ int _pid;

if (!vp->assoc)
return;
@@ -1168,7 +1302,14 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
memcpy(hdr->addr1, vp->bssid, ETH_ALEN);
memcpy(hdr->addr2, mac, ETH_ALEN);
memcpy(hdr->addr3, vp->bssid, ETH_ALEN);
- if (!mac80211_hwsim_tx_frame(data->hw, skb))
+
+ /* wmediumd mode check */
+ _pid = wmediumd_pid;
+
+ if (_pid)
+ return mac80211_hwsim_tx_frame_nl(data->hw, skb, _pid);
+
+ if (!mac80211_hwsim_tx_frame_no_nl(data->hw, skb))
printk(KERN_DEBUG "%s: nullfunc frame not ack'ed\n", __func__);
dev_kfree_skb(skb);
}
@@ -1248,6 +1389,273 @@ DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group,
hwsim_fops_group_read, hwsim_fops_group_write,
"%llx\n");

+struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(
+ struct mac_address *addr)
+{
+ struct mac80211_hwsim_data *data;
+ bool _found = false;
+
+ spin_lock_bh(&hwsim_radio_lock);
+ list_for_each_entry(data, &hwsim_radios, list) {
+ if (memcmp(data->addresses[1].addr, addr,
+ sizeof(struct mac_address)) == 0) {
+ _found = true;
+ break;
+ }
+ }
+ spin_unlock_bh(&hwsim_radio_lock);
+
+ if (!_found)
+ return NULL;
+
+ return data;
+}
+
+static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct ieee80211_hdr *hdr;
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_tx_info *txi;
+ struct hwsim_tx_rate *tx_attempts;
+ struct sk_buff __user *ret_skb;
+ struct sk_buff *skb, *tmp;
+ struct mac_address *src;
+ unsigned int hwsim_flags;
+
+ int i;
+ bool found = false;
+
+ if (!info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER] ||
+ !info->attrs[HWSIM_ATTR_FLAGS] ||
+ !info->attrs[HWSIM_ATTR_COOKIE] ||
+ !info->attrs[HWSIM_ATTR_TX_INFO])
+ goto out;
+
+ src = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]);
+ hwsim_flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]);
+
+ ret_skb = (struct sk_buff __user *)
+ (unsigned long) nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]);
+
+ data2 = get_hwsim_data_ref_from_addr(src);
+
+ if (data2 == NULL)
+ goto out;
+
+ /* look for the skb matching the cookie passed back from user */
+ skb_queue_walk_safe(&data2->pending, skb, tmp) {
+ if (skb == ret_skb) {
+ skb_unlink(skb, &data2->pending);
+ found = true;
+ break;
+ }
+ }
+
+ /* not found */
+ if (!found)
+ goto out;
+
+ /* Tx info received because the frame was broadcasted on user space,
+ so we get all the necessary info: tx attempts and skb control buff */
+
+ tx_attempts = (struct hwsim_tx_rate *)nla_data(
+ info->attrs[HWSIM_ATTR_TX_INFO]);
+
+ /* now send back TX status */
+ txi = IEEE80211_SKB_CB(skb);
+
+ if (txi->control.vif)
+ hwsim_check_magic(txi->control.vif);
+ if (txi->control.sta)
+ hwsim_check_sta_magic(txi->control.sta);
+
+ ieee80211_tx_info_clear_status(txi);
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ txi->status.rates[i].idx = tx_attempts[i].idx;
+ txi->status.rates[i].count = tx_attempts[i].count;
+ /*txi->status.rates[i].flags = 0;*/
+ }
+
+ txi->status.ack_signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ if (!(hwsim_flags & HWSIM_TX_CTL_NO_ACK) &&
+ (hwsim_flags & HWSIM_TX_STAT_ACK)) {
+ if (skb->len >= 16) {
+ hdr = (struct ieee80211_hdr *) skb->data;
+ mac80211_hwsim_monitor_ack(data2->hw, hdr->addr2);
+ }
+ }
+ ieee80211_tx_status_irqsafe(data2->hw, skb);
+ return 0;
+out:
+ return -EINVAL;
+
+}
+
+static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_rx_status rx_status;
+ struct mac_address *dst;
+ int frame_data_len;
+ char *frame_data;
+ struct sk_buff *skb = NULL;
+
+ if (!info->attrs[HWSIM_ATTR_ADDR_RECEIVER] ||
+ !info->attrs[HWSIM_ATTR_FRAME] ||
+ !info->attrs[HWSIM_ATTR_RX_RATE] ||
+ !info->attrs[HWSIM_ATTR_SIGNAL])
+ goto out;
+
+ dst = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_RECEIVER]);
+
+ frame_data_len = nla_len(info->attrs[HWSIM_ATTR_FRAME]);
+ frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_FRAME]);
+
+ /* Allocate new skb here */
+ skb = alloc_skb(frame_data_len, GFP_KERNEL);
+ if (skb == NULL)
+ goto err;
+
+ if (frame_data_len <= IEEE80211_MAX_DATA_LEN) {
+ /* Copy the data */
+ memcpy(skb_put(skb, frame_data_len), frame_data,
+ frame_data_len);
+ } else
+ goto err;
+
+ data2 = get_hwsim_data_ref_from_addr(dst);
+
+ if (data2 == NULL)
+ goto out;
+
+ /* check if radio is configured properly */
+
+ if (data2->idle || !data2->started || !data2->channel)
+ goto out;
+
+ /*A frame is received from user space*/
+ memset(&rx_status, 0, sizeof(rx_status));
+ rx_status.freq = data2->channel->center_freq;
+ rx_status.band = data2->channel->band;
+ rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
+ rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
+ ieee80211_rx_irqsafe(data2->hw, skb);
+
+ return 0;
+err:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ goto out;
+out:
+ dev_kfree_skb(skb);
+ return -EINVAL;
+}
+
+static int hwsim_register_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+ if (info == NULL)
+ goto out;
+
+ wmediumd_pid = info->snd_pid;
+
+ printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, "
+ "switching to wmediumd mode with pid %d\n", info->snd_pid);
+
+ return 0;
+out:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -EINVAL;
+}
+
+/* Generic Netlink operations array */
+static struct genl_ops hwsim_ops[] = {
+ {
+ .cmd = HWSIM_CMD_REGISTER,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_register_received_nl,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = HWSIM_CMD_FRAME,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_cloned_frame_received_nl,
+ },
+ {
+ .cmd = HWSIM_CMD_TX_INFO_FRAME,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_tx_info_frame_received_nl,
+ },
+};
+
+static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
+ unsigned long state,
+ void *_notify)
+{
+ struct netlink_notify *notify = _notify;
+
+ if (state != NETLINK_URELEASE)
+ return NOTIFY_DONE;
+
+ if (notify->pid == wmediumd_pid) {
+ printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink"
+ " socket, switching to perfect channel medium\n");
+ wmediumd_pid = 0;
+ }
+ return NOTIFY_DONE;
+
+}
+
+static struct notifier_block hwsim_netlink_notifier = {
+ .notifier_call = mac80211_hwsim_netlink_notify,
+};
+
+static int hwsim_init_netlink(void)
+{
+ int rc;
+ printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
+
+ wmediumd_pid = 0;
+
+ rc = genl_register_family_with_ops(&hwsim_genl_family,
+ hwsim_ops, ARRAY_SIZE(hwsim_ops));
+ if (rc)
+ goto failure;
+
+ rc = netlink_register_notifier(&hwsim_netlink_notifier);
+ if (rc)
+ goto failure;
+
+ return 0;
+
+failure:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -EINVAL;
+}
+
+static void hwsim_exit_netlink(void)
+{
+ int ret;
+
+ printk(KERN_INFO "mac80211_hwsim: closing netlink\n");
+ /* unregister the notifier */
+ netlink_unregister_notifier(&hwsim_netlink_notifier);
+ /* unregister the family */
+ ret = genl_unregister_family(&hwsim_genl_family);
+ if (ret)
+ printk(KERN_DEBUG "mac80211_hwsim: "
+ "unregister family %i\n", ret);
+}
+
static int __init init_mac80211_hwsim(void)
{
int i, err = 0;
@@ -1298,6 +1706,7 @@ static int __init init_mac80211_hwsim(void)
goto failed_drvdata;
}
data->dev->driver = &mac80211_hwsim_driver;
+ skb_queue_head_init(&data->pending);

SET_IEEE80211_DEV(hw, data->dev);
addr[3] = i >> 8;
@@ -1379,6 +1788,10 @@ static int __init init_mac80211_hwsim(void)
data->group = 1;
mutex_init(&data->mutex);

+ /* Enable frame retransmissions for lossy channels */
+ hw->max_rates = 4;
+ hw->max_rate_tries = 11;
+
/* Work to be done prior to ieee80211_register_hw() */
switch (regtest) {
case HWSIM_REGTEST_DISABLED:
@@ -1515,12 +1928,29 @@ static int __init init_mac80211_hwsim(void)
if (hwsim_mon == NULL)
goto failed;

- err = register_netdev(hwsim_mon);
+ rtnl_lock();
+
+ err = dev_alloc_name(hwsim_mon, hwsim_mon->name);
if (err < 0)
goto failed_mon;

+
+ err = register_netdevice(hwsim_mon);
+ if (err < 0)
+ goto failed_mon;
+
+ rtnl_unlock();
+
+ err = hwsim_init_netlink();
+ if (err < 0)
+ goto failed_nl;
+
return 0;

+failed_nl:
+ printk(KERN_DEBUG "mac_80211_hwsim: failed initializing netlink\n");
+ return err;
+
failed_mon:
rtnl_unlock();
free_netdev(hwsim_mon);
@@ -1541,6 +1971,8 @@ static void __exit exit_mac80211_hwsim(void)
{
printk(KERN_DEBUG "mac80211_hwsim: unregister radios\n");

+ hwsim_exit_netlink();
+
mac80211_hwsim_free();
unregister_netdev(hwsim_mon);
}
diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h
new file mode 100644
index 0000000..e9da5d4
--- /dev/null
+++ b/drivers/net/wireless/mac80211_hwsim.h
@@ -0,0 +1,131 @@
+/*
+ * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
+ * Copyright (c) 2008, Jouni Malinen <[email protected]>
+ * Copyright (c) 2011, Javier Lopez <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <net/mac80211.h>
+
+/**
+ * enum hwsim_tx_control_flags - flags to describe transmission info/status
+ *
+ * These flags are used to give the wmediumd extra information in order to
+ * modify its behavior for each frame
+ *
+ * @HWSIM_TX_CTL_REQ_TX_STATUS: require TX status callback for this frame.
+ * @HWSIM_TX_CTL_NO_ACK: tell the wmediumd not to wait for an ack
+ * @HWSIM_TX_STAT_ACK: Frame was acknowledged
+ *
+ */
+enum hwsim_tx_control_flags {
+ HWSIM_TX_CTL_REQ_TX_STATUS = BIT(0),
+ HWSIM_TX_CTL_NO_ACK = BIT(1),
+ HWSIM_TX_STAT_ACK = BIT(2),
+};
+
+/**
+ * DOC: Frame transmission/registration support
+ *
+ * Frame transmission and registration support exists to allow userspace
+ * entities such as wmediumd to receive and process all broadcasted
+ * frames from a mac80211_hwsim radio device.
+ *
+ * This allow user space applications to decide if the frame should be
+ * dropped or not and implement a wireless medium simulator at user space.
+ *
+ * Registration is done by sending a register message to the driver and
+ * will be automatically unregistered if the user application doesn't
+ * responds to sent frames.
+ * Once registered the user application has to take responsibility of
+ * broadcasting the frames to all listening mac80211_hwsim radio
+ * interfaces.
+ *
+ * For more technical details, see the corresponding command descriptions
+ * below.
+ */
+
+/**
+ * enum hwsim_commands - supported hwsim commands
+ *
+ * @HWSIM_CMD_UNSPEC: unspecified command to catch errors
+ *
+ * @HWSIM_CMD_REGISTER: request to register and received all broadcasted
+ * frames by any mac80211_hwsim radio device.
+ * @HWSIM_CMD_FRAME: send/receive a broadcasted frame from/to kernel/user
+ * space, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER,
+ * %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE,
+ * %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
+ * @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to
+ * kernel, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS,
+ * %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
+ * @__HWSIM_CMD_MAX: enum limit
+ */
+enum {
+ HWSIM_CMD_UNSPEC,
+ HWSIM_CMD_REGISTER,
+ HWSIM_CMD_FRAME,
+ HWSIM_CMD_TX_INFO_FRAME,
+ __HWSIM_CMD_MAX,
+};
+#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)
+
+/**
+ * enum hwsim_attrs - hwsim netlink attributes
+ *
+ * @HWSIM_ATTR_UNSPEC: unspecified attribute to catch errors
+ *
+ * @HWSIM_ATTR_ADDR_RECEIVER: MAC address of the radio device that
+ * the frame is broadcasted to
+ * @HWSIM_ATTR_ADDR_TRANSMITTER: MAC address of the radio device that
+ * the frame was broadcasted from
+ * @HWSIM_ATTR_FRAME: Data array
+ * @HWSIM_ATTR_FLAGS: mac80211 transmission flags, used to process
+ properly the frame at user space
+ * @HWSIM_ATTR_RX_RATE: estimated rx rate index for this frame at user
+ space
+ * @HWSIM_ATTR_SIGNAL: estimated RX signal for this frame at user
+ space
+ * @HWSIM_ATTR_TX_INFO: ieee80211_tx_rate array
+ * @HWSIM_ATTR_COOKIE: sk_buff cookie to identify the frame
+ * @__HWSIM_ATTR_MAX: enum limit
+ */
+
+
+enum {
+ HWSIM_ATTR_UNSPEC,
+ HWSIM_ATTR_ADDR_RECEIVER,
+ HWSIM_ATTR_ADDR_TRANSMITTER,
+ HWSIM_ATTR_FRAME,
+ HWSIM_ATTR_FLAGS,
+ HWSIM_ATTR_RX_RATE,
+ HWSIM_ATTR_SIGNAL,
+ HWSIM_ATTR_TX_INFO,
+ HWSIM_ATTR_COOKIE,
+ __HWSIM_ATTR_MAX,
+};
+#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
+
+/**
+ * struct hwsim_tx_rate - rate selection/status
+ *
+ * @idx: rate index to attempt to send with
+ * @count: number of tries in this rate before going to the next rate
+ *
+ * A value of -1 for @idx indicates an invalid rate and, if used
+ * in an array of retry rates, that no more rates should be tried.
+ *
+ * When used for transmit status reporting, the driver should
+ * always report the rate and number of retries used.
+ *
+ */
+struct hwsim_tx_rate {
+ s8 idx;
+ u8 count;
+} __packed;
+
--
1.7.0.4


2011-06-01 09:26:31

by Javier López

[permalink] [raw]
Subject: [PATCH v8] mac80211_hwsim driver support userspace frame tx/rx

This patch adds to mac80211_hwsim the capability to send traffic via
userspace.

Frame exchange between kernel and user spaces is done through generic
netlink communication protocol. A new generic netlink family
MAC80211_HWSIM is proposed, this family contains three basic commands
HWSIM_CMD_REGISTER, which is the command used to register a new
traffic listener, HWSIM_CMD_FRAME, to exchange the frames from kernel
to user and vice-versa, and HWSIM_CMD_TX_INFO_FRAME which returns
from user all the information about retransmissions, rates, rx signal,
and so on.

How it works:

Once the driver is loaded the MAC80211_HWSIM family will be registered.
In the absence of userspace daemon, the driver itselfs implements a
perfect wireless medium as it did in the past. When a daemon sends a
HWSIM_CMD_REGISTER command, the module stores the application PID, and
from this moment all frames will be sent to the registered daemon.

The user space application will be in charge of process/forward all
frames broadcast by any mac80211_hwsim radio. If the user application
is stopped, the kernel module will detect the release of the socket
and it will switch back to in-kernel perfect channel simulation.

The userspace daemon must be waiting for incoming HWSIM_CMD_FRAME
commands sent from kernel, for each HWSIM_CMD_FRAME command the
application will try to broadcast this frame to all mac80211_hwsim
radios, however the application may decide to forward/drop this frame.
In the case of forwarding the frame, a new HWSIM_CMD_FRAME command will
be created, all necessary attributes will be populated and the frame
will be sent back to the kernel.

Also after the frame broadcast phase, a HWSIM_CMD_TX_INFO_FRAME
command will be sent from userspace to kernel, this command contains
all the information regarding the transmission, such as number of
tries, rates, ack signal, etc.

You can find the actual implementation of wireless mediumd daemon
(wmediumd) at:

* Last version tarball: https://github.com/jlopex/cozybit/tarball/master
* Or visiting my github tree: https://github.com/jlopex/cozybit/tree

Signed-off-by: Javier Lopez <[email protected]>
---
Thanks to Johaness Berg, Javier Cardona and John Linville for their
effort, reviews and patience with me.

Changelog:

v1: Initial version
v2: Fixed memory check issues, added netlink attributes checks, erased
extra printks
v3: Changed HWSIM generic netlink to MAC80211_HWSIM generic netlink family,
solved a lock issue
v4: Added netlink notifier in order to detect user space socket release,
more accurate memory allocation, added skb_queue per radio to hold last
exchanged frames with userspace
v5: Avoided the use of ieee80211 data structures, changed the cookie netlink
attribute to U64, solved memory leak in no-wmediumd mode, erased extra
debug messages, added netlink attributes check per each command, nla_len
for all data structures sent over netlink, solved a bug with interfaces
not completely configured
v6: Added patch description and changelog
v7: Solved double free / memory leak, moved policy to .c file, changed
mistaken returns, now superuser permit is required to use this API
v8: Removed unused include clause from mac80211_hwsim.h

drivers/net/wireless/mac80211_hwsim.c | 462 +++++++++++++++++++++++++++++++-
drivers/net/wireless/mac80211_hwsim.h | 133 ++++++++++
2 files changed, 580 insertions(+), 15 deletions(-)
create mode 100644 drivers/net/wireless/mac80211_hwsim.h

diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
index 9d4a40e..7e1fa96 100644
--- a/drivers/net/wireless/mac80211_hwsim.c
+++ b/drivers/net/wireless/mac80211_hwsim.c
@@ -1,6 +1,7 @@
/*
* mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
* Copyright (c) 2008, Jouni Malinen <[email protected]>
+ * Copyright (c) 2011, Javier Lopez <[email protected]>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -25,11 +26,17 @@
#include <linux/rtnetlink.h>
#include <linux/etherdevice.h>
#include <linux/debugfs.h>
+#include <net/genetlink.h>
+#include "mac80211_hwsim.h"
+
+#define WARN_QUEUE 100
+#define MAX_QUEUE 200

MODULE_AUTHOR("Jouni Malinen");
MODULE_DESCRIPTION("Software simulator of 802.11 radio(s) for mac80211");
MODULE_LICENSE("GPL");

+int wmediumd_pid;
static int radios = 2;
module_param(radios, int, 0444);
MODULE_PARM_DESC(radios, "Number of simulated radios");
@@ -302,6 +309,7 @@ struct mac80211_hwsim_data {
struct dentry *debugfs;
struct dentry *debugfs_ps;

+ struct sk_buff_head pending; /* packets pending */
/*
* Only radios in the same group can communicate together (the
* channel has to match too). Each bit represents a group. A
@@ -322,6 +330,32 @@ struct hwsim_radiotap_hdr {
__le16 rt_chbitmask;
} __packed;

+/* MAC80211_HWSIM netlinf family */
+static struct genl_family hwsim_genl_family = {
+ .id = GENL_ID_GENERATE,
+ .hdrsize = 0,
+ .name = "MAC80211_HWSIM",
+ .version = 1,
+ .maxattr = HWSIM_ATTR_MAX,
+};
+
+/* MAC80211_HWSIM netlink policy */
+
+static struct nla_policy hwsim_genl_policy[HWSIM_ATTR_MAX + 1] = {
+ [HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_UNSPEC,
+ .len = 6*sizeof(u8) },
+ [HWSIM_ATTR_FRAME] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+ [HWSIM_ATTR_FLAGS] = { .type = NLA_U32 },
+ [HWSIM_ATTR_RX_RATE] = { .type = NLA_U32 },
+ [HWSIM_ATTR_SIGNAL] = { .type = NLA_U32 },
+ [HWSIM_ATTR_TX_INFO] = { .type = NLA_UNSPEC,
+ .len = IEEE80211_TX_MAX_RATES*sizeof(
+ struct hwsim_tx_rate)},
+ [HWSIM_ATTR_COOKIE] = { .type = NLA_U64 },
+};

static netdev_tx_t hwsim_mon_xmit(struct sk_buff *skb,
struct net_device *dev)
@@ -478,9 +512,89 @@ static bool mac80211_hwsim_addr_match(struct mac80211_hwsim_data *data,
return md.ret;
}

+static void mac80211_hwsim_tx_frame_nl(struct ieee80211_hw *hw,
+ struct sk_buff *my_skb,
+ int dst_pid)
+{
+ struct sk_buff *skb;
+ struct mac80211_hwsim_data *data = hw->priv;
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) my_skb->data;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(my_skb);
+ void *msg_head;
+ unsigned int hwsim_flags = 0;
+ int i;
+ struct hwsim_tx_rate tx_attempts[IEEE80211_TX_MAX_RATES];
+
+ if (data->idle) {
+ wiphy_debug(hw->wiphy, "Trying to TX when idle - reject\n");
+ dev_kfree_skb(my_skb);
+ return;
+ }
+
+ if (data->ps != PS_DISABLED)
+ hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM);
+ /* If the queue contains MAX_QUEUE skb's drop some */
+ if (skb_queue_len(&data->pending) >= MAX_QUEUE) {
+ /* Droping until WARN_QUEUE level */
+ while (skb_queue_len(&data->pending) >= WARN_QUEUE)
+ skb_dequeue(&data->pending);
+ }
+
+ skb = genlmsg_new(NLMSG_GOODSIZE, GFP_ATOMIC);
+ if (skb == NULL)
+ goto nla_put_failure;
+
+ msg_head = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
+ HWSIM_CMD_FRAME);
+ if (msg_head == NULL) {
+ printk(KERN_DEBUG "mac80211_hwsim: problem with msg_head\n");
+ goto nla_put_failure;
+ }
+
+ NLA_PUT(skb, HWSIM_ATTR_ADDR_TRANSMITTER,
+ sizeof(struct mac_address), data->addresses[1].addr);

-static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
- struct sk_buff *skb)
+ /* We get the skb->data */
+ NLA_PUT(skb, HWSIM_ATTR_FRAME, my_skb->len, my_skb->data);
+
+ /* We get the flags for this transmission, and we translate them to
+ wmediumd flags */
+
+ if (info->flags & IEEE80211_TX_CTL_REQ_TX_STATUS)
+ hwsim_flags |= HWSIM_TX_CTL_REQ_TX_STATUS;
+
+ if (info->flags & IEEE80211_TX_CTL_NO_ACK)
+ hwsim_flags |= HWSIM_TX_CTL_NO_ACK;
+
+ NLA_PUT_U32(skb, HWSIM_ATTR_FLAGS, hwsim_flags);
+
+ /* We get the tx control (rate and retries) info*/
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ tx_attempts[i].idx = info->status.rates[i].idx;
+ tx_attempts[i].count = info->status.rates[i].count;
+ }
+
+ NLA_PUT(skb, HWSIM_ATTR_TX_INFO,
+ sizeof(struct hwsim_tx_rate)*IEEE80211_TX_MAX_RATES,
+ tx_attempts);
+
+ /* We create a cookie to identify this skb */
+ NLA_PUT_U64(skb, HWSIM_ATTR_COOKIE, (unsigned long) my_skb);
+
+ genlmsg_end(skb, msg_head);
+ genlmsg_unicast(&init_net, skb, dst_pid);
+
+ /* Enqueue the packet */
+ skb_queue_tail(&data->pending, my_skb);
+ return;
+
+nla_put_failure:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+}
+
+static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
+ struct sk_buff *skb)
{
struct mac80211_hwsim_data *data = hw->priv, *data2;
bool ack = false;
@@ -540,11 +654,11 @@ static bool mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
return ack;
}

-
static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
{
bool ack;
struct ieee80211_tx_info *txi;
+ int _pid;

mac80211_hwsim_monitor_rx(hw, skb);

@@ -554,7 +668,15 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
return;
}

- ack = mac80211_hwsim_tx_frame(hw, skb);
+ /* wmediumd mode check */
+ _pid = wmediumd_pid;
+
+ if (_pid)
+ return mac80211_hwsim_tx_frame_nl(hw, skb, _pid);
+
+ /* NO wmediumd detected, perfect medium simulation */
+ ack = mac80211_hwsim_tx_frame_no_nl(hw, skb);
+
if (ack && skb->len >= 16) {
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
mac80211_hwsim_monitor_ack(hw, hdr->addr2);
@@ -635,6 +757,7 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
struct ieee80211_hw *hw = arg;
struct sk_buff *skb;
struct ieee80211_tx_info *info;
+ int _pid;

hwsim_check_magic(vif);

@@ -649,7 +772,14 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac,
info = IEEE80211_SKB_CB(skb);

mac80211_hwsim_monitor_rx(hw, skb);
- mac80211_hwsim_tx_frame(hw, skb);
+
+ /* wmediumd mode check */
+ _pid = wmediumd_pid;
+
+ if (_pid)
+ return mac80211_hwsim_tx_frame_nl(hw, skb, _pid);
+
+ mac80211_hwsim_tx_frame_no_nl(hw, skb);
dev_kfree_skb(skb);
}

@@ -966,12 +1096,7 @@ static int mac80211_hwsim_ampdu_action(struct ieee80211_hw *hw,

static void mac80211_hwsim_flush(struct ieee80211_hw *hw, bool drop)
{
- /*
- * In this special case, there's nothing we need to
- * do because hwsim does transmission synchronously.
- * In the future, when it does transmissions via
- * userspace, we may need to do something.
- */
+ /* Not implemented, queues only on kernel side */
}

struct hw_scan_done {
@@ -1119,6 +1244,7 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
struct sk_buff *skb;
struct ieee80211_pspoll *pspoll;
+ int _pid;

if (!vp->assoc)
return;
@@ -1137,8 +1263,15 @@ static void hwsim_send_ps_poll(void *dat, u8 *mac, struct ieee80211_vif *vif)
pspoll->aid = cpu_to_le16(0xc000 | vp->aid);
memcpy(pspoll->bssid, vp->bssid, ETH_ALEN);
memcpy(pspoll->ta, mac, ETH_ALEN);
- if (!mac80211_hwsim_tx_frame(data->hw, skb))
- printk(KERN_DEBUG "%s: PS-Poll frame not ack'ed\n", __func__);
+
+ /* wmediumd mode check */
+ _pid = wmediumd_pid;
+
+ if (_pid)
+ return mac80211_hwsim_tx_frame_nl(data->hw, skb, _pid);
+
+ if (!mac80211_hwsim_tx_frame_no_nl(data->hw, skb))
+ printk(KERN_DEBUG "%s: PS-poll frame not ack'ed\n", __func__);
dev_kfree_skb(skb);
}

@@ -1149,6 +1282,7 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
struct sk_buff *skb;
struct ieee80211_hdr *hdr;
+ int _pid;

if (!vp->assoc)
return;
@@ -1168,7 +1302,14 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
memcpy(hdr->addr1, vp->bssid, ETH_ALEN);
memcpy(hdr->addr2, mac, ETH_ALEN);
memcpy(hdr->addr3, vp->bssid, ETH_ALEN);
- if (!mac80211_hwsim_tx_frame(data->hw, skb))
+
+ /* wmediumd mode check */
+ _pid = wmediumd_pid;
+
+ if (_pid)
+ return mac80211_hwsim_tx_frame_nl(data->hw, skb, _pid);
+
+ if (!mac80211_hwsim_tx_frame_no_nl(data->hw, skb))
printk(KERN_DEBUG "%s: nullfunc frame not ack'ed\n", __func__);
dev_kfree_skb(skb);
}
@@ -1248,6 +1389,273 @@ DEFINE_SIMPLE_ATTRIBUTE(hwsim_fops_group,
hwsim_fops_group_read, hwsim_fops_group_write,
"%llx\n");

+struct mac80211_hwsim_data *get_hwsim_data_ref_from_addr(
+ struct mac_address *addr)
+{
+ struct mac80211_hwsim_data *data;
+ bool _found = false;
+
+ spin_lock_bh(&hwsim_radio_lock);
+ list_for_each_entry(data, &hwsim_radios, list) {
+ if (memcmp(data->addresses[1].addr, addr,
+ sizeof(struct mac_address)) == 0) {
+ _found = true;
+ break;
+ }
+ }
+ spin_unlock_bh(&hwsim_radio_lock);
+
+ if (!_found)
+ return NULL;
+
+ return data;
+}
+
+static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct ieee80211_hdr *hdr;
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_tx_info *txi;
+ struct hwsim_tx_rate *tx_attempts;
+ struct sk_buff __user *ret_skb;
+ struct sk_buff *skb, *tmp;
+ struct mac_address *src;
+ unsigned int hwsim_flags;
+
+ int i;
+ bool found = false;
+
+ if (!info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER] ||
+ !info->attrs[HWSIM_ATTR_FLAGS] ||
+ !info->attrs[HWSIM_ATTR_COOKIE] ||
+ !info->attrs[HWSIM_ATTR_TX_INFO])
+ goto out;
+
+ src = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_TRANSMITTER]);
+ hwsim_flags = nla_get_u32(info->attrs[HWSIM_ATTR_FLAGS]);
+
+ ret_skb = (struct sk_buff __user *)
+ (unsigned long) nla_get_u64(info->attrs[HWSIM_ATTR_COOKIE]);
+
+ data2 = get_hwsim_data_ref_from_addr(src);
+
+ if (data2 == NULL)
+ goto out;
+
+ /* look for the skb matching the cookie passed back from user */
+ skb_queue_walk_safe(&data2->pending, skb, tmp) {
+ if (skb == ret_skb) {
+ skb_unlink(skb, &data2->pending);
+ found = true;
+ break;
+ }
+ }
+
+ /* not found */
+ if (!found)
+ goto out;
+
+ /* Tx info received because the frame was broadcasted on user space,
+ so we get all the necessary info: tx attempts and skb control buff */
+
+ tx_attempts = (struct hwsim_tx_rate *)nla_data(
+ info->attrs[HWSIM_ATTR_TX_INFO]);
+
+ /* now send back TX status */
+ txi = IEEE80211_SKB_CB(skb);
+
+ if (txi->control.vif)
+ hwsim_check_magic(txi->control.vif);
+ if (txi->control.sta)
+ hwsim_check_sta_magic(txi->control.sta);
+
+ ieee80211_tx_info_clear_status(txi);
+
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ txi->status.rates[i].idx = tx_attempts[i].idx;
+ txi->status.rates[i].count = tx_attempts[i].count;
+ /*txi->status.rates[i].flags = 0;*/
+ }
+
+ txi->status.ack_signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ if (!(hwsim_flags & HWSIM_TX_CTL_NO_ACK) &&
+ (hwsim_flags & HWSIM_TX_STAT_ACK)) {
+ if (skb->len >= 16) {
+ hdr = (struct ieee80211_hdr *) skb->data;
+ mac80211_hwsim_monitor_ack(data2->hw, hdr->addr2);
+ }
+ }
+ ieee80211_tx_status_irqsafe(data2->hw, skb);
+ return 0;
+out:
+ return -EINVAL;
+
+}
+
+static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+
+ struct mac80211_hwsim_data *data2;
+ struct ieee80211_rx_status rx_status;
+ struct mac_address *dst;
+ int frame_data_len;
+ char *frame_data;
+ struct sk_buff *skb = NULL;
+
+ if (!info->attrs[HWSIM_ATTR_ADDR_RECEIVER] ||
+ !info->attrs[HWSIM_ATTR_FRAME] ||
+ !info->attrs[HWSIM_ATTR_RX_RATE] ||
+ !info->attrs[HWSIM_ATTR_SIGNAL])
+ goto out;
+
+ dst = (struct mac_address *)nla_data(
+ info->attrs[HWSIM_ATTR_ADDR_RECEIVER]);
+
+ frame_data_len = nla_len(info->attrs[HWSIM_ATTR_FRAME]);
+ frame_data = (char *)nla_data(info->attrs[HWSIM_ATTR_FRAME]);
+
+ /* Allocate new skb here */
+ skb = alloc_skb(frame_data_len, GFP_KERNEL);
+ if (skb == NULL)
+ goto err;
+
+ if (frame_data_len <= IEEE80211_MAX_DATA_LEN) {
+ /* Copy the data */
+ memcpy(skb_put(skb, frame_data_len), frame_data,
+ frame_data_len);
+ } else
+ goto err;
+
+ data2 = get_hwsim_data_ref_from_addr(dst);
+
+ if (data2 == NULL)
+ goto out;
+
+ /* check if radio is configured properly */
+
+ if (data2->idle || !data2->started || !data2->channel)
+ goto out;
+
+ /*A frame is received from user space*/
+ memset(&rx_status, 0, sizeof(rx_status));
+ rx_status.freq = data2->channel->center_freq;
+ rx_status.band = data2->channel->band;
+ rx_status.rate_idx = nla_get_u32(info->attrs[HWSIM_ATTR_RX_RATE]);
+ rx_status.signal = nla_get_u32(info->attrs[HWSIM_ATTR_SIGNAL]);
+
+ memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
+ ieee80211_rx_irqsafe(data2->hw, skb);
+
+ return 0;
+err:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ goto out;
+out:
+ dev_kfree_skb(skb);
+ return -EINVAL;
+}
+
+static int hwsim_register_received_nl(struct sk_buff *skb_2,
+ struct genl_info *info)
+{
+ if (info == NULL)
+ goto out;
+
+ wmediumd_pid = info->snd_pid;
+
+ printk(KERN_DEBUG "mac80211_hwsim: received a REGISTER, "
+ "switching to wmediumd mode with pid %d\n", info->snd_pid);
+
+ return 0;
+out:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -EINVAL;
+}
+
+/* Generic Netlink operations array */
+static struct genl_ops hwsim_ops[] = {
+ {
+ .cmd = HWSIM_CMD_REGISTER,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_register_received_nl,
+ .flags = GENL_ADMIN_PERM,
+ },
+ {
+ .cmd = HWSIM_CMD_FRAME,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_cloned_frame_received_nl,
+ },
+ {
+ .cmd = HWSIM_CMD_TX_INFO_FRAME,
+ .policy = hwsim_genl_policy,
+ .doit = hwsim_tx_info_frame_received_nl,
+ },
+};
+
+static int mac80211_hwsim_netlink_notify(struct notifier_block *nb,
+ unsigned long state,
+ void *_notify)
+{
+ struct netlink_notify *notify = _notify;
+
+ if (state != NETLINK_URELEASE)
+ return NOTIFY_DONE;
+
+ if (notify->pid == wmediumd_pid) {
+ printk(KERN_INFO "mac80211_hwsim: wmediumd released netlink"
+ " socket, switching to perfect channel medium\n");
+ wmediumd_pid = 0;
+ }
+ return NOTIFY_DONE;
+
+}
+
+static struct notifier_block hwsim_netlink_notifier = {
+ .notifier_call = mac80211_hwsim_netlink_notify,
+};
+
+static int hwsim_init_netlink(void)
+{
+ int rc;
+ printk(KERN_INFO "mac80211_hwsim: initializing netlink\n");
+
+ wmediumd_pid = 0;
+
+ rc = genl_register_family_with_ops(&hwsim_genl_family,
+ hwsim_ops, ARRAY_SIZE(hwsim_ops));
+ if (rc)
+ goto failure;
+
+ rc = netlink_register_notifier(&hwsim_netlink_notifier);
+ if (rc)
+ goto failure;
+
+ return 0;
+
+failure:
+ printk(KERN_DEBUG "mac80211_hwsim: error occured in %s\n", __func__);
+ return -EINVAL;
+}
+
+static void hwsim_exit_netlink(void)
+{
+ int ret;
+
+ printk(KERN_INFO "mac80211_hwsim: closing netlink\n");
+ /* unregister the notifier */
+ netlink_unregister_notifier(&hwsim_netlink_notifier);
+ /* unregister the family */
+ ret = genl_unregister_family(&hwsim_genl_family);
+ if (ret)
+ printk(KERN_DEBUG "mac80211_hwsim: "
+ "unregister family %i\n", ret);
+}
+
static int __init init_mac80211_hwsim(void)
{
int i, err = 0;
@@ -1298,6 +1706,7 @@ static int __init init_mac80211_hwsim(void)
goto failed_drvdata;
}
data->dev->driver = &mac80211_hwsim_driver;
+ skb_queue_head_init(&data->pending);

SET_IEEE80211_DEV(hw, data->dev);
addr[3] = i >> 8;
@@ -1379,6 +1788,10 @@ static int __init init_mac80211_hwsim(void)
data->group = 1;
mutex_init(&data->mutex);

+ /* Enable frame retransmissions for lossy channels */
+ hw->max_rates = 4;
+ hw->max_rate_tries = 11;
+
/* Work to be done prior to ieee80211_register_hw() */
switch (regtest) {
case HWSIM_REGTEST_DISABLED:
@@ -1515,12 +1928,29 @@ static int __init init_mac80211_hwsim(void)
if (hwsim_mon == NULL)
goto failed;

- err = register_netdev(hwsim_mon);
+ rtnl_lock();
+
+ err = dev_alloc_name(hwsim_mon, hwsim_mon->name);
if (err < 0)
goto failed_mon;

+
+ err = register_netdevice(hwsim_mon);
+ if (err < 0)
+ goto failed_mon;
+
+ rtnl_unlock();
+
+ err = hwsim_init_netlink();
+ if (err < 0)
+ goto failed_nl;
+
return 0;

+failed_nl:
+ printk(KERN_DEBUG "mac_80211_hwsim: failed initializing netlink\n");
+ return err;
+
failed_mon:
rtnl_unlock();
free_netdev(hwsim_mon);
@@ -1541,6 +1971,8 @@ static void __exit exit_mac80211_hwsim(void)
{
printk(KERN_DEBUG "mac80211_hwsim: unregister radios\n");

+ hwsim_exit_netlink();
+
mac80211_hwsim_free();
unregister_netdev(hwsim_mon);
}
diff --git a/drivers/net/wireless/mac80211_hwsim.h b/drivers/net/wireless/mac80211_hwsim.h
new file mode 100644
index 0000000..afaad5a
--- /dev/null
+++ b/drivers/net/wireless/mac80211_hwsim.h
@@ -0,0 +1,133 @@
+/*
+ * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
+ * Copyright (c) 2008, Jouni Malinen <[email protected]>
+ * Copyright (c) 2011, Javier Lopez <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MAC80211_HWSIM_H
+#define __MAC80211_HWSIM_H
+
+/**
+ * enum hwsim_tx_control_flags - flags to describe transmission info/status
+ *
+ * These flags are used to give the wmediumd extra information in order to
+ * modify its behavior for each frame
+ *
+ * @HWSIM_TX_CTL_REQ_TX_STATUS: require TX status callback for this frame.
+ * @HWSIM_TX_CTL_NO_ACK: tell the wmediumd not to wait for an ack
+ * @HWSIM_TX_STAT_ACK: Frame was acknowledged
+ *
+ */
+enum hwsim_tx_control_flags {
+ HWSIM_TX_CTL_REQ_TX_STATUS = BIT(0),
+ HWSIM_TX_CTL_NO_ACK = BIT(1),
+ HWSIM_TX_STAT_ACK = BIT(2),
+};
+
+/**
+ * DOC: Frame transmission/registration support
+ *
+ * Frame transmission and registration support exists to allow userspace
+ * entities such as wmediumd to receive and process all broadcasted
+ * frames from a mac80211_hwsim radio device.
+ *
+ * This allow user space applications to decide if the frame should be
+ * dropped or not and implement a wireless medium simulator at user space.
+ *
+ * Registration is done by sending a register message to the driver and
+ * will be automatically unregistered if the user application doesn't
+ * responds to sent frames.
+ * Once registered the user application has to take responsibility of
+ * broadcasting the frames to all listening mac80211_hwsim radio
+ * interfaces.
+ *
+ * For more technical details, see the corresponding command descriptions
+ * below.
+ */
+
+/**
+ * enum hwsim_commands - supported hwsim commands
+ *
+ * @HWSIM_CMD_UNSPEC: unspecified command to catch errors
+ *
+ * @HWSIM_CMD_REGISTER: request to register and received all broadcasted
+ * frames by any mac80211_hwsim radio device.
+ * @HWSIM_CMD_FRAME: send/receive a broadcasted frame from/to kernel/user
+ * space, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_ADDR_RECEIVER,
+ * %HWSIM_ATTR_FRAME, %HWSIM_ATTR_FLAGS, %HWSIM_ATTR_RX_RATE,
+ * %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
+ * @HWSIM_CMD_TX_INFO_FRAME: Transmission info report from user space to
+ * kernel, uses:
+ * %HWSIM_ATTR_ADDR_TRANSMITTER, %HWSIM_ATTR_FLAGS,
+ * %HWSIM_ATTR_TX_INFO, %HWSIM_ATTR_SIGNAL, %HWSIM_ATTR_COOKIE
+ * @__HWSIM_CMD_MAX: enum limit
+ */
+enum {
+ HWSIM_CMD_UNSPEC,
+ HWSIM_CMD_REGISTER,
+ HWSIM_CMD_FRAME,
+ HWSIM_CMD_TX_INFO_FRAME,
+ __HWSIM_CMD_MAX,
+};
+#define HWSIM_CMD_MAX (_HWSIM_CMD_MAX - 1)
+
+/**
+ * enum hwsim_attrs - hwsim netlink attributes
+ *
+ * @HWSIM_ATTR_UNSPEC: unspecified attribute to catch errors
+ *
+ * @HWSIM_ATTR_ADDR_RECEIVER: MAC address of the radio device that
+ * the frame is broadcasted to
+ * @HWSIM_ATTR_ADDR_TRANSMITTER: MAC address of the radio device that
+ * the frame was broadcasted from
+ * @HWSIM_ATTR_FRAME: Data array
+ * @HWSIM_ATTR_FLAGS: mac80211 transmission flags, used to process
+ properly the frame at user space
+ * @HWSIM_ATTR_RX_RATE: estimated rx rate index for this frame at user
+ space
+ * @HWSIM_ATTR_SIGNAL: estimated RX signal for this frame at user
+ space
+ * @HWSIM_ATTR_TX_INFO: ieee80211_tx_rate array
+ * @HWSIM_ATTR_COOKIE: sk_buff cookie to identify the frame
+ * @__HWSIM_ATTR_MAX: enum limit
+ */
+
+
+enum {
+ HWSIM_ATTR_UNSPEC,
+ HWSIM_ATTR_ADDR_RECEIVER,
+ HWSIM_ATTR_ADDR_TRANSMITTER,
+ HWSIM_ATTR_FRAME,
+ HWSIM_ATTR_FLAGS,
+ HWSIM_ATTR_RX_RATE,
+ HWSIM_ATTR_SIGNAL,
+ HWSIM_ATTR_TX_INFO,
+ HWSIM_ATTR_COOKIE,
+ __HWSIM_ATTR_MAX,
+};
+#define HWSIM_ATTR_MAX (__HWSIM_ATTR_MAX - 1)
+
+/**
+ * struct hwsim_tx_rate - rate selection/status
+ *
+ * @idx: rate index to attempt to send with
+ * @count: number of tries in this rate before going to the next rate
+ *
+ * A value of -1 for @idx indicates an invalid rate and, if used
+ * in an array of retry rates, that no more rates should be tried.
+ *
+ * When used for transmit status reporting, the driver should
+ * always report the rate and number of retries used.
+ *
+ */
+struct hwsim_tx_rate {
+ s8 idx;
+ u8 count;
+} __packed;
+
+#endif /* __MAC80211_HWSIM_H */
--
1.7.0.4


2011-06-01 04:17:04

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCH v7] mac80211_hwsim driver support userspace frame tx/rx

On Wed, 2011-06-01 at 00:06 +0200, Javier Lopez wrote:
> This patch adds to mac80211_hwsim the capability to send traffic via
> userspace.

Looks good, thanks for your patience.

> +++ b/drivers/net/wireless/mac80211_hwsim.h

This file is for userspace, no?

> @@ -0,0 +1,131 @@
> +/*
> + * mac80211_hwsim - software simulator of 802.11 radio(s) for mac80211
> + * Copyright (c) 2008, Jouni Malinen <[email protected]>
> + * Copyright (c) 2011, Javier Lopez <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <net/mac80211.h>

It shouldn't include that then, but it doesn't really matter anyway.

johannes