diff options
author | Arik Nemtsov <arik@wizery.com> | 2014-05-01 03:17:28 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2014-05-05 09:56:15 -0400 |
commit | 95224fe83e5e78e042c96f2c43fa9092a3bc10ef (patch) | |
tree | c42b846315dfc54d893655984d6066c58c4bdbdb /net | |
parent | 0c4972ccaa27620fe4281ac5c8c536978a563345 (diff) |
mac80211: move TDLS code to another file
With new additions planned, this code is getting too big for cfg.c.
Signed-off-by: Arik Nemtsov <arikx.nemtsov@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/Makefile | 3 | ||||
-rw-r--r-- | net/mac80211/cfg.c | 314 | ||||
-rw-r--r-- | net/mac80211/ieee80211_i.h | 9 | ||||
-rw-r--r-- | net/mac80211/tdls.c | 325 |
4 files changed, 336 insertions, 315 deletions
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile index 9d7d840aac6d..1e46ffa69167 100644 --- a/net/mac80211/Makefile +++ b/net/mac80211/Makefile | |||
@@ -25,7 +25,8 @@ mac80211-y := \ | |||
25 | wme.o \ | 25 | wme.o \ |
26 | event.o \ | 26 | event.o \ |
27 | chan.o \ | 27 | chan.o \ |
28 | trace.o mlme.o | 28 | trace.o mlme.o \ |
29 | tdls.o | ||
29 | 30 | ||
30 | mac80211-$(CONFIG_MAC80211_LEDS) += led.o | 31 | mac80211-$(CONFIG_MAC80211_LEDS) += led.o |
31 | mac80211-$(CONFIG_MAC80211_DEBUGFS) += \ | 32 | mac80211-$(CONFIG_MAC80211_DEBUGFS) += \ |
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index d8b236633ca3..19a7e6ff45d3 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -3508,320 +3508,6 @@ static int ieee80211_set_rekey_data(struct wiphy *wiphy, | |||
3508 | return 0; | 3508 | return 0; |
3509 | } | 3509 | } |
3510 | 3510 | ||
3511 | static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb) | ||
3512 | { | ||
3513 | u8 *pos = (void *)skb_put(skb, 7); | ||
3514 | |||
3515 | *pos++ = WLAN_EID_EXT_CAPABILITY; | ||
3516 | *pos++ = 5; /* len */ | ||
3517 | *pos++ = 0x0; | ||
3518 | *pos++ = 0x0; | ||
3519 | *pos++ = 0x0; | ||
3520 | *pos++ = 0x0; | ||
3521 | *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED; | ||
3522 | } | ||
3523 | |||
3524 | static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata) | ||
3525 | { | ||
3526 | struct ieee80211_local *local = sdata->local; | ||
3527 | u16 capab; | ||
3528 | |||
3529 | capab = 0; | ||
3530 | if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ) | ||
3531 | return capab; | ||
3532 | |||
3533 | if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) | ||
3534 | capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; | ||
3535 | if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)) | ||
3536 | capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; | ||
3537 | |||
3538 | return capab; | ||
3539 | } | ||
3540 | |||
3541 | static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, | ||
3542 | u8 *peer, u8 *bssid) | ||
3543 | { | ||
3544 | struct ieee80211_tdls_lnkie *lnkid; | ||
3545 | |||
3546 | lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); | ||
3547 | |||
3548 | lnkid->ie_type = WLAN_EID_LINK_ID; | ||
3549 | lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2; | ||
3550 | |||
3551 | memcpy(lnkid->bssid, bssid, ETH_ALEN); | ||
3552 | memcpy(lnkid->init_sta, src_addr, ETH_ALEN); | ||
3553 | memcpy(lnkid->resp_sta, peer, ETH_ALEN); | ||
3554 | } | ||
3555 | |||
3556 | static int | ||
3557 | ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, | ||
3558 | u8 *peer, u8 action_code, u8 dialog_token, | ||
3559 | u16 status_code, struct sk_buff *skb) | ||
3560 | { | ||
3561 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
3562 | enum ieee80211_band band = ieee80211_get_sdata_band(sdata); | ||
3563 | struct ieee80211_tdls_data *tf; | ||
3564 | |||
3565 | tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); | ||
3566 | |||
3567 | memcpy(tf->da, peer, ETH_ALEN); | ||
3568 | memcpy(tf->sa, sdata->vif.addr, ETH_ALEN); | ||
3569 | tf->ether_type = cpu_to_be16(ETH_P_TDLS); | ||
3570 | tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; | ||
3571 | |||
3572 | switch (action_code) { | ||
3573 | case WLAN_TDLS_SETUP_REQUEST: | ||
3574 | tf->category = WLAN_CATEGORY_TDLS; | ||
3575 | tf->action_code = WLAN_TDLS_SETUP_REQUEST; | ||
3576 | |||
3577 | skb_put(skb, sizeof(tf->u.setup_req)); | ||
3578 | tf->u.setup_req.dialog_token = dialog_token; | ||
3579 | tf->u.setup_req.capability = | ||
3580 | cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); | ||
3581 | |||
3582 | ieee80211_add_srates_ie(sdata, skb, false, band); | ||
3583 | ieee80211_add_ext_srates_ie(sdata, skb, false, band); | ||
3584 | ieee80211_tdls_add_ext_capab(skb); | ||
3585 | break; | ||
3586 | case WLAN_TDLS_SETUP_RESPONSE: | ||
3587 | tf->category = WLAN_CATEGORY_TDLS; | ||
3588 | tf->action_code = WLAN_TDLS_SETUP_RESPONSE; | ||
3589 | |||
3590 | skb_put(skb, sizeof(tf->u.setup_resp)); | ||
3591 | tf->u.setup_resp.status_code = cpu_to_le16(status_code); | ||
3592 | tf->u.setup_resp.dialog_token = dialog_token; | ||
3593 | tf->u.setup_resp.capability = | ||
3594 | cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); | ||
3595 | |||
3596 | ieee80211_add_srates_ie(sdata, skb, false, band); | ||
3597 | ieee80211_add_ext_srates_ie(sdata, skb, false, band); | ||
3598 | ieee80211_tdls_add_ext_capab(skb); | ||
3599 | break; | ||
3600 | case WLAN_TDLS_SETUP_CONFIRM: | ||
3601 | tf->category = WLAN_CATEGORY_TDLS; | ||
3602 | tf->action_code = WLAN_TDLS_SETUP_CONFIRM; | ||
3603 | |||
3604 | skb_put(skb, sizeof(tf->u.setup_cfm)); | ||
3605 | tf->u.setup_cfm.status_code = cpu_to_le16(status_code); | ||
3606 | tf->u.setup_cfm.dialog_token = dialog_token; | ||
3607 | break; | ||
3608 | case WLAN_TDLS_TEARDOWN: | ||
3609 | tf->category = WLAN_CATEGORY_TDLS; | ||
3610 | tf->action_code = WLAN_TDLS_TEARDOWN; | ||
3611 | |||
3612 | skb_put(skb, sizeof(tf->u.teardown)); | ||
3613 | tf->u.teardown.reason_code = cpu_to_le16(status_code); | ||
3614 | break; | ||
3615 | case WLAN_TDLS_DISCOVERY_REQUEST: | ||
3616 | tf->category = WLAN_CATEGORY_TDLS; | ||
3617 | tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; | ||
3618 | |||
3619 | skb_put(skb, sizeof(tf->u.discover_req)); | ||
3620 | tf->u.discover_req.dialog_token = dialog_token; | ||
3621 | break; | ||
3622 | default: | ||
3623 | return -EINVAL; | ||
3624 | } | ||
3625 | |||
3626 | return 0; | ||
3627 | } | ||
3628 | |||
3629 | static int | ||
3630 | ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, | ||
3631 | u8 *peer, u8 action_code, u8 dialog_token, | ||
3632 | u16 status_code, struct sk_buff *skb) | ||
3633 | { | ||
3634 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
3635 | enum ieee80211_band band = ieee80211_get_sdata_band(sdata); | ||
3636 | struct ieee80211_mgmt *mgmt; | ||
3637 | |||
3638 | mgmt = (void *)skb_put(skb, 24); | ||
3639 | memset(mgmt, 0, 24); | ||
3640 | memcpy(mgmt->da, peer, ETH_ALEN); | ||
3641 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); | ||
3642 | memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); | ||
3643 | |||
3644 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | ||
3645 | IEEE80211_STYPE_ACTION); | ||
3646 | |||
3647 | switch (action_code) { | ||
3648 | case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: | ||
3649 | skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp)); | ||
3650 | mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; | ||
3651 | mgmt->u.action.u.tdls_discover_resp.action_code = | ||
3652 | WLAN_PUB_ACTION_TDLS_DISCOVER_RES; | ||
3653 | mgmt->u.action.u.tdls_discover_resp.dialog_token = | ||
3654 | dialog_token; | ||
3655 | mgmt->u.action.u.tdls_discover_resp.capability = | ||
3656 | cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); | ||
3657 | |||
3658 | ieee80211_add_srates_ie(sdata, skb, false, band); | ||
3659 | ieee80211_add_ext_srates_ie(sdata, skb, false, band); | ||
3660 | ieee80211_tdls_add_ext_capab(skb); | ||
3661 | break; | ||
3662 | default: | ||
3663 | return -EINVAL; | ||
3664 | } | ||
3665 | |||
3666 | return 0; | ||
3667 | } | ||
3668 | |||
3669 | static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, | ||
3670 | u8 *peer, u8 action_code, u8 dialog_token, | ||
3671 | u16 status_code, u32 peer_capability, | ||
3672 | const u8 *extra_ies, size_t extra_ies_len) | ||
3673 | { | ||
3674 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
3675 | struct ieee80211_local *local = sdata->local; | ||
3676 | struct sk_buff *skb = NULL; | ||
3677 | bool send_direct; | ||
3678 | int ret; | ||
3679 | |||
3680 | if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) | ||
3681 | return -ENOTSUPP; | ||
3682 | |||
3683 | /* make sure we are in managed mode, and associated */ | ||
3684 | if (sdata->vif.type != NL80211_IFTYPE_STATION || | ||
3685 | !sdata->u.mgd.associated) | ||
3686 | return -EINVAL; | ||
3687 | |||
3688 | tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n", | ||
3689 | action_code, peer); | ||
3690 | |||
3691 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + | ||
3692 | max(sizeof(struct ieee80211_mgmt), | ||
3693 | sizeof(struct ieee80211_tdls_data)) + | ||
3694 | 50 + /* supported rates */ | ||
3695 | 7 + /* ext capab */ | ||
3696 | extra_ies_len + | ||
3697 | sizeof(struct ieee80211_tdls_lnkie)); | ||
3698 | if (!skb) | ||
3699 | return -ENOMEM; | ||
3700 | |||
3701 | skb_reserve(skb, local->hw.extra_tx_headroom); | ||
3702 | |||
3703 | switch (action_code) { | ||
3704 | case WLAN_TDLS_SETUP_REQUEST: | ||
3705 | case WLAN_TDLS_SETUP_RESPONSE: | ||
3706 | case WLAN_TDLS_SETUP_CONFIRM: | ||
3707 | case WLAN_TDLS_TEARDOWN: | ||
3708 | case WLAN_TDLS_DISCOVERY_REQUEST: | ||
3709 | ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer, | ||
3710 | action_code, dialog_token, | ||
3711 | status_code, skb); | ||
3712 | send_direct = false; | ||
3713 | break; | ||
3714 | case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: | ||
3715 | ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code, | ||
3716 | dialog_token, status_code, | ||
3717 | skb); | ||
3718 | send_direct = true; | ||
3719 | break; | ||
3720 | default: | ||
3721 | ret = -ENOTSUPP; | ||
3722 | break; | ||
3723 | } | ||
3724 | |||
3725 | if (ret < 0) | ||
3726 | goto fail; | ||
3727 | |||
3728 | if (extra_ies_len) | ||
3729 | memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); | ||
3730 | |||
3731 | /* the TDLS link IE is always added last */ | ||
3732 | switch (action_code) { | ||
3733 | case WLAN_TDLS_SETUP_REQUEST: | ||
3734 | case WLAN_TDLS_SETUP_CONFIRM: | ||
3735 | case WLAN_TDLS_TEARDOWN: | ||
3736 | case WLAN_TDLS_DISCOVERY_REQUEST: | ||
3737 | /* we are the initiator */ | ||
3738 | ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer, | ||
3739 | sdata->u.mgd.bssid); | ||
3740 | break; | ||
3741 | case WLAN_TDLS_SETUP_RESPONSE: | ||
3742 | case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: | ||
3743 | /* we are the responder */ | ||
3744 | ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr, | ||
3745 | sdata->u.mgd.bssid); | ||
3746 | break; | ||
3747 | default: | ||
3748 | ret = -ENOTSUPP; | ||
3749 | goto fail; | ||
3750 | } | ||
3751 | |||
3752 | if (send_direct) { | ||
3753 | ieee80211_tx_skb(sdata, skb); | ||
3754 | return 0; | ||
3755 | } | ||
3756 | |||
3757 | /* | ||
3758 | * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise | ||
3759 | * we should default to AC_VI. | ||
3760 | */ | ||
3761 | switch (action_code) { | ||
3762 | case WLAN_TDLS_SETUP_REQUEST: | ||
3763 | case WLAN_TDLS_SETUP_RESPONSE: | ||
3764 | skb_set_queue_mapping(skb, IEEE80211_AC_BK); | ||
3765 | skb->priority = 2; | ||
3766 | break; | ||
3767 | default: | ||
3768 | skb_set_queue_mapping(skb, IEEE80211_AC_VI); | ||
3769 | skb->priority = 5; | ||
3770 | break; | ||
3771 | } | ||
3772 | |||
3773 | /* disable bottom halves when entering the Tx path */ | ||
3774 | local_bh_disable(); | ||
3775 | ret = ieee80211_subif_start_xmit(skb, dev); | ||
3776 | local_bh_enable(); | ||
3777 | |||
3778 | return ret; | ||
3779 | |||
3780 | fail: | ||
3781 | dev_kfree_skb(skb); | ||
3782 | return ret; | ||
3783 | } | ||
3784 | |||
3785 | static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, | ||
3786 | u8 *peer, enum nl80211_tdls_operation oper) | ||
3787 | { | ||
3788 | struct sta_info *sta; | ||
3789 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
3790 | |||
3791 | if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) | ||
3792 | return -ENOTSUPP; | ||
3793 | |||
3794 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | ||
3795 | return -EINVAL; | ||
3796 | |||
3797 | tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer); | ||
3798 | |||
3799 | switch (oper) { | ||
3800 | case NL80211_TDLS_ENABLE_LINK: | ||
3801 | rcu_read_lock(); | ||
3802 | sta = sta_info_get(sdata, peer); | ||
3803 | if (!sta) { | ||
3804 | rcu_read_unlock(); | ||
3805 | return -ENOLINK; | ||
3806 | } | ||
3807 | |||
3808 | set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); | ||
3809 | rcu_read_unlock(); | ||
3810 | break; | ||
3811 | case NL80211_TDLS_DISABLE_LINK: | ||
3812 | return sta_info_destroy_addr(sdata, peer); | ||
3813 | case NL80211_TDLS_TEARDOWN: | ||
3814 | case NL80211_TDLS_SETUP: | ||
3815 | case NL80211_TDLS_DISCOVERY_REQ: | ||
3816 | /* We don't support in-driver setup/teardown/discovery */ | ||
3817 | return -ENOTSUPP; | ||
3818 | default: | ||
3819 | return -ENOTSUPP; | ||
3820 | } | ||
3821 | |||
3822 | return 0; | ||
3823 | } | ||
3824 | |||
3825 | static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, | 3511 | static int ieee80211_probe_client(struct wiphy *wiphy, struct net_device *dev, |
3826 | const u8 *peer, u64 *cookie) | 3512 | const u8 *peer, u64 *cookie) |
3827 | { | 3513 | { |
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index b455f62d357a..f86d06aaf54b 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h | |||
@@ -1833,6 +1833,15 @@ int ieee80211_check_combinations(struct ieee80211_sub_if_data *sdata, | |||
1833 | u8 radar_detect); | 1833 | u8 radar_detect); |
1834 | int ieee80211_max_num_channels(struct ieee80211_local *local); | 1834 | int ieee80211_max_num_channels(struct ieee80211_local *local); |
1835 | 1835 | ||
1836 | /* TDLS */ | ||
1837 | int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, | ||
1838 | u8 *peer, u8 action_code, u8 dialog_token, | ||
1839 | u16 status_code, u32 peer_capability, | ||
1840 | const u8 *extra_ies, size_t extra_ies_len); | ||
1841 | int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, | ||
1842 | u8 *peer, enum nl80211_tdls_operation oper); | ||
1843 | |||
1844 | |||
1836 | #ifdef CONFIG_MAC80211_NOINLINE | 1845 | #ifdef CONFIG_MAC80211_NOINLINE |
1837 | #define debug_noinline noinline | 1846 | #define debug_noinline noinline |
1838 | #else | 1847 | #else |
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c new file mode 100644 index 000000000000..8e14e2aaea11 --- /dev/null +++ b/net/mac80211/tdls.c | |||
@@ -0,0 +1,325 @@ | |||
1 | /* | ||
2 | * mac80211 TDLS handling code | ||
3 | * | ||
4 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * Copyright 2014, Intel Corporation | ||
6 | * | ||
7 | * This file is GPLv2 as found in COPYING. | ||
8 | */ | ||
9 | |||
10 | #include <linux/ieee80211.h> | ||
11 | #include "ieee80211_i.h" | ||
12 | |||
13 | static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb) | ||
14 | { | ||
15 | u8 *pos = (void *)skb_put(skb, 7); | ||
16 | |||
17 | *pos++ = WLAN_EID_EXT_CAPABILITY; | ||
18 | *pos++ = 5; /* len */ | ||
19 | *pos++ = 0x0; | ||
20 | *pos++ = 0x0; | ||
21 | *pos++ = 0x0; | ||
22 | *pos++ = 0x0; | ||
23 | *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED; | ||
24 | } | ||
25 | |||
26 | static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata) | ||
27 | { | ||
28 | struct ieee80211_local *local = sdata->local; | ||
29 | u16 capab; | ||
30 | |||
31 | capab = 0; | ||
32 | if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ) | ||
33 | return capab; | ||
34 | |||
35 | if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE)) | ||
36 | capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; | ||
37 | if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE)) | ||
38 | capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; | ||
39 | |||
40 | return capab; | ||
41 | } | ||
42 | |||
43 | static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr, | ||
44 | u8 *peer, u8 *bssid) | ||
45 | { | ||
46 | struct ieee80211_tdls_lnkie *lnkid; | ||
47 | |||
48 | lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); | ||
49 | |||
50 | lnkid->ie_type = WLAN_EID_LINK_ID; | ||
51 | lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2; | ||
52 | |||
53 | memcpy(lnkid->bssid, bssid, ETH_ALEN); | ||
54 | memcpy(lnkid->init_sta, src_addr, ETH_ALEN); | ||
55 | memcpy(lnkid->resp_sta, peer, ETH_ALEN); | ||
56 | } | ||
57 | |||
58 | static int | ||
59 | ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, | ||
60 | u8 *peer, u8 action_code, u8 dialog_token, | ||
61 | u16 status_code, struct sk_buff *skb) | ||
62 | { | ||
63 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
64 | enum ieee80211_band band = ieee80211_get_sdata_band(sdata); | ||
65 | struct ieee80211_tdls_data *tf; | ||
66 | |||
67 | tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); | ||
68 | |||
69 | memcpy(tf->da, peer, ETH_ALEN); | ||
70 | memcpy(tf->sa, sdata->vif.addr, ETH_ALEN); | ||
71 | tf->ether_type = cpu_to_be16(ETH_P_TDLS); | ||
72 | tf->payload_type = WLAN_TDLS_SNAP_RFTYPE; | ||
73 | |||
74 | switch (action_code) { | ||
75 | case WLAN_TDLS_SETUP_REQUEST: | ||
76 | tf->category = WLAN_CATEGORY_TDLS; | ||
77 | tf->action_code = WLAN_TDLS_SETUP_REQUEST; | ||
78 | |||
79 | skb_put(skb, sizeof(tf->u.setup_req)); | ||
80 | tf->u.setup_req.dialog_token = dialog_token; | ||
81 | tf->u.setup_req.capability = | ||
82 | cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); | ||
83 | |||
84 | ieee80211_add_srates_ie(sdata, skb, false, band); | ||
85 | ieee80211_add_ext_srates_ie(sdata, skb, false, band); | ||
86 | ieee80211_tdls_add_ext_capab(skb); | ||
87 | break; | ||
88 | case WLAN_TDLS_SETUP_RESPONSE: | ||
89 | tf->category = WLAN_CATEGORY_TDLS; | ||
90 | tf->action_code = WLAN_TDLS_SETUP_RESPONSE; | ||
91 | |||
92 | skb_put(skb, sizeof(tf->u.setup_resp)); | ||
93 | tf->u.setup_resp.status_code = cpu_to_le16(status_code); | ||
94 | tf->u.setup_resp.dialog_token = dialog_token; | ||
95 | tf->u.setup_resp.capability = | ||
96 | cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); | ||
97 | |||
98 | ieee80211_add_srates_ie(sdata, skb, false, band); | ||
99 | ieee80211_add_ext_srates_ie(sdata, skb, false, band); | ||
100 | ieee80211_tdls_add_ext_capab(skb); | ||
101 | break; | ||
102 | case WLAN_TDLS_SETUP_CONFIRM: | ||
103 | tf->category = WLAN_CATEGORY_TDLS; | ||
104 | tf->action_code = WLAN_TDLS_SETUP_CONFIRM; | ||
105 | |||
106 | skb_put(skb, sizeof(tf->u.setup_cfm)); | ||
107 | tf->u.setup_cfm.status_code = cpu_to_le16(status_code); | ||
108 | tf->u.setup_cfm.dialog_token = dialog_token; | ||
109 | break; | ||
110 | case WLAN_TDLS_TEARDOWN: | ||
111 | tf->category = WLAN_CATEGORY_TDLS; | ||
112 | tf->action_code = WLAN_TDLS_TEARDOWN; | ||
113 | |||
114 | skb_put(skb, sizeof(tf->u.teardown)); | ||
115 | tf->u.teardown.reason_code = cpu_to_le16(status_code); | ||
116 | break; | ||
117 | case WLAN_TDLS_DISCOVERY_REQUEST: | ||
118 | tf->category = WLAN_CATEGORY_TDLS; | ||
119 | tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST; | ||
120 | |||
121 | skb_put(skb, sizeof(tf->u.discover_req)); | ||
122 | tf->u.discover_req.dialog_token = dialog_token; | ||
123 | break; | ||
124 | default: | ||
125 | return -EINVAL; | ||
126 | } | ||
127 | |||
128 | return 0; | ||
129 | } | ||
130 | |||
131 | static int | ||
132 | ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, | ||
133 | u8 *peer, u8 action_code, u8 dialog_token, | ||
134 | u16 status_code, struct sk_buff *skb) | ||
135 | { | ||
136 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
137 | enum ieee80211_band band = ieee80211_get_sdata_band(sdata); | ||
138 | struct ieee80211_mgmt *mgmt; | ||
139 | |||
140 | mgmt = (void *)skb_put(skb, 24); | ||
141 | memset(mgmt, 0, 24); | ||
142 | memcpy(mgmt->da, peer, ETH_ALEN); | ||
143 | memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN); | ||
144 | memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN); | ||
145 | |||
146 | mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT | | ||
147 | IEEE80211_STYPE_ACTION); | ||
148 | |||
149 | switch (action_code) { | ||
150 | case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: | ||
151 | skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp)); | ||
152 | mgmt->u.action.category = WLAN_CATEGORY_PUBLIC; | ||
153 | mgmt->u.action.u.tdls_discover_resp.action_code = | ||
154 | WLAN_PUB_ACTION_TDLS_DISCOVER_RES; | ||
155 | mgmt->u.action.u.tdls_discover_resp.dialog_token = | ||
156 | dialog_token; | ||
157 | mgmt->u.action.u.tdls_discover_resp.capability = | ||
158 | cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); | ||
159 | |||
160 | ieee80211_add_srates_ie(sdata, skb, false, band); | ||
161 | ieee80211_add_ext_srates_ie(sdata, skb, false, band); | ||
162 | ieee80211_tdls_add_ext_capab(skb); | ||
163 | break; | ||
164 | default: | ||
165 | return -EINVAL; | ||
166 | } | ||
167 | |||
168 | return 0; | ||
169 | } | ||
170 | |||
171 | int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev, | ||
172 | u8 *peer, u8 action_code, u8 dialog_token, | ||
173 | u16 status_code, u32 peer_capability, | ||
174 | const u8 *extra_ies, size_t extra_ies_len) | ||
175 | { | ||
176 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
177 | struct ieee80211_local *local = sdata->local; | ||
178 | struct sk_buff *skb = NULL; | ||
179 | bool send_direct; | ||
180 | int ret; | ||
181 | |||
182 | if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) | ||
183 | return -ENOTSUPP; | ||
184 | |||
185 | /* make sure we are in managed mode, and associated */ | ||
186 | if (sdata->vif.type != NL80211_IFTYPE_STATION || | ||
187 | !sdata->u.mgd.associated) | ||
188 | return -EINVAL; | ||
189 | |||
190 | tdls_dbg(sdata, "TDLS mgmt action %d peer %pM\n", | ||
191 | action_code, peer); | ||
192 | |||
193 | skb = dev_alloc_skb(local->hw.extra_tx_headroom + | ||
194 | max(sizeof(struct ieee80211_mgmt), | ||
195 | sizeof(struct ieee80211_tdls_data)) + | ||
196 | 50 + /* supported rates */ | ||
197 | 7 + /* ext capab */ | ||
198 | extra_ies_len + | ||
199 | sizeof(struct ieee80211_tdls_lnkie)); | ||
200 | if (!skb) | ||
201 | return -ENOMEM; | ||
202 | |||
203 | skb_reserve(skb, local->hw.extra_tx_headroom); | ||
204 | |||
205 | switch (action_code) { | ||
206 | case WLAN_TDLS_SETUP_REQUEST: | ||
207 | case WLAN_TDLS_SETUP_RESPONSE: | ||
208 | case WLAN_TDLS_SETUP_CONFIRM: | ||
209 | case WLAN_TDLS_TEARDOWN: | ||
210 | case WLAN_TDLS_DISCOVERY_REQUEST: | ||
211 | ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer, | ||
212 | action_code, dialog_token, | ||
213 | status_code, skb); | ||
214 | send_direct = false; | ||
215 | break; | ||
216 | case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: | ||
217 | ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code, | ||
218 | dialog_token, status_code, | ||
219 | skb); | ||
220 | send_direct = true; | ||
221 | break; | ||
222 | default: | ||
223 | ret = -ENOTSUPP; | ||
224 | break; | ||
225 | } | ||
226 | |||
227 | if (ret < 0) | ||
228 | goto fail; | ||
229 | |||
230 | if (extra_ies_len) | ||
231 | memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); | ||
232 | |||
233 | /* the TDLS link IE is always added last */ | ||
234 | switch (action_code) { | ||
235 | case WLAN_TDLS_SETUP_REQUEST: | ||
236 | case WLAN_TDLS_SETUP_CONFIRM: | ||
237 | case WLAN_TDLS_TEARDOWN: | ||
238 | case WLAN_TDLS_DISCOVERY_REQUEST: | ||
239 | /* we are the initiator */ | ||
240 | ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer, | ||
241 | sdata->u.mgd.bssid); | ||
242 | break; | ||
243 | case WLAN_TDLS_SETUP_RESPONSE: | ||
244 | case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: | ||
245 | /* we are the responder */ | ||
246 | ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr, | ||
247 | sdata->u.mgd.bssid); | ||
248 | break; | ||
249 | default: | ||
250 | ret = -ENOTSUPP; | ||
251 | goto fail; | ||
252 | } | ||
253 | |||
254 | if (send_direct) { | ||
255 | ieee80211_tx_skb(sdata, skb); | ||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | /* | ||
260 | * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise | ||
261 | * we should default to AC_VI. | ||
262 | */ | ||
263 | switch (action_code) { | ||
264 | case WLAN_TDLS_SETUP_REQUEST: | ||
265 | case WLAN_TDLS_SETUP_RESPONSE: | ||
266 | skb_set_queue_mapping(skb, IEEE80211_AC_BK); | ||
267 | skb->priority = 2; | ||
268 | break; | ||
269 | default: | ||
270 | skb_set_queue_mapping(skb, IEEE80211_AC_VI); | ||
271 | skb->priority = 5; | ||
272 | break; | ||
273 | } | ||
274 | |||
275 | /* disable bottom halves when entering the Tx path */ | ||
276 | local_bh_disable(); | ||
277 | ret = ieee80211_subif_start_xmit(skb, dev); | ||
278 | local_bh_enable(); | ||
279 | |||
280 | return ret; | ||
281 | |||
282 | fail: | ||
283 | dev_kfree_skb(skb); | ||
284 | return ret; | ||
285 | } | ||
286 | |||
287 | int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, | ||
288 | u8 *peer, enum nl80211_tdls_operation oper) | ||
289 | { | ||
290 | struct sta_info *sta; | ||
291 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
292 | |||
293 | if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS)) | ||
294 | return -ENOTSUPP; | ||
295 | |||
296 | if (sdata->vif.type != NL80211_IFTYPE_STATION) | ||
297 | return -EINVAL; | ||
298 | |||
299 | tdls_dbg(sdata, "TDLS oper %d peer %pM\n", oper, peer); | ||
300 | |||
301 | switch (oper) { | ||
302 | case NL80211_TDLS_ENABLE_LINK: | ||
303 | rcu_read_lock(); | ||
304 | sta = sta_info_get(sdata, peer); | ||
305 | if (!sta) { | ||
306 | rcu_read_unlock(); | ||
307 | return -ENOLINK; | ||
308 | } | ||
309 | |||
310 | set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); | ||
311 | rcu_read_unlock(); | ||
312 | break; | ||
313 | case NL80211_TDLS_DISABLE_LINK: | ||
314 | return sta_info_destroy_addr(sdata, peer); | ||
315 | case NL80211_TDLS_TEARDOWN: | ||
316 | case NL80211_TDLS_SETUP: | ||
317 | case NL80211_TDLS_DISCOVERY_REQ: | ||
318 | /* We don't support in-driver setup/teardown/discovery */ | ||
319 | return -ENOTSUPP; | ||
320 | default: | ||
321 | return -ENOTSUPP; | ||
322 | } | ||
323 | |||
324 | return 0; | ||
325 | } | ||