diff options
author | Jouni Malinen <jouni.malinen@atheros.com> | 2009-01-13 09:03:29 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2009-01-29 16:00:35 -0500 |
commit | 9aed3cc124343d92be6697e9af3928bdfe8eb03e (patch) | |
tree | 6a49a68422656790f944f37e3f34379b753d1dab | |
parent | 0c1aa495961f03c964b3287cf5800217cf6f2cee (diff) |
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 <jouni.malinen@atheros.com>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | include/linux/nl80211.h | 22 | ||||
-rw-r--r-- | include/net/cfg80211.h | 26 | ||||
-rw-r--r-- | net/mac80211/cfg.c | 82 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 16 | ||||
-rw-r--r-- | net/mac80211/iface.c | 7 | ||||
-rw-r--r-- | net/mac80211/mlme.c | 52 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 47 |
7 files changed, 246 insertions, 6 deletions
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 4e7a7986a521..76aae3d8e97e 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h | |||
@@ -133,6 +133,14 @@ | |||
133 | * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the | 133 | * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the |
134 | * interface identified by %NL80211_ATTR_IFINDEX | 134 | * interface identified by %NL80211_ATTR_IFINDEX |
135 | * | 135 | * |
136 | * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The | ||
137 | * interface is identified with %NL80211_ATTR_IFINDEX and the management | ||
138 | * frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be | ||
139 | * added to the end of the specified management frame is specified with | ||
140 | * %NL80211_ATTR_IE. If the command succeeds, the requested data will be | ||
141 | * added to all specified management frames generated by | ||
142 | * kernel/firmware/driver. | ||
143 | * | ||
136 | * @NL80211_CMD_MAX: highest used command number | 144 | * @NL80211_CMD_MAX: highest used command number |
137 | * @__NL80211_CMD_AFTER_LAST: internal use | 145 | * @__NL80211_CMD_AFTER_LAST: internal use |
138 | */ | 146 | */ |
@@ -178,6 +186,8 @@ enum nl80211_commands { | |||
178 | NL80211_CMD_GET_MESH_PARAMS, | 186 | NL80211_CMD_GET_MESH_PARAMS, |
179 | NL80211_CMD_SET_MESH_PARAMS, | 187 | NL80211_CMD_SET_MESH_PARAMS, |
180 | 188 | ||
189 | NL80211_CMD_SET_MGMT_EXTRA_IE, | ||
190 | |||
181 | /* add new commands above here */ | 191 | /* add new commands above here */ |
182 | 192 | ||
183 | /* used to define NL80211_CMD_MAX below */ | 193 | /* used to define NL80211_CMD_MAX below */ |
@@ -190,6 +200,7 @@ enum nl80211_commands { | |||
190 | * here | 200 | * here |
191 | */ | 201 | */ |
192 | #define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS | 202 | #define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS |
203 | #define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE | ||
193 | 204 | ||
194 | /** | 205 | /** |
195 | * enum nl80211_attrs - nl80211 netlink attributes | 206 | * enum nl80211_attrs - nl80211 netlink attributes |
@@ -284,6 +295,12 @@ enum nl80211_commands { | |||
284 | * supported interface types, each a flag attribute with the number | 295 | * supported interface types, each a flag attribute with the number |
285 | * of the interface mode. | 296 | * of the interface mode. |
286 | * | 297 | * |
298 | * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for | ||
299 | * %NL80211_CMD_SET_MGMT_EXTRA_IE. | ||
300 | * | ||
301 | * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with | ||
302 | * %NL80211_CMD_SET_MGMT_EXTRA_IE). | ||
303 | * | ||
287 | * @NL80211_ATTR_MAX: highest attribute number currently defined | 304 | * @NL80211_ATTR_MAX: highest attribute number currently defined |
288 | * @__NL80211_ATTR_AFTER_LAST: internal use | 305 | * @__NL80211_ATTR_AFTER_LAST: internal use |
289 | */ | 306 | */ |
@@ -348,6 +365,9 @@ enum nl80211_attrs { | |||
348 | 365 | ||
349 | NL80211_ATTR_KEY_DEFAULT_MGMT, | 366 | NL80211_ATTR_KEY_DEFAULT_MGMT, |
350 | 367 | ||
368 | NL80211_ATTR_MGMT_SUBTYPE, | ||
369 | NL80211_ATTR_IE, | ||
370 | |||
351 | /* add attributes here, update the policy in nl80211.c */ | 371 | /* add attributes here, update the policy in nl80211.c */ |
352 | 372 | ||
353 | __NL80211_ATTR_AFTER_LAST, | 373 | __NL80211_ATTR_AFTER_LAST, |
@@ -363,6 +383,8 @@ enum nl80211_attrs { | |||
363 | #define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS | 383 | #define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS |
364 | #define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ | 384 | #define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ |
365 | #define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE | 385 | #define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE |
386 | #define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE | ||
387 | #define NL80211_ATTR_IE NL80211_ATTR_IE | ||
366 | 388 | ||
367 | #define NL80211_MAX_SUPP_RATES 32 | 389 | #define NL80211_MAX_SUPP_RATES 32 |
368 | #define NL80211_MAX_SUPP_REG_RULES 32 | 390 | #define NL80211_MAX_SUPP_REG_RULES 32 |
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index df78abc496f1..c7da88fb15b7 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h | |||
@@ -433,6 +433,26 @@ struct ieee80211_txq_params { | |||
433 | u8 aifs; | 433 | u8 aifs; |
434 | }; | 434 | }; |
435 | 435 | ||
436 | /** | ||
437 | * struct mgmt_extra_ie_params - Extra management frame IE parameters | ||
438 | * | ||
439 | * Used to add extra IE(s) into management frames. If the driver cannot add the | ||
440 | * requested data into all management frames of the specified subtype that are | ||
441 | * generated in kernel or firmware/hardware, it must reject the configuration | ||
442 | * call. The IE data buffer is added to the end of the specified management | ||
443 | * frame body after all other IEs. This addition is not applied to frames that | ||
444 | * are injected through a monitor interface. | ||
445 | * | ||
446 | * @subtype: Management frame subtype | ||
447 | * @ies: IE data buffer or %NULL to remove previous data | ||
448 | * @ies_len: Length of @ies in octets | ||
449 | */ | ||
450 | struct mgmt_extra_ie_params { | ||
451 | u8 subtype; | ||
452 | u8 *ies; | ||
453 | int ies_len; | ||
454 | }; | ||
455 | |||
436 | /* from net/wireless.h */ | 456 | /* from net/wireless.h */ |
437 | struct wiphy; | 457 | struct wiphy; |
438 | 458 | ||
@@ -501,6 +521,8 @@ struct ieee80211_channel; | |||
501 | * @set_txq_params: Set TX queue parameters | 521 | * @set_txq_params: Set TX queue parameters |
502 | * | 522 | * |
503 | * @set_channel: Set channel | 523 | * @set_channel: Set channel |
524 | * | ||
525 | * @set_mgmt_extra_ie: Set extra IE data for management frames | ||
504 | */ | 526 | */ |
505 | struct cfg80211_ops { | 527 | struct cfg80211_ops { |
506 | int (*add_virtual_intf)(struct wiphy *wiphy, char *name, | 528 | int (*add_virtual_intf)(struct wiphy *wiphy, char *name, |
@@ -571,6 +593,10 @@ struct cfg80211_ops { | |||
571 | int (*set_channel)(struct wiphy *wiphy, | 593 | int (*set_channel)(struct wiphy *wiphy, |
572 | struct ieee80211_channel *chan, | 594 | struct ieee80211_channel *chan, |
573 | enum nl80211_channel_type channel_type); | 595 | enum nl80211_channel_type channel_type); |
596 | |||
597 | int (*set_mgmt_extra_ie)(struct wiphy *wiphy, | ||
598 | struct net_device *dev, | ||
599 | struct mgmt_extra_ie_params *params); | ||
574 | }; | 600 | }; |
575 | 601 | ||
576 | /* temporary wext handlers */ | 602 | /* temporary wext handlers */ |
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 72c106915433..d1ac3ab2c515 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -1175,6 +1175,87 @@ static int ieee80211_set_channel(struct wiphy *wiphy, | |||
1175 | return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); | 1175 | return ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CHANNEL); |
1176 | } | 1176 | } |
1177 | 1177 | ||
1178 | static int set_mgmt_extra_ie_sta(struct ieee80211_if_sta *ifsta, u8 subtype, | ||
1179 | u8 *ies, size_t ies_len) | ||
1180 | { | ||
1181 | switch (subtype) { | ||
1182 | case IEEE80211_STYPE_PROBE_REQ >> 4: | ||
1183 | kfree(ifsta->ie_probereq); | ||
1184 | ifsta->ie_probereq = ies; | ||
1185 | ifsta->ie_probereq_len = ies_len; | ||
1186 | return 0; | ||
1187 | case IEEE80211_STYPE_PROBE_RESP >> 4: | ||
1188 | kfree(ifsta->ie_proberesp); | ||
1189 | ifsta->ie_proberesp = ies; | ||
1190 | ifsta->ie_proberesp_len = ies_len; | ||
1191 | return 0; | ||
1192 | case IEEE80211_STYPE_AUTH >> 4: | ||
1193 | kfree(ifsta->ie_auth); | ||
1194 | ifsta->ie_auth = ies; | ||
1195 | ifsta->ie_auth_len = ies_len; | ||
1196 | return 0; | ||
1197 | case IEEE80211_STYPE_ASSOC_REQ >> 4: | ||
1198 | kfree(ifsta->ie_assocreq); | ||
1199 | ifsta->ie_assocreq = ies; | ||
1200 | ifsta->ie_assocreq_len = ies_len; | ||
1201 | return 0; | ||
1202 | case IEEE80211_STYPE_REASSOC_REQ >> 4: | ||
1203 | kfree(ifsta->ie_reassocreq); | ||
1204 | ifsta->ie_reassocreq = ies; | ||
1205 | ifsta->ie_reassocreq_len = ies_len; | ||
1206 | return 0; | ||
1207 | case IEEE80211_STYPE_DEAUTH >> 4: | ||
1208 | kfree(ifsta->ie_deauth); | ||
1209 | ifsta->ie_deauth = ies; | ||
1210 | ifsta->ie_deauth_len = ies_len; | ||
1211 | return 0; | ||
1212 | case IEEE80211_STYPE_DISASSOC >> 4: | ||
1213 | kfree(ifsta->ie_disassoc); | ||
1214 | ifsta->ie_disassoc = ies; | ||
1215 | ifsta->ie_disassoc_len = ies_len; | ||
1216 | return 0; | ||
1217 | } | ||
1218 | |||
1219 | return -EOPNOTSUPP; | ||
1220 | } | ||
1221 | |||
1222 | static int ieee80211_set_mgmt_extra_ie(struct wiphy *wiphy, | ||
1223 | struct net_device *dev, | ||
1224 | struct mgmt_extra_ie_params *params) | ||
1225 | { | ||
1226 | struct ieee80211_sub_if_data *sdata; | ||
1227 | u8 *ies; | ||
1228 | size_t ies_len; | ||
1229 | int ret = -EOPNOTSUPP; | ||
1230 | |||
1231 | if (params->ies) { | ||
1232 | ies = kmemdup(params->ies, params->ies_len, GFP_KERNEL); | ||
1233 | if (ies == NULL) | ||
1234 | return -ENOMEM; | ||
1235 | ies_len = params->ies_len; | ||
1236 | } else { | ||
1237 | ies = NULL; | ||
1238 | ies_len = 0; | ||
1239 | } | ||
1240 | |||
1241 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
1242 | |||
1243 | switch (sdata->vif.type) { | ||
1244 | case NL80211_IFTYPE_STATION: | ||
1245 | case NL80211_IFTYPE_ADHOC: | ||
1246 | ret = set_mgmt_extra_ie_sta(&sdata->u.sta, params->subtype, | ||
1247 | ies, ies_len); | ||
1248 | break; | ||
1249 | default: | ||
1250 | ret = -EOPNOTSUPP; | ||
1251 | break; | ||
1252 | } | ||
1253 | |||
1254 | if (ret) | ||
1255 | kfree(ies); | ||
1256 | return ret; | ||
1257 | } | ||
1258 | |||
1178 | struct cfg80211_ops mac80211_config_ops = { | 1259 | struct cfg80211_ops mac80211_config_ops = { |
1179 | .add_virtual_intf = ieee80211_add_iface, | 1260 | .add_virtual_intf = ieee80211_add_iface, |
1180 | .del_virtual_intf = ieee80211_del_iface, | 1261 | .del_virtual_intf = ieee80211_del_iface, |
@@ -1204,4 +1285,5 @@ struct cfg80211_ops mac80211_config_ops = { | |||
1204 | .change_bss = ieee80211_change_bss, | 1285 | .change_bss = ieee80211_change_bss, |
1205 | .set_txq_params = ieee80211_set_txq_params, | 1286 | .set_txq_params = ieee80211_set_txq_params, |
1206 | .set_channel = ieee80211_set_channel, | 1287 | .set_channel = ieee80211_set_channel, |
1288 | .set_mgmt_extra_ie = ieee80211_set_mgmt_extra_ie, | ||
1207 | }; | 1289 | }; |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index c9ffadb55d36..5eafd3affe27 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -331,6 +331,22 @@ struct ieee80211_if_sta { | |||
331 | u32 supp_rates_bits[IEEE80211_NUM_BANDS]; | 331 | u32 supp_rates_bits[IEEE80211_NUM_BANDS]; |
332 | 332 | ||
333 | int wmm_last_param_set; | 333 | int wmm_last_param_set; |
334 | |||
335 | /* Extra IE data for management frames */ | ||
336 | u8 *ie_probereq; | ||
337 | size_t ie_probereq_len; | ||
338 | u8 *ie_proberesp; | ||
339 | size_t ie_proberesp_len; | ||
340 | u8 *ie_auth; | ||
341 | size_t ie_auth_len; | ||
342 | u8 *ie_assocreq; | ||
343 | size_t ie_assocreq_len; | ||
344 | u8 *ie_reassocreq; | ||
345 | size_t ie_reassocreq_len; | ||
346 | u8 *ie_deauth; | ||
347 | size_t ie_deauth_len; | ||
348 | u8 *ie_disassoc; | ||
349 | size_t ie_disassoc_len; | ||
334 | }; | 350 | }; |
335 | 351 | ||
336 | struct ieee80211_if_mesh { | 352 | struct ieee80211_if_mesh { |
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 5d5a029228be..8dc2c2188d92 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -632,6 +632,13 @@ static void ieee80211_teardown_sdata(struct net_device *dev) | |||
632 | kfree(sdata->u.sta.assocreq_ies); | 632 | kfree(sdata->u.sta.assocreq_ies); |
633 | kfree(sdata->u.sta.assocresp_ies); | 633 | kfree(sdata->u.sta.assocresp_ies); |
634 | kfree_skb(sdata->u.sta.probe_resp); | 634 | kfree_skb(sdata->u.sta.probe_resp); |
635 | kfree(sdata->u.sta.ie_probereq); | ||
636 | kfree(sdata->u.sta.ie_proberesp); | ||
637 | kfree(sdata->u.sta.ie_auth); | ||
638 | kfree(sdata->u.sta.ie_assocreq); | ||
639 | kfree(sdata->u.sta.ie_reassocreq); | ||
640 | kfree(sdata->u.sta.ie_deauth); | ||
641 | kfree(sdata->u.sta.ie_disassoc); | ||
635 | break; | 642 | break; |
636 | case NL80211_IFTYPE_WDS: | 643 | case NL80211_IFTYPE_WDS: |
637 | case NL80211_IFTYPE_AP_VLAN: | 644 | case NL80211_IFTYPE_AP_VLAN: |
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index f0d42498c257..43da6227b37c 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c | |||
@@ -131,6 +131,12 @@ u64 ieee80211_sta_get_rates(struct ieee80211_local *local, | |||
131 | 131 | ||
132 | /* frame sending functions */ | 132 | /* frame sending functions */ |
133 | 133 | ||
134 | static void add_extra_ies(struct sk_buff *skb, u8 *ies, size_t ies_len) | ||
135 | { | ||
136 | if (ies) | ||
137 | memcpy(skb_put(skb, ies_len), ies, ies_len); | ||
138 | } | ||
139 | |||
134 | /* also used by scanning code */ | 140 | /* also used by scanning code */ |
135 | void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, | 141 | void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, |
136 | u8 *ssid, size_t ssid_len) | 142 | u8 *ssid, size_t ssid_len) |
@@ -142,7 +148,8 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, | |||
142 | u8 *pos, *supp_rates, *esupp_rates = NULL; | 148 | u8 *pos, *supp_rates, *esupp_rates = NULL; |
143 | int i; | 149 | int i; |
144 | 150 | ||
145 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200); | 151 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + 200 + |
152 | sdata->u.sta.ie_probereq_len); | ||
146 | if (!skb) { | 153 | if (!skb) { |
147 | printk(KERN_DEBUG "%s: failed to allocate buffer for probe " | 154 | printk(KERN_DEBUG "%s: failed to allocate buffer for probe " |
148 | "request\n", sdata->dev->name); | 155 | "request\n", sdata->dev->name); |
@@ -189,6 +196,9 @@ void ieee80211_send_probe_req(struct ieee80211_sub_if_data *sdata, u8 *dst, | |||
189 | *pos = rate->bitrate / 5; | 196 | *pos = rate->bitrate / 5; |
190 | } | 197 | } |
191 | 198 | ||
199 | add_extra_ies(skb, sdata->u.sta.ie_probereq, | ||
200 | sdata->u.sta.ie_probereq_len); | ||
201 | |||
192 | ieee80211_tx_skb(sdata, skb, 0); | 202 | ieee80211_tx_skb(sdata, skb, 0); |
193 | } | 203 | } |
194 | 204 | ||
@@ -202,7 +212,8 @@ static void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, | |||
202 | struct ieee80211_mgmt *mgmt; | 212 | struct ieee80211_mgmt *mgmt; |
203 | 213 | ||
204 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + | 214 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + |
205 | sizeof(*mgmt) + 6 + extra_len); | 215 | sizeof(*mgmt) + 6 + extra_len + |
216 | sdata->u.sta.ie_auth_len); | ||
206 | if (!skb) { | 217 | if (!skb) { |
207 | printk(KERN_DEBUG "%s: failed to allocate buffer for auth " | 218 | printk(KERN_DEBUG "%s: failed to allocate buffer for auth " |
208 | "frame\n", sdata->dev->name); | 219 | "frame\n", sdata->dev->name); |
@@ -225,6 +236,7 @@ static void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, | |||
225 | mgmt->u.auth.status_code = cpu_to_le16(0); | 236 | mgmt->u.auth.status_code = cpu_to_le16(0); |
226 | if (extra) | 237 | if (extra) |
227 | memcpy(skb_put(skb, extra_len), extra, extra_len); | 238 | memcpy(skb_put(skb, extra_len), extra, extra_len); |
239 | add_extra_ies(skb, sdata->u.sta.ie_auth, sdata->u.sta.ie_auth_len); | ||
228 | 240 | ||
229 | ieee80211_tx_skb(sdata, skb, encrypt); | 241 | ieee80211_tx_skb(sdata, skb, encrypt); |
230 | } | 242 | } |
@@ -235,17 +247,26 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, | |||
235 | struct ieee80211_local *local = sdata->local; | 247 | struct ieee80211_local *local = sdata->local; |
236 | struct sk_buff *skb; | 248 | struct sk_buff *skb; |
237 | struct ieee80211_mgmt *mgmt; | 249 | struct ieee80211_mgmt *mgmt; |
238 | u8 *pos, *ies, *ht_ie; | 250 | u8 *pos, *ies, *ht_ie, *e_ies; |
239 | int i, len, count, rates_len, supp_rates_len; | 251 | int i, len, count, rates_len, supp_rates_len; |
240 | u16 capab; | 252 | u16 capab; |
241 | struct ieee80211_bss *bss; | 253 | struct ieee80211_bss *bss; |
242 | int wmm = 0; | 254 | int wmm = 0; |
243 | struct ieee80211_supported_band *sband; | 255 | struct ieee80211_supported_band *sband; |
244 | u64 rates = 0; | 256 | u64 rates = 0; |
257 | size_t e_ies_len; | ||
258 | |||
259 | if (ifsta->flags & IEEE80211_STA_PREV_BSSID_SET) { | ||
260 | e_ies = sdata->u.sta.ie_reassocreq; | ||
261 | e_ies_len = sdata->u.sta.ie_reassocreq_len; | ||
262 | } else { | ||
263 | e_ies = sdata->u.sta.ie_assocreq; | ||
264 | e_ies_len = sdata->u.sta.ie_assocreq_len; | ||
265 | } | ||
245 | 266 | ||
246 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + | 267 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + |
247 | sizeof(*mgmt) + 200 + ifsta->extra_ie_len + | 268 | sizeof(*mgmt) + 200 + ifsta->extra_ie_len + |
248 | ifsta->ssid_len); | 269 | ifsta->ssid_len + e_ies_len); |
249 | if (!skb) { | 270 | if (!skb) { |
250 | printk(KERN_DEBUG "%s: failed to allocate buffer for assoc " | 271 | printk(KERN_DEBUG "%s: failed to allocate buffer for assoc " |
251 | "frame\n", sdata->dev->name); | 272 | "frame\n", sdata->dev->name); |
@@ -436,6 +457,8 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata, | |||
436 | memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); | 457 | memcpy(pos, &sband->ht_cap.mcs, sizeof(sband->ht_cap.mcs)); |
437 | } | 458 | } |
438 | 459 | ||
460 | add_extra_ies(skb, e_ies, e_ies_len); | ||
461 | |||
439 | kfree(ifsta->assocreq_ies); | 462 | kfree(ifsta->assocreq_ies); |
440 | ifsta->assocreq_ies_len = (skb->data + skb->len) - ies; | 463 | ifsta->assocreq_ies_len = (skb->data + skb->len) - ies; |
441 | ifsta->assocreq_ies = kmalloc(ifsta->assocreq_ies_len, GFP_KERNEL); | 464 | ifsta->assocreq_ies = kmalloc(ifsta->assocreq_ies_len, GFP_KERNEL); |
@@ -453,8 +476,19 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, | |||
453 | struct ieee80211_if_sta *ifsta = &sdata->u.sta; | 476 | struct ieee80211_if_sta *ifsta = &sdata->u.sta; |
454 | struct sk_buff *skb; | 477 | struct sk_buff *skb; |
455 | struct ieee80211_mgmt *mgmt; | 478 | struct ieee80211_mgmt *mgmt; |
479 | u8 *ies; | ||
480 | size_t ies_len; | ||
456 | 481 | ||
457 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt)); | 482 | if (stype == IEEE80211_STYPE_DEAUTH) { |
483 | ies = sdata->u.sta.ie_deauth; | ||
484 | ies_len = sdata->u.sta.ie_deauth_len; | ||
485 | } else { | ||
486 | ies = sdata->u.sta.ie_disassoc; | ||
487 | ies_len = sdata->u.sta.ie_disassoc_len; | ||
488 | } | ||
489 | |||
490 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + sizeof(*mgmt) + | ||
491 | ies_len); | ||
458 | if (!skb) { | 492 | if (!skb) { |
459 | printk(KERN_DEBUG "%s: failed to allocate buffer for " | 493 | printk(KERN_DEBUG "%s: failed to allocate buffer for " |
460 | "deauth/disassoc frame\n", sdata->dev->name); | 494 | "deauth/disassoc frame\n", sdata->dev->name); |
@@ -472,6 +506,8 @@ static void ieee80211_send_deauth_disassoc(struct ieee80211_sub_if_data *sdata, | |||
472 | /* u.deauth.reason_code == u.disassoc.reason_code */ | 506 | /* u.deauth.reason_code == u.disassoc.reason_code */ |
473 | mgmt->u.deauth.reason_code = cpu_to_le16(reason); | 507 | mgmt->u.deauth.reason_code = cpu_to_le16(reason); |
474 | 508 | ||
509 | add_extra_ies(skb, ies, ies_len); | ||
510 | |||
475 | ieee80211_tx_skb(sdata, skb, ifsta->flags & IEEE80211_STA_MFP_ENABLED); | 511 | ieee80211_tx_skb(sdata, skb, ifsta->flags & IEEE80211_STA_MFP_ENABLED); |
476 | } | 512 | } |
477 | 513 | ||
@@ -1473,7 +1509,8 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, | |||
1473 | struct ieee80211_supported_band *sband; | 1509 | struct ieee80211_supported_band *sband; |
1474 | union iwreq_data wrqu; | 1510 | union iwreq_data wrqu; |
1475 | 1511 | ||
1476 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400); | 1512 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 + |
1513 | sdata->u.sta.ie_proberesp_len); | ||
1477 | if (!skb) { | 1514 | if (!skb) { |
1478 | printk(KERN_DEBUG "%s: failed to allocate buffer for probe " | 1515 | printk(KERN_DEBUG "%s: failed to allocate buffer for probe " |
1479 | "response\n", sdata->dev->name); | 1516 | "response\n", sdata->dev->name); |
@@ -1556,6 +1593,9 @@ static int ieee80211_sta_join_ibss(struct ieee80211_sub_if_data *sdata, | |||
1556 | memcpy(pos, &bss->supp_rates[8], rates); | 1593 | memcpy(pos, &bss->supp_rates[8], rates); |
1557 | } | 1594 | } |
1558 | 1595 | ||
1596 | add_extra_ies(skb, sdata->u.sta.ie_proberesp, | ||
1597 | sdata->u.sta.ie_proberesp_len); | ||
1598 | |||
1559 | ifsta->probe_resp = skb; | 1599 | ifsta->probe_resp = skb; |
1560 | 1600 | ||
1561 | ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON); | 1601 | ieee80211_if_config(sdata, IEEE80211_IFCC_BEACON); |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 123d3b160fad..09a5d0f1d6dc 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -105,6 +105,10 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
105 | 105 | ||
106 | [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, | 106 | [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, |
107 | .len = NL80211_HT_CAPABILITY_LEN }, | 107 | .len = NL80211_HT_CAPABILITY_LEN }, |
108 | |||
109 | [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 }, | ||
110 | [NL80211_ATTR_IE] = { .type = NLA_BINARY, | ||
111 | .len = IEEE80211_MAX_DATA_LEN }, | ||
108 | }; | 112 | }; |
109 | 113 | ||
110 | /* message building helper */ | 114 | /* message building helper */ |
@@ -2149,6 +2153,43 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
2149 | return -EINVAL; | 2153 | return -EINVAL; |
2150 | } | 2154 | } |
2151 | 2155 | ||
2156 | static int nl80211_set_mgmt_extra_ie(struct sk_buff *skb, | ||
2157 | struct genl_info *info) | ||
2158 | { | ||
2159 | struct cfg80211_registered_device *drv; | ||
2160 | int err; | ||
2161 | struct net_device *dev; | ||
2162 | struct mgmt_extra_ie_params params; | ||
2163 | |||
2164 | memset(¶ms, 0, sizeof(params)); | ||
2165 | |||
2166 | if (!info->attrs[NL80211_ATTR_MGMT_SUBTYPE]) | ||
2167 | return -EINVAL; | ||
2168 | params.subtype = nla_get_u8(info->attrs[NL80211_ATTR_MGMT_SUBTYPE]); | ||
2169 | if (params.subtype > 15) | ||
2170 | return -EINVAL; /* FC Subtype field is 4 bits (0..15) */ | ||
2171 | |||
2172 | if (info->attrs[NL80211_ATTR_IE]) { | ||
2173 | params.ies = nla_data(info->attrs[NL80211_ATTR_IE]); | ||
2174 | params.ies_len = nla_len(info->attrs[NL80211_ATTR_IE]); | ||
2175 | } | ||
2176 | |||
2177 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | ||
2178 | if (err) | ||
2179 | return err; | ||
2180 | |||
2181 | if (drv->ops->set_mgmt_extra_ie) { | ||
2182 | rtnl_lock(); | ||
2183 | err = drv->ops->set_mgmt_extra_ie(&drv->wiphy, dev, ¶ms); | ||
2184 | rtnl_unlock(); | ||
2185 | } else | ||
2186 | err = -EOPNOTSUPP; | ||
2187 | |||
2188 | cfg80211_put_dev(drv); | ||
2189 | dev_put(dev); | ||
2190 | return err; | ||
2191 | } | ||
2192 | |||
2152 | static struct genl_ops nl80211_ops[] = { | 2193 | static struct genl_ops nl80211_ops[] = { |
2153 | { | 2194 | { |
2154 | .cmd = NL80211_CMD_GET_WIPHY, | 2195 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -2310,6 +2351,12 @@ static struct genl_ops nl80211_ops[] = { | |||
2310 | .policy = nl80211_policy, | 2351 | .policy = nl80211_policy, |
2311 | .flags = GENL_ADMIN_PERM, | 2352 | .flags = GENL_ADMIN_PERM, |
2312 | }, | 2353 | }, |
2354 | { | ||
2355 | .cmd = NL80211_CMD_SET_MGMT_EXTRA_IE, | ||
2356 | .doit = nl80211_set_mgmt_extra_ie, | ||
2357 | .policy = nl80211_policy, | ||
2358 | .flags = GENL_ADMIN_PERM, | ||
2359 | }, | ||
2313 | }; | 2360 | }; |
2314 | 2361 | ||
2315 | /* multicast groups */ | 2362 | /* multicast groups */ |