Skip to contentSkip to conversation

userspace station-mode action frame handling

PATCH In review Johannes Berg 2 participants 5 messages Jan 17, 2010 – Feb 15, 2010

Subsystems (2)
lore
Share & tools
lore

Johannes Berg

author maintainer
#0 lore raw

cover userspace station-mode action frame handling

Jouni, all,

These two patches now implement returning unknown action frames and registering for certain action frames from userspace, as well as sending as before.

A few design decisions:
* AP mode is unchanged, hostapd is responsible for returning unknown action frames (cooked monitor).
* Registration/transmission works only in station mode (would be easy to change in the future).
* Registration needs to specify which frames to receive by providing a frame match buffer, which matches the first few bytes of the frame, so for instance the buffer [0x04] would register for all public action frames, and [0x7f, 0x00, 0x02, 0xb3] would register for certain Intel vendor action frames.
* Action frames that are registered for are unicast to the socket that registered for it.
* If userspace wants to know about returned action frames for ones it sent, it needs to register for them specifically, for my example above it would be [0xff, 0x00, 0x02, 0xb3].

johannes

author maintainer
#1 lore raw

PATCH 1/2 mac80211: reject unhandled action frames

802.11-2007 7.3.1.11 mandates that we need to reject action frames we don't handle by setting the 0x80 bit in the category and returning them to the sender, so do that. In AP mode, hostapd is responsible for this.

Additionally, drop completely malformed action frames or ones that should've been encrypted as unusable, userspace shouldn't see those.

---
 net/mac80211/rx.c |   90 ++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 57 insertions(+), 33 deletions(-)
net/mac80211/rx.c +57 -33
@@ -2,7 +2,7 @@
  * Copyright 2002-2005, Instant802 Networks, Inc.
  * Copyright 2005-2006, Devicescape Software, Inc.
  * Copyright 2006-2007	Jiri Benc <[email protected]>
- * Copyright 2007	Johannes Berg <[email protected]>
+ * Copyright 2007-2010	Johannes Berg <[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
@@ -1847,23 +1847,28 @@ ieee80211_rx_h_action(struct ieee80211_r
 	struct ieee80211_local *local = rx->local;
 	struct ieee80211_sub_if_data *sdata = rx->sdata;
 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
+	struct sk_buff *nskb;
 	int len = rx->skb->len;
 
 	if (!ieee80211_is_action(mgmt->frame_control))
 		return RX_CONTINUE;
 
 	if (!rx->sta)
-		return RX_DROP_MONITOR;
+		return RX_DROP_UNUSABLE;
 
 	if (!(rx->flags & IEEE80211_RX_RA_MATCH))
-		return RX_DROP_MONITOR;
+		return RX_DROP_UNUSABLE;
 
 	if (ieee80211_drop_unencrypted(rx, mgmt->frame_control))
-		return RX_DROP_MONITOR;
+		return RX_DROP_UNUSABLE;
 
-	/* all categories we currently handle have action_code */
+	/* drop too small frames */
+	if (len < IEEE80211_MIN_ACTION_SIZE)
+		return RX_DROP_UNUSABLE;
+
+	/* return action frames that have *only* category */
 	if (len < IEEE80211_MIN_ACTION_SIZE + 1)
-		return RX_DROP_MONITOR;
+		goto return_frame;
 
 	switch (mgmt->u.action.category) {
 	case WLAN_CATEGORY_BACK:
@@ -1876,7 +1881,7 @@ ieee80211_rx_h_action(struct ieee80211_r
 		if (sdata->vif.type != NL80211_IFTYPE_STATION &&
 		    sdata->vif.type != NL80211_IFTYPE_AP_VLAN &&
 		    sdata->vif.type != NL80211_IFTYPE_AP)
-			return RX_DROP_MONITOR;
+			break;
 
 		switch (mgmt->u.action.u.addba_req.action_code) {
 		case WLAN_ACTION_ADDBA_REQ:
@@ -1884,45 +1889,45 @@ ieee80211_rx_h_action(struct ieee80211_r
 				   sizeof(mgmt->u.action.u.addba_req)))
 				return RX_DROP_MONITOR;
 			ieee80211_process_addba_request(local, rx->sta, mgmt, len);
-			break;
+			goto handled;
 		case WLAN_ACTION_ADDBA_RESP:
 			if (len < (IEEE80211_MIN_ACTION_SIZE +
 				   sizeof(mgmt->u.action.u.addba_resp)))
-				return RX_DROP_MONITOR;
+				break;
 			ieee80211_process_addba_resp(local, rx->sta, mgmt, len);
-			break;
+			goto handled;
 		case WLAN_ACTION_DELBA:
 			if (len < (IEEE80211_MIN_ACTION_SIZE +
 				   sizeof(mgmt->u.action.u.delba)))
-				return RX_DROP_MONITOR;
+				break;
 			ieee80211_process_delba(sdata, rx->sta, mgmt, len);
-			break;
+			goto handled;
 		}
 		break;
 	case WLAN_CATEGORY_SPECTRUM_MGMT:
 		if (local->hw.conf.channel->band != IEEE80211_BAND_5GHZ)
-			return RX_DROP_MONITOR;
+			break;
 
 		if (sdata->vif.type != NL80211_IFTYPE_STATION)
-			return RX_DROP_MONITOR;
+			break;
 
 		switch (mgmt->u.action.u.measurement.action_code) {
 		case WLAN_ACTION_SPCT_MSR_REQ:
 			if (len < (IEEE80211_MIN_ACTION_SIZE +
 				   sizeof(mgmt->u.action.u.measurement)))
-				return RX_DROP_MONITOR;
+				break;
 			ieee80211_process_measurement_req(sdata, mgmt, len);
-			break;
+			goto handled;
 		case WLAN_ACTION_SPCT_CHL_SWITCH:
 			if (len < (IEEE80211_MIN_ACTION_SIZE +
 				   sizeof(mgmt->u.action.u.chan_switch)))
-				return RX_DROP_MONITOR;
+				break;
 
 			if (sdata->vif.type != NL80211_IFTYPE_STATION)
-				return RX_DROP_MONITOR;
+				break;
 
 			if (memcmp(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN))
-				return RX_DROP_MONITOR;
+				break;
 
 			return ieee80211_sta_rx_mgmt(sdata, rx->skb);
 		}
@@ -1930,29 +1935,48 @@ ieee80211_rx_h_action(struct ieee80211_r
 	case WLAN_CATEGORY_SA_QUERY:
 		if (len < (IEEE80211_MIN_ACTION_SIZE +
 			   sizeof(mgmt->u.action.u.sa_query)))
-			return RX_DROP_MONITOR;
+			break;
+
 		switch (mgmt->u.action.u.sa_query.action) {
 		case WLAN_ACTION_SA_QUERY_REQUEST:
 			if (sdata->vif.type != NL80211_IFTYPE_STATION)
-				return RX_DROP_MONITOR;
+				break;
 			ieee80211_process_sa_query_req(sdata, mgmt, len);
-			break;
-		case WLAN_ACTION_SA_QUERY_RESPONSE:
-			/*
-			 * SA Query response is currently only used in AP mode
-			 * and it is processed in user space.
-			 */
-			return RX_CONTINUE;
+			goto handled;
 		}
 		break;
-	default:
-		/* do not process rejected action frames */
-		if (mgmt->u.action.category & 0x80)
-			return RX_DROP_MONITOR;
+	}
+ return_frame:
+	/*
+	 * For AP mode, hostapd is responsible for handling any action
+	 * frames that we didn't handle, including returning unknown
+	 * ones. For all other modes we will return them to the sender,
+	 * setting the 0x80 bit in the action category, as required by
+	 * 802.11-2007 7.3.1.11.
+	 */
+	if (sdata->vif.type == NL80211_IFTYPE_AP ||
+	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+		return RX_DROP_MONITOR;
 
-		return RX_CONTINUE;
+	/* do not return rejected action frames */
+	if (mgmt->u.action.category & 0x80)
+		return RX_DROP_UNUSABLE;
+
+	nskb = skb_copy_expand(rx->skb, local->hw.extra_tx_headroom, 0,
+			       GFP_ATOMIC);
+	if (nskb) {
+		struct ieee80211_mgmt *mgmt = (void *)nskb->data;
+
+		mgmt->u.action.category |= 0x80;
+		memcpy(mgmt->da, mgmt->sa, ETH_ALEN);
+		memcpy(mgmt->sa, rx->sdata->vif.addr, ETH_ALEN);
+
+		memset(nskb->cb, 0, sizeof(nskb->cb));
+
+		ieee80211_tx_skb(rx->sdata, nskb);
 	}
 
+ handled:
 	rx->sta->rx_packets++;
 	dev_kfree_skb(rx->skb);
 	return RX_QUEUED;
author maintainer
#2 lore raw

PATCH 2/2 cfg80211/mac80211: allow registering for and sending action frames

In reply to Johannes Berg

From: Jouni Malinen <[email protected]>

This implements a new command to register for action frames that userspace wants to handle instead of the in-kernel rejection. It is then responsible for rejecting ones that it decided not to handle. There is no unregistration, but the socket can be closed for that.

Frames that are not registered for will not be forwarded to userspace and will be rejected by the kernel, the cfg80211 API helps implementing that.

Additionally, this patch adds a new command that allows doing action frame transmission from userspace. It can be used either to exchange action frames on the current operational channel (e.g., with the AP with which we are currently associated) or to exchange off-channel Public Action frames with the remain-on-channel command.

---
 include/linux/nl80211.h    |   41 ++++++-
 include/net/cfg80211.h     |   47 +++++++-
 include/net/mac80211.h     |    6 -
 net/mac80211/cfg.c         |   12 +-
 net/mac80211/ieee80211_i.h |    6 -
 net/mac80211/mlme.c        |   35 ++++++
 net/mac80211/rx.c          |   39 +++++-
 net/mac80211/status.c      |    7 +
 net/wireless/core.c        |    4 
 net/wireless/core.h        |    9 +
 net/wireless/mlme.c        |  166 ++++++++++++++++++++++++++++
 net/wireless/nl80211.c     |  260 ++++++++++++++++++++++++++++++++++++++++++++-
 net/wireless/nl80211.h     |    8 +
 13 files changed, 623 insertions(+), 17 deletions(-)
net/mac80211/rx.c +29 -10
@@ -1848,28 +1848,25 @@ ieee80211_rx_h_action(struct ieee80211_r
 	struct ieee80211_sub_if_data *sdata = rx->sdata;
 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
 	struct sk_buff *nskb;
+	struct ieee80211_rx_status *status;
 	int len = rx->skb->len;
 
 	if (!ieee80211_is_action(mgmt->frame_control))
 		return RX_CONTINUE;
 
-	if (!rx->sta)
+	/* drop too small frames */
+	if (len < IEEE80211_MIN_ACTION_SIZE)
 		return RX_DROP_UNUSABLE;
 
-	if (!(rx->flags & IEEE80211_RX_RA_MATCH))
+	if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC)
 		return RX_DROP_UNUSABLE;
 
-	if (ieee80211_drop_unencrypted(rx, mgmt->frame_control))
+	if (!(rx->flags & IEEE80211_RX_RA_MATCH))
 		return RX_DROP_UNUSABLE;
 
-	/* drop too small frames */
-	if (len < IEEE80211_MIN_ACTION_SIZE)
+	if (ieee80211_drop_unencrypted(rx, mgmt->frame_control))
 		return RX_DROP_UNUSABLE;
 
-	/* return action frames that have *only* category */
-	if (len < IEEE80211_MIN_ACTION_SIZE + 1)
-		goto return_frame;
-
 	switch (mgmt->u.action.category) {
 	case WLAN_CATEGORY_BACK:
 		/*
@@ -1883,6 +1880,10 @@ ieee80211_rx_h_action(struct ieee80211_r
 		    sdata->vif.type != NL80211_IFTYPE_AP)
 			break;
 
+		/* verify action_code is present */
+		if (len < IEEE80211_MIN_ACTION_SIZE + 1)
+			break;
+
 		switch (mgmt->u.action.u.addba_req.action_code) {
 		case WLAN_ACTION_ADDBA_REQ:
 			if (len < (IEEE80211_MIN_ACTION_SIZE +
@@ -1911,6 +1912,10 @@ ieee80211_rx_h_action(struct ieee80211_r
 		if (sdata->vif.type != NL80211_IFTYPE_STATION)
 			break;
 
+		/* verify action_code is present */
+		if (len < IEEE80211_MIN_ACTION_SIZE + 1)
+			break;
+
 		switch (mgmt->u.action.u.measurement.action_code) {
 		case WLAN_ACTION_SPCT_MSR_REQ:
 			if (len < (IEEE80211_MIN_ACTION_SIZE +
@@ -1946,7 +1951,7 @@ ieee80211_rx_h_action(struct ieee80211_r
 		}
 		break;
 	}
- return_frame:
+
 	/*
 	 * For AP mode, hostapd is responsible for handling any action
 	 * frames that we didn't handle, including returning unknown
@@ -1958,6 +1963,20 @@ ieee80211_rx_h_action(struct ieee80211_r
 	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
 		return RX_DROP_MONITOR;
 
+	/*
+	 * Getting here means the kernel doesn't know how to handle
+	 * it, but maybe userspace does ... include returned frames
+	 * so userspace can register for those to know whether ones
+	 * it transmitted were processed or returned.
+	 */
+	status = IEEE80211_SKB_RXCB(rx->skb);
+
+	if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+	    cfg80211_rx_action(rx->sdata->dev, status->freq,
+			       rx->skb->data, rx->skb->len,
+			       GFP_ATOMIC))
+		goto handled;
+
 	/* do not return rejected action frames */
 	if (mgmt->u.action.category & 0x80)
 		return RX_DROP_UNUSABLE;
net/wireless/core.h +9 -0
@@ -329,6 +329,15 @@ void __cfg80211_connect_result(struct ne
 			       const u8 *resp_ie, size_t resp_ie_len,
 			       u16 status, bool wextev,
 			       struct cfg80211_bss *bss);
+int cfg80211_mlme_register_action(struct wireless_dev *wdev, u32 snd_pid,
+				  const u8 *match_data, int match_len);
+void cfg80211_mlme_unregister_actions(struct wireless_dev *wdev, u32 nlpid);
+void cfg80211_mlme_purge_actions(struct wireless_dev *wdev);
+int cfg80211_mlme_action(struct cfg80211_registered_device *rdev,
+			 struct net_device *dev,
+			 struct ieee80211_channel *chan,
+			 enum nl80211_channel_type channel_type,
+			 const u8 *buf, size_t len, u64 *cookie);
 
 /* SME */
 int __cfg80211_connect(struct cfg80211_registered_device *rdev,
net/wireless/nl80211.c +259 -1

307 lines not on this page · raw message

net/wireless/nl80211.h +8 -0
@@ -74,4 +74,12 @@ void nl80211_send_sta_event(struct cfg80
 			    struct net_device *dev, const u8 *mac_addr,
 			    struct station_info *sinfo, gfp_t gfp);
 
+int nl80211_send_action(struct cfg80211_registered_device *rdev,
+			struct net_device *netdev, u32 nlpid, int freq,
+			const u8 *buf, size_t len, gfp_t gfp);
+void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev,
+				   struct net_device *netdev, u64 cookie,
+				   const u8 *buf, size_t len, bool ack,
+				   gfp_t gfp);
+
 #endif /* __NET_WIRELESS_NL80211_H */
include/linux/nl80211.h +40 -1

71 lines not on this page · raw message

include/net/cfg80211.h +46 -1

81 lines not on this page · raw message

net/mac80211/cfg.c +11 -1
@@ -1,7 +1,7 @@
 /*
  * mac80211 configuration hooks for cfg80211
  *
- * Copyright 2006, 2007	Johannes Berg <[email protected]>
+ * Copyright 2006-2010	Johannes Berg <[email protected]>
  *
  * This file is GPLv2 as found in COPYING.
  */
@@ -1461,6 +1461,15 @@ static int ieee80211_cancel_remain_on_ch
 	return ieee80211_wk_cancel_remain_on_channel(sdata, cookie);
 }
 
+static int ieee80211_action(struct wiphy *wiphy, struct net_device *dev,
+			    struct ieee80211_channel *chan,
+			    enum nl80211_channel_type channel_type,
+			    const u8 *buf, size_t len, u64 *cookie)
+{
+	return ieee80211_mgd_action(IEEE80211_DEV_TO_SUB_IF(dev), chan,
+				    channel_type, buf, len, cookie);
+}
+
 struct cfg80211_ops mac80211_config_ops = {
 	.add_virtual_intf = ieee80211_add_iface,
 	.del_virtual_intf = ieee80211_del_iface,
@@ -1509,4 +1518,5 @@ struct cfg80211_ops mac80211_config_ops 
 	.set_bitrate_mask = ieee80211_set_bitrate_mask,
 	.remain_on_channel = ieee80211_remain_on_channel,
 	.cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
+	.action = ieee80211_action,
 };
net/mac80211/ieee80211_i.h +5 -1
@@ -2,7 +2,7 @@
  * Copyright 2002-2005, Instant802 Networks, Inc.
  * Copyright 2005, Devicescape Software, Inc.
  * Copyright 2006-2007	Jiri Benc <[email protected]>
- * Copyright 2007-2008	Johannes Berg <[email protected]>
+ * Copyright 2007-2010	Johannes Berg <[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
@@ -967,6 +967,10 @@ int ieee80211_mgd_deauth(struct ieee8021
 int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
 			   struct cfg80211_disassoc_request *req,
 			   void *cookie);
+int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata,
+			 struct ieee80211_channel *chan,
+			 enum nl80211_channel_type channel_type,
+			 const u8 *buf, size_t len, u64 *cookie);
 ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata,
 					  struct sk_buff *skb);
 void ieee80211_send_pspoll(struct ieee80211_local *local,
net/mac80211/mlme.c +35 -0
@@ -2057,3 +2057,38 @@ int ieee80211_mgd_disassoc(struct ieee80
 
 	return 0;
 }
+
+int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata,
+			 struct ieee80211_channel *chan,
+			 enum nl80211_channel_type channel_type,
+			 const u8 *buf, size_t len, u64 *cookie)
+{
+	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+	struct sk_buff *skb;
+
+	/* Check that we are on the requested channel for transmission */
+	if ((chan != local->tmp_channel ||
+	     channel_type != local->tmp_channel_type) &&
+	    (chan != local->oper_channel ||
+	     channel_type != local->oper_channel_type))
+		return -EBUSY;
+
+	skb = dev_alloc_skb(local->hw.extra_tx_headroom + len);
+	if (!skb)
+		return -ENOMEM;
+	skb_reserve(skb, local->hw.extra_tx_headroom);
+
+	memcpy(skb_put(skb, len), buf, len);
+
+	if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED))
+		IEEE80211_SKB_CB(skb)->flags |=
+			IEEE80211_TX_INTFL_DONT_ENCRYPT;
+	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_CTL_NL80211_FRAME_TX |
+		IEEE80211_TX_CTL_REQ_TX_STATUS;
+	skb->dev = sdata->dev;
+	ieee80211_tx_skb(sdata, skb);
+
+	*cookie = (unsigned long) skb;
+	return 0;
+}
net/wireless/mlme.c +166 -0

169 lines not on this page · raw message

include/net/mac80211.h +5 -1
@@ -3,7 +3,7 @@
  *
  * Copyright 2002-2005, Devicescape Software, Inc.
  * Copyright 2006-2007	Jiri Benc <[email protected]>
- * Copyright 2007-2008	Johannes Berg <[email protected]>
+ * Copyright 2007-2010	Johannes Berg <[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
@@ -274,6 +274,9 @@ struct ieee80211_bss_conf {
  * @IEEE80211_TX_INTFL_RETRANSMISSION: This frame is being retransmitted
  *	after TX status because the destination was asleep, it must not
  *	be modified again (no seqno assignment, crypto, etc.)
+ * @IEEE80211_TX_CTL_NL80211_FRAME_TX: Frame was requested through nl80211
+ *	MLME command (internal to mac80211 to figure out whether to send TX
+ *	status to user space)
  */
 enum mac80211_tx_control_flags {
 	IEEE80211_TX_CTL_REQ_TX_STATUS		= BIT(0),
@@ -295,6 +298,7 @@ enum mac80211_tx_control_flags {
 	IEEE80211_TX_CTL_PSPOLL_RESPONSE	= BIT(17),
 	IEEE80211_TX_CTL_MORE_FRAMES		= BIT(18),
 	IEEE80211_TX_INTFL_RETRANSMISSION	= BIT(19),
+	IEEE80211_TX_CTL_NL80211_FRAME_TX	= BIT(20),
 };
 
 /**
net/mac80211/status.c +6 -1
@@ -2,7 +2,7 @@
  * Copyright 2002-2005, Instant802 Networks, Inc.
  * Copyright 2005-2006, Devicescape Software, Inc.
  * Copyright 2006-2007	Jiri Benc <[email protected]>
- * Copyright 2008-2009	Johannes Berg <[email protected]>
+ * Copyright 2008-2010	Johannes Berg <[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
@@ -272,6 +272,11 @@ void ieee80211_tx_status(struct ieee8021
 			local->dot11FailedCount++;
 	}
 
+	if (info->flags & IEEE80211_TX_CTL_NL80211_FRAME_TX)
+		cfg80211_action_tx_status(
+			skb->dev, (unsigned long) skb, skb->data, skb->len,
+			!!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC);
+
 	/* this was a transmitted frame, but now we want to reuse it */
 	skb_orphan(skb);
 
net/wireless/core.c +4 -0
@@ -665,6 +665,9 @@ static int cfg80211_netdev_notifier_call
 		INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work);
 		INIT_LIST_HEAD(&wdev->event_list);
 		spin_lock_init(&wdev->event_lock);
+		INIT_LIST_HEAD(&wdev->action_registrations);
+		spin_lock_init(&wdev->action_registrations_lock);
+
 		mutex_lock(&rdev->devlist_mtx);
 		list_add_rcu(&wdev->list, &rdev->netdev_list);
 		rdev->devlist_generation++;
@@ -780,6 +783,7 @@ static int cfg80211_netdev_notifier_call
 			sysfs_remove_link(&dev->dev.kobj, "phy80211");
 			list_del_rcu(&wdev->list);
 			rdev->devlist_generation++;
+			cfg80211_mlme_purge_actions(wdev);
 #ifdef CONFIG_CFG80211_WEXT
 			kfree(wdev->wext.keys);
 #endif
#3 lore raw
In reply to Johannes Berg
Quoted 3 lines · Johannes Berg

These two patches now implement returning unknown action frames and registering for certain action frames from userspace, as well as sending as before.

These look good (after couple of small fixes to the latter). I tested these by registering three different Public Action frame types (including vendor-specific) worked fine and I think these patches (with the fix below) should be ready to be submitted into wireless-testing.git now.

The fixes are for 006-cfg80211-mac80211-user-action-frame.patch:
- must check rx->sta != NULL before rx_packets++ in ieee80211_rx_h_action() (the earlier version would trigger kernel panic on receiving Public Action frame from non-associated STA)
- must store match_len in cfg80211_mlme_register_action()

The updated patch with these fixes:

Subject: cfg80211/mac80211: allow registering for and sending action frames

From: Jouni Malinen <[email protected]>

This implements a new command to register for action frames that userspace wants to handle instead of the in-kernel rejection. It is then responsible for rejecting ones that it decided not to handle. There is no unregistration, but the socket can be closed for that.

Frames that are not registered for will not be forwarded to userspace and will be rejected by the kernel, the cfg80211 API helps implementing that.

Additionally, this patch adds a new command that allows doing action frame transmission from userspace. It can be used either to exchange action frames on the current operational channel (e.g., with the AP with which we are currently associated) or to exchange off-channel Public Action frames with the remain-on-channel command.

TODO: capability for mac80211 drivers -- seems not all
will necessarily support this?? Ask Sujith.

---
 include/linux/nl80211.h    |   41 ++++++-
 include/net/cfg80211.h     |   47 +++++++-
 include/net/mac80211.h     |    6 -
 net/mac80211/cfg.c         |   12 +-
 net/mac80211/ieee80211_i.h |    6 -
 net/mac80211/mlme.c        |   35 ++++++
 net/mac80211/rx.c          |   42 +++++--
 net/mac80211/status.c      |    7 +
 net/wireless/core.c        |    4 
 net/wireless/core.h        |    9 +
 net/wireless/mlme.c        |  166 ++++++++++++++++++++++++++++
 net/wireless/nl80211.c     |  260 ++++++++++++++++++++++++++++++++++++++++++++-
 net/wireless/nl80211.h     |    8 +
 13 files changed, 625 insertions(+), 18 deletions(-)
net/mac80211/rx.c +31 -11
@@ -1856,28 +1856,25 @@ ieee80211_rx_h_action(struct ieee80211_r
 	struct ieee80211_sub_if_data *sdata = rx->sdata;
 	struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) rx->skb->data;
 	struct sk_buff *nskb;
+	struct ieee80211_rx_status *status;
 	int len = rx->skb->len;
 
 	if (!ieee80211_is_action(mgmt->frame_control))
 		return RX_CONTINUE;
 
-	if (!rx->sta)
+	/* drop too small frames */
+	if (len < IEEE80211_MIN_ACTION_SIZE)
 		return RX_DROP_UNUSABLE;
 
-	if (!(rx->flags & IEEE80211_RX_RA_MATCH))
+	if (!rx->sta && mgmt->u.action.category != WLAN_CATEGORY_PUBLIC)
 		return RX_DROP_UNUSABLE;
 
-	if (ieee80211_drop_unencrypted(rx, mgmt->frame_control))
+	if (!(rx->flags & IEEE80211_RX_RA_MATCH))
 		return RX_DROP_UNUSABLE;
 
-	/* drop too small frames */
-	if (len < IEEE80211_MIN_ACTION_SIZE)
+	if (ieee80211_drop_unencrypted(rx, mgmt->frame_control))
 		return RX_DROP_UNUSABLE;
 
-	/* return action frames that have *only* category */
-	if (len < IEEE80211_MIN_ACTION_SIZE + 1)
-		goto return_frame;
-
 	switch (mgmt->u.action.category) {
 	case WLAN_CATEGORY_BACK:
 		/*
@@ -1891,6 +1888,10 @@ ieee80211_rx_h_action(struct ieee80211_r
 		    sdata->vif.type != NL80211_IFTYPE_AP)
 			break;
 
+		/* verify action_code is present */
+		if (len < IEEE80211_MIN_ACTION_SIZE + 1)
+			break;
+
 		switch (mgmt->u.action.u.addba_req.action_code) {
 		case WLAN_ACTION_ADDBA_REQ:
 			if (len < (IEEE80211_MIN_ACTION_SIZE +
@@ -1919,6 +1920,10 @@ ieee80211_rx_h_action(struct ieee80211_r
 		if (sdata->vif.type != NL80211_IFTYPE_STATION)
 			break;
 
+		/* verify action_code is present */
+		if (len < IEEE80211_MIN_ACTION_SIZE + 1)
+			break;
+
 		switch (mgmt->u.action.u.measurement.action_code) {
 		case WLAN_ACTION_SPCT_MSR_REQ:
 			if (len < (IEEE80211_MIN_ACTION_SIZE +
@@ -1954,7 +1959,7 @@ ieee80211_rx_h_action(struct ieee80211_r
 		}
 		break;
 	}
- return_frame:
+
 	/*
 	 * For AP mode, hostapd is responsible for handling any action
 	 * frames that we didn't handle, including returning unknown
@@ -1966,6 +1971,20 @@ ieee80211_rx_h_action(struct ieee80211_r
 	    sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
 		return RX_DROP_MONITOR;
 
+	/*
+	 * Getting here means the kernel doesn't know how to handle
+	 * it, but maybe userspace does ... include returned frames
+	 * so userspace can register for those to know whether ones
+	 * it transmitted were processed or returned.
+	 */
+	status = IEEE80211_SKB_RXCB(rx->skb);
+
+	if (sdata->vif.type == NL80211_IFTYPE_STATION &&
+	    cfg80211_rx_action(rx->sdata->dev, status->freq,
+			       rx->skb->data, rx->skb->len,
+			       GFP_ATOMIC))
+		goto handled;
+
 	/* do not return rejected action frames */
 	if (mgmt->u.action.category & 0x80)
 		return RX_DROP_UNUSABLE;
@@ -1985,7 +2004,8 @@ ieee80211_rx_h_action(struct ieee80211_r
 	}
 
  handled:
-	rx->sta->rx_packets++;
+	if (rx->sta)
+		rx->sta->rx_packets++;
 	dev_kfree_skb(rx->skb);
 	return RX_QUEUED;
 }
net/wireless/core.h +9 -0
@@ -329,6 +329,15 @@ void __cfg80211_connect_result(struct ne
 			       const u8 *resp_ie, size_t resp_ie_len,
 			       u16 status, bool wextev,
 			       struct cfg80211_bss *bss);
+int cfg80211_mlme_register_action(struct wireless_dev *wdev, u32 snd_pid,
+				  const u8 *match_data, int match_len);
+void cfg80211_mlme_unregister_actions(struct wireless_dev *wdev, u32 nlpid);
+void cfg80211_mlme_purge_actions(struct wireless_dev *wdev);
+int cfg80211_mlme_action(struct cfg80211_registered_device *rdev,
+			 struct net_device *dev,
+			 struct ieee80211_channel *chan,
+			 enum nl80211_channel_type channel_type,
+			 const u8 *buf, size_t len, u64 *cookie);
 
 /* SME */
 int __cfg80211_connect(struct cfg80211_registered_device *rdev,
net/wireless/nl80211.c +259 -1

307 lines not on this page · raw message

net/wireless/nl80211.h +8 -0
@@ -74,4 +74,12 @@ void nl80211_send_sta_event(struct cfg80
 			    struct net_device *dev, const u8 *mac_addr,
 			    struct station_info *sinfo, gfp_t gfp);
 
+int nl80211_send_action(struct cfg80211_registered_device *rdev,
+			struct net_device *netdev, u32 nlpid, int freq,
+			const u8 *buf, size_t len, gfp_t gfp);
+void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev,
+				   struct net_device *netdev, u64 cookie,
+				   const u8 *buf, size_t len, bool ack,
+				   gfp_t gfp);
+
 #endif /* __NET_WIRELESS_NL80211_H */
include/linux/nl80211.h +40 -1

71 lines not on this page · raw message

include/net/cfg80211.h +46 -1

81 lines not on this page · raw message

net/mac80211/cfg.c +11 -1
@@ -1,7 +1,7 @@
 /*
  * mac80211 configuration hooks for cfg80211
  *
- * Copyright 2006, 2007	Johannes Berg <[email protected]>
+ * Copyright 2006-2010	Johannes Berg <[email protected]>
  *
  * This file is GPLv2 as found in COPYING.
  */
@@ -1448,6 +1448,15 @@ static int ieee80211_cancel_remain_on_ch
 	return ieee80211_wk_cancel_remain_on_channel(sdata, cookie);
 }
 
+static int ieee80211_action(struct wiphy *wiphy, struct net_device *dev,
+			    struct ieee80211_channel *chan,
+			    enum nl80211_channel_type channel_type,
+			    const u8 *buf, size_t len, u64 *cookie)
+{
+	return ieee80211_mgd_action(IEEE80211_DEV_TO_SUB_IF(dev), chan,
+				    channel_type, buf, len, cookie);
+}
+
 struct cfg80211_ops mac80211_config_ops = {
 	.add_virtual_intf = ieee80211_add_iface,
 	.del_virtual_intf = ieee80211_del_iface,
@@ -1496,4 +1505,5 @@ struct cfg80211_ops mac80211_config_ops 
 	.set_bitrate_mask = ieee80211_set_bitrate_mask,
 	.remain_on_channel = ieee80211_remain_on_channel,
 	.cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
+	.action = ieee80211_action,
 };
net/mac80211/ieee80211_i.h +5 -1
@@ -2,7 +2,7 @@
  * Copyright 2002-2005, Instant802 Networks, Inc.
  * Copyright 2005, Devicescape Software, Inc.
  * Copyright 2006-2007	Jiri Benc <[email protected]>
- * Copyright 2007-2008	Johannes Berg <[email protected]>
+ * Copyright 2007-2010	Johannes Berg <[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
@@ -966,6 +966,10 @@ int ieee80211_mgd_deauth(struct ieee8021
 int ieee80211_mgd_disassoc(struct ieee80211_sub_if_data *sdata,
 			   struct cfg80211_disassoc_request *req,
 			   void *cookie);
+int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata,
+			 struct ieee80211_channel *chan,
+			 enum nl80211_channel_type channel_type,
+			 const u8 *buf, size_t len, u64 *cookie);
 ieee80211_rx_result ieee80211_sta_rx_mgmt(struct ieee80211_sub_if_data *sdata,
 					  struct sk_buff *skb);
 void ieee80211_send_pspoll(struct ieee80211_local *local,
net/mac80211/mlme.c +35 -0
@@ -2084,3 +2084,38 @@ int ieee80211_mgd_disassoc(struct ieee80
 
 	return 0;
 }
+
+int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata,
+			 struct ieee80211_channel *chan,
+			 enum nl80211_channel_type channel_type,
+			 const u8 *buf, size_t len, u64 *cookie)
+{
+	struct ieee80211_local *local = sdata->local;
+	struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+	struct sk_buff *skb;
+
+	/* Check that we are on the requested channel for transmission */
+	if ((chan != local->tmp_channel ||
+	     channel_type != local->tmp_channel_type) &&
+	    (chan != local->oper_channel ||
+	     channel_type != local->oper_channel_type))
+		return -EBUSY;
+
+	skb = dev_alloc_skb(local->hw.extra_tx_headroom + len);
+	if (!skb)
+		return -ENOMEM;
+	skb_reserve(skb, local->hw.extra_tx_headroom);
+
+	memcpy(skb_put(skb, len), buf, len);
+
+	if (!(ifmgd->flags & IEEE80211_STA_MFP_ENABLED))
+		IEEE80211_SKB_CB(skb)->flags |=
+			IEEE80211_TX_INTFL_DONT_ENCRYPT;
+	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_NL80211_FRAME_TX |
+		IEEE80211_TX_CTL_REQ_TX_STATUS;
+	skb->dev = sdata->dev;
+	ieee80211_tx_skb(sdata, skb);
+
+	*cookie = (unsigned long) skb;
+	return 0;
+}
net/wireless/mlme.c +166 -0

169 lines not on this page · raw message

include/net/mac80211.h +5 -1
@@ -3,7 +3,7 @@
  *
  * Copyright 2002-2005, Devicescape Software, Inc.
  * Copyright 2006-2007	Jiri Benc <[email protected]>
- * Copyright 2007-2008	Johannes Berg <[email protected]>
+ * Copyright 2007-2010	Johannes Berg <[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
@@ -264,6 +264,9 @@ struct ieee80211_bss_conf {
  *	be modified again (no seqno assignment, crypto, etc.)
  * @IEEE80211_TX_INTFL_HAS_RADIOTAP: This frame was injected and still
  *	has a radiotap header at skb->data.
+ * @IEEE80211_TX_INTFL_NL80211_FRAME_TX: Frame was requested through nl80211
+ *	MLME command (internal to mac80211 to figure out whether to send TX
+ *	status to user space)
  */
 enum mac80211_tx_control_flags {
 	IEEE80211_TX_CTL_REQ_TX_STATUS		= BIT(0),
@@ -286,6 +289,7 @@ enum mac80211_tx_control_flags {
 	IEEE80211_TX_CTL_MORE_FRAMES		= BIT(18),
 	IEEE80211_TX_INTFL_RETRANSMISSION	= BIT(19),
 	IEEE80211_TX_INTFL_HAS_RADIOTAP		= BIT(20),
+	IEEE80211_TX_INTFL_NL80211_FRAME_TX	= BIT(21),
 };
 
 /**
net/mac80211/status.c +6 -1
@@ -2,7 +2,7 @@
  * Copyright 2002-2005, Instant802 Networks, Inc.
  * Copyright 2005-2006, Devicescape Software, Inc.
  * Copyright 2006-2007	Jiri Benc <[email protected]>
- * Copyright 2008-2009	Johannes Berg <[email protected]>
+ * Copyright 2008-2010	Johannes Berg <[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
@@ -288,6 +288,11 @@ void ieee80211_tx_status(struct ieee8021
 					msecs_to_jiffies(10));
 	}
 
+	if (info->flags & IEEE80211_TX_INTFL_NL80211_FRAME_TX)
+		cfg80211_action_tx_status(
+			skb->dev, (unsigned long) skb, skb->data, skb->len,
+			!!(info->flags & IEEE80211_TX_STAT_ACK), GFP_ATOMIC);
+
 	/* this was a transmitted frame, but now we want to reuse it */
 	skb_orphan(skb);
 
net/wireless/core.c +4 -0
@@ -677,6 +677,9 @@ static int cfg80211_netdev_notifier_call
 		INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work);
 		INIT_LIST_HEAD(&wdev->event_list);
 		spin_lock_init(&wdev->event_lock);
+		INIT_LIST_HEAD(&wdev->action_registrations);
+		spin_lock_init(&wdev->action_registrations_lock);
+
 		mutex_lock(&rdev->devlist_mtx);
 		list_add_rcu(&wdev->list, &rdev->netdev_list);
 		rdev->devlist_generation++;
@@ -792,6 +795,7 @@ static int cfg80211_netdev_notifier_call
 			sysfs_remove_link(&dev->dev.kobj, "phy80211");
 			list_del_rcu(&wdev->list);
 			rdev->devlist_generation++;
+			cfg80211_mlme_purge_actions(wdev);
 #ifdef CONFIG_CFG80211_WEXT
 			kfree(wdev->wext.keys);
 #endif
Signature · 1 line
Jouni Malinen                                            PGP id EFC895FA
author maintainer
#4 lore raw
Quoted 14 lines · Jouni Malinen
Quoted 3 lines · Johannes Berg

These two patches now implement returning unknown action frames and registering for certain action frames from userspace, as well as sending as before.

These look good (after couple of small fixes to the latter). I tested these by registering three different Public Action frame types (including vendor-specific) worked fine and I think these patches (with
the fix below) should be ready to be submitted into wireless-testing.git
now.

Quoted 7 lines

The fixes are for 006-cfg80211-mac80211-user-action-frame.patch:
- must check rx->sta != NULL before rx_packets++ in ieee80211_rx_h_action() (the earlier version would trigger kernel panic on receiving Public Action frame from non-associated STA)
- must store match_len in cfg80211_mlme_register_action()

The updated patch with these fixes:

...

Want to just submit them then?

johannes