diff options
author | Arik Nemtsov <arik@wizery.com> | 2014-11-09 11:50:20 -0500 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2014-11-19 12:45:26 -0500 |
commit | 8a4d32f30d11d6d8cc29594c7a36b9be6b0edbb5 (patch) | |
tree | 54d51263b8b4f0e05cc17071f7117e557400965d /net/mac80211 | |
parent | a7a6bdd0670feb8bfc26d41cda32b6064dbca50e (diff) |
mac80211: add TDLS channel-switch Rx flow
When receiving a TDLS channel switch request or response, parse the frame
and call a new tdls_recv_channel_switch op in the low level driver with
the parsed data.
Signed-off-by: Arik Nemtsov <arikx.nemtsov@intel.com>
Signed-off-by: Arik Nemtsov <arik@wizery.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211')
-rw-r--r-- | net/mac80211/driver-ops.h | 12 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 3 | ||||
-rw-r--r-- | net/mac80211/iface.c | 2 | ||||
-rw-r--r-- | net/mac80211/main.c | 3 | ||||
-rw-r--r-- | net/mac80211/rx.c | 21 | ||||
-rw-r--r-- | net/mac80211/tdls.c | 328 | ||||
-rw-r--r-- | net/mac80211/trace.h | 45 |
7 files changed, 413 insertions, 1 deletions
diff --git a/net/mac80211/driver-ops.h b/net/mac80211/driver-ops.h index ec4ae42ac15f..ba0d2cb5df12 100644 --- a/net/mac80211/driver-ops.h +++ b/net/mac80211/driver-ops.h | |||
@@ -1337,4 +1337,16 @@ drv_tdls_cancel_channel_switch(struct ieee80211_local *local, | |||
1337 | trace_drv_return_void(local); | 1337 | trace_drv_return_void(local); |
1338 | } | 1338 | } |
1339 | 1339 | ||
1340 | static inline void | ||
1341 | drv_tdls_recv_channel_switch(struct ieee80211_local *local, | ||
1342 | struct ieee80211_sub_if_data *sdata, | ||
1343 | struct ieee80211_tdls_ch_sw_params *params) | ||
1344 | { | ||
1345 | trace_drv_tdls_recv_channel_switch(local, sdata, params); | ||
1346 | if (local->ops->tdls_recv_channel_switch) | ||
1347 | local->ops->tdls_recv_channel_switch(&local->hw, &sdata->vif, | ||
1348 | params); | ||
1349 | trace_drv_return_void(local); | ||
1350 | } | ||
1351 | |||
1340 | #endif /* __MAC80211_DRIVER_OPS */ | 1352 | #endif /* __MAC80211_DRIVER_OPS */ |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 2c7abc077b6b..5de2e5f3a57e 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -993,6 +993,7 @@ enum sdata_queue_type { | |||
993 | IEEE80211_SDATA_QUEUE_AGG_STOP = 2, | 993 | IEEE80211_SDATA_QUEUE_AGG_STOP = 2, |
994 | IEEE80211_SDATA_QUEUE_RX_AGG_START = 3, | 994 | IEEE80211_SDATA_QUEUE_RX_AGG_START = 3, |
995 | IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4, | 995 | IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4, |
996 | IEEE80211_SDATA_QUEUE_TDLS_CHSW = 5, | ||
996 | }; | 997 | }; |
997 | 998 | ||
998 | enum { | 999 | enum { |
@@ -2013,6 +2014,8 @@ int ieee80211_tdls_channel_switch(struct wiphy *wiphy, struct net_device *dev, | |||
2013 | void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy, | 2014 | void ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy, |
2014 | struct net_device *dev, | 2015 | struct net_device *dev, |
2015 | const u8 *addr); | 2016 | const u8 *addr); |
2017 | void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata, | ||
2018 | struct sk_buff *skb); | ||
2016 | 2019 | ||
2017 | extern const struct ethtool_ops ieee80211_ethtool_ops; | 2020 | extern const struct ethtool_ops ieee80211_ethtool_ops; |
2018 | 2021 | ||
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 6b631c049eba..82473d909bb6 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c | |||
@@ -1202,6 +1202,8 @@ static void ieee80211_iface_work(struct work_struct *work) | |||
1202 | WLAN_BACK_RECIPIENT, 0, | 1202 | WLAN_BACK_RECIPIENT, 0, |
1203 | false); | 1203 | false); |
1204 | mutex_unlock(&local->sta_mtx); | 1204 | mutex_unlock(&local->sta_mtx); |
1205 | } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_TDLS_CHSW) { | ||
1206 | ieee80211_process_tdls_channel_switch(sdata, skb); | ||
1205 | } else if (ieee80211_is_action(mgmt->frame_control) && | 1207 | } else if (ieee80211_is_action(mgmt->frame_control) && |
1206 | mgmt->u.action.category == WLAN_CATEGORY_BACK) { | 1208 | mgmt->u.action.category == WLAN_CATEGORY_BACK) { |
1207 | int len = skb->len; | 1209 | int len = skb->len; |
diff --git a/net/mac80211/main.c b/net/mac80211/main.c index 774ccb2d9a76..6ab99da38db9 100644 --- a/net/mac80211/main.c +++ b/net/mac80211/main.c | |||
@@ -766,7 +766,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) | |||
766 | 766 | ||
767 | if ((hw->wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) && | 767 | if ((hw->wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) && |
768 | (!local->ops->tdls_channel_switch || | 768 | (!local->ops->tdls_channel_switch || |
769 | !local->ops->tdls_cancel_channel_switch)) | 769 | !local->ops->tdls_cancel_channel_switch || |
770 | !local->ops->tdls_recv_channel_switch)) | ||
770 | return -EOPNOTSUPP; | 771 | return -EOPNOTSUPP; |
771 | 772 | ||
772 | #ifdef CONFIG_PM | 773 | #ifdef CONFIG_PM |
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 0f4297e2aae2..d9bbb73d4436 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c | |||
@@ -2333,6 +2333,27 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx) | |||
2333 | if (!ieee80211_frame_allowed(rx, fc)) | 2333 | if (!ieee80211_frame_allowed(rx, fc)) |
2334 | return RX_DROP_MONITOR; | 2334 | return RX_DROP_MONITOR; |
2335 | 2335 | ||
2336 | /* directly handle TDLS channel switch requests/responses */ | ||
2337 | if (unlikely(((struct ethhdr *)rx->skb->data)->h_proto == | ||
2338 | cpu_to_be16(ETH_P_TDLS))) { | ||
2339 | struct ieee80211_tdls_data *tf = (void *)rx->skb->data; | ||
2340 | |||
2341 | if (pskb_may_pull(rx->skb, | ||
2342 | offsetof(struct ieee80211_tdls_data, u)) && | ||
2343 | tf->payload_type == WLAN_TDLS_SNAP_RFTYPE && | ||
2344 | tf->category == WLAN_CATEGORY_TDLS && | ||
2345 | (tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST || | ||
2346 | tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) { | ||
2347 | rx->skb->pkt_type = IEEE80211_SDATA_QUEUE_TDLS_CHSW; | ||
2348 | skb_queue_tail(&sdata->skb_queue, rx->skb); | ||
2349 | ieee80211_queue_work(&rx->local->hw, &sdata->work); | ||
2350 | if (rx->sta) | ||
2351 | rx->sta->rx_packets++; | ||
2352 | |||
2353 | return RX_QUEUED; | ||
2354 | } | ||
2355 | } | ||
2356 | |||
2336 | if (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && | 2357 | if (rx->sdata->vif.type == NL80211_IFTYPE_AP_VLAN && |
2337 | unlikely(port_control) && sdata->bss) { | 2358 | unlikely(port_control) && sdata->bss) { |
2338 | sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, | 2359 | sdata = container_of(sdata->bss, struct ieee80211_sub_if_data, |
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 358f9a4512ad..55ddd77b865d 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c | |||
@@ -491,6 +491,20 @@ ieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_sub_if_data *sdata, | |||
491 | } | 491 | } |
492 | } | 492 | } |
493 | 493 | ||
494 | static void | ||
495 | ieee80211_tdls_add_chan_switch_resp_ies(struct ieee80211_sub_if_data *sdata, | ||
496 | struct sk_buff *skb, const u8 *peer, | ||
497 | u16 status_code, bool initiator, | ||
498 | const u8 *extra_ies, | ||
499 | size_t extra_ies_len) | ||
500 | { | ||
501 | if (status_code == 0) | ||
502 | ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); | ||
503 | |||
504 | if (extra_ies_len) | ||
505 | memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); | ||
506 | } | ||
507 | |||
494 | static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata, | 508 | static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata, |
495 | struct sk_buff *skb, const u8 *peer, | 509 | struct sk_buff *skb, const u8 *peer, |
496 | u8 action_code, u16 status_code, | 510 | u8 action_code, u16 status_code, |
@@ -529,6 +543,12 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata, | |||
529 | extra_ies_len, | 543 | extra_ies_len, |
530 | oper_class, chandef); | 544 | oper_class, chandef); |
531 | break; | 545 | break; |
546 | case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: | ||
547 | ieee80211_tdls_add_chan_switch_resp_ies(sdata, skb, peer, | ||
548 | status_code, | ||
549 | initiator, extra_ies, | ||
550 | extra_ies_len); | ||
551 | break; | ||
532 | } | 552 | } |
533 | 553 | ||
534 | } | 554 | } |
@@ -601,6 +621,13 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, | |||
601 | 621 | ||
602 | skb_put(skb, sizeof(tf->u.chan_switch_req)); | 622 | skb_put(skb, sizeof(tf->u.chan_switch_req)); |
603 | break; | 623 | break; |
624 | case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: | ||
625 | tf->category = WLAN_CATEGORY_TDLS; | ||
626 | tf->action_code = WLAN_TDLS_CHANNEL_SWITCH_RESPONSE; | ||
627 | |||
628 | skb_put(skb, sizeof(tf->u.chan_switch_resp)); | ||
629 | tf->u.chan_switch_resp.status_code = cpu_to_le16(status_code); | ||
630 | break; | ||
604 | default: | 631 | default: |
605 | return -EINVAL; | 632 | return -EINVAL; |
606 | } | 633 | } |
@@ -681,6 +708,7 @@ ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata, | |||
681 | case WLAN_TDLS_TEARDOWN: | 708 | case WLAN_TDLS_TEARDOWN: |
682 | case WLAN_TDLS_DISCOVERY_REQUEST: | 709 | case WLAN_TDLS_DISCOVERY_REQUEST: |
683 | case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: | 710 | case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: |
711 | case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: | ||
684 | ret = ieee80211_prep_tdls_encap_data(local->hw.wiphy, | 712 | ret = ieee80211_prep_tdls_encap_data(local->hw.wiphy, |
685 | sdata->dev, peer, | 713 | sdata->dev, peer, |
686 | action_code, dialog_token, | 714 | action_code, dialog_token, |
@@ -755,6 +783,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, | |||
755 | break; | 783 | break; |
756 | case WLAN_TDLS_TEARDOWN: | 784 | case WLAN_TDLS_TEARDOWN: |
757 | case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: | 785 | case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: |
786 | case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: | ||
758 | /* any value is ok */ | 787 | /* any value is ok */ |
759 | break; | 788 | break; |
760 | default: | 789 | default: |
@@ -1280,3 +1309,302 @@ ieee80211_tdls_cancel_channel_switch(struct wiphy *wiphy, | |||
1280 | out: | 1309 | out: |
1281 | mutex_unlock(&local->sta_mtx); | 1310 | mutex_unlock(&local->sta_mtx); |
1282 | } | 1311 | } |
1312 | |||
1313 | static struct sk_buff * | ||
1314 | ieee80211_tdls_ch_sw_resp_tmpl_get(struct sta_info *sta, | ||
1315 | u32 *ch_sw_tm_ie_offset) | ||
1316 | { | ||
1317 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
1318 | struct sk_buff *skb; | ||
1319 | u8 extra_ies[2 + sizeof(struct ieee80211_ch_switch_timing)]; | ||
1320 | |||
1321 | /* initial timing are always zero in the template */ | ||
1322 | iee80211_tdls_add_ch_switch_timing(extra_ies, 0, 0); | ||
1323 | |||
1324 | skb = ieee80211_tdls_build_mgmt_packet_data(sdata, sta->sta.addr, | ||
1325 | WLAN_TDLS_CHANNEL_SWITCH_RESPONSE, | ||
1326 | 0, 0, !sta->sta.tdls_initiator, | ||
1327 | extra_ies, sizeof(extra_ies), 0, NULL); | ||
1328 | if (!skb) | ||
1329 | return NULL; | ||
1330 | |||
1331 | skb = ieee80211_build_data_template(sdata, skb, 0); | ||
1332 | if (IS_ERR(skb)) { | ||
1333 | tdls_dbg(sdata, | ||
1334 | "Failed building TDLS channel switch resp frame\n"); | ||
1335 | return NULL; | ||
1336 | } | ||
1337 | |||
1338 | if (ch_sw_tm_ie_offset) { | ||
1339 | const u8 *tm_ie = ieee80211_tdls_find_sw_timing_ie(skb); | ||
1340 | |||
1341 | if (!tm_ie) { | ||
1342 | tdls_dbg(sdata, | ||
1343 | "No switch timing IE in TDLS switch resp\n"); | ||
1344 | dev_kfree_skb_any(skb); | ||
1345 | return NULL; | ||
1346 | } | ||
1347 | |||
1348 | *ch_sw_tm_ie_offset = tm_ie - skb->data; | ||
1349 | } | ||
1350 | |||
1351 | tdls_dbg(sdata, "TDLS get channel switch response template for %pM\n", | ||
1352 | sta->sta.addr); | ||
1353 | return skb; | ||
1354 | } | ||
1355 | |||
1356 | static int | ||
1357 | ieee80211_process_tdls_channel_switch_resp(struct ieee80211_sub_if_data *sdata, | ||
1358 | struct sk_buff *skb) | ||
1359 | { | ||
1360 | struct ieee80211_local *local = sdata->local; | ||
1361 | struct ieee802_11_elems elems; | ||
1362 | struct sta_info *sta; | ||
1363 | struct ieee80211_tdls_data *tf = (void *)skb->data; | ||
1364 | bool local_initiator; | ||
1365 | struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); | ||
1366 | int baselen = offsetof(typeof(*tf), u.chan_switch_resp.variable); | ||
1367 | struct ieee80211_tdls_ch_sw_params params = {}; | ||
1368 | int ret; | ||
1369 | |||
1370 | params.action_code = WLAN_TDLS_CHANNEL_SWITCH_RESPONSE; | ||
1371 | params.timestamp = rx_status->device_timestamp; | ||
1372 | |||
1373 | if (skb->len < baselen) { | ||
1374 | tdls_dbg(sdata, "TDLS channel switch resp too short: %d\n", | ||
1375 | skb->len); | ||
1376 | return -EINVAL; | ||
1377 | } | ||
1378 | |||
1379 | mutex_lock(&local->sta_mtx); | ||
1380 | sta = sta_info_get(sdata, tf->sa); | ||
1381 | if (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) { | ||
1382 | tdls_dbg(sdata, "TDLS chan switch from non-peer sta %pM\n", | ||
1383 | tf->sa); | ||
1384 | ret = -EINVAL; | ||
1385 | goto out; | ||
1386 | } | ||
1387 | |||
1388 | params.sta = &sta->sta; | ||
1389 | params.status = le16_to_cpu(tf->u.chan_switch_resp.status_code); | ||
1390 | if (params.status != 0) { | ||
1391 | ret = 0; | ||
1392 | goto call_drv; | ||
1393 | } | ||
1394 | |||
1395 | ieee802_11_parse_elems(tf->u.chan_switch_resp.variable, | ||
1396 | skb->len - baselen, false, &elems); | ||
1397 | if (elems.parse_error) { | ||
1398 | tdls_dbg(sdata, "Invalid IEs in TDLS channel switch resp\n"); | ||
1399 | ret = -EINVAL; | ||
1400 | goto out; | ||
1401 | } | ||
1402 | |||
1403 | if (!elems.ch_sw_timing || !elems.lnk_id) { | ||
1404 | tdls_dbg(sdata, "TDLS channel switch resp - missing IEs\n"); | ||
1405 | ret = -EINVAL; | ||
1406 | goto out; | ||
1407 | } | ||
1408 | |||
1409 | /* validate the initiator is set correctly */ | ||
1410 | local_initiator = | ||
1411 | !memcmp(elems.lnk_id->init_sta, sdata->vif.addr, ETH_ALEN); | ||
1412 | if (local_initiator == sta->sta.tdls_initiator) { | ||
1413 | tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n"); | ||
1414 | ret = -EINVAL; | ||
1415 | goto out; | ||
1416 | } | ||
1417 | |||
1418 | params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time); | ||
1419 | params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout); | ||
1420 | |||
1421 | params.tmpl_skb = | ||
1422 | ieee80211_tdls_ch_sw_resp_tmpl_get(sta, ¶ms.ch_sw_tm_ie); | ||
1423 | if (!params.tmpl_skb) { | ||
1424 | ret = -ENOENT; | ||
1425 | goto out; | ||
1426 | } | ||
1427 | |||
1428 | call_drv: | ||
1429 | drv_tdls_recv_channel_switch(sdata->local, sdata, ¶ms); | ||
1430 | |||
1431 | tdls_dbg(sdata, | ||
1432 | "TDLS channel switch response received from %pM status %d\n", | ||
1433 | tf->sa, params.status); | ||
1434 | |||
1435 | out: | ||
1436 | mutex_unlock(&local->sta_mtx); | ||
1437 | dev_kfree_skb_any(params.tmpl_skb); | ||
1438 | return ret; | ||
1439 | } | ||
1440 | |||
1441 | static int | ||
1442 | ieee80211_process_tdls_channel_switch_req(struct ieee80211_sub_if_data *sdata, | ||
1443 | struct sk_buff *skb) | ||
1444 | { | ||
1445 | struct ieee80211_local *local = sdata->local; | ||
1446 | struct ieee802_11_elems elems; | ||
1447 | struct cfg80211_chan_def chandef; | ||
1448 | struct ieee80211_channel *chan; | ||
1449 | enum nl80211_channel_type chan_type; | ||
1450 | int freq; | ||
1451 | u8 target_channel, oper_class; | ||
1452 | bool local_initiator; | ||
1453 | struct sta_info *sta; | ||
1454 | enum ieee80211_band band; | ||
1455 | struct ieee80211_tdls_data *tf = (void *)skb->data; | ||
1456 | struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); | ||
1457 | int baselen = offsetof(typeof(*tf), u.chan_switch_req.variable); | ||
1458 | struct ieee80211_tdls_ch_sw_params params = {}; | ||
1459 | int ret = 0; | ||
1460 | |||
1461 | params.action_code = WLAN_TDLS_CHANNEL_SWITCH_REQUEST; | ||
1462 | params.timestamp = rx_status->device_timestamp; | ||
1463 | |||
1464 | if (skb->len < baselen) { | ||
1465 | tdls_dbg(sdata, "TDLS channel switch req too short: %d\n", | ||
1466 | skb->len); | ||
1467 | return -EINVAL; | ||
1468 | } | ||
1469 | |||
1470 | target_channel = tf->u.chan_switch_req.target_channel; | ||
1471 | oper_class = tf->u.chan_switch_req.oper_class; | ||
1472 | |||
1473 | /* | ||
1474 | * We can't easily infer the channel band. The operating class is | ||
1475 | * ambiguous - there are multiple tables (US/Europe/JP/Global). The | ||
1476 | * solution here is to treat channels with number >14 as 5GHz ones, | ||
1477 | * and specifically check for the (oper_class, channel) combinations | ||
1478 | * where this doesn't hold. These are thankfully unique according to | ||
1479 | * IEEE802.11-2012. | ||
1480 | * We consider only the 2GHz and 5GHz bands and 20MHz+ channels as | ||
1481 | * valid here. | ||
1482 | */ | ||
1483 | if ((oper_class == 112 || oper_class == 2 || oper_class == 3 || | ||
1484 | oper_class == 4 || oper_class == 5 || oper_class == 6) && | ||
1485 | target_channel < 14) | ||
1486 | band = IEEE80211_BAND_5GHZ; | ||
1487 | else | ||
1488 | band = target_channel < 14 ? IEEE80211_BAND_2GHZ : | ||
1489 | IEEE80211_BAND_5GHZ; | ||
1490 | |||
1491 | freq = ieee80211_channel_to_frequency(target_channel, band); | ||
1492 | if (freq == 0) { | ||
1493 | tdls_dbg(sdata, "Invalid channel in TDLS chan switch: %d\n", | ||
1494 | target_channel); | ||
1495 | return -EINVAL; | ||
1496 | } | ||
1497 | |||
1498 | chan = ieee80211_get_channel(sdata->local->hw.wiphy, freq); | ||
1499 | if (!chan) { | ||
1500 | tdls_dbg(sdata, | ||
1501 | "Unsupported channel for TDLS chan switch: %d\n", | ||
1502 | target_channel); | ||
1503 | return -EINVAL; | ||
1504 | } | ||
1505 | |||
1506 | ieee802_11_parse_elems(tf->u.chan_switch_req.variable, | ||
1507 | skb->len - baselen, false, &elems); | ||
1508 | if (elems.parse_error) { | ||
1509 | tdls_dbg(sdata, "Invalid IEs in TDLS channel switch req\n"); | ||
1510 | return -EINVAL; | ||
1511 | } | ||
1512 | |||
1513 | if (!elems.ch_sw_timing || !elems.lnk_id) { | ||
1514 | tdls_dbg(sdata, "TDLS channel switch req - missing IEs\n"); | ||
1515 | return -EINVAL; | ||
1516 | } | ||
1517 | |||
1518 | mutex_lock(&local->sta_mtx); | ||
1519 | sta = sta_info_get(sdata, tf->sa); | ||
1520 | if (!sta || !test_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH)) { | ||
1521 | tdls_dbg(sdata, "TDLS chan switch from non-peer sta %pM\n", | ||
1522 | tf->sa); | ||
1523 | ret = -EINVAL; | ||
1524 | goto out; | ||
1525 | } | ||
1526 | |||
1527 | params.sta = &sta->sta; | ||
1528 | |||
1529 | /* validate the initiator is set correctly */ | ||
1530 | local_initiator = | ||
1531 | !memcmp(elems.lnk_id->init_sta, sdata->vif.addr, ETH_ALEN); | ||
1532 | if (local_initiator == sta->sta.tdls_initiator) { | ||
1533 | tdls_dbg(sdata, "TDLS chan switch invalid lnk-id initiator\n"); | ||
1534 | ret = -EINVAL; | ||
1535 | goto out; | ||
1536 | } | ||
1537 | |||
1538 | if (!sta->sta.ht_cap.ht_supported) { | ||
1539 | chan_type = NL80211_CHAN_NO_HT; | ||
1540 | } else if (!elems.sec_chan_offs) { | ||
1541 | chan_type = NL80211_CHAN_HT20; | ||
1542 | } else { | ||
1543 | switch (elems.sec_chan_offs->sec_chan_offs) { | ||
1544 | case IEEE80211_HT_PARAM_CHA_SEC_ABOVE: | ||
1545 | chan_type = NL80211_CHAN_HT40PLUS; | ||
1546 | break; | ||
1547 | case IEEE80211_HT_PARAM_CHA_SEC_BELOW: | ||
1548 | chan_type = NL80211_CHAN_HT40MINUS; | ||
1549 | break; | ||
1550 | default: | ||
1551 | chan_type = NL80211_CHAN_HT20; | ||
1552 | break; | ||
1553 | } | ||
1554 | } | ||
1555 | |||
1556 | cfg80211_chandef_create(&chandef, chan, chan_type); | ||
1557 | params.chandef = &chandef; | ||
1558 | |||
1559 | params.switch_time = le16_to_cpu(elems.ch_sw_timing->switch_time); | ||
1560 | params.switch_timeout = le16_to_cpu(elems.ch_sw_timing->switch_timeout); | ||
1561 | |||
1562 | params.tmpl_skb = | ||
1563 | ieee80211_tdls_ch_sw_resp_tmpl_get(sta, | ||
1564 | ¶ms.ch_sw_tm_ie); | ||
1565 | if (!params.tmpl_skb) { | ||
1566 | ret = -ENOENT; | ||
1567 | goto out; | ||
1568 | } | ||
1569 | |||
1570 | drv_tdls_recv_channel_switch(sdata->local, sdata, ¶ms); | ||
1571 | |||
1572 | tdls_dbg(sdata, | ||
1573 | "TDLS ch switch request received from %pM ch %d width %d\n", | ||
1574 | tf->sa, params.chandef->chan->center_freq, | ||
1575 | params.chandef->width); | ||
1576 | out: | ||
1577 | mutex_unlock(&local->sta_mtx); | ||
1578 | dev_kfree_skb_any(params.tmpl_skb); | ||
1579 | return ret; | ||
1580 | } | ||
1581 | |||
1582 | void ieee80211_process_tdls_channel_switch(struct ieee80211_sub_if_data *sdata, | ||
1583 | struct sk_buff *skb) | ||
1584 | { | ||
1585 | struct ieee80211_tdls_data *tf = (void *)skb->data; | ||
1586 | struct wiphy *wiphy = sdata->local->hw.wiphy; | ||
1587 | |||
1588 | /* make sure the driver supports it */ | ||
1589 | if (!(wiphy->features & NL80211_FEATURE_TDLS_CHANNEL_SWITCH)) | ||
1590 | return; | ||
1591 | |||
1592 | /* we want to access the entire packet */ | ||
1593 | if (skb_linearize(skb)) | ||
1594 | return; | ||
1595 | /* | ||
1596 | * The packet/size was already validated by mac80211 Rx path, only look | ||
1597 | * at the action type. | ||
1598 | */ | ||
1599 | switch (tf->action_code) { | ||
1600 | case WLAN_TDLS_CHANNEL_SWITCH_REQUEST: | ||
1601 | ieee80211_process_tdls_channel_switch_req(sdata, skb); | ||
1602 | break; | ||
1603 | case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE: | ||
1604 | ieee80211_process_tdls_channel_switch_resp(sdata, skb); | ||
1605 | break; | ||
1606 | default: | ||
1607 | WARN_ON_ONCE(1); | ||
1608 | return; | ||
1609 | } | ||
1610 | } | ||
diff --git a/net/mac80211/trace.h b/net/mac80211/trace.h index c0c0fcace9d8..7f76e2f25744 100644 --- a/net/mac80211/trace.h +++ b/net/mac80211/trace.h | |||
@@ -16,6 +16,7 @@ | |||
16 | 16 | ||
17 | #define STA_ENTRY __array(char, sta_addr, ETH_ALEN) | 17 | #define STA_ENTRY __array(char, sta_addr, ETH_ALEN) |
18 | #define STA_ASSIGN (sta ? memcpy(__entry->sta_addr, sta->addr, ETH_ALEN) : memset(__entry->sta_addr, 0, ETH_ALEN)) | 18 | #define STA_ASSIGN (sta ? memcpy(__entry->sta_addr, sta->addr, ETH_ALEN) : memset(__entry->sta_addr, 0, ETH_ALEN)) |
19 | #define STA_NAMED_ASSIGN(s) memcpy(__entry->sta_addr, (s)->addr, ETH_ALEN) | ||
19 | #define STA_PR_FMT " sta:%pM" | 20 | #define STA_PR_FMT " sta:%pM" |
20 | #define STA_PR_ARG __entry->sta_addr | 21 | #define STA_PR_ARG __entry->sta_addr |
21 | 22 | ||
@@ -2254,6 +2255,50 @@ TRACE_EVENT(drv_tdls_cancel_channel_switch, | |||
2254 | ) | 2255 | ) |
2255 | ); | 2256 | ); |
2256 | 2257 | ||
2258 | TRACE_EVENT(drv_tdls_recv_channel_switch, | ||
2259 | TP_PROTO(struct ieee80211_local *local, | ||
2260 | struct ieee80211_sub_if_data *sdata, | ||
2261 | struct ieee80211_tdls_ch_sw_params *params), | ||
2262 | |||
2263 | TP_ARGS(local, sdata, params), | ||
2264 | |||
2265 | TP_STRUCT__entry( | ||
2266 | LOCAL_ENTRY | ||
2267 | VIF_ENTRY | ||
2268 | __field(u8, action_code) | ||
2269 | STA_ENTRY | ||
2270 | CHANDEF_ENTRY | ||
2271 | __field(u32, status) | ||
2272 | __field(bool, peer_initiator) | ||
2273 | __field(u32, timestamp) | ||
2274 | __field(u16, switch_time) | ||
2275 | __field(u16, switch_timeout) | ||
2276 | ), | ||
2277 | |||
2278 | TP_fast_assign( | ||
2279 | LOCAL_ASSIGN; | ||
2280 | VIF_ASSIGN; | ||
2281 | STA_NAMED_ASSIGN(params->sta); | ||
2282 | CHANDEF_ASSIGN(params->chandef) | ||
2283 | __entry->peer_initiator = params->sta->tdls_initiator; | ||
2284 | __entry->action_code = params->action_code; | ||
2285 | __entry->status = params->status; | ||
2286 | __entry->timestamp = params->timestamp; | ||
2287 | __entry->switch_time = params->switch_time; | ||
2288 | __entry->switch_timeout = params->switch_timeout; | ||
2289 | ), | ||
2290 | |||
2291 | TP_printk( | ||
2292 | LOCAL_PR_FMT VIF_PR_FMT " received tdls channel switch packet" | ||
2293 | " action:%d status:%d time:%d switch time:%d switch" | ||
2294 | " timeout:%d initiator: %d chan:" CHANDEF_PR_FMT STA_PR_FMT, | ||
2295 | LOCAL_PR_ARG, VIF_PR_ARG, __entry->action_code, __entry->status, | ||
2296 | __entry->timestamp, __entry->switch_time, | ||
2297 | __entry->switch_timeout, __entry->peer_initiator, | ||
2298 | CHANDEF_PR_ARG, STA_PR_ARG | ||
2299 | ) | ||
2300 | ); | ||
2301 | |||
2257 | #ifdef CONFIG_MAC80211_MESSAGE_TRACING | 2302 | #ifdef CONFIG_MAC80211_MESSAGE_TRACING |
2258 | #undef TRACE_SYSTEM | 2303 | #undef TRACE_SYSTEM |
2259 | #define TRACE_SYSTEM mac80211_msg | 2304 | #define TRACE_SYSTEM mac80211_msg |