wlan_fullmac_mlme/convert/
mlme_to_fullmac.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use anyhow::{bail, Result};
6use fidl_fuchsia_wlan_ieee80211::MAX_SSID_BYTE_LEN;
7use {
8    fidl_fuchsia_wlan_common as fidl_common, fidl_fuchsia_wlan_fullmac as fidl_fullmac,
9    fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, fidl_fuchsia_wlan_mlme as fidl_mlme,
10};
11
12pub fn convert_scan_request(
13    req: fidl_mlme::ScanRequest,
14) -> Result<fidl_fullmac::WlanFullmacImplStartScanRequest> {
15    for ssid in &req.ssid_list {
16        if ssid.len() > MAX_SSID_BYTE_LEN.into() {
17            bail!(
18                "ScanRequest ssid len {} exceeds allowed meximum {}",
19                ssid.len(),
20                MAX_SSID_BYTE_LEN
21            );
22        }
23    }
24    Ok(fidl_fullmac::WlanFullmacImplStartScanRequest {
25        txn_id: Some(req.txn_id),
26        scan_type: Some(match req.scan_type {
27            fidl_mlme::ScanTypes::Active => fidl_fullmac::WlanScanType::Active,
28            fidl_mlme::ScanTypes::Passive => fidl_fullmac::WlanScanType::Passive,
29        }),
30
31        // TODO(https://fxbug.dev/301104836): Consider using None instead of Some(vec![]) for empty
32        // vectors.
33        channels: Some(req.channel_list),
34        ssids: Some(req.ssid_list),
35        min_channel_time: Some(req.min_channel_time),
36        max_channel_time: Some(req.max_channel_time),
37        ..Default::default()
38    })
39}
40
41pub fn convert_connect_request(
42    req: fidl_mlme::ConnectRequest,
43) -> fidl_fullmac::WlanFullmacImplConnectRequest {
44    fidl_fullmac::WlanFullmacImplConnectRequest {
45        selected_bss: Some(req.selected_bss),
46        connect_failure_timeout: Some(req.connect_failure_timeout),
47        auth_type: Some(convert_auth_type(req.auth_type)),
48        sae_password: Some(req.sae_password),
49
50        // NOTE: Provide both wep_key and wep_key_desc until v/g is updated to use fuchsia.wlan.ieee80211/SetKeyDescriptor.
51        wep_key: req.wep_key.clone().map(|key| convert_set_key_descriptor_legacy(&key)),
52        security_ie: Some(req.security_ie),
53        wep_key_desc: req.wep_key.map(|key| convert_set_key_descriptor(&key)),
54        ..Default::default()
55    }
56}
57
58pub fn convert_reconnect_request(
59    req: fidl_mlme::ReconnectRequest,
60) -> fidl_fullmac::WlanFullmacImplReconnectRequest {
61    fidl_fullmac::WlanFullmacImplReconnectRequest {
62        peer_sta_address: Some(req.peer_sta_address),
63        ..Default::default()
64    }
65}
66
67pub fn convert_roam_request(
68    req: fidl_mlme::RoamRequest,
69) -> fidl_fullmac::WlanFullmacImplRoamRequest {
70    fidl_fullmac::WlanFullmacImplRoamRequest {
71        selected_bss: Some(req.selected_bss),
72        ..Default::default()
73    }
74}
75pub fn convert_authenticate_response(
76    resp: fidl_mlme::AuthenticateResponse,
77) -> fidl_fullmac::WlanFullmacImplAuthRespRequest {
78    fidl_fullmac::WlanFullmacImplAuthRespRequest {
79        peer_sta_address: Some(resp.peer_sta_address),
80        result_code: Some(match resp.result_code {
81            fidl_mlme::AuthenticateResultCode::Success => fidl_fullmac::WlanAuthResult::Success,
82            fidl_mlme::AuthenticateResultCode::Refused => fidl_fullmac::WlanAuthResult::Refused,
83            fidl_mlme::AuthenticateResultCode::AntiCloggingTokenRequired => {
84                fidl_fullmac::WlanAuthResult::AntiCloggingTokenRequired
85            }
86            fidl_mlme::AuthenticateResultCode::FiniteCyclicGroupNotSupported => {
87                fidl_fullmac::WlanAuthResult::FiniteCyclicGroupNotSupported
88            }
89            fidl_mlme::AuthenticateResultCode::AuthenticationRejected => {
90                fidl_fullmac::WlanAuthResult::Rejected
91            }
92            fidl_mlme::AuthenticateResultCode::AuthFailureTimeout => {
93                fidl_fullmac::WlanAuthResult::FailureTimeout
94            }
95        }),
96        ..Default::default()
97    }
98}
99
100pub fn convert_deauthenticate_request(
101    req: fidl_mlme::DeauthenticateRequest,
102) -> fidl_fullmac::WlanFullmacImplDeauthRequest {
103    fidl_fullmac::WlanFullmacImplDeauthRequest {
104        peer_sta_address: Some(req.peer_sta_address),
105        reason_code: Some(req.reason_code),
106        ..Default::default()
107    }
108}
109
110pub fn convert_associate_response(
111    resp: fidl_mlme::AssociateResponse,
112) -> fidl_fullmac::WlanFullmacImplAssocRespRequest {
113    use fidl_fullmac::WlanAssocResult;
114    fidl_fullmac::WlanFullmacImplAssocRespRequest {
115        peer_sta_address: Some(resp.peer_sta_address),
116        result_code: Some(match resp.result_code {
117            fidl_mlme::AssociateResultCode::Success => WlanAssocResult::Success,
118            fidl_mlme::AssociateResultCode::RefusedReasonUnspecified => {
119                WlanAssocResult::RefusedReasonUnspecified
120            }
121            fidl_mlme::AssociateResultCode::RefusedNotAuthenticated => {
122                WlanAssocResult::RefusedNotAuthenticated
123            }
124            fidl_mlme::AssociateResultCode::RefusedCapabilitiesMismatch => {
125                WlanAssocResult::RefusedCapabilitiesMismatch
126            }
127            fidl_mlme::AssociateResultCode::RefusedExternalReason => {
128                WlanAssocResult::RefusedExternalReason
129            }
130            fidl_mlme::AssociateResultCode::RefusedApOutOfMemory => {
131                WlanAssocResult::RefusedApOutOfMemory
132            }
133            fidl_mlme::AssociateResultCode::RefusedBasicRatesMismatch => {
134                WlanAssocResult::RefusedBasicRatesMismatch
135            }
136            fidl_mlme::AssociateResultCode::RejectedEmergencyServicesNotSupported => {
137                WlanAssocResult::RejectedEmergencyServicesNotSupported
138            }
139            fidl_mlme::AssociateResultCode::RefusedTemporarily => {
140                WlanAssocResult::RefusedTemporarily
141            }
142        }),
143        association_id: Some(resp.association_id),
144        ..Default::default()
145    }
146}
147pub fn convert_disassociate_request(
148    req: fidl_mlme::DisassociateRequest,
149) -> fidl_fullmac::WlanFullmacImplDisassocRequest {
150    fidl_fullmac::WlanFullmacImplDisassocRequest {
151        peer_sta_address: Some(req.peer_sta_address),
152        reason_code: Some(req.reason_code),
153        ..Default::default()
154    }
155}
156
157pub fn convert_start_bss_request(
158    req: fidl_mlme::StartRequest,
159) -> Result<fidl_fullmac::WlanFullmacImplStartBssRequest> {
160    if let Some(rsne) = &req.rsne {
161        if rsne.len() > fidl_ieee80211::WLAN_IE_MAX_LEN as usize {
162            bail!(
163                "MLME RSNE length ({}) exceeds allowed maximum ({})",
164                rsne.len(),
165                fidl_ieee80211::WLAN_IE_BODY_MAX_LEN
166            );
167        }
168    }
169    Ok(fidl_fullmac::WlanFullmacImplStartBssRequest {
170        ssid: Some(req.ssid),
171        bss_type: Some(req.bss_type),
172        beacon_period: Some(req.beacon_period as u32),
173        dtim_period: Some(req.dtim_period as u32),
174        channel: Some(req.channel),
175        rsne: req.rsne,
176
177        // TODO(https://fxbug.dev/301104836): Consider removing this field or using None instead of Some(vec![]).
178        vendor_ie: Some(vec![]),
179        ..Default::default()
180    })
181}
182
183pub fn convert_stop_bss_request(
184    req: fidl_mlme::StopRequest,
185) -> Result<fidl_fullmac::WlanFullmacImplStopBssRequest> {
186    if req.ssid.len() > MAX_SSID_BYTE_LEN.into() {
187        bail!(
188            "StopBssRequest ssid len {} exceeds allowed meximum {}",
189            req.ssid.len(),
190            MAX_SSID_BYTE_LEN
191        );
192    }
193    Ok(fidl_fullmac::WlanFullmacImplStopBssRequest { ssid: Some(req.ssid), ..Default::default() })
194}
195
196// Note: this takes a reference since |req| will be used later to convert the response.
197pub fn convert_set_keys_request(
198    req: &fidl_mlme::SetKeysRequest,
199) -> Result<fidl_fullmac::WlanFullmacImplSetKeysRequest> {
200    const MAX_NUM_KEYS: usize = fidl_fullmac::WLAN_MAX_KEYLIST_SIZE as usize;
201    if req.keylist.len() > MAX_NUM_KEYS {
202        bail!(
203            "SetKeysRequest keylist len {} exceeds allowed maximum {}",
204            req.keylist.len(),
205            MAX_NUM_KEYS
206        );
207    }
208    let keylist: Vec<_> = req.keylist.iter().map(convert_set_key_descriptor_legacy).collect();
209    let key_descriptors: Vec<_> = req.keylist.iter().map(convert_set_key_descriptor).collect();
210
211    Ok(fidl_fullmac::WlanFullmacImplSetKeysRequest {
212        keylist: Some(keylist),
213        key_descriptors: Some(key_descriptors),
214        ..Default::default()
215    })
216}
217
218pub fn convert_eapol_request(
219    req: fidl_mlme::EapolRequest,
220) -> fidl_fullmac::WlanFullmacImplEapolTxRequest {
221    fidl_fullmac::WlanFullmacImplEapolTxRequest {
222        src_addr: Some(req.src_addr),
223        dst_addr: Some(req.dst_addr),
224        data: Some(req.data),
225        ..Default::default()
226    }
227}
228
229pub fn convert_sae_handshake_response(
230    resp: fidl_mlme::SaeHandshakeResponse,
231) -> fidl_fullmac::WlanFullmacImplSaeHandshakeRespRequest {
232    fidl_fullmac::WlanFullmacImplSaeHandshakeRespRequest {
233        peer_sta_address: Some(resp.peer_sta_address),
234        status_code: Some(resp.status_code),
235        ..Default::default()
236    }
237}
238
239pub fn convert_sae_frame(frame: fidl_mlme::SaeFrame) -> fidl_fullmac::SaeFrame {
240    fidl_fullmac::SaeFrame {
241        peer_sta_address: Some(frame.peer_sta_address),
242        status_code: Some(frame.status_code),
243        seq_num: Some(frame.seq_num),
244        sae_fields: Some(frame.sae_fields),
245        ..Default::default()
246    }
247}
248
249//
250// Internal helper functions
251//
252
253fn convert_auth_type(mlme_auth: fidl_mlme::AuthenticationTypes) -> fidl_fullmac::WlanAuthType {
254    match mlme_auth {
255        fidl_mlme::AuthenticationTypes::OpenSystem => fidl_fullmac::WlanAuthType::OpenSystem,
256        fidl_mlme::AuthenticationTypes::SharedKey => fidl_fullmac::WlanAuthType::SharedKey,
257        fidl_mlme::AuthenticationTypes::FastBssTransition => {
258            fidl_fullmac::WlanAuthType::FastBssTransition
259        }
260        fidl_mlme::AuthenticationTypes::Sae => fidl_fullmac::WlanAuthType::Sae,
261    }
262}
263fn convert_set_key_descriptor(
264    mlme_key: &fidl_mlme::SetKeyDescriptor,
265) -> fidl_ieee80211::SetKeyDescriptor {
266    fidl_ieee80211::SetKeyDescriptor {
267        cipher_oui: Some(mlme_key.cipher_suite_oui.clone()),
268        cipher_type: Some(mlme_key.cipher_suite_type),
269        key_type: Some(convert_key_type(mlme_key.key_type)),
270        peer_addr: Some(mlme_key.address.clone()),
271        key_id: Some(mlme_key.key_id),
272        key: Some(mlme_key.key.clone()),
273        rsc: Some(mlme_key.rsc),
274        ..Default::default()
275    }
276}
277fn convert_set_key_descriptor_legacy(
278    mlme_key: &fidl_mlme::SetKeyDescriptor,
279) -> fidl_common::WlanKeyConfig {
280    fidl_common::WlanKeyConfig {
281        // TODO(https://fxbug.dev/301104836): This is always set to RxTx. Consider removing if it's
282        // always the same value.
283        protection: Some(fidl_common::WlanProtection::RxTx),
284        cipher_oui: Some(mlme_key.cipher_suite_oui.clone()),
285        cipher_type: Some(mlme_key.cipher_suite_type),
286        key_type: Some(convert_key_type_legacy(mlme_key.key_type)),
287        peer_addr: Some(mlme_key.address.clone()),
288        key_idx: Some(mlme_key.key_id as u8),
289        key: Some(mlme_key.key.clone()),
290        rsc: Some(mlme_key.rsc),
291        ..Default::default()
292    }
293}
294
295fn convert_key_type(mlme_key_type: fidl_mlme::KeyType) -> fidl_ieee80211::KeyType {
296    match mlme_key_type {
297        fidl_mlme::KeyType::Group => fidl_ieee80211::KeyType::Group,
298        fidl_mlme::KeyType::Pairwise => fidl_ieee80211::KeyType::Pairwise,
299        fidl_mlme::KeyType::PeerKey => fidl_ieee80211::KeyType::Peer,
300        fidl_mlme::KeyType::Igtk => fidl_ieee80211::KeyType::Igtk,
301    }
302}
303
304fn convert_key_type_legacy(mlme_key_type: fidl_mlme::KeyType) -> fidl_common::WlanKeyType {
305    match mlme_key_type {
306        fidl_mlme::KeyType::Group => fidl_common::WlanKeyType::Group,
307        fidl_mlme::KeyType::Pairwise => fidl_common::WlanKeyType::Pairwise,
308        fidl_mlme::KeyType::PeerKey => fidl_common::WlanKeyType::Peer,
309        fidl_mlme::KeyType::Igtk => fidl_common::WlanKeyType::Igtk,
310    }
311}
312
313#[cfg(test)]
314mod tests {
315    use super::*;
316    use fidl_fuchsia_wlan_common as fidl_common;
317
318    fn fake_bss_description() -> fidl_common::BssDescription {
319        fidl_common::BssDescription {
320            bssid: [6, 5, 4, 3, 2, 1],
321            bss_type: fidl_common::BssType::Infrastructure,
322            beacon_period: 123u16,
323            capability_info: 456u16,
324            ies: vec![1, 2, 3, 4],
325            channel: fidl_common::WlanChannel {
326                primary: 112,
327                cbw: fidl_common::ChannelBandwidth::Cbw20,
328                secondary80: 45,
329            },
330            rssi_dbm: -41i8,
331            snr_db: -90i8,
332        }
333    }
334
335    fn fake_set_key_descriptor() -> fidl_mlme::SetKeyDescriptor {
336        fidl_mlme::SetKeyDescriptor {
337            key: vec![99, 100, 101, 102, 103, 14],
338            key_id: 23,
339            key_type: fidl_mlme::KeyType::Group,
340            address: [4u8; 6],
341            rsc: 123456,
342            cipher_suite_oui: [77, 88, 99],
343            cipher_suite_type: fidl_ieee80211::CipherSuiteType::Ccmp128,
344        }
345    }
346
347    #[test]
348    fn test_convert_scan_request_empty_vectors() {
349        let mlme = fidl_mlme::ScanRequest {
350            txn_id: 123,
351            scan_type: fidl_mlme::ScanTypes::Passive,
352            channel_list: vec![],
353            ssid_list: vec![],
354            probe_delay: 42,
355            min_channel_time: 10,
356            max_channel_time: 100,
357        };
358
359        assert_eq!(
360            convert_scan_request(mlme.clone()).unwrap(),
361            fidl_fullmac::WlanFullmacImplStartScanRequest {
362                txn_id: Some(123),
363                scan_type: Some(fidl_fullmac::WlanScanType::Passive),
364                channels: Some(vec![]),
365                ssids: Some(vec![]),
366                min_channel_time: Some(10),
367                max_channel_time: Some(100),
368                ..Default::default()
369            }
370        );
371    }
372
373    #[test]
374    fn test_convert_scan_request_ssid_too_long() {
375        let mlme = fidl_mlme::ScanRequest {
376            txn_id: 123,
377            scan_type: fidl_mlme::ScanTypes::Passive,
378            channel_list: vec![],
379            ssid_list: vec![vec![123; 4], vec![42; fidl_ieee80211::MAX_SSID_BYTE_LEN as usize + 1]],
380            probe_delay: 42,
381            min_channel_time: 10,
382            max_channel_time: 100,
383        };
384
385        assert!(convert_scan_request(mlme).is_err());
386    }
387
388    #[test]
389    fn test_convert_connect_request_no_wep_key() {
390        let mlme = fidl_mlme::ConnectRequest {
391            selected_bss: fake_bss_description(),
392            connect_failure_timeout: 60,
393            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
394            sae_password: vec![10, 11, 12, 13, 14],
395            wep_key: None,
396            security_ie: vec![44, 55, 66],
397        };
398
399        assert_eq!(
400            convert_connect_request(mlme.clone()),
401            fidl_fullmac::WlanFullmacImplConnectRequest {
402                selected_bss: Some(mlme.selected_bss.clone()),
403                connect_failure_timeout: Some(60),
404                auth_type: Some(fidl_fullmac::WlanAuthType::OpenSystem),
405                sae_password: Some(vec![10, 11, 12, 13, 14]),
406                security_ie: Some(vec![44, 55, 66]),
407                ..Default::default()
408            }
409        );
410    }
411
412    #[test]
413    fn test_convert_connect_request_empty_vectors() {
414        let mlme = fidl_mlme::ConnectRequest {
415            selected_bss: fake_bss_description(),
416            connect_failure_timeout: 60,
417            auth_type: fidl_mlme::AuthenticationTypes::OpenSystem,
418            sae_password: vec![],
419            wep_key: None,
420            security_ie: vec![],
421        };
422
423        assert_eq!(
424            convert_connect_request(mlme.clone()),
425            fidl_fullmac::WlanFullmacImplConnectRequest {
426                selected_bss: Some(mlme.selected_bss.clone()),
427                connect_failure_timeout: Some(60),
428                auth_type: Some(fidl_fullmac::WlanAuthType::OpenSystem),
429                sae_password: Some(vec![]),
430                security_ie: Some(vec![]),
431                ..Default::default()
432            }
433        );
434    }
435
436    #[test]
437    fn test_convert_start_bss_request_rsne_too_long() {
438        let mlme = fidl_mlme::StartRequest {
439            ssid: vec![1, 2, 3, 4],
440            bss_type: fidl_common::BssType::Independent,
441            beacon_period: 10000,
442            dtim_period: 123,
443            channel: 12,
444            capability_info: 4321,
445            rates: vec![10, 20, 30, 40],
446            country: fidl_mlme::Country { alpha2: [1, 2], suffix: 45 },
447            mesh_id: vec![6, 5, 6, 5],
448            rsne: Some(vec![123; fidl_ieee80211::WLAN_IE_MAX_LEN as usize + 1]),
449            phy: fidl_common::WlanPhyType::Ofdm,
450            channel_bandwidth: fidl_common::ChannelBandwidth::Cbw20,
451        };
452
453        assert!(convert_start_bss_request(mlme).is_err());
454    }
455
456    #[test]
457    fn test_convert_stop_bss_request_ssid_too_long() {
458        let mlme = fidl_mlme::StopRequest {
459            ssid: vec![42; fidl_ieee80211::MAX_SSID_BYTE_LEN as usize + 1],
460        };
461        assert!(convert_stop_bss_request(mlme).is_err());
462    }
463
464    #[test]
465    fn test_convert_set_keys_request() {
466        let mlme = fidl_mlme::SetKeysRequest { keylist: vec![fake_set_key_descriptor(); 2] };
467
468        let fullmac = convert_set_keys_request(&mlme).unwrap();
469
470        assert_eq!(fullmac.keylist.as_ref().unwrap().len(), 2);
471        let keylist = fullmac.keylist.unwrap();
472        for key in &keylist[0..2] {
473            assert_eq!(key, &convert_set_key_descriptor_legacy(&fake_set_key_descriptor()));
474        }
475
476        let key_descriptors = fullmac.key_descriptors.unwrap();
477        for key in &key_descriptors[0..2] {
478            assert_eq!(key, &convert_set_key_descriptor(&fake_set_key_descriptor()));
479        }
480    }
481
482    #[test]
483    fn test_convert_set_keys_request_keylist_too_long() {
484        let mlme = fidl_mlme::SetKeysRequest {
485            keylist: vec![
486                fake_set_key_descriptor();
487                fidl_fullmac::WLAN_MAX_KEYLIST_SIZE as usize + 1
488            ],
489        };
490        assert!(convert_set_keys_request(&mlme).is_err());
491    }
492
493    //
494    // Helper function unit tests
495    //
496    #[test]
497    fn test_convert_set_key_descriptor() {
498        let mlme = fidl_mlme::SetKeyDescriptor {
499            key: vec![1, 2, 3],
500            key_id: 123,
501            key_type: fidl_mlme::KeyType::Group,
502            address: [3; 6],
503            rsc: 1234567,
504            cipher_suite_oui: [4, 3, 2],
505            cipher_suite_type: fidl_ieee80211::CipherSuiteType::Ccmp128,
506        };
507
508        assert_eq!(
509            convert_set_key_descriptor(&mlme),
510            fidl_ieee80211::SetKeyDescriptor {
511                cipher_oui: Some([4, 3, 2]),
512                cipher_type: Some(fidl_ieee80211::CipherSuiteType::Ccmp128),
513                key_type: Some(fidl_ieee80211::KeyType::Group),
514                peer_addr: Some([3; 6]),
515                key_id: Some(123),
516                key: Some(vec![1, 2, 3]),
517                rsc: Some(1234567),
518                ..Default::default()
519            }
520        );
521    }
522}