1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* Copyright (c) 2015-2016 Quantenna Communications. All rights reserved. */ |
3 | |
4 | #include <linux/kernel.h> |
5 | #include <linux/module.h> |
6 | #include <linux/slab.h> |
7 | #include <linux/nospec.h> |
8 | |
9 | #include "cfg80211.h" |
10 | #include "core.h" |
11 | #include "qlink.h" |
12 | #include "bus.h" |
13 | #include "trans.h" |
14 | #include "util.h" |
15 | #include "event.h" |
16 | #include "qlink_util.h" |
17 | |
18 | static int |
19 | qtnf_event_handle_sta_assoc(struct qtnf_wmac *mac, struct qtnf_vif *vif, |
20 | const struct qlink_event_sta_assoc *sta_assoc, |
21 | u16 len) |
22 | { |
23 | const u8 *sta_addr; |
24 | u16 frame_control; |
25 | struct station_info *sinfo; |
26 | size_t payload_len; |
27 | u16 tlv_type; |
28 | u16 tlv_value_len; |
29 | const struct qlink_tlv_hdr *tlv; |
30 | int ret = 0; |
31 | |
32 | if (unlikely(len < sizeof(*sta_assoc))) { |
33 | pr_err("VIF%u.%u: payload is too short (%u < %zu)\n" , |
34 | mac->macid, vif->vifid, len, sizeof(*sta_assoc)); |
35 | return -EINVAL; |
36 | } |
37 | |
38 | if (vif->wdev.iftype != NL80211_IFTYPE_AP) { |
39 | pr_err("VIF%u.%u: STA_ASSOC event when not in AP mode\n" , |
40 | mac->macid, vif->vifid); |
41 | return -EPROTO; |
42 | } |
43 | |
44 | sinfo = kzalloc(size: sizeof(*sinfo), GFP_KERNEL); |
45 | if (!sinfo) |
46 | return -ENOMEM; |
47 | |
48 | sta_addr = sta_assoc->sta_addr; |
49 | frame_control = le16_to_cpu(sta_assoc->frame_control); |
50 | |
51 | pr_debug("VIF%u.%u: MAC:%pM FC:%x\n" , mac->macid, vif->vifid, sta_addr, |
52 | frame_control); |
53 | |
54 | qtnf_sta_list_add(vif, mac: sta_addr); |
55 | |
56 | sinfo->assoc_req_ies = NULL; |
57 | sinfo->assoc_req_ies_len = 0; |
58 | sinfo->generation = vif->generation; |
59 | |
60 | payload_len = len - sizeof(*sta_assoc); |
61 | |
62 | qlink_for_each_tlv(tlv, sta_assoc->ies, payload_len) { |
63 | tlv_type = le16_to_cpu(tlv->type); |
64 | tlv_value_len = le16_to_cpu(tlv->len); |
65 | |
66 | if (tlv_type == QTN_TLV_ID_IE_SET) { |
67 | const struct qlink_tlv_ie_set *ie_set; |
68 | unsigned int ie_len; |
69 | |
70 | if (tlv_value_len < |
71 | (sizeof(*ie_set) - sizeof(ie_set->hdr))) { |
72 | ret = -EINVAL; |
73 | goto out; |
74 | } |
75 | |
76 | ie_set = (const struct qlink_tlv_ie_set *)tlv; |
77 | ie_len = tlv_value_len - |
78 | (sizeof(*ie_set) - sizeof(ie_set->hdr)); |
79 | |
80 | if (ie_set->type == QLINK_IE_SET_ASSOC_REQ && ie_len) { |
81 | sinfo->assoc_req_ies = ie_set->ie_data; |
82 | sinfo->assoc_req_ies_len = ie_len; |
83 | } |
84 | } |
85 | } |
86 | |
87 | if (!qlink_tlv_parsing_ok(tlv, sta_assoc->ies, payload_len)) { |
88 | pr_err("Malformed TLV buffer\n" ); |
89 | ret = -EINVAL; |
90 | goto out; |
91 | } |
92 | |
93 | cfg80211_new_sta(dev: vif->netdev, mac_addr: sta_assoc->sta_addr, sinfo, |
94 | GFP_KERNEL); |
95 | |
96 | out: |
97 | kfree(objp: sinfo); |
98 | return ret; |
99 | } |
100 | |
101 | static int |
102 | qtnf_event_handle_sta_deauth(struct qtnf_wmac *mac, struct qtnf_vif *vif, |
103 | const struct qlink_event_sta_deauth *sta_deauth, |
104 | u16 len) |
105 | { |
106 | const u8 *sta_addr; |
107 | u16 reason; |
108 | |
109 | if (unlikely(len < sizeof(*sta_deauth))) { |
110 | pr_err("VIF%u.%u: payload is too short (%u < %zu)\n" , |
111 | mac->macid, vif->vifid, len, |
112 | sizeof(struct qlink_event_sta_deauth)); |
113 | return -EINVAL; |
114 | } |
115 | |
116 | if (vif->wdev.iftype != NL80211_IFTYPE_AP) { |
117 | pr_err("VIF%u.%u: STA_DEAUTH event when not in AP mode\n" , |
118 | mac->macid, vif->vifid); |
119 | return -EPROTO; |
120 | } |
121 | |
122 | sta_addr = sta_deauth->sta_addr; |
123 | reason = le16_to_cpu(sta_deauth->reason); |
124 | |
125 | pr_debug("VIF%u.%u: MAC:%pM reason:%x\n" , mac->macid, vif->vifid, |
126 | sta_addr, reason); |
127 | |
128 | if (qtnf_sta_list_del(vif, mac: sta_addr)) |
129 | cfg80211_del_sta(dev: vif->netdev, mac_addr: sta_deauth->sta_addr, |
130 | GFP_KERNEL); |
131 | |
132 | return 0; |
133 | } |
134 | |
135 | static int |
136 | qtnf_event_handle_bss_join(struct qtnf_vif *vif, |
137 | const struct qlink_event_bss_join *join_info, |
138 | u16 len) |
139 | { |
140 | struct wiphy *wiphy = priv_to_wiphy(priv: vif->mac); |
141 | enum ieee80211_statuscode status = le16_to_cpu(join_info->status); |
142 | struct cfg80211_chan_def chandef; |
143 | struct cfg80211_bss *bss = NULL; |
144 | u8 *ie = NULL; |
145 | size_t payload_len; |
146 | u16 tlv_type; |
147 | u16 tlv_value_len; |
148 | const struct qlink_tlv_hdr *tlv; |
149 | const u8 *rsp_ies = NULL; |
150 | size_t rsp_ies_len = 0; |
151 | |
152 | if (unlikely(len < sizeof(*join_info))) { |
153 | pr_err("VIF%u.%u: payload is too short (%u < %zu)\n" , |
154 | vif->mac->macid, vif->vifid, len, |
155 | sizeof(struct qlink_event_bss_join)); |
156 | return -EINVAL; |
157 | } |
158 | |
159 | if (vif->wdev.iftype != NL80211_IFTYPE_STATION) { |
160 | pr_err("VIF%u.%u: BSS_JOIN event when not in STA mode\n" , |
161 | vif->mac->macid, vif->vifid); |
162 | return -EPROTO; |
163 | } |
164 | |
165 | pr_debug("VIF%u.%u: BSSID:%pM chan:%u status:%u\n" , |
166 | vif->mac->macid, vif->vifid, join_info->bssid, |
167 | le16_to_cpu(join_info->chan.chan.center_freq), status); |
168 | |
169 | if (status != WLAN_STATUS_SUCCESS) |
170 | goto done; |
171 | |
172 | qlink_chandef_q2cfg(wiphy, qch: &join_info->chan, chdef: &chandef); |
173 | if (!cfg80211_chandef_valid(chandef: &chandef)) { |
174 | pr_warn("MAC%u.%u: bad channel freq=%u cf1=%u cf2=%u bw=%u\n" , |
175 | vif->mac->macid, vif->vifid, |
176 | chandef.chan ? chandef.chan->center_freq : 0, |
177 | chandef.center_freq1, |
178 | chandef.center_freq2, |
179 | chandef.width); |
180 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
181 | goto done; |
182 | } |
183 | |
184 | bss = cfg80211_get_bss(wiphy, channel: chandef.chan, bssid: join_info->bssid, |
185 | NULL, ssid_len: 0, bss_type: IEEE80211_BSS_TYPE_ESS, |
186 | privacy: IEEE80211_PRIVACY_ANY); |
187 | if (!bss) { |
188 | pr_warn("VIF%u.%u: add missing BSS:%pM chan:%u\n" , |
189 | vif->mac->macid, vif->vifid, |
190 | join_info->bssid, chandef.chan->hw_value); |
191 | |
192 | if (!vif->wdev.u.client.ssid_len) { |
193 | pr_warn("VIF%u.%u: SSID unknown for BSS:%pM\n" , |
194 | vif->mac->macid, vif->vifid, |
195 | join_info->bssid); |
196 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
197 | goto done; |
198 | } |
199 | |
200 | ie = kzalloc(size: 2 + vif->wdev.u.client.ssid_len, GFP_KERNEL); |
201 | if (!ie) { |
202 | pr_warn("VIF%u.%u: IE alloc failed for BSS:%pM\n" , |
203 | vif->mac->macid, vif->vifid, |
204 | join_info->bssid); |
205 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
206 | goto done; |
207 | } |
208 | |
209 | ie[0] = WLAN_EID_SSID; |
210 | ie[1] = vif->wdev.u.client.ssid_len; |
211 | memcpy(ie + 2, vif->wdev.u.client.ssid, |
212 | vif->wdev.u.client.ssid_len); |
213 | |
214 | bss = cfg80211_inform_bss(wiphy, rx_channel: chandef.chan, |
215 | ftype: CFG80211_BSS_FTYPE_UNKNOWN, |
216 | bssid: join_info->bssid, tsf: 0, |
217 | WLAN_CAPABILITY_ESS, beacon_interval: 100, |
218 | ie, ielen: 2 + vif->wdev.u.client.ssid_len, |
219 | signal: 0, GFP_KERNEL); |
220 | if (!bss) { |
221 | pr_warn("VIF%u.%u: can't connect to unknown BSS: %pM\n" , |
222 | vif->mac->macid, vif->vifid, |
223 | join_info->bssid); |
224 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
225 | goto done; |
226 | } |
227 | } |
228 | |
229 | payload_len = len - sizeof(*join_info); |
230 | |
231 | qlink_for_each_tlv(tlv, join_info->ies, payload_len) { |
232 | tlv_type = le16_to_cpu(tlv->type); |
233 | tlv_value_len = le16_to_cpu(tlv->len); |
234 | |
235 | if (tlv_type == QTN_TLV_ID_IE_SET) { |
236 | const struct qlink_tlv_ie_set *ie_set; |
237 | unsigned int ie_len; |
238 | |
239 | if (tlv_value_len < |
240 | (sizeof(*ie_set) - sizeof(ie_set->hdr))) { |
241 | pr_warn("invalid IE_SET TLV\n" ); |
242 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
243 | goto done; |
244 | } |
245 | |
246 | ie_set = (const struct qlink_tlv_ie_set *)tlv; |
247 | ie_len = tlv_value_len - |
248 | (sizeof(*ie_set) - sizeof(ie_set->hdr)); |
249 | |
250 | switch (ie_set->type) { |
251 | case QLINK_IE_SET_ASSOC_RESP: |
252 | if (ie_len) { |
253 | rsp_ies = ie_set->ie_data; |
254 | rsp_ies_len = ie_len; |
255 | } |
256 | break; |
257 | default: |
258 | pr_warn("unexpected IE type: %u\n" , |
259 | ie_set->type); |
260 | break; |
261 | } |
262 | } |
263 | } |
264 | |
265 | if (!qlink_tlv_parsing_ok(tlv, join_info->ies, payload_len)) |
266 | pr_warn("Malformed TLV buffer\n" ); |
267 | done: |
268 | cfg80211_connect_result(dev: vif->netdev, bssid: join_info->bssid, NULL, req_ie_len: 0, resp_ie: rsp_ies, |
269 | resp_ie_len: rsp_ies_len, status, GFP_KERNEL); |
270 | if (bss) { |
271 | if (!ether_addr_equal(addr1: vif->bssid, addr2: join_info->bssid)) |
272 | ether_addr_copy(dst: vif->bssid, src: join_info->bssid); |
273 | cfg80211_put_bss(wiphy, bss); |
274 | } |
275 | |
276 | if (status == WLAN_STATUS_SUCCESS) |
277 | netif_carrier_on(dev: vif->netdev); |
278 | |
279 | kfree(objp: ie); |
280 | return 0; |
281 | } |
282 | |
283 | static int |
284 | qtnf_event_handle_bss_leave(struct qtnf_vif *vif, |
285 | const struct qlink_event_bss_leave *leave_info, |
286 | u16 len) |
287 | { |
288 | if (unlikely(len < sizeof(*leave_info))) { |
289 | pr_err("VIF%u.%u: payload is too short (%u < %zu)\n" , |
290 | vif->mac->macid, vif->vifid, len, |
291 | sizeof(struct qlink_event_bss_leave)); |
292 | return -EINVAL; |
293 | } |
294 | |
295 | if (vif->wdev.iftype != NL80211_IFTYPE_STATION) { |
296 | pr_err("VIF%u.%u: BSS_LEAVE event when not in STA mode\n" , |
297 | vif->mac->macid, vif->vifid); |
298 | return -EPROTO; |
299 | } |
300 | |
301 | pr_debug("VIF%u.%u: disconnected\n" , vif->mac->macid, vif->vifid); |
302 | |
303 | cfg80211_disconnected(dev: vif->netdev, le16_to_cpu(leave_info->reason), |
304 | NULL, ie_len: 0, locally_generated: 0, GFP_KERNEL); |
305 | netif_carrier_off(dev: vif->netdev); |
306 | |
307 | return 0; |
308 | } |
309 | |
310 | static int |
311 | qtnf_event_handle_mgmt_received(struct qtnf_vif *vif, |
312 | const struct qlink_event_rxmgmt *rxmgmt, |
313 | u16 len) |
314 | { |
315 | const size_t min_len = sizeof(*rxmgmt) + |
316 | sizeof(struct ieee80211_hdr_3addr); |
317 | const struct ieee80211_hdr_3addr *frame = (void *)rxmgmt->frame_data; |
318 | const u16 frame_len = len - sizeof(*rxmgmt); |
319 | enum nl80211_rxmgmt_flags flags = 0; |
320 | |
321 | if (unlikely(len < min_len)) { |
322 | pr_err("VIF%u.%u: payload is too short (%u < %zu)\n" , |
323 | vif->mac->macid, vif->vifid, len, min_len); |
324 | return -EINVAL; |
325 | } |
326 | |
327 | if (le32_to_cpu(rxmgmt->flags) & QLINK_RXMGMT_FLAG_ANSWERED) |
328 | flags |= NL80211_RXMGMT_FLAG_ANSWERED; |
329 | |
330 | pr_debug("%s LEN:%u FC:%.4X SA:%pM\n" , vif->netdev->name, frame_len, |
331 | le16_to_cpu(frame->frame_control), frame->addr2); |
332 | |
333 | cfg80211_rx_mgmt(wdev: &vif->wdev, le32_to_cpu(rxmgmt->freq), sig_dbm: rxmgmt->sig_dbm, |
334 | buf: rxmgmt->frame_data, len: frame_len, flags); |
335 | |
336 | return 0; |
337 | } |
338 | |
339 | static int |
340 | qtnf_event_handle_scan_results(struct qtnf_vif *vif, |
341 | const struct qlink_event_scan_result *sr, |
342 | u16 len) |
343 | { |
344 | struct cfg80211_bss *bss; |
345 | struct ieee80211_channel *channel; |
346 | struct wiphy *wiphy = priv_to_wiphy(priv: vif->mac); |
347 | enum cfg80211_bss_frame_type frame_type = CFG80211_BSS_FTYPE_UNKNOWN; |
348 | size_t payload_len; |
349 | u16 tlv_type; |
350 | u16 tlv_value_len; |
351 | const struct qlink_tlv_hdr *tlv; |
352 | const u8 *ies = NULL; |
353 | size_t ies_len = 0; |
354 | |
355 | if (len < sizeof(*sr)) { |
356 | pr_err("VIF%u.%u: payload is too short\n" , vif->mac->macid, |
357 | vif->vifid); |
358 | return -EINVAL; |
359 | } |
360 | |
361 | channel = ieee80211_get_channel(wiphy, le16_to_cpu(sr->freq)); |
362 | if (!channel) { |
363 | pr_err("VIF%u.%u: channel at %u MHz not found\n" , |
364 | vif->mac->macid, vif->vifid, le16_to_cpu(sr->freq)); |
365 | return -EINVAL; |
366 | } |
367 | |
368 | payload_len = len - sizeof(*sr); |
369 | |
370 | qlink_for_each_tlv(tlv, sr->payload, payload_len) { |
371 | tlv_type = le16_to_cpu(tlv->type); |
372 | tlv_value_len = le16_to_cpu(tlv->len); |
373 | |
374 | if (tlv_type == QTN_TLV_ID_IE_SET) { |
375 | const struct qlink_tlv_ie_set *ie_set; |
376 | unsigned int ie_len; |
377 | |
378 | if (tlv_value_len < |
379 | (sizeof(*ie_set) - sizeof(ie_set->hdr))) |
380 | return -EINVAL; |
381 | |
382 | ie_set = (const struct qlink_tlv_ie_set *)tlv; |
383 | ie_len = tlv_value_len - |
384 | (sizeof(*ie_set) - sizeof(ie_set->hdr)); |
385 | |
386 | switch (ie_set->type) { |
387 | case QLINK_IE_SET_BEACON_IES: |
388 | frame_type = CFG80211_BSS_FTYPE_BEACON; |
389 | break; |
390 | case QLINK_IE_SET_PROBE_RESP_IES: |
391 | frame_type = CFG80211_BSS_FTYPE_PRESP; |
392 | break; |
393 | default: |
394 | frame_type = CFG80211_BSS_FTYPE_UNKNOWN; |
395 | } |
396 | |
397 | if (ie_len) { |
398 | ies = ie_set->ie_data; |
399 | ies_len = ie_len; |
400 | } |
401 | } |
402 | } |
403 | |
404 | if (!qlink_tlv_parsing_ok(tlv, sr->payload, payload_len)) |
405 | return -EINVAL; |
406 | |
407 | bss = cfg80211_inform_bss(wiphy, rx_channel: channel, ftype: frame_type, |
408 | bssid: sr->bssid, tsf: get_unaligned_le64(p: &sr->tsf), |
409 | le16_to_cpu(sr->capab), |
410 | le16_to_cpu(sr->bintval), ie: ies, ielen: ies_len, |
411 | DBM_TO_MBM(sr->sig_dbm), GFP_KERNEL); |
412 | if (!bss) |
413 | return -ENOMEM; |
414 | |
415 | cfg80211_put_bss(wiphy, bss); |
416 | |
417 | return 0; |
418 | } |
419 | |
420 | static int |
421 | qtnf_event_handle_scan_complete(struct qtnf_wmac *mac, |
422 | const struct qlink_event_scan_complete *status, |
423 | u16 len) |
424 | { |
425 | if (len < sizeof(*status)) { |
426 | pr_err("MAC%u: payload is too short\n" , mac->macid); |
427 | return -EINVAL; |
428 | } |
429 | |
430 | qtnf_scan_done(mac, le32_to_cpu(status->flags) & QLINK_SCAN_ABORTED); |
431 | |
432 | return 0; |
433 | } |
434 | |
435 | static int |
436 | qtnf_event_handle_freq_change(struct qtnf_wmac *mac, |
437 | const struct qlink_event_freq_change *data, |
438 | u16 len) |
439 | { |
440 | struct wiphy *wiphy = priv_to_wiphy(priv: mac); |
441 | struct cfg80211_chan_def chandef; |
442 | struct qtnf_vif *vif; |
443 | int i; |
444 | |
445 | if (len < sizeof(*data)) { |
446 | pr_err("MAC%u: payload is too short\n" , mac->macid); |
447 | return -EINVAL; |
448 | } |
449 | |
450 | if (!wiphy->registered) |
451 | return 0; |
452 | |
453 | qlink_chandef_q2cfg(wiphy, qch: &data->chan, chdef: &chandef); |
454 | |
455 | if (!cfg80211_chandef_valid(chandef: &chandef)) { |
456 | pr_err("MAC%u: bad channel freq=%u cf1=%u cf2=%u bw=%u\n" , |
457 | mac->macid, chandef.chan->center_freq, |
458 | chandef.center_freq1, chandef.center_freq2, |
459 | chandef.width); |
460 | return -EINVAL; |
461 | } |
462 | |
463 | pr_debug("MAC%d: new channel ieee=%u freq1=%u freq2=%u bw=%u\n" , |
464 | mac->macid, chandef.chan->hw_value, chandef.center_freq1, |
465 | chandef.center_freq2, chandef.width); |
466 | |
467 | for (i = 0; i < QTNF_MAX_INTF; i++) { |
468 | vif = &mac->iflist[i]; |
469 | |
470 | if (vif->wdev.iftype == NL80211_IFTYPE_UNSPECIFIED) |
471 | continue; |
472 | |
473 | if (vif->wdev.iftype == NL80211_IFTYPE_STATION && |
474 | !vif->wdev.connected) |
475 | continue; |
476 | |
477 | if (!vif->netdev) |
478 | continue; |
479 | |
480 | wiphy_lock(wiphy: priv_to_wiphy(priv: vif->mac)); |
481 | cfg80211_ch_switch_notify(dev: vif->netdev, chandef: &chandef, link_id: 0); |
482 | wiphy_unlock(wiphy: priv_to_wiphy(priv: vif->mac)); |
483 | } |
484 | |
485 | return 0; |
486 | } |
487 | |
488 | static int qtnf_event_handle_radar(struct qtnf_vif *vif, |
489 | const struct qlink_event_radar *ev, |
490 | u16 len) |
491 | { |
492 | struct wiphy *wiphy = priv_to_wiphy(priv: vif->mac); |
493 | struct cfg80211_chan_def chandef; |
494 | |
495 | if (len < sizeof(*ev)) { |
496 | pr_err("MAC%u: payload is too short\n" , vif->mac->macid); |
497 | return -EINVAL; |
498 | } |
499 | |
500 | if (!wiphy->registered || !vif->netdev) |
501 | return 0; |
502 | |
503 | qlink_chandef_q2cfg(wiphy, qch: &ev->chan, chdef: &chandef); |
504 | |
505 | if (!cfg80211_chandef_valid(chandef: &chandef)) { |
506 | pr_err("MAC%u: bad channel f1=%u f2=%u bw=%u\n" , |
507 | vif->mac->macid, |
508 | chandef.center_freq1, chandef.center_freq2, |
509 | chandef.width); |
510 | return -EINVAL; |
511 | } |
512 | |
513 | pr_info("%s: radar event=%u f1=%u f2=%u bw=%u\n" , |
514 | vif->netdev->name, ev->event, |
515 | chandef.center_freq1, chandef.center_freq2, |
516 | chandef.width); |
517 | |
518 | switch (ev->event) { |
519 | case QLINK_RADAR_DETECTED: |
520 | cfg80211_radar_event(wiphy, chandef: &chandef, GFP_KERNEL); |
521 | break; |
522 | case QLINK_RADAR_CAC_FINISHED: |
523 | if (!vif->wdev.cac_started) |
524 | break; |
525 | |
526 | cfg80211_cac_event(netdev: vif->netdev, chandef: &chandef, |
527 | event: NL80211_RADAR_CAC_FINISHED, GFP_KERNEL); |
528 | break; |
529 | case QLINK_RADAR_CAC_ABORTED: |
530 | if (!vif->wdev.cac_started) |
531 | break; |
532 | |
533 | cfg80211_cac_event(netdev: vif->netdev, chandef: &chandef, |
534 | event: NL80211_RADAR_CAC_ABORTED, GFP_KERNEL); |
535 | break; |
536 | case QLINK_RADAR_CAC_STARTED: |
537 | if (vif->wdev.cac_started) |
538 | break; |
539 | |
540 | if (!wiphy_ext_feature_isset(wiphy, |
541 | ftidx: NL80211_EXT_FEATURE_DFS_OFFLOAD)) |
542 | break; |
543 | |
544 | cfg80211_cac_event(netdev: vif->netdev, chandef: &chandef, |
545 | event: NL80211_RADAR_CAC_STARTED, GFP_KERNEL); |
546 | break; |
547 | default: |
548 | pr_warn("%s: unhandled radar event %u\n" , |
549 | vif->netdev->name, ev->event); |
550 | break; |
551 | } |
552 | |
553 | return 0; |
554 | } |
555 | |
556 | static int |
557 | qtnf_event_handle_external_auth(struct qtnf_vif *vif, |
558 | const struct qlink_event_external_auth *ev, |
559 | u16 len) |
560 | { |
561 | struct cfg80211_external_auth_params auth = {0}; |
562 | struct wiphy *wiphy = priv_to_wiphy(priv: vif->mac); |
563 | int ret; |
564 | |
565 | if (len < sizeof(*ev)) { |
566 | pr_err("MAC%u: payload is too short\n" , vif->mac->macid); |
567 | return -EINVAL; |
568 | } |
569 | |
570 | if (!wiphy->registered || !vif->netdev) |
571 | return 0; |
572 | |
573 | if (ev->ssid_len) { |
574 | int len = clamp_val(ev->ssid_len, 0, IEEE80211_MAX_SSID_LEN); |
575 | |
576 | memcpy(auth.ssid.ssid, ev->ssid, len); |
577 | auth.ssid.ssid_len = len; |
578 | } |
579 | |
580 | auth.key_mgmt_suite = le32_to_cpu(ev->akm_suite); |
581 | ether_addr_copy(dst: auth.bssid, src: ev->bssid); |
582 | auth.action = ev->action; |
583 | |
584 | pr_debug("%s: external SAE processing: bss=%pM action=%u akm=%u\n" , |
585 | vif->netdev->name, auth.bssid, auth.action, |
586 | auth.key_mgmt_suite); |
587 | |
588 | ret = cfg80211_external_auth_request(netdev: vif->netdev, params: &auth, GFP_KERNEL); |
589 | if (ret) |
590 | pr_warn("failed to offload external auth request\n" ); |
591 | |
592 | return ret; |
593 | } |
594 | |
595 | static int |
596 | qtnf_event_handle_mic_failure(struct qtnf_vif *vif, |
597 | const struct qlink_event_mic_failure *mic_ev, |
598 | u16 len) |
599 | { |
600 | struct wiphy *wiphy = priv_to_wiphy(priv: vif->mac); |
601 | u8 pairwise; |
602 | |
603 | if (len < sizeof(*mic_ev)) { |
604 | pr_err("VIF%u.%u: payload is too short (%u < %zu)\n" , |
605 | vif->mac->macid, vif->vifid, len, |
606 | sizeof(struct qlink_event_mic_failure)); |
607 | return -EINVAL; |
608 | } |
609 | |
610 | if (!wiphy->registered || !vif->netdev) |
611 | return 0; |
612 | |
613 | if (vif->wdev.iftype != NL80211_IFTYPE_STATION) { |
614 | pr_err("VIF%u.%u: MIC_FAILURE event when not in STA mode\n" , |
615 | vif->mac->macid, vif->vifid); |
616 | return -EPROTO; |
617 | } |
618 | |
619 | pairwise = mic_ev->pairwise ? |
620 | NL80211_KEYTYPE_PAIRWISE : NL80211_KEYTYPE_GROUP; |
621 | |
622 | pr_info("%s: MIC error: src=%pM key_index=%u pairwise=%u\n" , |
623 | vif->netdev->name, mic_ev->src, mic_ev->key_index, pairwise); |
624 | |
625 | cfg80211_michael_mic_failure(dev: vif->netdev, addr: mic_ev->src, key_type: pairwise, |
626 | key_id: mic_ev->key_index, NULL, GFP_KERNEL); |
627 | |
628 | return 0; |
629 | } |
630 | |
631 | static int |
632 | qtnf_event_handle_update_owe(struct qtnf_vif *vif, |
633 | const struct qlink_event_update_owe *owe_ev, |
634 | u16 len) |
635 | { |
636 | struct wiphy *wiphy = priv_to_wiphy(priv: vif->mac); |
637 | struct cfg80211_update_owe_info owe_info = {}; |
638 | const u16 ie_len = len - sizeof(*owe_ev); |
639 | u8 *ie; |
640 | |
641 | if (len < sizeof(*owe_ev)) { |
642 | pr_err("VIF%u.%u: payload is too short (%u < %zu)\n" , |
643 | vif->mac->macid, vif->vifid, len, |
644 | sizeof(struct qlink_event_update_owe)); |
645 | return -EINVAL; |
646 | } |
647 | |
648 | if (!wiphy->registered || !vif->netdev) |
649 | return 0; |
650 | |
651 | if (vif->wdev.iftype != NL80211_IFTYPE_AP) { |
652 | pr_err("VIF%u.%u: UPDATE_OWE event when not in AP mode\n" , |
653 | vif->mac->macid, vif->vifid); |
654 | return -EPROTO; |
655 | } |
656 | |
657 | ie = kzalloc(size: ie_len, GFP_KERNEL); |
658 | if (!ie) |
659 | return -ENOMEM; |
660 | |
661 | memcpy(owe_info.peer, owe_ev->peer, ETH_ALEN); |
662 | memcpy(ie, owe_ev->ies, ie_len); |
663 | owe_info.ie_len = ie_len; |
664 | owe_info.ie = ie; |
665 | owe_info.assoc_link_id = -1; |
666 | |
667 | pr_info("%s: external OWE processing: peer=%pM\n" , |
668 | vif->netdev->name, owe_ev->peer); |
669 | |
670 | cfg80211_update_owe_info_event(netdev: vif->netdev, owe_info: &owe_info, GFP_KERNEL); |
671 | kfree(objp: ie); |
672 | |
673 | return 0; |
674 | } |
675 | |
676 | static int qtnf_event_parse(struct qtnf_wmac *mac, |
677 | const struct sk_buff *event_skb) |
678 | { |
679 | const struct qlink_event *event; |
680 | struct qtnf_vif *vif = NULL; |
681 | int ret = -1; |
682 | u16 event_id; |
683 | u16 event_len; |
684 | u8 vifid; |
685 | |
686 | event = (const struct qlink_event *)event_skb->data; |
687 | event_id = le16_to_cpu(event->event_id); |
688 | event_len = le16_to_cpu(event->mhdr.len); |
689 | |
690 | if (event->vifid >= QTNF_MAX_INTF) { |
691 | pr_err("invalid vif(%u)\n" , event->vifid); |
692 | return -EINVAL; |
693 | } |
694 | |
695 | vifid = array_index_nospec(event->vifid, QTNF_MAX_INTF); |
696 | vif = &mac->iflist[vifid]; |
697 | |
698 | switch (event_id) { |
699 | case QLINK_EVENT_STA_ASSOCIATED: |
700 | ret = qtnf_event_handle_sta_assoc(mac, vif, sta_assoc: (const void *)event, |
701 | len: event_len); |
702 | break; |
703 | case QLINK_EVENT_STA_DEAUTH: |
704 | ret = qtnf_event_handle_sta_deauth(mac, vif, |
705 | sta_deauth: (const void *)event, |
706 | len: event_len); |
707 | break; |
708 | case QLINK_EVENT_MGMT_RECEIVED: |
709 | ret = qtnf_event_handle_mgmt_received(vif, rxmgmt: (const void *)event, |
710 | len: event_len); |
711 | break; |
712 | case QLINK_EVENT_SCAN_RESULTS: |
713 | ret = qtnf_event_handle_scan_results(vif, sr: (const void *)event, |
714 | len: event_len); |
715 | break; |
716 | case QLINK_EVENT_SCAN_COMPLETE: |
717 | ret = qtnf_event_handle_scan_complete(mac, status: (const void *)event, |
718 | len: event_len); |
719 | break; |
720 | case QLINK_EVENT_BSS_JOIN: |
721 | ret = qtnf_event_handle_bss_join(vif, join_info: (const void *)event, |
722 | len: event_len); |
723 | break; |
724 | case QLINK_EVENT_BSS_LEAVE: |
725 | ret = qtnf_event_handle_bss_leave(vif, leave_info: (const void *)event, |
726 | len: event_len); |
727 | break; |
728 | case QLINK_EVENT_FREQ_CHANGE: |
729 | ret = qtnf_event_handle_freq_change(mac, data: (const void *)event, |
730 | len: event_len); |
731 | break; |
732 | case QLINK_EVENT_RADAR: |
733 | ret = qtnf_event_handle_radar(vif, ev: (const void *)event, |
734 | len: event_len); |
735 | break; |
736 | case QLINK_EVENT_EXTERNAL_AUTH: |
737 | ret = qtnf_event_handle_external_auth(vif, ev: (const void *)event, |
738 | len: event_len); |
739 | break; |
740 | case QLINK_EVENT_MIC_FAILURE: |
741 | ret = qtnf_event_handle_mic_failure(vif, mic_ev: (const void *)event, |
742 | len: event_len); |
743 | break; |
744 | case QLINK_EVENT_UPDATE_OWE: |
745 | ret = qtnf_event_handle_update_owe(vif, owe_ev: (const void *)event, |
746 | len: event_len); |
747 | break; |
748 | default: |
749 | pr_warn("unknown event type: %x\n" , event_id); |
750 | break; |
751 | } |
752 | |
753 | return ret; |
754 | } |
755 | |
756 | static int qtnf_event_process_skb(struct qtnf_bus *bus, |
757 | const struct sk_buff *skb) |
758 | { |
759 | const struct qlink_event *event; |
760 | struct qtnf_wmac *mac; |
761 | int res; |
762 | |
763 | if (unlikely(!skb || skb->len < sizeof(*event))) { |
764 | pr_err("invalid event buffer\n" ); |
765 | return -EINVAL; |
766 | } |
767 | |
768 | event = (struct qlink_event *)skb->data; |
769 | |
770 | mac = qtnf_core_get_mac(bus, macid: event->macid); |
771 | |
772 | pr_debug("new event id:%x len:%u mac:%u vif:%u\n" , |
773 | le16_to_cpu(event->event_id), le16_to_cpu(event->mhdr.len), |
774 | event->macid, event->vifid); |
775 | |
776 | if (unlikely(!mac)) |
777 | return -ENXIO; |
778 | |
779 | rtnl_lock(); |
780 | res = qtnf_event_parse(mac, event_skb: skb); |
781 | rtnl_unlock(); |
782 | |
783 | return res; |
784 | } |
785 | |
786 | void qtnf_event_work_handler(struct work_struct *work) |
787 | { |
788 | struct qtnf_bus *bus = container_of(work, struct qtnf_bus, event_work); |
789 | struct sk_buff_head *event_queue = &bus->trans.event_queue; |
790 | struct sk_buff *current_event_skb = skb_dequeue(list: event_queue); |
791 | |
792 | while (current_event_skb) { |
793 | qtnf_event_process_skb(bus, skb: current_event_skb); |
794 | dev_kfree_skb_any(skb: current_event_skb); |
795 | current_event_skb = skb_dequeue(list: event_queue); |
796 | } |
797 | } |
798 | |