1 /** @file mlan_11v.c
2  *
3  *  @brief  This file provides functions for process 11v(BTM) frames
4  *
5  *  Copyright 2022-2024 NXP
6  *
7  *  SPDX-License-Identifier: BSD-3-Clause
8  *
9  */
10 
11 /********************************************************
12 Change log:
13     08/11/2022: initial version
14 ********************************************************/
15 
16 #include <mlan_api.h>
17 #ifndef RW610
18 #include "wifi-sdio.h"
19 #endif
20 #if CONFIG_11V
21 #define BTM_RESP_BUF_SIZE      200
22 #define WNM_BTM_QUERY_BUF_SIZE 10U
23 #define WLAN_FC_TYPE_MGMT      0
24 
25 /********************************************************
26                 Local Variables
27 ********************************************************/
28 
29 /********************************************************
30                 Global Variables
31 ********************************************************/
32 
33 /********************************************************
34                 Local Functions
35 ********************************************************/
wlan_wnm_parse_neighbor_report(t_u8 * pos,t_u8 len,struct wnm_neighbor_report * rep)36 static void wlan_wnm_parse_neighbor_report(t_u8 *pos, t_u8 len, struct wnm_neighbor_report *rep)
37 {
38     t_u8 remain_len = 0;
39     if (len < (t_u8)13U)
40     {
41         wifi_d("WNM: This neighbor report is too short");
42     }
43 
44     (void)memcpy(rep->bssid, pos, MLAN_MAC_ADDR_LENGTH);
45     rep->bssid_info = wlan_cpu_to_le32(*(t_u32 *)(void *)(pos + MLAN_MAC_ADDR_LENGTH));
46     rep->reg_class  = *(pos + 10);
47     rep->channel    = *(pos + 11);
48     rep->PhyType    = *(pos + 12);
49     pos += 13;
50     remain_len = (t_u8)(len - (t_u8)13U);
51 
52     while (remain_len >= (t_u8)2U)
53     {
54         t_u8 e_id, e_len;
55 
56         e_id  = *pos++;
57         e_len = *pos++;
58         remain_len -= (t_u8)2U;
59         if (e_len > remain_len)
60         {
61             wifi_d("WNM: neighbor report length not matched");
62             break;
63         }
64         switch (e_id)
65         {
66             case MGMT_WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
67                 if (e_len < (t_u8)1U)
68                 {
69                     break;
70                 }
71                 rep->prefer        = pos[0];
72                 rep->prefer_select = 1;
73                 break;
74             default:
75                 (void)PRINTF("UNKNOWN nbor Report e id\r\n");
76                 break;
77         }
78 
79         remain_len -= e_len;
80         pos += e_len;
81     }
82 }
83 
wlan_send_mgmt_wnm_btm_resp(t_u8 dialog_token,enum wnm_btm_status_code status,t_u8 * dst_addr,t_u8 * src_addr,t_u8 * target_bssid,t_u8 * tag_nr,t_u8 tag_len,bool protect)84 void wlan_send_mgmt_wnm_btm_resp(t_u8 dialog_token,
85                                  enum wnm_btm_status_code status,
86                                  t_u8 *dst_addr,
87                                  t_u8 *src_addr,
88                                  t_u8 *target_bssid,
89                                  t_u8 *tag_nr,
90                                  t_u8 tag_len,
91                                  bool protect)
92 {
93     wlan_mgmt_pkt *pmgmt_pkt_hdr    = MNULL;
94     IEEEtypes_FrameCtl_t *mgmt_fc_p = MNULL;
95     t_u8 *pos                       = MNULL;
96     t_u16 pkt_len                   = 0;
97 
98     pmgmt_pkt_hdr = wifi_PrepDefaultMgtMsg(
99         SUBTYPE_ACTION, (mlan_802_11_mac_addr *)(void *)dst_addr, (mlan_802_11_mac_addr *)(void *)src_addr,
100         (mlan_802_11_mac_addr *)(void *)dst_addr, sizeof(wlan_mgmt_pkt) + (size_t)BTM_RESP_BUF_SIZE);
101     if (pmgmt_pkt_hdr == MNULL)
102     {
103         wifi_d("No memory available for BTM resp");
104         return;
105     }
106 
107     mgmt_fc_p = (IEEEtypes_FrameCtl_t *)(void *)&pmgmt_pkt_hdr->wlan_header.frm_ctl;
108     if (protect)
109     {
110         mgmt_fc_p->wep = 1;
111     }
112 
113     /* 802.11 management body */
114     pos    = (t_u8 *)pmgmt_pkt_hdr + sizeof(wlan_mgmt_pkt);
115     pos[0] = (t_u8)IEEE_MGMT_ACTION_CATEGORY_WNM;
116     pos[1] = (t_u8)IEEE_MGMT_WNM_BTM_RESPONSE;
117     pos[2] = dialog_token;
118     pos[3] = (t_u8)status;
119     pos[4] = 0; /* delay */
120     pos += 5;
121 
122     if (target_bssid != NULL)
123     {
124         (void)memcpy((void *)pos, (const void *)target_bssid, (size_t)MLAN_MAC_ADDR_LENGTH);
125         pos += 6;
126     }
127     else if (status == WNM_BTM_ACCEPT)
128     {
129         (void)memcpy((void *)pos, "\0\0\0\0\0\0", (size_t)MLAN_MAC_ADDR_LENGTH);
130         pos += 6;
131     }
132     else
133     {
134         /* Do nothing */
135     }
136 
137     if (status == WNM_BTM_ACCEPT && tag_nr != NULL)
138     {
139         (void)memcpy((void *)pos, (const void *)tag_nr, (size_t)tag_len);
140         pos += tag_len;
141     }
142     pkt_len                = (t_u16)(pos - (t_u8 *)pmgmt_pkt_hdr);
143     pmgmt_pkt_hdr->frm_len = (t_u16)((t_u16)pkt_len - sizeof(t_u16));
144     (void)wifi_inject_frame(WLAN_BSS_TYPE_STA, (t_u8 *)pmgmt_pkt_hdr, pkt_len);
145 #if !CONFIG_MEM_POOLS
146     OSA_MemoryFree(pmgmt_pkt_hdr);
147 #else
148     OSA_MemoryPoolFree(buf_1536_MemoryPool, pmgmt_pkt_hdr);
149 #endif
150 }
151 
wlan_11v_find_in_channels(t_u8 * channels,t_u8 entry_num,t_u8 chan)152 static bool wlan_11v_find_in_channels(t_u8 *channels, t_u8 entry_num, t_u8 chan)
153 {
154     t_u8 i;
155     for (i = 0; i < entry_num; i++)
156     {
157         if (channels[i] == chan)
158         {
159             return true;
160         }
161     }
162     return false;
163 }
164 /********************************************************
165                 Global functions
166 ********************************************************/
167 /**
168  *  @brief This function process BTM request frame
169  *
170  *  @param pos          BTM request frame head
171  *  @param end          end of frame
172  *  @param src_addr     source address
173  *
174  */
wlan_process_mgmt_wnm_btm_req(t_u8 * pos,t_u8 * end,t_u8 * src_addr,t_u8 * dest_addr,bool protect)175 void wlan_process_mgmt_wnm_btm_req(t_u8 *pos, t_u8 *end, t_u8 *src_addr, t_u8 *dest_addr, bool protect)
176 {
177     t_u8 dialog_token;
178     t_u8 wnm_num_neighbor_report = 0, neighbor_index = 0;
179     t_u8 btm_mode;
180     t_u8 prefer_old = 0, prefer_select = 0;
181     t_u8 *ptagnr                              = NULL;
182     t_u8 tagnr_len                            = 0;
183     wlan_nlist_report_param *pnlist_rep_param = MNULL;
184     t_u8 entry_num                            = 0;
185 
186     if (end - pos < 5)
187     {
188         return;
189     }
190 
191 #if !CONFIG_MEM_POOLS
192     pnlist_rep_param = (wlan_nlist_report_param *)OSA_MemoryAllocate(sizeof(wlan_nlist_report_param));
193 #else
194     pnlist_rep_param = (wlan_nlist_report_param *)OSA_MemoryPoolAllocate(buf_128_MemoryPool);
195 #endif
196 
197     if (pnlist_rep_param == MNULL)
198     {
199         wifi_e("11v nlist report param buffer alloc failed %d", sizeof(wlan_nlist_report_param));
200         return;
201     }
202 
203     (void)memset(pnlist_rep_param, 0, sizeof(wlan_nlist_report_param));
204 
205     dialog_token = pos[0];
206     btm_mode     = pos[1];
207     pos += 5;
208 
209     if ((btm_mode & IEEE_WNM_BTM_REQUEST_BSS_TERMINATION_INCLUDED) != 0U)
210     {
211         pos += 12; /* BSS Termination Duration */
212     }
213 
214     if ((btm_mode & IEEE_WNM_BTM_REQUEST_PREFERENCE_CAND_LIST_INCLUDED) != 0U)
215     {
216 #if !CONFIG_MEM_POOLS
217         struct wnm_neighbor_report *preport =
218             OSA_MemoryAllocate((size_t)WLAN_WNM_MAX_NEIGHBOR_REPORT * sizeof(struct wnm_neighbor_report));
219 #else
220         struct wnm_neighbor_report *preport = OSA_MemoryPoolAllocate(buf_128_MemoryPool);
221 #endif
222         if (preport == NULL)
223         {
224             wifi_e("No memory available for neighbor report.");
225 #if !CONFIG_MEM_POOLS
226             OSA_MemoryFree((void *)pnlist_rep_param);
227 #else
228             OSA_MemoryPoolFree(buf_128_MemoryPool, pnlist_rep_param);
229 #endif
230             return;
231         }
232 
233         while (end - pos >= 2 && wnm_num_neighbor_report < (t_u8)WLAN_WNM_MAX_NEIGHBOR_REPORT)
234         {
235             t_u8 tag = *pos++;
236             t_u8 len = *pos++;
237 
238             if ((int)len > (end - pos))
239             {
240                 wifi_d("WNM: Truncated BTM request");
241 #if !CONFIG_MEM_POOLS
242                 OSA_MemoryFree((void *)preport);
243                 OSA_MemoryFree((void *)pnlist_rep_param);
244 #else
245                 OSA_MemoryPoolFree(buf_128_MemoryPool, preport);
246                 OSA_MemoryPoolFree(buf_128_MemoryPool, pnlist_rep_param);
247 #endif
248                 return;
249             }
250 
251             if (tag == (t_u8)NEIGHBOR_REPORT)
252             {
253                 struct wnm_neighbor_report *rep;
254                 rep = &preport[wnm_num_neighbor_report];
255                 wlan_wnm_parse_neighbor_report(pos, len, rep);
256                 if (!wlan_11v_find_in_channels(pnlist_rep_param->channels, entry_num, rep->channel))
257                 {
258                     pnlist_rep_param->channels[entry_num] = rep->channel;
259                     entry_num++;
260                 }
261                 if (rep->prefer_select != (t_u8)0U && (rep->prefer > prefer_old))
262                 {
263                     ptagnr         = pos - 2;
264                     tagnr_len      = len + (t_u8)2U;
265                     prefer_old     = (t_u8)rep->prefer;
266                     prefer_select  = 1;
267                     neighbor_index = wnm_num_neighbor_report;
268                 }
269                 wnm_num_neighbor_report++;
270             }
271             pos += len;
272         }
273 
274         if (wnm_num_neighbor_report == (t_u8)0U)
275         {
276             wlan_send_mgmt_wnm_btm_resp(dialog_token, WNM_BTM_REJECT_NO_SUITABLE_CANDIDATES, dest_addr, src_addr, NULL,
277                                         ptagnr, tagnr_len, protect);
278 #if !CONFIG_MEM_POOLS
279             OSA_MemoryFree((void *)preport);
280             OSA_MemoryFree((void *)pnlist_rep_param);
281 #else
282             OSA_MemoryPoolFree(buf_128_MemoryPool, preport);
283             OSA_MemoryPoolFree(buf_128_MemoryPool, pnlist_rep_param);
284 #endif
285 
286             return;
287         }
288 
289         if (prefer_select == (t_u8)1U)
290         {
291             wlan_send_mgmt_wnm_btm_resp(dialog_token, WNM_BTM_ACCEPT, dest_addr, src_addr,
292                                         preport[neighbor_index].bssid, ptagnr, tagnr_len, protect);
293 
294             /* disconnect and re-assocate with AP2 */
295             pnlist_rep_param->nlist_mode   = WLAN_NLIST_11V_PREFERRED;
296             pnlist_rep_param->num_channels = 1;
297             pnlist_rep_param->channels[0]  = preport[neighbor_index].channel;
298             pnlist_rep_param->btm_mode     = btm_mode;
299             (void)memcpy((void *)pnlist_rep_param->bssid, (const void *)preport[neighbor_index].bssid,
300                          (size_t)MLAN_MAC_ADDR_LENGTH);
301         }
302         else
303         {
304             pnlist_rep_param->nlist_mode   = WLAN_NLIST_11V;
305             pnlist_rep_param->num_channels = entry_num;
306             pnlist_rep_param->btm_mode     = btm_mode;
307             pnlist_rep_param->dialog_token = dialog_token;
308             pnlist_rep_param->protect      = protect;
309             (void)memcpy((void *)pnlist_rep_param->dst_addr, (const void *)dest_addr, (size_t)MLAN_MAC_ADDR_LENGTH);
310         }
311 
312         if (wifi_event_completion(WIFI_EVENT_NLIST_REPORT, WIFI_EVENT_REASON_SUCCESS, (void *)pnlist_rep_param) !=
313             WM_SUCCESS)
314         {
315             /* If fail to send message on queue, free allocated memory ! */
316 #if !CONFIG_MEM_POOLS
317             OSA_MemoryFree((void *)pnlist_rep_param);
318 #else
319             OSA_MemoryPoolFree(buf_128_MemoryPool, pnlist_rep_param);
320 #endif
321         }
322 #if !CONFIG_MEM_POOLS
323         OSA_MemoryFree(preport);
324 #else
325         OSA_MemoryPoolFree(buf_128_MemoryPool, preport);
326 #endif
327     }
328     else
329     {
330         enum wnm_btm_status_code status;
331         if ((btm_mode & IEEE_WNM_BTM_REQUEST_ESS_DISASSOC_IMMINENT) != 0U)
332         {
333             status = WNM_BTM_ACCEPT;
334         }
335         else
336         {
337             status = WNM_BTM_REJECT_UNSPECIFIED;
338         }
339 
340         wlan_send_mgmt_wnm_btm_resp(dialog_token, status, dest_addr, src_addr, NULL, ptagnr, tagnr_len, protect);
341         /* If don't use variable pnlist_rep_param, free allocated memory ! */
342 #if !CONFIG_MEM_POOLS
343         OSA_MemoryFree((void *)pnlist_rep_param);
344 #else
345         OSA_MemoryPoolFree(buf_128_MemoryPool, pnlist_rep_param);
346 #endif
347     }
348 }
349 
wlan_send_mgmt_bss_trans_query(mlan_private * pmpriv,t_u8 query_reason)350 int wlan_send_mgmt_bss_trans_query(mlan_private *pmpriv, t_u8 query_reason)
351 {
352     t_u16 pkt_len;
353     mlan_802_11_mac_addr *da     = MNULL;
354     mlan_802_11_mac_addr *sa     = MNULL;
355     wlan_mgmt_pkt *pmgmt_pkt_hdr = MNULL;
356     t_u8 *pos                    = MNULL;
357     int meas_pkt_len             = 0;
358 
359     if (pmpriv->bss_index != (t_u8)MLAN_BSS_ROLE_STA || pmpriv->media_connected != MTRUE)
360     {
361         wifi_d("invalid interface %d for sending neighbor report request", pmpriv->bss_index);
362         return (int)MLAN_STATUS_FAILURE;
363     }
364 
365     da            = &pmpriv->curr_bss_params.bss_descriptor.mac_address;
366     sa            = (mlan_802_11_mac_addr *)(void *)(&pmpriv->curr_addr[0]);
367     pmgmt_pkt_hdr = wifi_PrepDefaultMgtMsg(SUBTYPE_ACTION, da, sa, da, sizeof(wlan_mgmt_pkt) + WNM_BTM_QUERY_BUF_SIZE);
368     if (pmgmt_pkt_hdr == MNULL)
369     {
370         wifi_e("No memory for bss transition management query");
371         return (int)MLAN_STATUS_FAILURE;
372     }
373 
374     /* 802.11 management body */
375     pos    = (t_u8 *)pmgmt_pkt_hdr + sizeof(wlan_mgmt_pkt);
376     pos[0] = (t_u8)IEEE_MGMT_ACTION_CATEGORY_WNM;
377     pos[1] = (t_u8)IEEE_MGMT_WNM_BTM_QUERY;
378     pos[2] = pmpriv->bss_trans_query_token++;
379 
380     if (pmpriv->bss_trans_query_token == (t_u8)255U)
381     {
382         pmpriv->bss_trans_query_token = (t_u8)1U;
383     }
384 
385     pos[3] = query_reason;
386     pos += 4;
387 
388     meas_pkt_len           = pos - (t_u8 *)pmgmt_pkt_hdr;
389     pkt_len                = (t_u16)meas_pkt_len;
390     pmgmt_pkt_hdr->frm_len = pkt_len - (t_u16)sizeof(pmgmt_pkt_hdr->frm_len);
391 
392     (void)wifi_inject_frame(WLAN_BSS_TYPE_STA, (t_u8 *)pmgmt_pkt_hdr, pkt_len);
393 #if !CONFIG_MEM_POOLS
394     OSA_MemoryFree(pmgmt_pkt_hdr);
395 #else
396     OSA_MemoryPoolFree(buf_1536_MemoryPool, pmgmt_pkt_hdr);
397 #endif
398 
399     return (int)MLAN_STATUS_SUCCESS;
400 }
401 #endif /* CONFIG_11V */
402