A new nl80211 command, NL80211_CMD_SET_MGMT_EXTRA_IE, can be used to
add arbitrary IE data into the end of management frames in client
modes. The interface allows extra IEs to be configured for each
management frame subtype, but only some of them (ProbeReq, ProbeResp,
Auth, (Re)AssocReq, Deauth, Disassoc) are currently used.
This makes it easier to implement IEEE 802.11 extensions like WPS and
FT that add IE(s) into some management frames. In addition, this can
be useful for testing and experimentation purposes.
Signed-off-by: Jouni Malinen <[email protected]>
---
include/linux/nl80211.h | 20 +++++++++++++++
include/net/cfg80211.h | 21 +++++++++++++++
net/mac80211/cfg.c | 29 +++++++++++++++++++++
net/mac80211/ieee80211_i.h | 5 +++
net/mac80211/iface.c | 2 +
net/mac80211/mlme.c | 60 +++++++++++++++++++++++++++++++++++++++------
net/wireless/nl80211.c | 50 +++++++++++++++++++++++++++++++++++++
7 files changed, 180 insertions(+), 7 deletions(-)
--- wireless-testing.orig/include/linux/nl80211.h 2009-01-12 13:14:19.000000000 +0200
+++ wireless-testing/include/linux/nl80211.h 2009-01-12 13:15:56.000000000 +0200
@@ -133,6 +133,12 @@
* @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the
* interface identified by %NL80211_ATTR_IFINDEX
*
+ * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The
+ * interface is identified with %NL80211_ATTR_IFINDEX and the management
+ * frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be
+ * added to the end of the specified management frame is specified with
+ * %NL80211_ATTR_IE.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -178,6 +184,8 @@ enum nl80211_commands {
NL80211_CMD_GET_MESH_PARAMS,
NL80211_CMD_SET_MESH_PARAMS,
+ NL80211_CMD_SET_MGMT_EXTRA_IE,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -190,6 +198,7 @@ enum nl80211_commands {
* here
*/
#define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS
+#define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE
/**
* enum nl80211_attrs - nl80211 netlink attributes
@@ -284,6 +293,12 @@ enum nl80211_commands {
* supported interface types, each a flag attribute with the number
* of the interface mode.
*
+ * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for
+ * %NL80211_CMD_SET_MGMT_EXTRA_IE.
+ *
+ * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with
+ * %NL80211_CMD_SET_MGMT_EXTRA_IE).
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -348,6 +363,9 @@ enum nl80211_attrs {
NL80211_ATTR_KEY_DEFAULT_MGMT,
+ NL80211_ATTR_MGMT_SUBTYPE,
+ NL80211_ATTR_IE,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -363,6 +381,8 @@ enum nl80211_attrs {
#define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS
#define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ
#define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE
+#define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE
+#define NL80211_ATTR_IE NL80211_ATTR_IE
#define NL80211_MAX_SUPP_RATES 32
#define NL80211_MAX_SUPP_REG_RULES 32
--- wireless-testing.orig/net/wireless/nl80211.c 2009-01-12 13:12:38.000000000 +0200
+++ wireless-testing/net/wireless/nl80211.c 2009-01-12 13:32:06.000000000 +0200
@@ -105,6 +105,10 @@ static struct nla_policy nl80211_policy[
[NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
.len = NL80211_HT_CAPABILITY_LEN },
+
+ [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
+ [NL80211_ATTR_IE] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
};
/* message building helper */
@@ -2149,6 +2153,46 @@ static int nl80211_set_reg(struct sk_buf
return -EINVAL;
}
+static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *drv;
+ int err;
+ struct net_device *dev;
+ struct mgmt_extra_ie_params params;
+
+ memset(¶ms, 0, sizeof(params));
+
+ if (!info->attrs[NL80211_ATTR_MGMT_SUBTYPE])
+ return -EINVAL;
+ params.subtype = nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]);
+ if (params.subtype > 15)
+ return -EINVAL; /* FC Subtype field is 4 bits (0..15) */
+
+ if (info->attrs[NL80211_ATTR_IE]) {
+ params.ies = nla_data(info->attrs[NL80211_ATTR_IE]);
+ params.ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ }
+
+ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+ if (err)
+ return err;
+
+ if (!drv->ops->set_mgmt_extra_ie) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ rtnl_lock();
+ err = drv->ops->set_mgmt_extra_ie(&drv->wiphy, dev, ¶ms);
+ rtnl_unlock();
+
+ out:
+ cfg80211_put_dev(drv);
+ dev_put(dev);
+ return err;
+}
+
static struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_GET_WIPHY,
@@ -2310,6 +2354,12 @@ static struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
+ {
+ .cmd = NL80211_CMD_SET_MGMT_EXTRA_IE,
+ .doit = nl80211_set_mgmt_extra_ie,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
};
/* multicast groups */
--- wireless-testing.orig/include/net/cfg80211.h 2009-01-12 13:18:24.000000000 +0200
+++ wireless-testing/include/net/cfg80211.h 2009-01-12 13:23:07.000000000 +0200
@@ -433,6 +433,21 @@ struct ieee80211_txq_params {
u8 aifs;
};
+/**
+ * struct mgmt_extra_ie_params - Extra management frame IE parameters
+ *
+ * Used to add extra IE(s) into management frames in client modes.
+ *
+ * @subtype: Management frame subtype
+ * @ies: IE data buffer or %NULL to remove previous data
+ * @ies_len: Length of @ies in octets
+ */
+struct mgmt_extra_ie_params {
+ u8 subtype;
+ u8 *ies;
+ int ies_len;
+};
+
/* from net/wireless.h */
struct wiphy;
@@ -501,6 +516,8 @@ struct ieee80211_channel;
* @set_txq_params: Set TX queue parameters
*
* @set_channel: Set channel
+ *
+ * @set_mgmt_extra_ie: Set extra IE data for management frames
*/
struct cfg80211_ops {
int (*add_virtual_intf)(struct wiphy *wiphy, char *name,
@@ -571,6 +588,10 @@ struct cfg80211_ops {
int (*set_channel)(struct wiphy *wiphy,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type);
+
+ int (*set_mgmt_extra_ie)(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct mgmt_extra_ie_params *params);
};
/* temporary wext handlers */
--- wireless-testing.orig/net/mac80211/cfg.c 2009-01-12 13:29:45.000000000 +0200
+++ wireless-testing/net/mac80211/cfg.c 2009-01-12 13:43:33.000000000 +0200
@@ -1175,6 +1175,34 @@ static int ieee80211_set_channel(struct
return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
}
+static int ieee80211_set_mgmt_extra_ie(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct mgmt_extra_ie_params *params)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ if (sdata->vif.type != NL80211_IFTYPE_STATION &&
+ sdata->vif.type != NL80211_IFTYPE_ADHOC)
+ return -EINVAL;
+
+ kfree(sdata->u.sta.mgmt_ie[params->subtype]);
+ sdata->u.sta.mgmt_ie[params->subtype] = NULL;
+ sdata->u.sta.mgmt_ie_len[params->subtype] = 0;
+
+ if (params->ies) {
+ sdata->u.sta.mgmt_ie[params->subtype] =
+ kmalloc(params->ies_len, GFP_KERNEL);
+ if (sdata->u.sta.mgmt_ie[params->subtype] == NULL)
+ return -ENOMEM;
+ memcpy(sdata->u.sta.mgmt_ie[params->subtype], params->ies,
+ params->ies_len);
+ sdata->u.sta.mgmt_ie_len[params->subtype] = params->ies_len;
+ }
+
+ return 0;
+}
+
struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -1204,4 +1232,5 @@ struct cfg80211_ops mac80211_config_ops
.change_bss = ieee80211_change_bss,
.set_txq_params = ieee80211_set_txq_params,
.set_channel = ieee80211_set_channel,
+ .set_mgmt_extra_ie = ieee80211_set_mgmt_extra_ie,
};
--- wireless-testing.orig/net/mac80211/ieee80211_i.h 2009-01-12 13:35:10.000000000 +0200
+++ wireless-testing/net/mac80211/ieee80211_i.h 2009-01-12 13:38:41.000000000 +0200
@@ -331,6 +331,11 @@ struct ieee80211_if_sta {
u32 supp_rates_bits[IEEE80211_NUM_BANDS];
int wmm_last_param_set;
+
+ /* Extra IE data for management frames */
+#define IEEE80211_NUM_SUBTYPES 16
+ u8 *mgmt_ie[IEEE80211_NUM_SUBTYPES];
+ size_t mgmt_ie_len[IEEE80211_NUM_SUBTYPES];
};
struct ieee80211_if_mesh {
--- wireless-testing.orig/net/mac80211/iface.c 2009-01-12 13:37:32.000000000 +0200
+++ wireless-testing/net/mac80211/iface.c 2009-01-12 13:39:10.000000000 +0200
@@ -632,6 +632,8 @@ static void ieee80211_teardown_sdata(str
kfree(sdata->u.sta.assocreq_ies);
kfree(sdata->u.sta.assocresp_ies);
kfree_skb(sdata->u.sta.probe_resp);
+ for (i = 0; i < IEEE80211_NUM_SUBTYPES; i++)
+ kfree(sdata->u.sta.mgmt_ie[i]);
break;
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_AP_VLAN:
--- wireless-testing.orig/net/mac80211/mlme.c 2009-01-12 13:44:05.000000000 +0200
+++ wireless-testing/net/mac80211/mlme.c 2009-01-12 13:56:09.000000000 +0200
@@ -139,10 +139,15 @@ void ieee80211_send_probe_req(struct iee
struct ieee80211_supported_band *sband;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
- u8 *pos, *supp_rates, *esupp_rates = NULL;
+ u8 *pos, *supp_rates, *esupp_rates = NULL, *ies;
int i;
+ size_t ies_len;
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200);
+ ies = sdata->u.sta.mgmt_ie[IEEE80211_STYPE_PROBE_REQ >> 4];
+ ies_len = sdata->u.sta.mgmt_ie_len[IEEE80211_STYPE_PROBE_REQ >> 4];
+
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 +
+ ies_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
"request\n", sdata->dev->name);
@@ -189,6 +194,9 @@ void ieee80211_send_probe_req(struct iee
*pos = rate->bitrate / 5;
}
+ if (ies)
+ memcpy(skb_put(skb, ies_len), ies, ies_len);
+
ieee80211_tx_skb(sdata, skb, 0);
}
@@ -200,9 +208,14 @@ static void ieee80211_send_auth(struct i
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
+ u8 *ies;
+ size_t ies_len;
+
+ ies = sdata->u.sta.mgmt_ie[IEEE80211_STYPE_AUTH >> 4];
+ ies_len = sdata->u.sta.mgmt_ie_len[IEEE80211_STYPE_AUTH >> 4];
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
- sizeof(*mgmt) + 6 + extra_len);
+ sizeof(*mgmt) + 6 + extra_len + ies_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for auth "
"frame\n", sdata->dev->name);
@@ -225,6 +238,8 @@ static void ieee80211_send_auth(struct i
mgmt->u.auth.status_code = cpu_to_le16(0);
if (extra)
memcpy(skb_put(skb, extra_len), extra, extra_len);
+ if (ies)
+ memcpy(skb_put(skb, ies_len), ies, ies_len);
ieee80211_tx_skb(sdata, skb, encrypt);
}
@@ -235,17 +250,28 @@ static void ieee80211_send_assoc(struct
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
- u8 *pos, *ies, *ht_ie;
+ u8 *pos, *ies, *ht_ie, *e_ies;
int i, len, count, rates_len, supp_rates_len;
u16 capab;
struct ieee80211_bss *bss;
int wmm = 0;
struct ieee80211_supported_band *sband;
u64 rates = 0;
+ size_t e_ies_len;
+
+ if (ifsta->flags & IEEE80211_STA_PREV_BSSID_SET) {
+ e_ies = sdata->u.sta.mgmt_ie[IEEE80211_STYPE_REASSOC_REQ >> 4];
+ e_ies_len = sdata->u.sta.mgmt_ie_len[
+ IEEE80211_STYPE_REASSOC_REQ >> 4];
+ } else {
+ e_ies = sdata->u.sta.mgmt_ie[IEEE80211_STYPE_ASSOC_REQ >> 4];
+ e_ies_len = sdata->u.sta.mgmt_ie_len[
+ IEEE80211_STYPE_ASSOC_REQ >> 4];
+ }
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
sizeof(*mgmt) + 200 + ifsta->extra_ie_len +
- ifsta->ssid_len);
+ ifsta->ssid_len + e_ies_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
"frame\n", sdata->dev->name);
@@ -436,6 +462,9 @@ static void ieee80211_send_assoc(struct
memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
}
+ if (e_ies)
+ memcpy(skb_put(skb, e_ies_len), e_ies, e_ies_len);
+
kfree(ifsta->assocreq_ies);
ifsta->assocreq_ies_len = (skb->data + skb->len) - ies;
ifsta->assocreq_ies = kmalloc(ifsta->assocreq_ies_len, GFP_KERNEL);
@@ -453,8 +482,14 @@ static void ieee80211_send_deauth_disass
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
+ u8 *ies;
+ size_t ies_len;
+
+ ies = sdata->u.sta.mgmt_ie[stype >> 4];
+ ies_len = sdata->u.sta.mgmt_ie_len[stype >> 4];
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt));
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) +
+ ies_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for "
"deauth/disassoc frame\n", sdata->dev->name);
@@ -472,6 +507,9 @@ static void ieee80211_send_deauth_disass
/* u.deauth.reason_code == u.disassoc.reason_code */
mgmt->u.deauth.reason_code = cpu_to_le16(reason);
+ if (ies)
+ memcpy(skb_put(skb, ies_len), ies, ies_len);
+
ieee80211_tx_skb(sdata, skb, ifsta->flags & IEEE80211_STA_MFP_ENABLED);
}
@@ -1485,8 +1523,13 @@ static int ieee80211_sta_join_ibss(struc
u8 *pos;
struct ieee80211_supported_band *sband;
union iwreq_data wrqu;
+ u8 *ies;
+ size_t ies_len;
+
+ ies = sdata->u.sta.mgmt_ie[IEEE80211_STYPE_PROBE_RESP >> 4];
+ ies_len = sdata->u.sta.mgmt_ie_len[IEEE80211_STYPE_PROBE_RESP >> 4];
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 + ies_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
"response\n", sdata->dev->name);
@@ -1569,6 +1612,9 @@ static int ieee80211_sta_join_ibss(struc
memcpy(pos, &bss->supp_rates[8], rates);
}
+ if (ies)
+ memcpy(skb_put(skb, ies_len), ies, ies_len);
+
ifsta->probe_resp = skb;
ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
--
Jouni Malinen PGP id EFC895FA
On Mon, 2009-01-12 at 17:03 +0200, Jouni Malinen wrote:
> > Should we sanity-check the input? E.g. in nl80211, ensure that it's
> > (<type><len><data...>)* with <len> being correct and no final padding?
> > And maybe that there isn't anything in those IEs that we've already
> > added, like an SSID?
>
> [snip long explanation]
Ok, makes sense, let's leave it as-is then, we allow root to shoot
himself into the foot anyway.
johannes
On Mon, 2009-01-12 at 16:26 +0200, Jouni Malinen wrote:
> + /* Extra IE data for management frames */
> + u8 *ie_probereq;
> + size_t ie_probereq_len;
> + u8 *ie_proberesp;
> + size_t ie_proberesp_len;
> + u8 *ie_auth;
> + size_t ie_auth_len;
> + u8 *ie_assocreq;
> + size_t ie_assocreq_len;
> + u8 *ie_reassocreq;
> + size_t ie_reassocreq_len;
> + u8 *ie_deauth;
> + size_t ie_deauth_len;
> + u8 *ie_disassoc;
> + size_t ie_disassoc_len;
It'd be more memory efficient on 64-bit to not alternate between 64 and
32 bit values, but I don't really care too much in this struct.
Should we sanity-check the input? E.g. in nl80211, ensure that it's
(<type><len><data...>)* with <len> being correct and no final padding?
And maybe that there isn't anything in those IEs that we've already
added, like an SSID?
johannes
On Mon, 2009-01-12 at 14:26 +0200, Jouni Malinen wrote:
> A new nl80211 command, NL80211_CMD_SET_MGMT_EXTRA_IE, can be used to
> add arbitrary IE data into the end of management frames in client
> modes. The interface allows extra IEs to be configured for each
> management frame subtype, but only some of them (ProbeReq, ProbeResp,
> Auth, (Re)AssocReq, Deauth, Disassoc) are currently used.
>
> This makes it easier to implement IEEE 802.11 extensions like WPS and
> FT that add IE(s) into some management frames. In addition, this can
> be useful for testing and experimentation purposes.
Looks sane to me. Would it make sense to provide support for getting
this information as well?
> + if (!drv->ops->set_mgmt_extra_ie) {
> + err = -EOPNOTSUPP;
> + goto out;
> + }
You could check for that earlier and avoid the goto, I think.
> +/**
> + * struct mgmt_extra_ie_params - Extra management frame IE parameters
> + *
> + * Used to add extra IE(s) into management frames in client modes.
That could be a little more specific, I think. You seem to implement it
only for STA/IBSS, but it could make sense for mesh as well, for
instance to add things into the beacon there? Same with IBSS I guess?
I think we should provide, to userspace, a guarantee such as:
If this operation completes successfully, then any frame generated by
the kernel or hardware with that subtype will include the information.
This obviously excludes injected frames and that could be stated
explicitly. Also means that the "client modes" above should be removed
and instead the handler has to just fail in other modes and for
unsupported frame types.
Therefore, beacons should include it in ibss/mesh, and mac80211's cfg
handler should reject attempts to configure anything it doesn't support.
Since then you need to do things based on the frame type, I think I'd
rather open code the subtypes array into ie_presp, ie_preq, etc.
johannes
On Mon, Jan 12, 2009 at 02:08:44PM +0100, Johannes Berg wrote:
> Looks sane to me. Would it make sense to provide support for getting
> this information as well?
I did not come up with any good use case for this.
> > + if (!drv->ops->set_mgmt_extra_ie) {
> > + err = -EOPNOTSUPP;
> > + goto out;
> > + }
>
> You could check for that earlier and avoid the goto, I think.
Nope.. Need to cfg80211_put_dev(drv) after this check. If you want to
get rid of goto, I can do that by moving the rtnl locking and actual
call into else {}..
> > +/**
> > + * struct mgmt_extra_ie_params - Extra management frame IE parameters
> > + *
> > + * Used to add extra IE(s) into management frames in client modes.
>
> That could be a little more specific, I think. You seem to implement it
> only for STA/IBSS, but it could make sense for mesh as well, for
> instance to add things into the beacon there? Same with IBSS I guess?
Mesh could make sense, but I did not want to go through all the details
for it now. I would have added IBSS Beacon, had I found a place where we
actually configure it.. Do we or is it just completely missing? The
original d80211 submission did generate the Beacon template when joining
IBSS.. Now mac80211 seems to be just generating a Probe Response frame.
Anyway, defining the interface to be more generic sounds reasonable.
> I think we should provide, to userspace, a guarantee such as:
>
> If this operation completes successfully, then any frame generated by
> the kernel or hardware with that subtype will include the information.
>
> This obviously excludes injected frames and that could be stated
> explicitly. Also means that the "client modes" above should be removed
> and instead the handler has to just fail in other modes and for
> unsupported frame types.
Agreed.
> Therefore, beacons should include it in ibss/mesh, and mac80211's cfg
> handler should reject attempts to configure anything it doesn't support.
> Since then you need to do things based on the frame type, I think I'd
> rather open code the subtypes array into ie_presp, ie_preq, etc.
OK. I'll change the design to do that, but will probably not implement
actual handlers for anything else than managed and adhoc now, i.e.,
net/mac80211/cfg.c will see the config attempts, but it will reject them
for now.
--
Jouni Malinen PGP id EFC895FA
On Mon, Jan 12, 2009 at 03:48:22PM +0100, Johannes Berg wrote:
> On Mon, 2009-01-12 at 16:26 +0200, Jouni Malinen wrote:
> > + /* Extra IE data for management frames */
> > + u8 *ie_probereq;
> > + size_t ie_probereq_len;
> > + u8 *ie_proberesp;
> > + size_t ie_proberesp_len;
> It'd be more memory efficient on 64-bit to not alternate between 64 and
> 32 bit values, but I don't really care too much in this struct.
Yeah.. I could reorder or even change the _len to be u16, but it is
probably cleaner to just leave it as-is taken into account there is only
one of these structs per virtual interface.
> Should we sanity-check the input? E.g. in nl80211, ensure that it's
> (<type><len><data...>)* with <len> being correct and no final padding?
> And maybe that there isn't anything in those IEs that we've already
> added, like an SSID?
I would actually like to be able to include invalid data for testing
purposes and some frames might even not require an IE in the end. Action
frames would be an example of this, but those would require category and
code fields to allow more detailed selection of which frames to extend.
That can be added in the future, if a reasonable use is found. Anyway, I
would not bother adding validating the provided input.
As far as figuring out whether there are any IEs that we (as in
mac80211) have added gets bit complex taken into account that we do not
know that at the time this command is run. It is even more complex for
firmware-generated frames. Furthermore, it is unclear what exactly we
should do if a duplicate IE is found. With some IEs, it is actually find
to have multiple occurrences in the same frame.
If we would like to provide more intelligent processing of such things,
it would probably have to be done on case by case basis and with
different rules for each IE depending on the subtype. For example, some
IEs might be used to replace the generated IE while others would be
added and some might trigger some kind of error notification to
userspace when the frame is being transmitted, etc. This gets very
complex and I would rather not go there before someone comes with a very
good justification and use case explaining why it is needed..
--
Jouni Malinen PGP id EFC895FA
Updated based on your comments..
nl80211: New command for adding extra IE(s) into management frames
A new nl80211 command, NL80211_CMD_SET_MGMT_EXTRA_IE, can be used to
add arbitrary IE data into the end of management frames. The interface
allows extra IEs to be configured for each management frame subtype, but
only some of them (ProbeReq, ProbeResp, Auth, (Re)AssocReq, Deauth,
Disassoc) are currently accepted in mac80211 implementation.
This makes it easier to implement IEEE 802.11 extensions like WPS and
FT that add IE(s) into some management frames. In addition, this can
be useful for testing and experimentation purposes.
Signed-off-by: Jouni Malinen <[email protected]>
---
include/linux/nl80211.h | 22 ++++++++++++
include/net/cfg80211.h | 26 ++++++++++++++
net/mac80211/cfg.c | 82 +++++++++++++++++++++++++++++++++++++++++++++
net/mac80211/ieee80211_i.h | 16 ++++++++
net/mac80211/iface.c | 7 +++
net/mac80211/mlme.c | 52 +++++++++++++++++++++++++---
net/wireless/nl80211.c | 47 +++++++++++++++++++++++++
7 files changed, 246 insertions(+), 6 deletions(-)
--- wireless-testing.orig/include/linux/nl80211.h 2009-01-12 13:14:19.000000000 +0200
+++ wireless-testing/include/linux/nl80211.h 2009-01-12 16:16:48.000000000 +0200
@@ -133,6 +133,14 @@
* @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the
* interface identified by %NL80211_ATTR_IFINDEX
*
+ * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The
+ * interface is identified with %NL80211_ATTR_IFINDEX and the management
+ * frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be
+ * added to the end of the specified management frame is specified with
+ * %NL80211_ATTR_IE. If the command succeeds, the requested data will be
+ * added to all specified management frames generated by
+ * kernel/firmware/driver.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -178,6 +186,8 @@ enum nl80211_commands {
NL80211_CMD_GET_MESH_PARAMS,
NL80211_CMD_SET_MESH_PARAMS,
+ NL80211_CMD_SET_MGMT_EXTRA_IE,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -190,6 +200,7 @@ enum nl80211_commands {
* here
*/
#define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS
+#define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE
/**
* enum nl80211_attrs - nl80211 netlink attributes
@@ -284,6 +295,12 @@ enum nl80211_commands {
* supported interface types, each a flag attribute with the number
* of the interface mode.
*
+ * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for
+ * %NL80211_CMD_SET_MGMT_EXTRA_IE.
+ *
+ * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with
+ * %NL80211_CMD_SET_MGMT_EXTRA_IE).
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -348,6 +365,9 @@ enum nl80211_attrs {
NL80211_ATTR_KEY_DEFAULT_MGMT,
+ NL80211_ATTR_MGMT_SUBTYPE,
+ NL80211_ATTR_IE,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -363,6 +383,8 @@ enum nl80211_attrs {
#define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS
#define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ
#define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE
+#define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE
+#define NL80211_ATTR_IE NL80211_ATTR_IE
#define NL80211_MAX_SUPP_RATES 32
#define NL80211_MAX_SUPP_REG_RULES 32
--- wireless-testing.orig/net/wireless/nl80211.c 2009-01-12 13:12:38.000000000 +0200
+++ wireless-testing/net/wireless/nl80211.c 2009-01-12 16:22:10.000000000 +0200
@@ -105,6 +105,10 @@ static struct nla_policy nl80211_policy[
[NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
.len = NL80211_HT_CAPABILITY_LEN },
+
+ [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
+ [NL80211_ATTR_IE] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
};
/* message building helper */
@@ -2149,6 +2153,43 @@ static int nl80211_set_reg(struct sk_buf
return -EINVAL;
}
+static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb,
+ struct genl_info *info)
+{
+ struct cfg80211_registered_device *drv;
+ int err;
+ struct net_device *dev;
+ struct mgmt_extra_ie_params params;
+
+ memset(¶ms, 0, sizeof(params));
+
+ if (!info->attrs[NL80211_ATTR_MGMT_SUBTYPE])
+ return -EINVAL;
+ params.subtype = nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]);
+ if (params.subtype > 15)
+ return -EINVAL; /* FC Subtype field is 4 bits (0..15) */
+
+ if (info->attrs[NL80211_ATTR_IE]) {
+ params.ies = nla_data(info->attrs[NL80211_ATTR_IE]);
+ params.ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
+ }
+
+ err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev);
+ if (err)
+ return err;
+
+ if (drv->ops->set_mgmt_extra_ie) {
+ rtnl_lock();
+ err = drv->ops->set_mgmt_extra_ie(&drv->wiphy, dev, ¶ms);
+ rtnl_unlock();
+ } else
+ err = -EOPNOTSUPP;
+
+ cfg80211_put_dev(drv);
+ dev_put(dev);
+ return err;
+}
+
static struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_GET_WIPHY,
@@ -2310,6 +2351,12 @@ static struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
.flags = GENL_ADMIN_PERM,
},
+ {
+ .cmd = NL80211_CMD_SET_MGMT_EXTRA_IE,
+ .doit = nl80211_set_mgmt_extra_ie,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
};
/* multicast groups */
--- wireless-testing.orig/include/net/cfg80211.h 2009-01-12 13:18:24.000000000 +0200
+++ wireless-testing/include/net/cfg80211.h 2009-01-12 15:54:51.000000000 +0200
@@ -433,6 +433,26 @@ struct ieee80211_txq_params {
u8 aifs;
};
+/**
+ * struct mgmt_extra_ie_params - Extra management frame IE parameters
+ *
+ * Used to add extra IE(s) into management frames. If the driver cannot add the
+ * requested data into all management frames of the specified subtype that are
+ * generated in kernel or firmware/hardware, it must reject the configuration
+ * call. The IE data buffer is added to the end of the specified management
+ * frame body after all other IEs. This addition is not applied to frames that
+ * are injected through a monitor interface.
+ *
+ * @subtype: Management frame subtype
+ * @ies: IE data buffer or %NULL to remove previous data
+ * @ies_len: Length of @ies in octets
+ */
+struct mgmt_extra_ie_params {
+ u8 subtype;
+ u8 *ies;
+ int ies_len;
+};
+
/* from net/wireless.h */
struct wiphy;
@@ -501,6 +521,8 @@ struct ieee80211_channel;
* @set_txq_params: Set TX queue parameters
*
* @set_channel: Set channel
+ *
+ * @set_mgmt_extra_ie: Set extra IE data for management frames
*/
struct cfg80211_ops {
int (*add_virtual_intf)(struct wiphy *wiphy, char *name,
@@ -571,6 +593,10 @@ struct cfg80211_ops {
int (*set_channel)(struct wiphy *wiphy,
struct ieee80211_channel *chan,
enum nl80211_channel_type channel_type);
+
+ int (*set_mgmt_extra_ie)(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct mgmt_extra_ie_params *params);
};
/* temporary wext handlers */
--- wireless-testing.orig/net/mac80211/cfg.c 2009-01-12 13:29:45.000000000 +0200
+++ wireless-testing/net/mac80211/cfg.c 2009-01-12 16:19:18.000000000 +0200
@@ -1175,6 +1175,87 @@ static int ieee80211_set_channel(struct
return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL);
}
+static int set_mgmt_extra_ie_sta(struct ieee80211_if_sta *ifsta, u8 subtype,
+ u8 *ies, size_t ies_len)
+{
+ switch (subtype) {
+ case IEEE80211_STYPE_PROBE_REQ >> 4:
+ kfree(ifsta->ie_probereq);
+ ifsta->ie_probereq = ies;
+ ifsta->ie_probereq_len = ies_len;
+ return 0;
+ case IEEE80211_STYPE_PROBE_RESP >> 4:
+ kfree(ifsta->ie_proberesp);
+ ifsta->ie_proberesp = ies;
+ ifsta->ie_proberesp_len = ies_len;
+ return 0;
+ case IEEE80211_STYPE_AUTH >> 4:
+ kfree(ifsta->ie_auth);
+ ifsta->ie_auth = ies;
+ ifsta->ie_auth_len = ies_len;
+ return 0;
+ case IEEE80211_STYPE_ASSOC_REQ >> 4:
+ kfree(ifsta->ie_assocreq);
+ ifsta->ie_assocreq = ies;
+ ifsta->ie_assocreq_len = ies_len;
+ return 0;
+ case IEEE80211_STYPE_REASSOC_REQ >> 4:
+ kfree(ifsta->ie_reassocreq);
+ ifsta->ie_reassocreq = ies;
+ ifsta->ie_reassocreq_len = ies_len;
+ return 0;
+ case IEEE80211_STYPE_DEAUTH >> 4:
+ kfree(ifsta->ie_deauth);
+ ifsta->ie_deauth = ies;
+ ifsta->ie_deauth_len = ies_len;
+ return 0;
+ case IEEE80211_STYPE_DISASSOC >> 4:
+ kfree(ifsta->ie_disassoc);
+ ifsta->ie_disassoc = ies;
+ ifsta->ie_disassoc_len = ies_len;
+ return 0;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static int ieee80211_set_mgmt_extra_ie(struct wiphy *wiphy,
+ struct net_device *dev,
+ struct mgmt_extra_ie_params *params)
+{
+ struct ieee80211_sub_if_data *sdata;
+ u8 *ies;
+ size_t ies_len;
+ int ret = -EOPNOTSUPP;
+
+ if (params->ies) {
+ ies = kmalloc(params->ies_len, GFP_KERNEL);
+ if (ies == NULL)
+ return -ENOMEM;
+ ies_len = params->ies_len;
+ } else {
+ ies = NULL;
+ ies_len = 0;
+ }
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ switch (sdata->vif.type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ ret = set_mgmt_extra_ie_sta(&sdata->u.sta, params->subtype,
+ ies, ies_len);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ if (ret)
+ kfree(ies);
+ return ret;
+}
+
struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -1204,4 +1285,5 @@ struct cfg80211_ops mac80211_config_ops
.change_bss = ieee80211_change_bss,
.set_txq_params = ieee80211_set_txq_params,
.set_channel = ieee80211_set_channel,
+ .set_mgmt_extra_ie = ieee80211_set_mgmt_extra_ie,
};
--- wireless-testing.orig/net/mac80211/ieee80211_i.h 2009-01-12 13:35:10.000000000 +0200
+++ wireless-testing/net/mac80211/ieee80211_i.h 2009-01-12 15:57:54.000000000 +0200
@@ -331,6 +331,22 @@ struct ieee80211_if_sta {
u32 supp_rates_bits[IEEE80211_NUM_BANDS];
int wmm_last_param_set;
+
+ /* Extra IE data for management frames */
+ u8 *ie_probereq;
+ size_t ie_probereq_len;
+ u8 *ie_proberesp;
+ size_t ie_proberesp_len;
+ u8 *ie_auth;
+ size_t ie_auth_len;
+ u8 *ie_assocreq;
+ size_t ie_assocreq_len;
+ u8 *ie_reassocreq;
+ size_t ie_reassocreq_len;
+ u8 *ie_deauth;
+ size_t ie_deauth_len;
+ u8 *ie_disassoc;
+ size_t ie_disassoc_len;
};
struct ieee80211_if_mesh {
--- wireless-testing.orig/net/mac80211/iface.c 2009-01-12 13:37:32.000000000 +0200
+++ wireless-testing/net/mac80211/iface.c 2009-01-12 15:58:44.000000000 +0200
@@ -632,6 +632,13 @@ static void ieee80211_teardown_sdata(str
kfree(sdata->u.sta.assocreq_ies);
kfree(sdata->u.sta.assocresp_ies);
kfree_skb(sdata->u.sta.probe_resp);
+ kfree(sdata->u.sta.ie_probereq);
+ kfree(sdata->u.sta.ie_proberesp);
+ kfree(sdata->u.sta.ie_auth);
+ kfree(sdata->u.sta.ie_assocreq);
+ kfree(sdata->u.sta.ie_reassocreq);
+ kfree(sdata->u.sta.ie_deauth);
+ kfree(sdata->u.sta.ie_disassoc);
break;
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_AP_VLAN:
--- wireless-testing.orig/net/mac80211/mlme.c 2009-01-12 13:44:05.000000000 +0200
+++ wireless-testing/net/mac80211/mlme.c 2009-01-12 16:07:35.000000000 +0200
@@ -131,6 +131,12 @@ u64 ieee80211_sta_get_rates(struct ieee8
/* frame sending functions */
+static void add_extra_ies(struct sk_buff *skb, u8 *ies, size_t ies_len)
+{
+ if (ies)
+ memcpy(skb_put(skb, ies_len), ies, ies_len);
+}
+
/* also used by scanning code */
void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst,
u8 *ssid, size_t ssid_len)
@@ -142,7 +148,8 @@ void ieee80211_send_probe_req(struct iee
u8 *pos, *supp_rates, *esupp_rates = NULL;
int i;
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200);
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 +
+ sdata->u.sta.ie_probereq_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
"request\n", sdata->dev->name);
@@ -189,6 +196,9 @@ void ieee80211_send_probe_req(struct iee
*pos = rate->bitrate / 5;
}
+ add_extra_ies(skb, sdata->u.sta.ie_probereq,
+ sdata->u.sta.ie_probereq_len);
+
ieee80211_tx_skb(sdata, skb, 0);
}
@@ -202,7 +212,8 @@ static void ieee80211_send_auth(struct i
struct ieee80211_mgmt *mgmt;
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
- sizeof(*mgmt) + 6 + extra_len);
+ sizeof(*mgmt) + 6 + extra_len +
+ sdata->u.sta.ie_auth_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for auth "
"frame\n", sdata->dev->name);
@@ -225,6 +236,7 @@ static void ieee80211_send_auth(struct i
mgmt->u.auth.status_code = cpu_to_le16(0);
if (extra)
memcpy(skb_put(skb, extra_len), extra, extra_len);
+ add_extra_ies(skb, sdata->u.sta.ie_auth, sdata->u.sta.ie_auth_len);
ieee80211_tx_skb(sdata, skb, encrypt);
}
@@ -235,17 +247,26 @@ static void ieee80211_send_assoc(struct
struct ieee80211_local *local = sdata->local;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
- u8 *pos, *ies, *ht_ie;
+ u8 *pos, *ies, *ht_ie, *e_ies;
int i, len, count, rates_len, supp_rates_len;
u16 capab;
struct ieee80211_bss *bss;
int wmm = 0;
struct ieee80211_supported_band *sband;
u64 rates = 0;
+ size_t e_ies_len;
+
+ if (ifsta->flags & IEEE80211_STA_PREV_BSSID_SET) {
+ e_ies = sdata->u.sta.ie_reassocreq;
+ e_ies_len = sdata->u.sta.ie_reassocreq_len;
+ } else {
+ e_ies = sdata->u.sta.ie_assocreq;
+ e_ies_len = sdata->u.sta.ie_assocreq_len;
+ }
skb = dev_alloc_skb(local->hw.extra_tx_headroom +
sizeof(*mgmt) + 200 + ifsta->extra_ie_len +
- ifsta->ssid_len);
+ ifsta->ssid_len + e_ies_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for assoc "
"frame\n", sdata->dev->name);
@@ -436,6 +457,8 @@ static void ieee80211_send_assoc(struct
memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs));
}
+ add_extra_ies(skb, e_ies, e_ies_len);
+
kfree(ifsta->assocreq_ies);
ifsta->assocreq_ies_len = (skb->data + skb->len) - ies;
ifsta->assocreq_ies = kmalloc(ifsta->assocreq_ies_len, GFP_KERNEL);
@@ -453,8 +476,19 @@ static void ieee80211_send_deauth_disass
struct ieee80211_if_sta *ifsta = &sdata->u.sta;
struct sk_buff *skb;
struct ieee80211_mgmt *mgmt;
+ u8 *ies;
+ size_t ies_len;
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt));
+ if (stype == IEEE80211_STYPE_DEAUTH) {
+ ies = sdata->u.sta.ie_deauth;
+ ies_len = sdata->u.sta.ie_deauth_len;
+ } else {
+ ies = sdata->u.sta.ie_disassoc;
+ ies_len = sdata->u.sta.ie_disassoc_len;
+ }
+
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) +
+ ies_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for "
"deauth/disassoc frame\n", sdata->dev->name);
@@ -472,6 +506,8 @@ static void ieee80211_send_deauth_disass
/* u.deauth.reason_code == u.disassoc.reason_code */
mgmt->u.deauth.reason_code = cpu_to_le16(reason);
+ add_extra_ies(skb, ies, ies_len);
+
ieee80211_tx_skb(sdata, skb, ifsta->flags & IEEE80211_STA_MFP_ENABLED);
}
@@ -1486,7 +1522,8 @@ static int ieee80211_sta_join_ibss(struc
struct ieee80211_supported_band *sband;
union iwreq_data wrqu;
- skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
+ skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 +
+ sdata->u.sta.ie_proberesp_len);
if (!skb) {
printk(KERN_DEBUG "%s: failed to allocate buffer for probe "
"response\n", sdata->dev->name);
@@ -1569,6 +1606,9 @@ static int ieee80211_sta_join_ibss(struc
memcpy(pos, &bss->supp_rates[8], rates);
}
+ add_extra_ies(skb, sdata->u.sta.ie_proberesp,
+ sdata->u.sta.ie_proberesp_len);
+
ifsta->probe_resp = skb;
ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON);
--
Jouni Malinen PGP id EFC895FA