lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250603190506.6382-1-ramonreisfontes@gmail.com>
Date: Tue,  3 Jun 2025 16:05:06 -0300
From: Ramon Fontes <ramonreisfontes@...il.com>
To: davem@...emloft.net,
	kuba@...nel.org,
	pabeni@...hat.com
Cc: linux-wpan@...r.kernel.org,
	alex.aring@...il.com,
	miquel.raynal@...tlin.com,
	netdev@...r.kernel.org,
	Ramon Fontes <ramonreisfontes@...il.com>
Subject: [PATCH] Integration with the user space

This PR introduces support for use space tools such as wmediumd in the mac802154_hwsim kernel module, similar to the existing support for Wi-Fi simulations via mac80211_hwsim. With this addition, it becomes possible to simulate interference and control transmission behavior in IEEE 802.15.4 networks using a userspace backend.

Key changes:

* Introduced communication between mac802154_hwsim and the wmediumd daemon via netlink.
* Included a packet queue (skb_queue) that ensures packets are only transmitted after validation by wmediumd.
* Integrated support to consult wmediumd before transmitting packets, enabling the simulation of interference or transmission failure.

Backward Compatibility:

The current behavior of mac802154_hwsim remains unchanged when wmediumd is not running. If the daemon is unavailable or disabled, the module continues to operate normally, ensuring full compatibility with existing tools and workflows.

Purpose:

Enable more realistic simulations of IEEE 802.15.4-based sensor and IoT networks, accounting for factors like interference, link quality, and connectivity. This fills a gap in current emulation tools where mac802154_hwsim lacks channel behavior modeling.

Testing:

A wmediumd release for mac802154_hwsim is available at https://github.com/ramonfontes/wmediumd_802154/ and you can follow updated instructions there. However, you can also follow the steps below:

1. Building wmediumd_802154:

$ cd wmediumd_802154
$ make
$ sudo make install

2. Loading the modified mac802154_hwsim kernel module (this PR)

$ sudo modprobe mac802154_hwsim radios=3

3. Running wmediumd_802154

$ cd tests
$ sudo ./interference.sh
$ sudo wmediumd_802154 -s -c tree.cfg

4. Terminal 1: Pinging virtual sensors

The network topology of tree.cfg looks like below:

s1 -- s0 -- s2

s2 is too far away from s0 and the communication between s0 and s2 is not possible. You can now test connectivity between the virtual sensors using IPv6 link-local addresses.

Ping from sensor0 to sensor1:

ping -c 2 fe80::2
PING fe80::2 (fe80::2) 56 data bytes
64 bytes from fe80::2%pan0: icmp_seq=1 ttl=64 time=2.33 ms
64 bytes from fe80::2%pan0: icmp_seq=2 ttl=64 time=1.77 ms

--- fe80::2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 1.770/2.051/2.332/0.281 ms

Ping from sensor0 to sensor2:

ping -c 2 fe80::3
PING fe80::3 (fe80::3) 56 data bytes
--- fe80::3 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1054ms

Simulated custom topologies using Mininet-WiFi with 802.15.4 support is also possible by running https://github.com/intrig-unicamp/mininet-wifi/blob/9f2cba0d6ebcfebf6c19161b81b6186cfa110cca/examples/wmediumd_interference_lowpan.py

Signed-off-by: Ramon Fontes <ramonreisfontes@...il.com>
---
 drivers/net/ieee802154/mac802154_hwsim.c | 975 ++++++++++++++++++++++-
 drivers/net/ieee802154/mac802154_hwsim.h |  57 +-
 2 files changed, 999 insertions(+), 33 deletions(-)

diff --git a/drivers/net/ieee802154/mac802154_hwsim.c b/drivers/net/ieee802154/mac802154_hwsim.c
index 2f7520454..782aba5fa 100644
--- a/drivers/net/ieee802154/mac802154_hwsim.c
+++ b/drivers/net/ieee802154/mac802154_hwsim.c
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/timer.h>
 #include <linux/platform_device.h>
+#include <linux/debugfs.h>
 #include <linux/rtnetlink.h>
 #include <linux/netdevice.h>
 #include <linux/device.h>
@@ -22,13 +23,23 @@
 #include <net/mac802154.h>
 #include <net/cfg802154.h>
 #include <net/genetlink.h>
+#include <net/netns/generic.h>
+#include <linux/rhashtable.h>
 #include "mac802154_hwsim.h"
+#include <linux/virtio.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_config.h>
 
 MODULE_DESCRIPTION("Software simulator of IEEE 802.15.4 radio(s) for mac802154");
 MODULE_LICENSE("GPL");
 
+static int radios = 2;
+module_param(radios, int, 0444);
+MODULE_PARM_DESC(radios, "Number of simulated radios");
+
 static LIST_HEAD(hwsim_phys);
 static DEFINE_MUTEX(hwsim_phys_lock);
+static int hwsim_radios_generation = 1;
 
 static struct platform_device *mac802154hwsim_dev;
 
@@ -37,6 +48,50 @@ static struct genl_family hwsim_genl_family;
 
 static int hwsim_radio_idx;
 
+static unsigned int hwsim_net_id;
+
+static DEFINE_IDA(hwsim_netgroup_ida);
+
+static struct class *hwsim_class;
+
+struct hwsim_net {
+	int netgroup;
+	u32 wmediumd;
+};
+
+struct hwsim_cb {
+	uintptr_t cookie;
+};
+
+static inline u32 hwsim_net_get_wmediumd(struct net *net)
+{
+	struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id);
+
+	return hwsim_net->wmediumd;
+}
+
+static inline void hwsim_net_set_wmediumd(struct net *net, u32 portid)
+{
+	struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id);
+
+	hwsim_net->wmediumd = portid;
+}
+
+static inline int hwsim_net_get_netgroup(struct net *net)
+{
+	struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id);
+
+	return hwsim_net->netgroup;
+}
+
+static inline int hwsim_net_set_netgroup(struct net *net)
+{
+	struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id);
+
+	hwsim_net->netgroup = ida_alloc(&hwsim_netgroup_ida, GFP_KERNEL);
+	return hwsim_net->netgroup >= 0 ? 0 : -ENOMEM;
+}
+
 enum hwsim_multicast_groups {
 	HWSIM_MCGRP_CONFIG,
 };
@@ -45,6 +100,14 @@ static const struct genl_multicast_group hwsim_mcgrps[] = {
 	[HWSIM_MCGRP_CONFIG] = { .name = "config", },
 };
 
+struct hwsim_pending_tx {
+	struct ieee802154_hw *hw;
+	struct sk_buff *skb;
+	struct list_head list;
+};
+
+static LIST_HEAD(pending_tx_list);
+
 struct hwsim_pib {
 	u8 page;
 	u8 channel;
@@ -73,13 +136,42 @@ struct hwsim_phy {
 	u32 idx;
 
 	struct hwsim_pib __rcu *pib;
+	bool rht_inserted;
+	u8 ieee_addr[8];
+
+	struct rhash_head rht;
+	struct dentry *debugfs;
+	atomic_t pending_cookie;
+	struct sk_buff_head pending;
+	struct sk_buff *pending_skb;
+	struct device *dev;
+	struct mutex mutex;
+
+	bool destroy_on_close;
+	u32 portid;
 
 	bool suspended;
 	struct list_head edges;
 
 	struct list_head list;
+
+	/* group shared by radios created in the same netns */
+	int netgroup;
+	/* wmediumd portid responsible for netgroup of this radio */
+	u32 wmediumd;
 };
 
+static const struct rhashtable_params hwsim_rht_params = {
+	.nelem_hint = 2,
+	.automatic_shrinking = true,
+	.key_len = 8, /* ETH_ALEN */
+	.key_offset = offsetof(struct hwsim_phy, ieee_addr),
+	.head_offset = offsetof(struct hwsim_phy, rht),
+};
+
+static DEFINE_SPINLOCK(hwsim_radio_lock);
+static struct rhashtable hwsim_radios_rht;
+
 static int hwsim_add_one(struct genl_info *info, struct device *dev,
 			 bool init);
 static void hwsim_del(struct hwsim_phy *phy);
@@ -91,6 +183,17 @@ static int hwsim_hw_ed(struct ieee802154_hw *hw, u8 *level)
 	return 0;
 }
 
+static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb,
+				   struct genl_info *info)
+{
+	if (info)
+		genl_notify(&hwsim_genl_family, mcast_skb, info,
+			    HWSIM_MCGRP_CONFIG, GFP_KERNEL);
+	else
+		genlmsg_multicast(&hwsim_genl_family, mcast_skb, 0,
+				  HWSIM_MCGRP_CONFIG, GFP_KERNEL);
+}
+
 static int hwsim_update_pib(struct ieee802154_hw *hw, u8 page, u8 channel,
 			    struct ieee802154_hw_addr_filt *filt,
 			    enum ieee802154_filtering_level filt_level)
@@ -114,9 +217,15 @@ static int hwsim_update_pib(struct ieee802154_hw *hw, u8 page, u8 channel,
 
 	rcu_assign_pointer(phy->pib, pib);
 	kfree_rcu(pib_old, rcu);
+
 	return 0;
 }
 
+static struct hwsim_phy *get_hwsim_data_ref_from_addr(const u8 *addr)
+{
+	return rhashtable_lookup_fast(&hwsim_radios_rht, addr, hwsim_rht_params);
+}
+
 static int hwsim_hw_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
 {
 	struct hwsim_phy *phy = hw->priv;
@@ -147,6 +256,140 @@ static int hwsim_hw_addr_filt(struct ieee802154_hw *hw,
 	return ret;
 }
 
+static int hwsim_unicast_netgroup(struct hwsim_phy *data,
+				  struct sk_buff *skb, int portid)
+{
+	struct net *net;
+	bool found = false;
+	int res = -ENOENT;
+
+	rcu_read_lock();
+	for_each_net_rcu(net) {
+		if (data->netgroup == hwsim_net_get_netgroup(net)) {
+			res = genlmsg_unicast(net, skb, portid);
+			found = true;
+			break;
+		}
+	}
+	rcu_read_unlock();
+
+	if (!found)
+		nlmsg_free(skb);
+
+	return res;
+}
+
+#if IS_REACHABLE(CONFIG_VIRTIO)
+
+/* MAC80211_HWSIM virtio queues */
+static struct virtqueue *hwsim_vqs[HWSIM_NUM_VQS];
+static bool hwsim_virtio_enabled;
+static DEFINE_SPINLOCK(hwsim_virtio_lock);
+
+static void hwsim_virtio_rx_work(struct work_struct *work);
+static DECLARE_WORK(hwsim_virtio_rx, hwsim_virtio_rx_work);
+
+static int hwsim_tx_virtio(struct hwsim_phy *phy,
+			   struct sk_buff *skb)
+{
+	struct scatterlist sg[1];
+	unsigned long flags;
+	int err;
+
+	spin_lock_irqsave(&hwsim_virtio_lock, flags);
+	if (!hwsim_virtio_enabled) {
+		err = -ENODEV;
+		goto out_free;
+	}
+
+	sg_init_one(sg, skb->head, skb_end_offset(skb));
+	err = virtqueue_add_outbuf(hwsim_vqs[HWSIM_VQ_TX], sg, 1, skb,
+				   GFP_ATOMIC);
+	if (err)
+		goto out_free;
+	virtqueue_kick(hwsim_vqs[HWSIM_VQ_TX]);
+	spin_unlock_irqrestore(&hwsim_virtio_lock, flags);
+	return 0;
+
+out_free:
+	spin_unlock_irqrestore(&hwsim_virtio_lock, flags);
+	nlmsg_free(skb);
+	return err;
+}
+#else
+/* cause a linker error if this ends up being needed */
+extern int hwsim_tx_virtio(struct hwsim_phy *phy,
+			   struct sk_buff *skb);
+#define hwsim_virtio_enabled false
+#endif
+
+static void mac802154_hwsim_tx_frame_nl(struct ieee802154_hw *hw, struct sk_buff *my_skb,
+			     int dst_portid)
+{
+	struct sk_buff *skb;
+	struct hwsim_phy *phy = hw->priv;
+	void *msg_head;
+	unsigned int hwsim_flags = 0;
+	uintptr_t cookie;
+
+	skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_ATOMIC);
+
+	if (skb == NULL)
+		goto nla_put_failure;
+
+	msg_head = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
+			       MAC802154_HWSIM_CMD_FRAME);
+	if (msg_head == NULL) {
+		pr_debug("mac802154_hwsim: problem with msg_head\n");
+		goto nla_put_failure;
+	}
+
+	u8 addr_buf[8];
+	put_unaligned_le64(hw->phy->perm_extended_addr, addr_buf);
+	put_unaligned_le64(hw->phy->perm_extended_addr, phy->ieee_addr);
+
+	if (nla_put(skb, MAC802154_HWSIM_ATTR_ADDR_TRANSMITTER,
+		    8, addr_buf))
+		goto nla_put_failure;
+
+	/* We get the skb->data */
+	if (nla_put(skb, MAC802154_HWSIM_ATTR_FRAME, my_skb->len, my_skb->data))
+		goto nla_put_failure;
+
+	if (nla_put_u32(skb, MAC802154_HWSIM_ATTR_FLAGS, hwsim_flags))
+		goto nla_put_failure;
+
+	/* We create a cookie to identify this skb */
+	cookie = atomic_inc_return(&phy->pending_cookie);
+
+	if (nla_put_u64_64bit(skb, MAC802154_HWSIM_ATTR_COOKIE, cookie, MAC802154_HWSIM_ATTR_PAD))
+		goto nla_put_failure;
+
+	genlmsg_end(skb, msg_head);
+
+	struct sk_buff *skb_2 = skb_clone(my_skb, GFP_ATOMIC);
+	if (!skb_2)
+		goto err_free_txskb;
+
+	if (hwsim_virtio_enabled) {
+		if (hwsim_tx_virtio(phy, skb))
+			goto err_free_txskb;
+	} else {
+		if (hwsim_unicast_netgroup(phy, skb, dst_portid))
+			goto err_free_txskb;
+	}
+
+	/* Enqueue the packet */
+	skb_queue_tail(&phy->pending, skb_2);
+
+	return;
+
+nla_put_failure:
+	nlmsg_free(skb);
+err_free_txskb:
+	pr_debug("mac802154_hwsim: error occurred in %s\n", __func__);
+}
+
 static void hwsim_hw_receive(struct ieee802154_hw *hw, struct sk_buff *skb,
 			     u8 lqi)
 {
@@ -259,25 +502,142 @@ static int hwsim_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
 	struct hwsim_pib *current_pib, *endpoint_pib;
 	struct hwsim_edge_info *einfo;
 	struct hwsim_edge *e;
+	u32 _portid;
 
 	WARN_ON(current_phy->suspended);
 
+	/* wmediumd mode check */
+	_portid = READ_ONCE(current_phy->wmediumd);
+
+	if (_portid || hwsim_virtio_enabled){
+		mac802154_hwsim_tx_frame_nl(hw, skb, _portid);
+
+		ieee802154_xmit_complete(hw, skb, false);
+		return 0;
+	}
+	else{
+		rcu_read_lock();
+		current_pib = rcu_dereference(current_phy->pib);
+
+		list_for_each_entry_rcu(e, &current_phy->edges, list) {
+			/* Can be changed later in rx_irqsafe, but this is only a
+			* performance tweak. Received radio should drop the frame
+			* in mac802154 stack anyway... so we don't need to be
+			* 100% of locking here to check on suspended
+			*/
+			if (e->endpoint->suspended)
+				continue;
+
+			endpoint_pib = rcu_dereference(e->endpoint->pib);
+			if (current_pib->page == endpoint_pib->page &&
+				current_pib->channel == endpoint_pib->channel) {
+					struct sk_buff *newskb = pskb_copy(skb, GFP_ATOMIC);
+
+					einfo = rcu_dereference(e->info);
+					if (newskb)
+						hwsim_hw_receive(e->endpoint->hw, newskb, einfo->lqi);
+			}
+		}
+		rcu_read_unlock();
+
+		ieee802154_xmit_complete(hw, skb, false);
+		return 0;
+	}
+}
+
+static int hwsim_cloned_frame_received_nl(struct sk_buff *skb_2,
+					  struct genl_info *info)
+{
+	struct hwsim_phy *data2;
+	struct ieee802154_hdr *hdr;
+	const u8 *dst;
+	int frame_data_len;
+	void *frame_data;
+	struct sk_buff *skb = NULL;
+	struct hwsim_pib *current_pib, *endpoint_pib;
+	struct hwsim_edge *e;
+	struct hwsim_edge_info *einfo;
+
+	if (!info->attrs[MAC802154_HWSIM_ATTR_ADDR_RECEIVER] ||
+	    !info->attrs[MAC802154_HWSIM_ATTR_FRAME])
+		goto out;
+
+	dst = (void *)nla_data(info->attrs[MAC802154_HWSIM_ATTR_ADDR_RECEIVER]);
+	frame_data_len = nla_len(info->attrs[MAC802154_HWSIM_ATTR_FRAME]);
+	frame_data = (void *)nla_data(info->attrs[MAC802154_HWSIM_ATTR_FRAME]);
+
+	if (frame_data_len < IEEE802154_MIN_HDR_LEN ||
+		frame_data_len > IEEE802154_MAX_FRAME_LEN)
+		goto err;
+
+	/* Allocate new skb here */
+	skb = alloc_skb(frame_data_len, GFP_KERNEL);
+	if (skb == NULL)
+		goto err;
+
+	/* Copy the data */
+	skb_put_data(skb, frame_data, frame_data_len);
+
+	ieee802154_hdr_peek_addrs(skb, hdr);
+
+	u8 *frame = frame_data;
+	u16 fcf = le16_to_cpup((__le16 *)frame);
+	u8 *ptr = frame + 3;
+
+	u8 src_addr[8];
+	u8 dst_addr_mode = (fcf >> 10) & 0x3;
+	u8 src_addr_mode = (fcf >> 14) & 0x3;
+	bool intra_pan = (fcf >> 6) & 0x1;
+
+	/* skip the destination */
+	if (dst_addr_mode == IEEE802154_ADDR_SHORT) {
+		/* dest_pan + short addr */
+		ptr += 2 + 2;
+	} else if (dst_addr_mode == IEEE802154_ADDR_LONG) {
+		/* dest_pan + extended addr */
+		ptr += 2 + 8;
+	}
+
+	/* src_pan */
+	if (!intra_pan)
+		ptr += 2;
+
+	if (src_addr_mode == IEEE802154_ADDR_SHORT)
+		goto out;
+	else if (src_addr_mode == IEEE802154_ADDR_LONG)
+		memcpy(src_addr, ptr, 8);
+
+	data2 = get_hwsim_data_ref_from_addr(src_addr);
+	if (!data2)
+		goto out;
+
+	struct ieee802154_hw *hw = data2->hw;
+
+	struct hwsim_phy *current_phy = hw->priv;
+
 	rcu_read_lock();
 	current_pib = rcu_dereference(current_phy->pib);
 	list_for_each_entry_rcu(e, &current_phy->edges, list) {
 		/* Can be changed later in rx_irqsafe, but this is only a
-		 * performance tweak. Received radio should drop the frame
-		 * in mac802154 stack anyway... so we don't need to be
-		 * 100% of locking here to check on suspended
-		 */
+		* performance tweak. Received radio should drop the frame
+		* in mac802154 stack anyway... so we don't need to be
+		* 100% of locking here to check on suspended
+		*/
 		if (e->endpoint->suspended)
 			continue;
 
 		endpoint_pib = rcu_dereference(e->endpoint->pib);
 		if (current_pib->page == endpoint_pib->page &&
-		    current_pib->channel == endpoint_pib->channel) {
-			struct sk_buff *newskb = pskb_copy(skb, GFP_ATOMIC);
+			current_pib->channel == endpoint_pib->channel) {
+
+			struct ieee802154_hw *hw1 = e->endpoint->hw;
+			struct hwsim_phy *phy1 = hw1->priv;
+			u8* addr64 = phy1->ieee_addr;
 
+			if (dst_addr_mode == IEEE802154_ADDR_LONG && memcmp(dst, addr64, 8) != 0)
+				continue;
+
+			struct sk_buff *newskb = pskb_copy(skb, GFP_ATOMIC);
 			einfo = rcu_dereference(e->info);
 			if (newskb)
 				hwsim_hw_receive(e->endpoint->hw, newskb, einfo->lqi);
@@ -285,8 +645,12 @@ static int hwsim_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
 	}
 	rcu_read_unlock();
 
-	ieee802154_xmit_complete(hw, skb, false);
 	return 0;
+err:
+	pr_debug("mac802154_hwsim: error occurred in %s\n", __func__);
+out:
+	dev_kfree_skb(skb);
+	return -EINVAL;
 }
 
 static int hwsim_hw_start(struct ieee802154_hw *hw)
@@ -342,6 +706,36 @@ static int hwsim_new_radio_nl(struct sk_buff *msg, struct genl_info *info)
 	return hwsim_add_one(info, &mac802154hwsim_dev->dev, false);
 }
 
+static void hwsim_mcast_del_radio(int id, const char *hwname,
+				  struct genl_info *info)
+{
+	struct sk_buff *skb;
+	void *data;
+	int ret;
+
+	skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!skb)
+		return;
+
+	data = genlmsg_put(skb, 0, 0, &hwsim_genl_family, 0,
+			   MAC802154_HWSIM_CMD_DEL_RADIO);
+	if (!data)
+		goto error;
+
+	ret = nla_put_u32(skb, MAC802154_HWSIM_ATTR_RADIO_ID, id);
+	if (ret < 0)
+		goto error;
+
+	genlmsg_end(skb, data);
+
+	hwsim_mcast_config_msg(skb, info);
+
+	return;
+
+error:
+	nlmsg_free(skb);
+}
+
 static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
 {
 	struct hwsim_phy *phy, *tmp;
@@ -365,6 +759,19 @@ static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
 	return -ENODEV;
 }
 
+static void hwsim_del_radio(struct hwsim_phy *data,
+				     const char *hwname,
+				     struct genl_info *info)
+{
+	hwsim_mcast_del_radio(data->idx, hwname, info);
+	debugfs_remove_recursive(data->debugfs);
+	ieee802154_unregister_hw(data->hw);
+	device_release_driver(data->dev);
+	device_unregister(data->dev);
+	ieee802154_free_hw(data->hw);
+}
+
+
 static int append_radio_msg(struct sk_buff *skb, struct hwsim_phy *phy)
 {
 	struct nlattr *nl_edges, *nl_edge;
@@ -459,6 +866,7 @@ static int hwsim_get_radio_nl(struct sk_buff *msg, struct genl_info *info)
 	struct sk_buff *skb;
 	int idx, res = -ENODEV;
 
+
 	if (!info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID])
 		return -EINVAL;
 	idx = nla_get_u32(info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID]);
@@ -739,12 +1147,103 @@ static int hwsim_set_edge_lqi(struct sk_buff *msg, struct genl_info *info)
 	return -ENOENT;
 }
 
+static int hwsim_tx_info_frame_received_nl(struct sk_buff *skb_2,
+					   struct genl_info *info)
+{
+	struct nlattr *edge_attrs[MAC802154_HWSIM_EDGE_ATTR_MAX + 1];
+	struct hwsim_edge_info *einfo, *einfo_old;
+	struct hwsim_phy *phy_v0;
+	struct hwsim_edge *e;
+	u8 lqi;
+	u32 v0, v1;
+
+	if (nla_parse_nested_deprecated(edge_attrs, MAC802154_HWSIM_EDGE_ATTR_MAX, info->attrs[MAC802154_HWSIM_ATTR_RADIO_EDGE], hwsim_edge_policy, NULL))
+		return -EINVAL;
+
+	if (!edge_attrs[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID] ||
+	    !edge_attrs[MAC802154_HWSIM_EDGE_ATTR_LQI])
+		return -EINVAL;
+
+	v0 = nla_get_u32(info->attrs[MAC802154_HWSIM_ATTR_RADIO_ID]);
+	v1 = nla_get_u32(edge_attrs[MAC802154_HWSIM_EDGE_ATTR_ENDPOINT_ID]);
+	lqi = nla_get_u8(edge_attrs[MAC802154_HWSIM_EDGE_ATTR_LQI]);
+
+	mutex_lock(&hwsim_phys_lock);
+	phy_v0 = hwsim_get_radio_by_id(v0);
+	if (!phy_v0) {
+		mutex_unlock(&hwsim_phys_lock);
+		return -ENOENT;
+	}
+
+	einfo = kzalloc(sizeof(*einfo), GFP_KERNEL);
+	if (!einfo) {
+		mutex_unlock(&hwsim_phys_lock);
+		return -ENOMEM;
+	}
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(e, &phy_v0->edges, list) {
+		if (e->endpoint->idx == v1) {
+			einfo->lqi = lqi;
+			einfo_old = rcu_replace_pointer(e->info, einfo,
+							lockdep_is_held(&hwsim_phys_lock));
+			rcu_read_unlock();
+			kfree_rcu(einfo_old, rcu);
+			mutex_unlock(&hwsim_phys_lock);
+			return 0;
+		}
+	}
+	rcu_read_unlock();
+
+	kfree(einfo);
+	mutex_unlock(&hwsim_phys_lock);
+
+	return -ENOENT;
+}
+
+static void hwsim_register_wmediumd(struct net *net, u32 portid)
+{
+	struct hwsim_phy *data;
+
+	hwsim_net_set_wmediumd(net, portid);
+
+	spin_lock_bh(&hwsim_radio_lock);
+	list_for_each_entry(data, &hwsim_phys, list) {
+		if (data->netgroup == hwsim_net_get_netgroup(net))
+			data->wmediumd = portid;
+	}
+	spin_unlock_bh(&hwsim_radio_lock);
+}
+
+static int hwsim_register_received_nl(struct sk_buff *msg, struct genl_info *info)
+{
+	struct net *net = genl_info_net(info);
+	int chans = 1;
+
+	if (chans > 1)
+		return -EOPNOTSUPP;
+
+	if (hwsim_net_get_wmediumd(net))
+		return -EBUSY;
+
+	hwsim_register_wmediumd(net, info->snd_portid);
+
+	pr_debug("mac802154_hwsim: received a REGISTER, "
+	       "switching to wmediumd mode with pid %d\n", info->snd_portid);
+
+	return 0;
+}
+
 /* MAC802154_HWSIM netlink policy */
 
 static const struct nla_policy hwsim_genl_policy[MAC802154_HWSIM_ATTR_MAX + 1] = {
 	[MAC802154_HWSIM_ATTR_RADIO_ID] = { .type = NLA_U32 },
 	[MAC802154_HWSIM_ATTR_RADIO_EDGE] = { .type = NLA_NESTED },
 	[MAC802154_HWSIM_ATTR_RADIO_EDGES] = { .type = NLA_NESTED },
+	[MAC802154_HWSIM_ATTR_ADDR_RECEIVER] = { .type = NLA_BINARY, .len = 8 },
+	[MAC802154_HWSIM_ATTR_ADDR_TRANSMITTER] = { .type = NLA_BINARY, .len = 8 },
+	[MAC802154_HWSIM_ATTR_FRAME] = { .type = NLA_BINARY,
+			       .len = IEEE802154_MAX_FRAME_LEN },
 };
 
 /* Generic Netlink operations array */
@@ -785,6 +1284,22 @@ static const struct genl_small_ops hwsim_nl_ops[] = {
 		.doit = hwsim_set_edge_lqi,
 		.flags = GENL_UNS_ADMIN_PERM,
 	},
+	{
+		.cmd = MAC802154_HWSIM_CMD_REGISTER,
+		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+		.doit = hwsim_register_received_nl,
+		.flags = GENL_UNS_ADMIN_PERM,
+	},
+	{
+		.cmd = MAC802154_HWSIM_CMD_TX_INFO_FRAME,
+		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+		.doit = hwsim_tx_info_frame_received_nl,
+	},
+	{
+		.cmd = MAC802154_HWSIM_CMD_FRAME,
+		.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
+		.doit = hwsim_cloned_frame_received_nl,
+	},
 };
 
 static struct genl_family hwsim_genl_family __ro_after_init = {
@@ -792,25 +1307,15 @@ static struct genl_family hwsim_genl_family __ro_after_init = {
 	.version = 1,
 	.maxattr = MAC802154_HWSIM_ATTR_MAX,
 	.policy = hwsim_genl_policy,
+	.netnsok = true,
 	.module = THIS_MODULE,
 	.small_ops = hwsim_nl_ops,
 	.n_small_ops = ARRAY_SIZE(hwsim_nl_ops),
-	.resv_start_op = MAC802154_HWSIM_CMD_NEW_EDGE + 1,
+	.resv_start_op = MAC802154_HWSIM_CMD_TX_INFO_FRAME + 1,
 	.mcgrps = hwsim_mcgrps,
 	.n_mcgrps = ARRAY_SIZE(hwsim_mcgrps),
 };
 
-static void hwsim_mcast_config_msg(struct sk_buff *mcast_skb,
-				   struct genl_info *info)
-{
-	if (info)
-		genl_notify(&hwsim_genl_family, mcast_skb, info,
-			    HWSIM_MCGRP_CONFIG, GFP_KERNEL);
-	else
-		genlmsg_multicast(&hwsim_genl_family, mcast_skb, 0,
-				  HWSIM_MCGRP_CONFIG, GFP_KERNEL);
-}
-
 static void hwsim_mcast_new_radio(struct genl_info *info, struct hwsim_phy *phy)
 {
 	struct sk_buff *mcast_skb;
@@ -899,8 +1404,10 @@ static int hwsim_add_one(struct genl_info *info, struct device *dev,
 	struct ieee802154_hw *hw;
 	struct hwsim_phy *phy;
 	struct hwsim_pib *pib;
+	struct net *net;
 	int idx;
 	int err;
+	int ret;
 
 	idx = hwsim_radio_idx++;
 
@@ -908,9 +1415,17 @@ static int hwsim_add_one(struct genl_info *info, struct device *dev,
 	if (!hw)
 		return -ENOMEM;
 
+	if (info)
+		net = genl_info_net(info);
+	else
+		net = &init_net;
+	wpan_phy_net_set(hw->phy, net);
+
 	phy = hw->priv;
 	phy->hw = hw;
 
+	skb_queue_head_init(&phy->pending);
+
 	/* 868 MHz BPSK	802.15.4-2003 */
 	hw->phy->supported.channels[0] |= 1;
 	/* 915 MHz BPSK	802.15.4-2003 */
@@ -943,6 +1458,7 @@ static int hwsim_add_one(struct genl_info *info, struct device *dev,
 	hw->phy->supported.channels[6] |= 0x3ffc00;
 
 	hw->phy->perm_extended_addr = cpu_to_le64(((u64)0x02 << 56) | ((u64)idx));
+	memcpy(phy->ieee_addr, &hw->phy->perm_extended_addr, 8);
 
 	/* hwsim phy channel 13 as default */
 	hw->phy->current_channel = 13;
@@ -952,6 +1468,9 @@ static int hwsim_add_one(struct genl_info *info, struct device *dev,
 		goto err_pib;
 	}
 
+	if (info)
+		phy->portid = info->snd_portid;
+
 	pib->channel = 13;
 	pib->filt.short_addr = cpu_to_le16(IEEE802154_ADDR_BROADCAST);
 	pib->filt.pan_id = cpu_to_le16(IEEE802154_PANID_BROADCAST);
@@ -962,6 +1481,9 @@ static int hwsim_add_one(struct genl_info *info, struct device *dev,
 	hw->flags = IEEE802154_HW_PROMISCUOUS;
 	hw->parent = dev;
 
+	phy->netgroup = hwsim_net_get_netgroup(net);
+	phy->wmediumd = hwsim_net_get_wmediumd(net);
+
 	err = ieee802154_register_hw(hw);
 	if (err)
 		goto err_reg;
@@ -977,10 +1499,20 @@ static int hwsim_add_one(struct genl_info *info, struct device *dev,
 	list_add_tail(&phy->list, &hwsim_phys);
 	mutex_unlock(&hwsim_phys_lock);
 
+	ret = rhashtable_insert_fast(&hwsim_radios_rht, &phy->rht, hwsim_rht_params);
+	if (ret < 0) {
+		pr_err("Error in adding PHY into rhashtable: %d\n", ret);
+		goto failed_final_insert;
+	}
+	phy->rht_inserted = true;
+
 	hwsim_mcast_new_radio(info, phy);
 
 	return idx;
 
+failed_final_insert:
+	debugfs_remove_recursive(phy->debugfs);
+	ieee802154_unregister_hw(phy->hw);
 err_subscribe:
 	ieee802154_unregister_hw(phy->hw);
 err_reg:
@@ -1011,6 +1543,7 @@ static void hwsim_del(struct hwsim_phy *phy)
 
 	ieee802154_unregister_hw(phy->hw);
 	ieee802154_free_hw(phy->hw);
+	class_destroy(hwsim_class);
 }
 
 static int hwsim_probe(struct platform_device *pdev)
@@ -1018,13 +1551,13 @@ static int hwsim_probe(struct platform_device *pdev)
 	struct hwsim_phy *phy, *tmp;
 	int err, i;
 
-	for (i = 0; i < 2; i++) {
+	for (i = 0; i < radios; i++) {
 		err = hwsim_add_one(NULL, &pdev->dev, true);
 		if (err < 0)
 			goto err_slave;
 	}
 
-	dev_info(&pdev->dev, "Added 2 mac802154 hwsim hardware radios\n");
+	dev_info(&pdev->dev, "Added %d mac802154 hwsim hardware radios\n", radios);
 	return 0;
 
 err_slave:
@@ -1053,40 +1586,420 @@ static struct platform_driver mac802154hwsim_driver = {
 	},
 };
 
-static __init int hwsim_init_module(void)
+static void remove_user_radios(u32 portid)
+{
+	struct hwsim_phy *entry, *tmp;
+	LIST_HEAD(list);
+
+	spin_lock_bh(&hwsim_radio_lock);
+	list_for_each_entry_safe(entry, tmp, &hwsim_phys, list) {
+		if (entry->destroy_on_close && entry->portid == portid) {
+			list_move(&entry->list, &list);
+			rhashtable_remove_fast(&hwsim_radios_rht, &entry->rht,
+					       hwsim_rht_params);
+			hwsim_radios_generation++;
+		}
+	}
+	spin_unlock_bh(&hwsim_radio_lock);
+
+	list_for_each_entry_safe(entry, tmp, &list, list) {
+		list_del(&entry->list);
+		hwsim_del_radio(entry, wpan_phy_name(entry->hw->phy),
+					 NULL);
+	}
+}
+
+static int mac802154_hwsim_netlink_notify(struct notifier_block *nb,
+					 unsigned long state,
+					 void *_notify)
+{
+	struct netlink_notify *notify = _notify;
+
+	if (state != NETLINK_URELEASE)
+		return NOTIFY_DONE;
+
+	remove_user_radios(notify->portid);
+
+	if (notify->portid == hwsim_net_get_wmediumd(notify->net)) {
+		printk(KERN_INFO "mac802154_hwsim: wmediumd released netlink"
+		       " socket, switching to perfect channel medium\n");
+		hwsim_register_wmediumd(notify->net, 0);
+	}
+	return NOTIFY_DONE;
+
+}
+
+static void __net_exit hwsim_exit_net(struct net *net)
+{
+	struct hwsim_phy *data, *tmp;
+	LIST_HEAD(list);
+
+	spin_lock_bh(&hwsim_radio_lock);
+	list_for_each_entry_safe(data, tmp, &hwsim_phys, list) {
+		if (!net_eq(wpan_phy_net(data->hw->phy), net))
+			continue;
+
+		/* Radios created in init_net are returned to init_net. */
+		if (data->netgroup == hwsim_net_get_netgroup(&init_net))
+			continue;
+
+		list_move(&data->list, &list);
+		rhashtable_remove_fast(&hwsim_radios_rht, &data->rht,
+				       hwsim_rht_params);
+		hwsim_radios_generation++;
+	}
+	spin_unlock_bh(&hwsim_radio_lock);
+
+	list_for_each_entry_safe(data, tmp, &list, list) {
+		list_del(&data->list);
+		hwsim_del_radio(data,
+					 wpan_phy_name(data->hw->phy),
+					 NULL);
+	}
+
+	ida_free(&hwsim_netgroup_ida, hwsim_net_get_netgroup(net));
+}
+
+#if IS_REACHABLE(CONFIG_VIRTIO)
+
+static void hwsim_virtio_tx_done(struct virtqueue *vq)
+{
+	unsigned int len;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	spin_lock_irqsave(&hwsim_virtio_lock, flags);
+	while ((skb = virtqueue_get_buf(vq, &len)))
+		dev_kfree_skb_irq(skb);
+	spin_unlock_irqrestore(&hwsim_virtio_lock, flags);
+}
+
+static int hwsim_virtio_handle_cmd(struct sk_buff *skb)
+{
+	struct nlmsghdr *nlh;
+	struct genlmsghdr *gnlh;
+	struct nlattr *tb[MAC802154_HWSIM_ATTR_MAX + 1];
+	struct genl_info info = {};
+	int err;
+
+	nlh = nlmsg_hdr(skb);
+	gnlh = nlmsg_data(nlh);
+
+	if (skb->len < nlh->nlmsg_len)
+		return -EINVAL;
+
+	err = genlmsg_parse(nlh, &hwsim_genl_family, tb, MAC802154_HWSIM_ATTR_MAX,
+			    hwsim_genl_policy, NULL);
+	if (err) {
+		pr_err_ratelimited("hwsim: genlmsg_parse returned %d\n", err);
+		return err;
+	}
+
+	info.attrs = tb;
+
+	switch (gnlh->cmd) {
+	/*case MAC802154_HWSIM_CMD_FRAME:
+		hwsim_cloned_frame_received_nl(skb, &info);
+		break;
+	case MAC802154_HWSIM_CMD_TX_INFO_FRAME:
+		hwsim_tx_info_frame_received_nl(skb, &info);
+		break;
+	case HWSIM_CMD_REPORT_PMSR:
+		hwsim_pmsr_report_nl(skb, &info);
+		break;*/
+	default:
+		pr_err_ratelimited("hwsim: invalid cmd: %d\n", gnlh->cmd);
+		return -EPROTO;
+	}
+	return 0;
+}
+
+static void hwsim_virtio_rx_work(struct work_struct *work)
+{
+	struct virtqueue *vq;
+	unsigned int len;
+	struct sk_buff *skb;
+	struct scatterlist sg[1];
+	int err;
+	unsigned long flags;
+
+	spin_lock_irqsave(&hwsim_virtio_lock, flags);
+	if (!hwsim_virtio_enabled)
+		goto out_unlock;
+
+	skb = virtqueue_get_buf(hwsim_vqs[HWSIM_VQ_RX], &len);
+	if (!skb)
+		goto out_unlock;
+	spin_unlock_irqrestore(&hwsim_virtio_lock, flags);
+
+	skb->data = skb->head;
+	skb_reset_tail_pointer(skb);
+	skb_put(skb, len);
+	hwsim_virtio_handle_cmd(skb);
+
+	spin_lock_irqsave(&hwsim_virtio_lock, flags);
+	if (!hwsim_virtio_enabled) {
+		dev_kfree_skb_irq(skb);
+		goto out_unlock;
+	}
+	vq = hwsim_vqs[HWSIM_VQ_RX];
+	sg_init_one(sg, skb->head, skb_end_offset(skb));
+	err = virtqueue_add_inbuf(vq, sg, 1, skb, GFP_ATOMIC);
+	if (WARN(err, "virtqueue_add_inbuf returned %d\n", err))
+		dev_kfree_skb_irq(skb);
+	else
+		virtqueue_kick(vq);
+	schedule_work(&hwsim_virtio_rx);
+
+out_unlock:
+	spin_unlock_irqrestore(&hwsim_virtio_lock, flags);
+}
+
+static void hwsim_virtio_rx_done(struct virtqueue *vq)
+{
+	schedule_work(&hwsim_virtio_rx);
+}
+
+static int init_vqs(struct virtio_device *vdev)
+{
+	struct virtqueue_info vqs_info[HWSIM_NUM_VQS] = {
+		[HWSIM_VQ_TX] = { "tx", hwsim_virtio_tx_done },
+		[HWSIM_VQ_RX] = { "rx", hwsim_virtio_rx_done },
+	};
+
+	return virtio_find_vqs(vdev, HWSIM_NUM_VQS,
+			       hwsim_vqs, vqs_info, NULL);
+}
+
+static int fill_vq(struct virtqueue *vq)
+{
+	int i, err;
+	struct sk_buff *skb;
+	struct scatterlist sg[1];
+
+	for (i = 0; i < virtqueue_get_vring_size(vq); i++) {
+		skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
+		if (!skb)
+			return -ENOMEM;
+
+		sg_init_one(sg, skb->head, skb_end_offset(skb));
+		err = virtqueue_add_inbuf(vq, sg, 1, skb, GFP_KERNEL);
+		if (err) {
+			nlmsg_free(skb);
+			return err;
+		}
+	}
+	virtqueue_kick(vq);
+	return 0;
+}
+
+static void remove_vqs(struct virtio_device *vdev)
+{
+	int i;
+
+	virtio_reset_device(vdev);
+
+	for (i = 0; i < ARRAY_SIZE(hwsim_vqs); i++) {
+		struct virtqueue *vq = hwsim_vqs[i];
+		struct sk_buff *skb;
+
+		while ((skb = virtqueue_detach_unused_buf(vq)))
+			nlmsg_free(skb);
+	}
+
+	vdev->config->del_vqs(vdev);
+}
+
+static int hwsim_virtio_probe(struct virtio_device *vdev)
+{
+	int err;
+	unsigned long flags;
+
+	spin_lock_irqsave(&hwsim_virtio_lock, flags);
+	if (hwsim_virtio_enabled) {
+		spin_unlock_irqrestore(&hwsim_virtio_lock, flags);
+		return -EEXIST;
+	}
+	spin_unlock_irqrestore(&hwsim_virtio_lock, flags);
+
+	err = init_vqs(vdev);
+	if (err)
+		return err;
+
+	virtio_device_ready(vdev);
+
+	err = fill_vq(hwsim_vqs[HWSIM_VQ_RX]);
+	if (err)
+		goto out_remove;
+
+	spin_lock_irqsave(&hwsim_virtio_lock, flags);
+	hwsim_virtio_enabled = true;
+	spin_unlock_irqrestore(&hwsim_virtio_lock, flags);
+
+	schedule_work(&hwsim_virtio_rx);
+	return 0;
+
+out_remove:
+	remove_vqs(vdev);
+	return err;
+}
+
+static void hwsim_virtio_remove(struct virtio_device *vdev)
+{
+	hwsim_virtio_enabled = false;
+
+	cancel_work_sync(&hwsim_virtio_rx);
+
+	remove_vqs(vdev);
+}
+
+/* MAC802154_HWSIM virtio device id table */
+static const struct virtio_device_id id_table[] = {
+	{ 42, VIRTIO_DEV_ANY_ID },
+	{ 0 }
+};
+MODULE_DEVICE_TABLE(virtio, id_table);
+
+static struct virtio_driver virtio_hwsim = {
+	.driver.name = KBUILD_MODNAME,
+	.id_table = id_table,
+	.probe = hwsim_virtio_probe,
+	.remove = hwsim_virtio_remove,
+};
+
+static int hwsim_register_virtio_driver(void)
+{
+	return register_virtio_driver(&virtio_hwsim);
+}
+
+static void hwsim_unregister_virtio_driver(void)
+{
+	unregister_virtio_driver(&virtio_hwsim);
+}
+#else
+static inline int hwsim_register_virtio_driver(void)
+{
+	return 0;
+}
+
+static inline void hwsim_unregister_virtio_driver(void)
+{
+}
+#endif
+
+static struct notifier_block hwsim_netlink_notifier = {
+	.notifier_call = mac802154_hwsim_netlink_notify,
+};
+
+static int __init hwsim_init_netlink(void)
 {
 	int rc;
 
+	printk(KERN_INFO "mac802154_hwsim: initializing netlink\n");
+
 	rc = genl_register_family(&hwsim_genl_family);
 	if (rc)
-		return rc;
+		goto failure;
+
+	rc = netlink_register_notifier(&hwsim_netlink_notifier);
+	if (rc) {
+		genl_unregister_family(&hwsim_genl_family);
+		goto failure;
+	}
+
+	return 0;
+
+failure:
+	pr_debug("mac802154_hwsim: error occurred in %s\n", __func__);
+	return -EINVAL;
+}
+
+static __net_init int hwsim_init_net(struct net *net)
+{
+	return hwsim_net_set_netgroup(net);
+}
+
+static struct pernet_operations hwsim_net_ops = {
+	.init = hwsim_init_net,
+	.exit = hwsim_exit_net,
+	.id   = &hwsim_net_id,
+	.size = sizeof(struct hwsim_net),
+};
+
+static void hwsim_exit_netlink(void)
+{
+	/* unregister the notifier */
+	netlink_unregister_notifier(&hwsim_netlink_notifier);
+	/* unregister the family */
+	genl_unregister_family(&hwsim_genl_family);
+}
+
+static __init int hwsim_init_module(void)
+{
+	int rc, err;
+
+	if (radios < 0)
+		return -EINVAL;
+
+	err = rhashtable_init(&hwsim_radios_rht, &hwsim_rht_params);
+	if (err)
+		return err;
+
+	err = register_pernet_device(&hwsim_net_ops);
+	if (err)
+		goto out_free_rht;
+
+	err = hwsim_init_netlink();
+	if (err)
+		goto out_unregister_driver;
+
+	err = hwsim_register_virtio_driver();
+	if (err)
+		goto out_exit_netlink;
+
+	hwsim_class = class_create("mac802154_hwsim");
+	if (IS_ERR(hwsim_class)) {
+		err = PTR_ERR(hwsim_class);
+		goto out_exit_virtio;
+	}
 
 	mac802154hwsim_dev = platform_device_register_simple("mac802154_hwsim",
 							     -1, NULL, 0);
 	if (IS_ERR(mac802154hwsim_dev)) {
 		rc = PTR_ERR(mac802154hwsim_dev);
-		goto platform_dev;
+		goto out_unregister_driver;
 	}
 
 	rc = platform_driver_register(&mac802154hwsim_driver);
 	if (rc < 0)
-		goto platform_drv;
+		goto out_unregister_pernet;
 
 	return 0;
 
-platform_drv:
-	platform_device_unregister(mac802154hwsim_dev);
-platform_dev:
-	genl_unregister_family(&hwsim_genl_family);
+out_exit_virtio:
+	hwsim_unregister_virtio_driver();
+out_exit_netlink:
+	hwsim_exit_netlink();
+out_unregister_pernet:
+	unregister_pernet_device(&hwsim_net_ops);
 	return rc;
+out_unregister_driver:
+	platform_driver_unregister(&mac802154hwsim_driver);
+out_free_rht:
+	rhashtable_destroy(&hwsim_radios_rht);
+	return err;
 }
 
 static __exit void hwsim_remove_module(void)
 {
-	genl_unregister_family(&hwsim_genl_family);
+	pr_debug("mac80211_hwsim: unregister radios\n");
+	hwsim_unregister_virtio_driver();
+	hwsim_exit_netlink();
+	rhashtable_destroy(&hwsim_radios_rht);
 	platform_driver_unregister(&mac802154hwsim_driver);
 	platform_device_unregister(mac802154hwsim_dev);
+	unregister_pernet_device(&hwsim_net_ops);
 }
 
 module_init(hwsim_init_module);
-module_exit(hwsim_remove_module);
+module_exit(hwsim_remove_module);
\ No newline at end of file
diff --git a/drivers/net/ieee802154/mac802154_hwsim.h b/drivers/net/ieee802154/mac802154_hwsim.h
index 6c6e30e38..b1d83ff75 100644
--- a/drivers/net/ieee802154/mac802154_hwsim.h
+++ b/drivers/net/ieee802154/mac802154_hwsim.h
@@ -1,8 +1,31 @@
 #ifndef __MAC802154_HWSIM_H
 #define __MAC802154_HWSIM_H
 
-/* mac802154 hwsim netlink commands
+
+#define IEEE802154_MAX_FRAME_LEN 127
+#define IEEE802154_MIN_HDR_LEN 3
+//#define IEEE802154_MAX_DATA_LEN (IEEE802154_PHY_FRAME_LEN - IEEE802154_MAX_HEADER_LEN)
+/**
+ * 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 {
+	MAC802154_HWSIM_TX_CTL_REQ_TX_STATUS		= BIT(0),
+	MAC802154_HWSIM_TX_CTL_NO_ACK			= BIT(1),
+	MAC802154_HWSIM_TX_STAT_ACK			= BIT(2),
+};
+
+
+/* mac802154 hwsim netlink commands
+ * @HWSIM_CMD_REGISTER: request to register and received all broadcasted
+ *		frames by any mac802154_hwsim radio device.
  * @MAC802154_HWSIM_CMD_UNSPEC: unspecified command to catch error
  * @MAC802154_HWSIM_CMD_GET_RADIO: fetch information about existing radios
  * @MAC802154_HWSIM_CMD_SET_RADIO: change radio parameters during runtime
@@ -28,6 +51,9 @@ enum {
 	MAC802154_HWSIM_CMD_SET_EDGE,
 	MAC802154_HWSIM_CMD_DEL_EDGE,
 	MAC802154_HWSIM_CMD_NEW_EDGE,
+	MAC802154_HWSIM_CMD_REGISTER,
+	MAC802154_HWSIM_CMD_FRAME,
+	MAC802154_HWSIM_CMD_TX_INFO_FRAME,
 
 	__MAC802154_HWSIM_CMD_MAX,
 };
@@ -48,6 +74,13 @@ enum {
 	MAC802154_HWSIM_ATTR_RADIO_ID,
 	MAC802154_HWSIM_ATTR_RADIO_EDGE,
 	MAC802154_HWSIM_ATTR_RADIO_EDGES,
+	MAC802154_HWSIM_ATTR_COOKIE,
+	MAC802154_HWSIM_ATTR_PAD,
+	MAC802154_HWSIM_ATTR_FRAME,
+	MAC802154_HWSIM_ATTR_ADDR_TRANSMITTER,
+	MAC802154_HWSIM_ATTR_ADDR_RECEIVER,
+	MAC802154_HWSIM_ATTR_TX_INFO,
+	MAC802154_HWSIM_ATTR_FLAGS,
 	__MAC802154_HWSIM_ATTR_MAX,
 };
 
@@ -70,4 +103,24 @@ enum {
 
 #define MAC802154_HWSIM_EDGE_ATTR_MAX (__MAC802154_HWSIM_EDGE_ATTR_MAX - 1)
 
-#endif /* __MAC802154_HWSIM_H */
+/**
+ * DOC: Frame transmission support over virtio
+ *
+ * Frame transmission is also supported over virtio to allow communication
+ * with external entities.
+ */
+
+/**
+ * enum hwsim_vqs - queues for virtio frame transmission
+ *
+ * @HWSIM_VQ_TX: send frames to external entity
+ * @HWSIM_VQ_RX: receive frames and transmission info reports
+ * @HWSIM_NUM_VQS: enum limit
+ */
+enum hwsim_vqs {
+	HWSIM_VQ_TX,
+	HWSIM_VQ_RX,
+	HWSIM_NUM_VQS,
+};
+
+#endif /* __MAC802154_HWSIM_H */
\ No newline at end of file
-- 
2.43.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ