wifi: mac80211: handle TDLS negotiation with MLO
authorMukesh Sisodiya <mukesh.sisodiya@intel.com>
Fri, 16 Jun 2023 06:53:51 +0000 (09:53 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 19 Jun 2023 10:05:27 +0000 (12:05 +0200)
Userspace can now select the link to use for TDLS management
frames (indicating e.g. which BSSID should be used), use the
link_id received from cfg80211 to build the frames.

Signed-off-by: Mukesh Sisodiya <mukesh.sisodiya@intel.com>
Signed-off-by: Gregory Greenman <gregory.greenman@intel.com>
Link: https://lore.kernel.org/r/20230616094948.ce1fc230b505.Ie773c5679805001f5a52680d68d9ce0232c57648@changeid
[Benjamin fixed some locking]
Co-developed-by: Benjamin Berg <benjamin.berg@intel.com>
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
[fix sta mutex locking too]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/tdls.c

index 6575b28..a920e2a 100644 (file)
@@ -39,9 +39,10 @@ void ieee80211_tdls_peer_del_work(struct work_struct *wk)
        mutex_unlock(&local->mtx);
 }
 
-static void ieee80211_tdls_add_ext_capab(struct ieee80211_sub_if_data *sdata,
+static void ieee80211_tdls_add_ext_capab(struct ieee80211_link_data *link,
                                         struct sk_buff *skb)
 {
+       struct ieee80211_sub_if_data *sdata = link->sdata;
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
        bool chan_switch = local->hw.wiphy->features &
@@ -50,7 +51,7 @@ static void ieee80211_tdls_add_ext_capab(struct ieee80211_sub_if_data *sdata,
                          !ifmgd->tdls_wider_bw_prohibited;
        bool buffer_sta = ieee80211_hw_check(&local->hw,
                                             SUPPORTS_TDLS_BUFFER_STA);
-       struct ieee80211_supported_band *sband = ieee80211_get_sband(sdata);
+       struct ieee80211_supported_band *sband = ieee80211_get_link_sband(link);
        bool vht = sband && sband->vht_cap.vht_supported;
        u8 *pos = skb_put(skb, 10);
 
@@ -152,13 +153,13 @@ ieee80211_tdls_add_supp_channels(struct ieee80211_sub_if_data *sdata,
        *pos = 2 * subband_cnt;
 }
 
-static void ieee80211_tdls_add_oper_classes(struct ieee80211_sub_if_data *sdata,
+static void ieee80211_tdls_add_oper_classes(struct ieee80211_link_data *link,
                                            struct sk_buff *skb)
 {
        u8 *pos;
        u8 op_class;
 
-       if (!ieee80211_chandef_to_operating_class(&sdata->vif.bss_conf.chandef,
+       if (!ieee80211_chandef_to_operating_class(&link->conf->chandef,
                                                  &op_class))
                return;
 
@@ -180,7 +181,7 @@ static void ieee80211_tdls_add_bss_coex_ie(struct sk_buff *skb)
        *pos++ = WLAN_BSS_COEX_INFORMATION_REQUEST;
 }
 
-static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata,
+static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_link_data *link,
                                        u16 status_code)
 {
        struct ieee80211_supported_band *sband;
@@ -189,7 +190,8 @@ static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata,
        if (status_code != 0)
                return 0;
 
-       sband = ieee80211_get_sband(sdata);
+       sband = ieee80211_get_link_sband(link);
+
        if (sband && sband->band == NL80211_BAND_2GHZ) {
                return WLAN_CAPABILITY_SHORT_SLOT_TIME |
                       WLAN_CAPABILITY_SHORT_PREAMBLE;
@@ -198,10 +200,11 @@ static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata,
        return 0;
 }
 
-static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata,
+static void ieee80211_tdls_add_link_ie(struct ieee80211_link_data *link,
                                       struct sk_buff *skb, const u8 *peer,
                                       bool initiator)
 {
+       struct ieee80211_sub_if_data *sdata = link->sdata;
        struct ieee80211_tdls_lnkie *lnkid;
        const u8 *init_addr, *rsp_addr;
 
@@ -218,7 +221,7 @@ static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata,
        lnkid->ie_type = WLAN_EID_LINK_ID;
        lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
 
-       memcpy(lnkid->bssid, sdata->deflink.u.mgd.bssid, ETH_ALEN);
+       memcpy(lnkid->bssid, link->u.mgd.bssid, ETH_ALEN);
        memcpy(lnkid->init_sta, init_addr, ETH_ALEN);
        memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN);
 }
@@ -359,11 +362,12 @@ ieee80211_tdls_chandef_vht_upgrade(struct ieee80211_sub_if_data *sdata,
 }
 
 static void
-ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
+ieee80211_tdls_add_setup_start_ies(struct ieee80211_link_data *link,
                                   struct sk_buff *skb, const u8 *peer,
                                   u8 action_code, bool initiator,
                                   const u8 *extra_ies, size_t extra_ies_len)
 {
+       struct ieee80211_sub_if_data *sdata = link->sdata;
        struct ieee80211_supported_band *sband;
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_sta_ht_cap ht_cap;
@@ -372,8 +376,8 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
        size_t offset = 0, noffset;
        u8 *pos;
 
-       sband = ieee80211_get_sband(sdata);
-       if (!sband)
+       sband = ieee80211_get_link_sband(link);
+       if (WARN_ON_ONCE(!sband))
                return;
 
        ieee80211_add_srates_ie(sdata, skb, false, sband->band);
@@ -397,7 +401,7 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
                offset = noffset;
        }
 
-       ieee80211_tdls_add_ext_capab(sdata, skb);
+       ieee80211_tdls_add_ext_capab(link, skb);
 
        /* add the QoS element if we support it */
        if (local->hw.queues >= IEEE80211_NUM_ACS &&
@@ -426,20 +430,16 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
                offset = noffset;
        }
 
-       mutex_lock(&local->sta_mtx);
-
        /* we should have the peer STA if we're already responding */
        if (action_code == WLAN_TDLS_SETUP_RESPONSE) {
                sta = sta_info_get(sdata, peer);
-               if (WARN_ON_ONCE(!sta)) {
-                       mutex_unlock(&local->sta_mtx);
+               if (WARN_ON_ONCE(!sta))
                        return;
-               }
 
-               sta->tdls_chandef = sdata->vif.bss_conf.chandef;
+               sta->tdls_chandef = link->conf->chandef;
        }
 
-       ieee80211_tdls_add_oper_classes(sdata, skb);
+       ieee80211_tdls_add_oper_classes(link, skb);
 
        /*
         * with TDLS we can switch channels, and HT-caps are not necessarily
@@ -472,7 +472,7 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
            (ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40))
                ieee80211_tdls_add_bss_coex_ie(skb);
 
-       ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+       ieee80211_tdls_add_link_ie(link, skb, peer, initiator);
 
        /* add any custom IEs that go before VHT capabilities */
        if (extra_ies_len) {
@@ -529,8 +529,6 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
                        ieee80211_tdls_chandef_vht_upgrade(sdata, sta);
        }
 
-       mutex_unlock(&local->sta_mtx);
-
        /* add any remaining IEs */
        if (extra_ies_len) {
                noffset = extra_ies_len;
@@ -540,31 +538,29 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata,
 }
 
 static void
-ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
+ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_link_data *link,
                                 struct sk_buff *skb, const u8 *peer,
                                 bool initiator, const u8 *extra_ies,
                                 size_t extra_ies_len)
 {
+       struct ieee80211_sub_if_data *sdata = link->sdata;
        struct ieee80211_local *local = sdata->local;
        size_t offset = 0, noffset;
        struct sta_info *sta, *ap_sta;
        struct ieee80211_supported_band *sband;
        u8 *pos;
 
-       sband = ieee80211_get_sband(sdata);
-       if (!sband)
+       sband = ieee80211_get_link_sband(link);
+       if (WARN_ON_ONCE(!sband))
                return;
 
-       mutex_lock(&local->sta_mtx);
-
        sta = sta_info_get(sdata, peer);
-       ap_sta = sta_info_get(sdata, sdata->deflink.u.mgd.bssid);
-       if (WARN_ON_ONCE(!sta || !ap_sta)) {
-               mutex_unlock(&local->sta_mtx);
+       ap_sta = sta_info_get(sdata, sdata->vif.cfg.ap_addr);
+
+       if (WARN_ON_ONCE(!sta || !ap_sta))
                return;
-       }
 
-       sta->tdls_chandef = sdata->vif.bss_conf.chandef;
+       sta->tdls_chandef = link->conf->chandef;
 
        /* add any custom IEs that go before the QoS IE */
        if (extra_ies_len) {
@@ -610,11 +606,11 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
 
                pos = skb_put(skb, 2 + sizeof(struct ieee80211_ht_operation));
                ieee80211_ie_build_ht_oper(pos, &sta->sta.deflink.ht_cap,
-                                          &sdata->vif.bss_conf.chandef, prot,
+                                          &link->conf->chandef, prot,
                                           true);
        }
 
-       ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+       ieee80211_tdls_add_link_ie(link, skb, peer, initiator);
 
        /* only include VHT-operation if not on the 2.4GHz band */
        if (sband->band != NL80211_BAND_2GHZ &&
@@ -631,8 +627,6 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
                                            &sta->tdls_chandef);
        }
 
-       mutex_unlock(&local->sta_mtx);
-
        /* add any remaining IEs */
        if (extra_ies_len) {
                noffset = extra_ies_len;
@@ -641,7 +635,7 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata,
 }
 
 static void
-ieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_sub_if_data *sdata,
+ieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_link_data *link,
                                       struct sk_buff *skb, const u8 *peer,
                                       bool initiator, const u8 *extra_ies,
                                       size_t extra_ies_len, u8 oper_class,
@@ -670,7 +664,7 @@ ieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_sub_if_data *sdata,
                offset = noffset;
        }
 
-       ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+       ieee80211_tdls_add_link_ie(link, skb, peer, initiator);
 
        /* add any remaining IEs */
        if (extra_ies_len) {
@@ -680,20 +674,20 @@ ieee80211_tdls_add_chan_switch_req_ies(struct ieee80211_sub_if_data *sdata,
 }
 
 static void
-ieee80211_tdls_add_chan_switch_resp_ies(struct ieee80211_sub_if_data *sdata,
+ieee80211_tdls_add_chan_switch_resp_ies(struct ieee80211_link_data *link,
                                        struct sk_buff *skb, const u8 *peer,
                                        u16 status_code, bool initiator,
                                        const u8 *extra_ies,
                                        size_t extra_ies_len)
 {
        if (status_code == 0)
-               ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+               ieee80211_tdls_add_link_ie(link, skb, peer, initiator);
 
        if (extra_ies_len)
                skb_put_data(skb, extra_ies, extra_ies_len);
 }
 
-static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
+static void ieee80211_tdls_add_ies(struct ieee80211_link_data *link,
                                   struct sk_buff *skb, const u8 *peer,
                                   u8 action_code, u16 status_code,
                                   bool initiator, const u8 *extra_ies,
@@ -705,7 +699,8 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
        case WLAN_TDLS_SETUP_RESPONSE:
        case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
                if (status_code == 0)
-                       ieee80211_tdls_add_setup_start_ies(sdata, skb, peer,
+                       ieee80211_tdls_add_setup_start_ies(link,
+                                                          skb, peer,
                                                           action_code,
                                                           initiator,
                                                           extra_ies,
@@ -713,7 +708,7 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
                break;
        case WLAN_TDLS_SETUP_CONFIRM:
                if (status_code == 0)
-                       ieee80211_tdls_add_setup_cfm_ies(sdata, skb, peer,
+                       ieee80211_tdls_add_setup_cfm_ies(link, skb, peer,
                                                         initiator, extra_ies,
                                                         extra_ies_len);
                break;
@@ -722,16 +717,17 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
                if (extra_ies_len)
                        skb_put_data(skb, extra_ies, extra_ies_len);
                if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN)
-                       ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator);
+                       ieee80211_tdls_add_link_ie(link, skb,
+                                                  peer, initiator);
                break;
        case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
-               ieee80211_tdls_add_chan_switch_req_ies(sdata, skb, peer,
+               ieee80211_tdls_add_chan_switch_req_ies(link, skb, peer,
                                                       initiator, extra_ies,
                                                       extra_ies_len,
                                                       oper_class, chandef);
                break;
        case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
-               ieee80211_tdls_add_chan_switch_resp_ies(sdata, skb, peer,
+               ieee80211_tdls_add_chan_switch_resp_ies(link, skb, peer,
                                                        status_code,
                                                        initiator, extra_ies,
                                                        extra_ies_len);
@@ -742,6 +738,7 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata,
 
 static int
 ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
+                              struct ieee80211_link_data *link,
                               const u8 *peer, u8 action_code, u8 dialog_token,
                               u16 status_code, struct sk_buff *skb)
 {
@@ -766,7 +763,7 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
                skb_put(skb, sizeof(tf->u.setup_req));
                tf->u.setup_req.dialog_token = dialog_token;
                tf->u.setup_req.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(link,
                                                                 status_code));
                break;
        case WLAN_TDLS_SETUP_RESPONSE:
@@ -777,7 +774,7 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
                tf->u.setup_resp.status_code = cpu_to_le16(status_code);
                tf->u.setup_resp.dialog_token = dialog_token;
                tf->u.setup_resp.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(link,
                                                                 status_code));
                break;
        case WLAN_TDLS_SETUP_CONFIRM:
@@ -824,7 +821,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
 
 static int
 ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
-                          const u8 *peer, u8 action_code, u8 dialog_token,
+                          const u8 *peer, struct ieee80211_link_data *link,
+                          u8 action_code, u8 dialog_token,
                           u16 status_code, struct sk_buff *skb)
 {
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
@@ -833,8 +831,7 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
        mgmt = skb_put_zero(skb, 24);
        memcpy(mgmt->da, peer, ETH_ALEN);
        memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
-       memcpy(mgmt->bssid, sdata->deflink.u.mgd.bssid, ETH_ALEN);
-
+       memcpy(mgmt->bssid, link->u.mgd.bssid, ETH_ALEN);
        mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
                                          IEEE80211_STYPE_ACTION);
 
@@ -847,7 +844,7 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
                mgmt->u.action.u.tdls_discover_resp.dialog_token =
                        dialog_token;
                mgmt->u.action.u.tdls_discover_resp.capability =
-                       cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata,
+                       cpu_to_le16(ieee80211_get_tdls_sta_capab(link,
                                                                 status_code));
                break;
        default:
@@ -859,15 +856,23 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
 
 static struct sk_buff *
 ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata,
-                                     const u8 *peer, u8 action_code,
-                                     u8 dialog_token, u16 status_code,
-                                     bool initiator, const u8 *extra_ies,
-                                     size_t extra_ies_len, u8 oper_class,
+                                     const u8 *peer, int link_id,
+                                     u8 action_code, u8 dialog_token,
+                                     u16 status_code, bool initiator,
+                                     const u8 *extra_ies, size_t extra_ies_len,
+                                     u8 oper_class,
                                      struct cfg80211_chan_def *chandef)
 {
        struct ieee80211_local *local = sdata->local;
        struct sk_buff *skb;
        int ret;
+       struct ieee80211_link_data *link;
+
+       link_id = link_id >= 0 ? link_id : 0;
+       rcu_read_lock();
+       link = rcu_dereference(sdata->link[link_id]);
+       if (WARN_ON(!link))
+               goto unlock;
 
        skb = netdev_alloc_skb(sdata->dev,
                               local->hw.extra_tx_headroom +
@@ -887,7 +892,7 @@ ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata,
                               extra_ies_len +
                               sizeof(struct ieee80211_tdls_lnkie));
        if (!skb)
-               return NULL;
+               goto unlock;
 
        skb_reserve(skb, local->hw.extra_tx_headroom);
 
@@ -900,13 +905,13 @@ ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata,
        case WLAN_TDLS_CHANNEL_SWITCH_REQUEST:
        case WLAN_TDLS_CHANNEL_SWITCH_RESPONSE:
                ret = ieee80211_prep_tdls_encap_data(local->hw.wiphy,
-                                                    sdata->dev, peer,
+                                                    sdata->dev, link, peer,
                                                     action_code, dialog_token,
                                                     status_code, skb);
                break;
        case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
                ret = ieee80211_prep_tdls_direct(local->hw.wiphy, sdata->dev,
-                                                peer, action_code,
+                                                peer, link, action_code,
                                                 dialog_token, status_code,
                                                 skb);
                break;
@@ -918,19 +923,23 @@ ieee80211_tdls_build_mgmt_packet_data(struct ieee80211_sub_if_data *sdata,
        if (ret < 0)
                goto fail;
 
-       ieee80211_tdls_add_ies(sdata, skb, peer, action_code, status_code,
+       ieee80211_tdls_add_ies(link, skb, peer, action_code, status_code,
                               initiator, extra_ies, extra_ies_len, oper_class,
                               chandef);
+       rcu_read_unlock();
        return skb;
 
 fail:
        dev_kfree_skb(skb);
+unlock:
+       rcu_read_unlock();
        return NULL;
 }
 
 static int
 ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
-                               const u8 *peer, u8 action_code, u8 dialog_token,
+                               const u8 *peer, int link_id,
+                               u8 action_code, u8 dialog_token,
                                u16 status_code, u32 peer_capability,
                                bool initiator, const u8 *extra_ies,
                                size_t extra_ies_len, u8 oper_class,
@@ -988,7 +997,8 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
        if (ret < 0)
                goto fail;
 
-       skb = ieee80211_tdls_build_mgmt_packet_data(sdata, peer, action_code,
+       skb = ieee80211_tdls_build_mgmt_packet_data(sdata, peer,
+                                                   link_id, action_code,
                                                    dialog_token, status_code,
                                                    initiator, extra_ies,
                                                    extra_ies_len, oper_class,
@@ -999,7 +1009,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
        }
 
        if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
-               ieee80211_tx_skb(sdata, skb);
+               ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
                return 0;
        }
 
@@ -1066,7 +1076,8 @@ fail:
 
 static int
 ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
-                         const u8 *peer, u8 action_code, u8 dialog_token,
+                         const u8 *peer, int link_id,
+                         u8 action_code, u8 dialog_token,
                          u16 status_code, u32 peer_capability, bool initiator,
                          const u8 *extra_ies, size_t extra_ies_len)
 {
@@ -1115,7 +1126,8 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev,
        mutex_unlock(&local->mtx);
 
        /* we cannot take the mutex while preparing the setup packet */
-       ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
+       ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer,
+                                             link_id, action_code,
                                              dialog_token, status_code,
                                              peer_capability, initiator,
                                              extra_ies, extra_ies_len, 0,
@@ -1139,7 +1151,8 @@ out_unlock:
 
 static int
 ieee80211_tdls_mgmt_teardown(struct wiphy *wiphy, struct net_device *dev,
-                            const u8 *peer, u8 action_code, u8 dialog_token,
+                            const u8 *peer, int link_id,
+                            u8 action_code, u8 dialog_token,
                             u16 status_code, u32 peer_capability,
                             bool initiator, const u8 *extra_ies,
                             size_t extra_ies_len)
@@ -1159,7 +1172,8 @@ ieee80211_tdls_mgmt_teardown(struct wiphy *wiphy, struct net_device *dev,
                                  IEEE80211_QUEUE_STOP_REASON_TDLS_TEARDOWN);
        ieee80211_flush_queues(local, sdata, false);
 
-       ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer, action_code,
+       ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer,
+                                             link_id, action_code,
                                              dialog_token, status_code,
                                              peer_capability, initiator,
                                              extra_ies, extra_ies_len, 0,
@@ -1204,13 +1218,14 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
        switch (action_code) {
        case WLAN_TDLS_SETUP_REQUEST:
        case WLAN_TDLS_SETUP_RESPONSE:
-               ret = ieee80211_tdls_mgmt_setup(wiphy, dev, peer, action_code,
+               ret = ieee80211_tdls_mgmt_setup(wiphy, dev, peer,
+                                               link_id, action_code,
                                                dialog_token, status_code,
                                                peer_capability, initiator,
                                                extra_ies, extra_ies_len);
                break;
        case WLAN_TDLS_TEARDOWN:
-               ret = ieee80211_tdls_mgmt_teardown(wiphy, dev, peer,
+               ret = ieee80211_tdls_mgmt_teardown(wiphy, dev, peer, link_id,
                                                   action_code, dialog_token,
                                                   status_code,
                                                   peer_capability, initiator,
@@ -1228,7 +1243,7 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
        case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
                /* no special handling */
                ret = ieee80211_tdls_prep_mgmt_packet(wiphy, dev, peer,
-                                                     action_code,
+                                                     link_id, action_code,
                                                      dialog_token,
                                                      status_code,
                                                      peer_capability,
@@ -1240,8 +1255,8 @@ int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
                break;
        }
 
-       tdls_dbg(sdata, "TDLS mgmt action %d peer %pM status %d\n",
-                action_code, peer, ret);
+       tdls_dbg(sdata, "TDLS mgmt action %d peer %pM link_id %d status %d\n",
+                action_code, peer, link_id, ret);
        return ret;
 }
 
@@ -1497,6 +1512,7 @@ ieee80211_tdls_ch_sw_tmpl_get(struct sta_info *sta, u8 oper_class,
        int extra_ies_len = 2 + sizeof(struct ieee80211_ch_switch_timing);
        u8 *pos = extra_ies;
        struct sk_buff *skb;
+       int link_id = sta->sta.valid_links ? ffs(sta->sta.valid_links) - 1 : 0;
 
        /*
         * if chandef points to a wide channel add a Secondary-Channel
@@ -1524,6 +1540,7 @@ ieee80211_tdls_ch_sw_tmpl_get(struct sta_info *sta, u8 oper_class,
        iee80211_tdls_add_ch_switch_timing(pos, 0, 0);
 
        skb = ieee80211_tdls_build_mgmt_packet_data(sdata, sta->sta.addr,
+                                             link_id,
                                              WLAN_TDLS_CHANNEL_SWITCH_REQUEST,
                                              0, 0, !sta->sta.tdls_initiator,
                                              extra_ies, extra_ies_len,
@@ -1644,11 +1661,13 @@ ieee80211_tdls_ch_sw_resp_tmpl_get(struct sta_info *sta,
        struct ieee80211_sub_if_data *sdata = sta->sdata;
        struct sk_buff *skb;
        u8 extra_ies[2 + sizeof(struct ieee80211_ch_switch_timing)];
+       int link_id = sta->sta.valid_links ? ffs(sta->sta.valid_links) - 1 : 0;
 
        /* initial timing are always zero in the template */
        iee80211_tdls_add_ch_switch_timing(extra_ies, 0, 0);
 
        skb = ieee80211_tdls_build_mgmt_packet_data(sdata, sta->sta.addr,
+                                       link_id,
                                        WLAN_TDLS_CHANNEL_SWITCH_RESPONSE,
                                        0, 0, !sta->sta.tdls_initiator,
                                        extra_ies, sizeof(extra_ies), 0, NULL);