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