blob: b7f514c958cc173f0eb1ec00a9953dce88a41068 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
anyhow::Error,
fidl::endpoints::{create_endpoints, create_proxy},
fidl_fuchsia_wlan_common as fidl_common,
fidl_fuchsia_wlan_common::WlanMacRole,
fidl_fuchsia_wlan_ieee80211 as fidl_ieee80211, fidl_fuchsia_wlan_mlme as fidl_mlme,
fidl_fuchsia_wlan_policy as fidl_policy,
fidl_fuchsia_wlan_tap::{
SetChannelArgs, TxArgs, WlanRxInfo, WlantapPhyConfig, WlantapPhyEvent, WlantapPhyProxy,
},
fuchsia_component::client::connect_to_protocol,
fuchsia_syslog as syslog,
fuchsia_zircon::prelude::*,
ieee80211::{Bssid, Ssid},
lazy_static::lazy_static,
log::{debug, error},
pin_utils::pin_mut,
std::{convert::TryFrom, future::Future, marker::Unpin},
wlan_common::{
bss::Protection,
data_writer,
ie::{
rsn::{
cipher::{Cipher, CIPHER_CCMP_128, CIPHER_TKIP},
rsne,
suite_filter::DEFAULT_GROUP_MGMT_CIPHER,
},
wpa,
},
mac, mgmt_writer, TimeUnit,
},
wlan_frame_writer::write_frame_with_dynamic_buf,
wlan_rsn::{self, rsna::SecAssocUpdate},
};
pub mod test_utils;
pub use eth_helper::*;
pub use event_handler_helper::*;
pub use wlancfg_helper::*;
pub use wlanstack_helper::*;
mod config;
mod eth_helper;
mod event_handler_helper;
mod wlancfg_helper;
mod wlanstack_helper;
pub const CLIENT_MAC_ADDR: [u8; 6] = [0x67, 0x62, 0x6f, 0x6e, 0x69, 0x6b];
pub const AP_MAC_ADDR: Bssid = Bssid([0x70, 0xf1, 0x1c, 0x05, 0x2d, 0x7f]);
lazy_static! {
pub static ref AP_SSID: Ssid = Ssid::try_from("ap_ssid").unwrap();
}
pub const ETH_DST_MAC: [u8; 6] = [0x65, 0x74, 0x68, 0x64, 0x73, 0x74];
pub const CHANNEL: fidl_common::WlanChannel = fidl_common::WlanChannel {
primary: 1,
secondary80: 0,
cbw: fidl_common::ChannelBandwidth::Cbw20,
};
pub const WLANCFG_DEFAULT_AP_CHANNEL: fidl_common::WlanChannel = fidl_common::WlanChannel {
primary: 11,
secondary80: 0,
cbw: fidl_common::ChannelBandwidth::Cbw20,
};
pub fn default_wlantap_config_client() -> WlantapPhyConfig {
wlantap_config_client(format!("wlantap-client"), CLIENT_MAC_ADDR)
}
pub fn wlantap_config_client(name: String, mac_addr: [u8; 6]) -> WlantapPhyConfig {
config::create_wlantap_config(name, mac_addr, WlanMacRole::Client)
}
pub fn default_wlantap_config_ap() -> WlantapPhyConfig {
wlantap_config_ap(format!("wlantap-ap"), AP_MAC_ADDR.0)
}
pub fn wlantap_config_ap(name: String, mac_addr: [u8; 6]) -> WlantapPhyConfig {
config::create_wlantap_config(name, mac_addr, WlanMacRole::Ap)
}
pub fn create_rx_info(channel: &fidl_common::WlanChannel, rssi_dbm: i8) -> WlanRxInfo {
// should match enum WlanRxInfoValid::RSSI in zircon/system/banjo/fuchsia.hardware.wlan.associnfo/info.banjo
const WLAN_RX_INFO_VALID_RSSI: u32 = 0x10;
WlanRxInfo {
rx_flags: 0,
valid_fields: if rssi_dbm == 0 { 0 } else { WLAN_RX_INFO_VALID_RSSI },
phy: fidl_common::WlanPhyType::Dsss,
data_rate: 0,
channel: channel.clone(),
mcs: 0,
rssi_dbm,
snr_dbh: 0,
}
}
enum BeaconOrProbeResp<'a> {
Beacon,
ProbeResp { wsc_ie: Option<&'a [u8]> },
}
fn generate_probe_or_beacon(
type_: BeaconOrProbeResp<'_>,
channel: &fidl_common::WlanChannel,
bssid: &Bssid,
ssid: &Ssid,
protection: &Protection,
) -> Result<Vec<u8>, anyhow::Error> {
let wpa1_ie = wpa::fake_wpa_ies::fake_deprecated_wpa1_vendor_ie();
// Unrealistically long beacon period so that auth/assoc don't timeout on slow bots.
let beacon_interval = TimeUnit::DEFAULT_BEACON_INTERVAL * 20u16;
let capabilities = mac::CapabilityInfo(0)
// IEEE Std 802.11-2016, 9.4.1.4: An AP sets the ESS subfield to 1 and the IBSS
// subfield to 0 within transmitted Beacon or Probe Response frames.
.with_ess(true)
.with_ibss(false)
// IEEE Std 802.11-2016, 9.4.1.4: An AP sets the Privacy subfield to 1 within
// transmitted Beacon, Probe Response, (Re)Association Response frames if data
// confidentiality is required for all Data frames exchanged within the BSS.
.with_privacy(*protection != Protection::Open);
let beacon_hdr = match type_ {
BeaconOrProbeResp::Beacon => Some(mac::BeaconHdr::new(beacon_interval, capabilities)),
BeaconOrProbeResp::ProbeResp { wsc_ie: _ } => None,
};
let proberesp_hdr = match type_ {
BeaconOrProbeResp::Beacon => None,
BeaconOrProbeResp::ProbeResp { .. } => {
Some(mac::ProbeRespHdr::new(beacon_interval, capabilities))
}
};
let (buf, _bytes_written) = write_frame_with_dynamic_buf!(vec![], {
headers: {
mac::MgmtHdr: &mgmt_writer::mgmt_hdr_from_ap(
mac::FrameControl(0)
.with_frame_type(mac::FrameType::MGMT)
.with_mgmt_subtype(match type_ {
BeaconOrProbeResp::Beacon => mac::MgmtSubtype::BEACON,
BeaconOrProbeResp::ProbeResp{..} => mac::MgmtSubtype::PROBE_RESP
}),
match type_ {
BeaconOrProbeResp::Beacon => mac::BCAST_ADDR,
BeaconOrProbeResp::ProbeResp{..} => CLIENT_MAC_ADDR
},
*bssid,
mac::SequenceControl(0).with_seq_num(123),
),
mac::BeaconHdr?: beacon_hdr,
mac::ProbeRespHdr?: proberesp_hdr,
},
ies: {
ssid: ssid,
supported_rates: &[0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24, 0x30, 0x48, 0xe0, 0x6c],
extended_supported_rates: { /* continues from supported_rates */ },
dsss_param_set: &ie::DsssParamSet { current_channel: channel.primary },
rsne?: match protection {
Protection::Unknown => panic!("Cannot send beacon with unknown protection"),
Protection::Open | Protection::Wep | Protection::Wpa1 => None,
Protection::Wpa1Wpa2Personal | Protection::Wpa2Personal => Some(rsne::Rsne::wpa2_rsne()),
Protection::Wpa2Wpa3Personal => Some(rsne::Rsne::wpa2_wpa3_rsne()),
Protection::Wpa3Personal => Some(rsne::Rsne::wpa3_rsne()),
_ => panic!("unsupported fake beacon: {:?}", protection),
},
wpa1?: match protection {
Protection::Unknown => panic!("Cannot send beacon with unknown protection"),
Protection::Open | Protection::Wep => None,
Protection::Wpa1 | Protection::Wpa1Wpa2Personal => Some(&wpa1_ie),
Protection::Wpa2Personal | Protection::Wpa2Wpa3Personal | Protection::Wpa3Personal => None,
_ => panic!("unsupported fake beacon: {:?}", protection),
},
wsc?: match type_ {
BeaconOrProbeResp::Beacon => None,
BeaconOrProbeResp::ProbeResp{ wsc_ie } => wsc_ie.clone()
}
},
})?;
Ok(buf)
}
pub fn send_beacon(
channel: &fidl_common::WlanChannel,
bssid: &Bssid,
ssid: &Ssid,
protection: &Protection,
proxy: &WlantapPhyProxy,
rssi_dbm: i8,
) -> Result<(), anyhow::Error> {
let buf =
generate_probe_or_beacon(BeaconOrProbeResp::Beacon, channel, bssid, ssid, protection)?;
proxy.rx(0, &buf, &mut create_rx_info(channel, rssi_dbm))?;
Ok(())
}
pub fn send_probe_resp(
channel: &fidl_common::WlanChannel,
bssid: &Bssid,
ssid: &Ssid,
protection: &Protection,
wsc_ie: Option<&[u8]>,
proxy: &WlantapPhyProxy,
rssi_dbm: i8,
) -> Result<(), anyhow::Error> {
let buf = generate_probe_or_beacon(
BeaconOrProbeResp::ProbeResp { wsc_ie },
channel,
bssid,
ssid,
protection,
)?;
proxy.rx(0, &buf, &mut create_rx_info(channel, rssi_dbm))?;
Ok(())
}
pub fn send_sae_authentication_frame(
sae_frame: &fidl_mlme::SaeFrame,
channel: &fidl_common::WlanChannel,
bssid: &Bssid,
proxy: &WlantapPhyProxy,
) -> Result<(), anyhow::Error> {
let (buf, _bytes_written) = write_frame_with_dynamic_buf!(vec![], {
headers: {
mac::MgmtHdr: &mgmt_writer::mgmt_hdr_from_ap(
mac::FrameControl(0)
.with_frame_type(mac::FrameType::MGMT)
.with_mgmt_subtype(mac::MgmtSubtype::AUTH),
CLIENT_MAC_ADDR,
*bssid,
mac::SequenceControl(0).with_seq_num(123),
),
mac::AuthHdr: &mac::AuthHdr {
auth_alg_num: mac::AuthAlgorithmNumber::SAE,
auth_txn_seq_num: sae_frame.seq_num,
status_code: sae_frame.status_code.into(),
},
},
body: &sae_frame.sae_fields[..],
})?;
proxy.rx(0, &buf, &mut create_rx_info(channel, 0))?;
Ok(())
}
pub fn send_open_authentication_success(
channel: &fidl_common::WlanChannel,
bssid: &Bssid,
proxy: &WlantapPhyProxy,
) -> Result<(), anyhow::Error> {
let (buf, _bytes_written) = write_frame_with_dynamic_buf!(vec![], {
headers: {
mac::MgmtHdr: &mgmt_writer::mgmt_hdr_from_ap(
mac::FrameControl(0)
.with_frame_type(mac::FrameType::MGMT)
.with_mgmt_subtype(mac::MgmtSubtype::AUTH),
CLIENT_MAC_ADDR,
*bssid,
mac::SequenceControl(0).with_seq_num(123),
),
mac::AuthHdr: &mac::AuthHdr {
auth_alg_num: mac::AuthAlgorithmNumber::OPEN,
auth_txn_seq_num: 2,
status_code: fidl_ieee80211::StatusCode::Success.into(),
},
},
})?;
proxy.rx(0, &buf, &mut create_rx_info(channel, 0))?;
Ok(())
}
pub fn send_association_response(
channel: &fidl_common::WlanChannel,
bssid: &Bssid,
status_code: mac::StatusCode,
proxy: &WlantapPhyProxy,
) -> Result<(), anyhow::Error> {
let (buf, _bytes_written) = write_frame_with_dynamic_buf!(vec![], {
headers: {
mac::MgmtHdr: &mgmt_writer::mgmt_hdr_from_ap(
mac::FrameControl(0)
.with_frame_type(mac::FrameType::MGMT)
.with_mgmt_subtype(mac::MgmtSubtype::ASSOC_RESP),
CLIENT_MAC_ADDR,
*bssid,
mac::SequenceControl(0).with_seq_num(123),
),
mac::AssocRespHdr: &mac::AssocRespHdr {
capabilities: mac::CapabilityInfo(0).with_ess(true).with_short_preamble(true),
status_code,
aid: 2, // does not matter
},
},
ies: {
// These rates will be captured in assoc_ctx to initialize Minstrel. 11b rates are
// ignored.
// tx_vec_idx: _ _ _ 129 130 _ 131 132
supported_rates: &[0x82, 0x84, 0x8b, 0x0c, 0x12, 0x96, 0x18, 0x24],
// tx_vec_idx: 133 134 basic_135 136
extended_supported_rates: &[48, 72, 128 + 96, 108],
},
})?;
proxy.rx(0, &buf, &mut create_rx_info(channel, 0))?;
Ok(())
}
pub fn send_disassociate(
channel: &fidl_common::WlanChannel,
bssid: &Bssid,
reason_code: mac::ReasonCode,
proxy: &WlantapPhyProxy,
) -> Result<(), anyhow::Error> {
let (buf, _bytes_written) = write_frame_with_dynamic_buf!(vec![], {
headers: {
mac::MgmtHdr: &mgmt_writer::mgmt_hdr_from_ap(
mac::FrameControl(0)
.with_frame_type(mac::FrameType::MGMT)
.with_mgmt_subtype(mac::MgmtSubtype::DISASSOC),
CLIENT_MAC_ADDR,
*bssid,
mac::SequenceControl(0).with_seq_num(123),
),
mac::DisassocHdr: &mac::DisassocHdr {
reason_code,
},
},
})?;
proxy.rx(0, &buf, &mut create_rx_info(channel, 0))?;
Ok(())
}
pub fn password_to_policy_credential<S: ToString>(password: Option<S>) -> fidl_policy::Credential {
return match password {
None => fidl_policy::Credential::None(fidl_policy::Empty),
Some(p) => fidl_policy::Credential::Password(p.to_string().as_bytes().to_vec()),
};
}
pub fn create_authenticator(
bssid: &Bssid,
ssid: &Ssid,
password: &str,
// The group key cipher
gtk_cipher: Cipher,
// The advertised protection in the IEs during the 4-way handshake
advertised_protection: Protection,
// The protection used for the actual handshake
supplicant_protection: Protection,
) -> wlan_rsn::Authenticator {
let nonce_rdr = wlan_rsn::nonce::NonceReader::new(&bssid.0).expect("creating nonce reader");
let gtk_provider = wlan_rsn::GtkProvider::new(gtk_cipher).expect("creating gtk provider");
let advertised_protection_info = match advertised_protection {
Protection::Wpa3Personal => wlan_rsn::ProtectionInfo::Rsne(rsne::Rsne::wpa3_rsne()),
Protection::Wpa2Wpa3Personal => {
wlan_rsn::ProtectionInfo::Rsne(rsne::Rsne::wpa2_wpa3_rsne())
}
Protection::Wpa2Personal | Protection::Wpa1Wpa2Personal => {
wlan_rsn::ProtectionInfo::Rsne(rsne::Rsne::wpa2_rsne())
}
Protection::Wpa2PersonalTkipOnly | Protection::Wpa1Wpa2PersonalTkipOnly => {
panic!("need tkip support")
}
Protection::Wpa1 => {
wlan_rsn::ProtectionInfo::LegacyWpa(wpa::fake_wpa_ies::fake_deprecated_wpa1_vendor_ie())
}
_ => {
panic!("{} not implemented", advertised_protection)
}
};
match supplicant_protection {
Protection::Wpa1 | Protection::Wpa2Personal => {
let psk = wlan_rsn::psk::compute(password.as_bytes(), ssid).expect("computing PSK");
let supplicant_protection_info = match supplicant_protection {
Protection::Wpa1 => wlan_rsn::ProtectionInfo::LegacyWpa(
wpa::fake_wpa_ies::fake_deprecated_wpa1_vendor_ie(),
),
Protection::Wpa2Personal => wlan_rsn::ProtectionInfo::Rsne(rsne::Rsne::wpa2_rsne()),
_ => unreachable!("impossible combination in this nested match"),
};
wlan_rsn::Authenticator::new_wpa2psk_ccmp128(
nonce_rdr,
std::sync::Arc::new(std::sync::Mutex::new(gtk_provider)),
psk,
CLIENT_MAC_ADDR,
supplicant_protection_info,
bssid.0,
advertised_protection_info,
)
.expect("creating authenticator")
}
Protection::Wpa3Personal => {
let igtk_provider = wlan_rsn::IgtkProvider::new(DEFAULT_GROUP_MGMT_CIPHER)
.expect("creating igtk provider");
let supplicant_protection_info =
wlan_rsn::ProtectionInfo::Rsne(rsne::Rsne::wpa3_rsne());
wlan_rsn::Authenticator::new_wpa3(
nonce_rdr,
std::sync::Arc::new(std::sync::Mutex::new(gtk_provider)),
std::sync::Arc::new(std::sync::Mutex::new(igtk_provider)),
ssid.clone(),
password.as_bytes().to_vec(),
CLIENT_MAC_ADDR,
supplicant_protection_info,
bssid.0,
advertised_protection_info,
)
.expect("creating authenticator")
}
_ => {
panic!("Cannot create an authenticator for {}", supplicant_protection)
}
}
}
pub fn process_tx_auth_updates(
authenticator: &mut wlan_rsn::Authenticator,
update_sink: &mut wlan_rsn::rsna::UpdateSink,
channel: &fidl_common::WlanChannel,
bssid: &Bssid,
phy: &WlantapPhyProxy,
ready_for_sae_frames: bool,
ready_for_eapol_frames: bool,
) -> Result<(), anyhow::Error> {
if !ready_for_sae_frames && !ready_for_eapol_frames {
return Ok(());
}
// TODO(fxbug.dev/69580): Use Vec::drain_filter instead.
let mut i = 0;
while i < update_sink.len() {
match &update_sink[i] {
SecAssocUpdate::TxSaeFrame(sae_frame) if ready_for_sae_frames => {
send_sae_authentication_frame(&sae_frame, &CHANNEL, bssid, &phy)
.expect("Error sending fake SAE authentication frame.");
update_sink.remove(i);
}
SecAssocUpdate::TxEapolKeyFrame { frame, .. } if ready_for_eapol_frames => {
rx_wlan_data_frame(
channel,
&CLIENT_MAC_ADDR,
&bssid.0,
&bssid.0,
&frame[..],
mac::ETHER_TYPE_EAPOL,
phy,
)?;
update_sink.remove(i);
authenticator
.on_eapol_conf(update_sink, fidl_mlme::EapolResultCode::Success)
.expect("Error sending EAPOL confirm");
}
_ => i += 1,
};
}
Ok(())
}
pub fn handle_set_channel_event(
args: &SetChannelArgs,
phy: &WlantapPhyProxy,
ssid: &Ssid,
bssid: &Bssid,
protection: &Protection,
) {
debug!("Handling set channel event on channel {:?}", args.channel);
if args.channel.primary == CHANNEL.primary {
send_beacon(&args.channel, bssid, ssid, protection, &phy, 0).unwrap();
}
}
pub fn handle_tx_event<F>(
args: &TxArgs,
phy: &WlantapPhyProxy,
ssid: &Ssid,
bssid: &Bssid,
protection: &Protection,
authenticator: &mut Option<wlan_rsn::Authenticator>,
update_sink: &mut Option<wlan_rsn::rsna::UpdateSink>,
mut process_auth_update: F,
) where
F: FnMut(
&mut wlan_rsn::Authenticator,
&mut wlan_rsn::rsna::UpdateSink,
&fidl_common::WlanChannel,
&Bssid,
&WlantapPhyProxy,
bool, // ready_for_sae_frames
bool, // ready_for_eapol_frames
) -> Result<(), anyhow::Error>,
{
debug!("Handling tx event.");
match mac::MacFrame::parse(&args.packet.data[..], false) {
Some(mac::MacFrame::Mgmt { mgmt_hdr, body, .. }) => {
match mac::MgmtBody::parse({ mgmt_hdr.frame_ctrl }.mgmt_subtype(), body) {
Some(mac::MgmtBody::Authentication { auth_hdr, elements }) => {
match auth_hdr.auth_alg_num {
mac::AuthAlgorithmNumber::OPEN => match authenticator {
Some(wlan_rsn::Authenticator {
auth_cfg: wlan_rsn::auth::Config::ComputedPsk(_),
..
})
| None => {
send_open_authentication_success(&CHANNEL, bssid, &phy)
.expect("Error sending fake OPEN authentication frame.");
}
_ => panic!(
"Unexpected OPEN authentication frame for {:?}",
authenticator
),
},
mac::AuthAlgorithmNumber::SAE => {
let mut authenticator = authenticator.as_mut().unwrap_or_else(|| {
panic!("Unexpected SAE authentication frame with no Authenticator")
});
let mut update_sink = update_sink.as_mut().unwrap_or_else(|| {
panic!("No UpdateSink provided with Authenticator.")
});
log::info!("auth_txn_seq_num: {}", { auth_hdr.auth_txn_seq_num });
// Reset authenticator state just in case this is not the 1st
// connection attempt. SAE handshake uses multiple frames, so only
// reset on the first auth frame.
// Note: If we want to test retransmission of auth frame 1, we should
// find a different way to do this.
if auth_hdr.auth_txn_seq_num == 1 {
authenticator.reset();
// Clear out update sink so that the authenticator doesn't process
// the PMK from the first attempt.
update_sink.clear();
}
authenticator
.on_sae_frame_rx(
&mut update_sink,
fidl_mlme::SaeFrame {
peer_sta_address: bssid.0,
// TODO(fxbug.dev/91353): All reserved values mapped to REFUSED_REASON_UNSPECIFIED.
status_code: Option::<fidl_ieee80211::StatusCode>::from(
auth_hdr.status_code,
)
.unwrap_or(
fidl_ieee80211::StatusCode::RefusedReasonUnspecified,
),
seq_num: auth_hdr.auth_txn_seq_num,
sae_fields: elements.to_vec(),
},
)
.expect("processing SAE frame");
process_auth_update(
&mut authenticator,
&mut update_sink,
&CHANNEL,
bssid,
&phy,
true,
false,
)
.expect("processing authenticator updates during authentication");
}
auth_alg_num => {
panic!("Unexpected authentication algorithm number: {:?}", auth_alg_num)
}
}
}
Some(mac::MgmtBody::AssociationReq { .. }) => {
send_association_response(
&CHANNEL,
bssid,
fidl_ieee80211::StatusCode::Success.into(),
&phy,
)
.expect("Error sending fake association response frame.");
if let Some(authenticator) = authenticator {
let mut update_sink = update_sink.as_mut().unwrap_or_else(|| {
panic!("No UpdateSink provided with Authenticator.")
});
match authenticator.auth_cfg {
wlan_rsn::auth::Config::Sae { .. } => {}
wlan_rsn::auth::Config::ComputedPsk(_) => authenticator.reset(),
wlan_rsn::auth::Config::DriverSae { .. } => panic!(
"hw-sim does not support wlan_rsn::auth::Config::DriverSae(_)"
),
}
authenticator.initiate(&mut update_sink).expect("initiating authenticator");
process_auth_update(authenticator, &mut update_sink, &CHANNEL, bssid, &phy, true, true).expect(
"processing authenticator updates immediately after association complete",
);
}
}
Some(mac::MgmtBody::ProbeReq { .. }) => {
// Normally, the AP would only send probe response on the channel it's
// on, but our TestHelper doesn't have that feature yet and it
// does not affect any current tests.
send_probe_resp(&CHANNEL, &bssid, ssid, protection, None, &phy, -10)
.expect("Error sending fake probe response frame");
}
_ => {}
}
}
// EAPOL frames are transmitted as data frames with LLC protocol being EAPOL
Some(mac::MacFrame::Data { .. }) => {
let msdus = mac::MsduIterator::from_raw_data_frame(&args.packet.data[..], false)
.expect("reading msdu from data frame");
for mac::Msdu { llc_frame, .. } in msdus {
assert_eq!(llc_frame.hdr.protocol_id.to_native(), mac::ETHER_TYPE_EAPOL);
if let Some(authenticator) = authenticator {
let mut update_sink = update_sink
.as_mut()
.unwrap_or_else(|| panic!("No UpdateSink provided with Authenticator."));
let mic_size = authenticator.get_negotiated_protection().mic_size;
let frame_rx = eapol::KeyFrameRx::parse(mic_size as usize, llc_frame.body)
.expect("parsing EAPOL frame");
if let Err(e) =
authenticator.on_eapol_frame(&mut update_sink, eapol::Frame::Key(frame_rx))
{
error!("error sending EAPOL frame to authenticator: {}", e);
}
process_auth_update(
authenticator,
update_sink,
&CHANNEL,
bssid,
&phy,
true,
true,
)
.expect("processing authenticator updates after EAPOL frame");
}
}
}
_ => (),
}
}
pub fn handle_connect_events(
event: &WlantapPhyEvent,
phy: &WlantapPhyProxy,
ssid: &Ssid,
bssid: &Bssid,
protection: &Protection,
authenticator: &mut Option<wlan_rsn::Authenticator>,
update_sink: &mut Option<wlan_rsn::rsna::UpdateSink>,
) {
match event {
WlantapPhyEvent::SetChannel { args } => {
handle_set_channel_event(&args, phy, ssid, bssid, protection)
}
WlantapPhyEvent::Tx { args } => match authenticator {
Some(_) => match update_sink {
Some(_) => handle_tx_event(
&args,
phy,
ssid,
bssid,
protection,
authenticator,
update_sink,
process_tx_auth_updates,
),
None => panic!("No UpdateSink provided with Authenticator."),
},
None => handle_tx_event(
&args,
phy,
ssid,
bssid,
protection,
&mut None,
&mut None,
process_tx_auth_updates,
),
},
_ => (),
}
}
pub async fn save_network_and_wait_until_connected(
ssid: &Ssid,
security_type: fidl_policy::SecurityType,
credential: fidl_policy::Credential,
) -> (fidl_policy::ClientControllerProxy, fidl_policy::ClientStateUpdatesRequestStream) {
// Connect to the client policy service and get a client controller.
let (client_controller, mut client_state_update_stream) =
wlancfg_helper::init_client_controller().await;
save_network(&client_controller, ssid, security_type, credential).await;
// Wait until the policy layer indicates that the client has successfully connected.
wait_until_client_state(&mut client_state_update_stream, |update| {
has_ssid_and_state(update, ssid, fidl_policy::ConnectionState::Connected)
})
.await;
(client_controller, client_state_update_stream)
}
pub async fn connect_to_ap<F, R>(
connect_fut: F,
helper: &mut test_utils::TestHelper,
ap_ssid: &Ssid,
ap_bssid: &Bssid,
protection: &Protection,
authenticator: &mut Option<wlan_rsn::Authenticator>,
update_sink: &mut Option<wlan_rsn::rsna::UpdateSink>,
timeout: Option<i64>,
) -> R
where
F: Future<Output = R> + Unpin,
{
// Assert UpdateSink provided if there is an Authenticator.
if matches!(authenticator, Some(_)) && !matches!(update_sink, Some(_)) {
panic!("No UpdateSink provided with Authenticator");
};
let phy = helper.proxy();
helper
.run_until_complete_or_timeout(
timeout.unwrap_or(30).seconds(),
format!("connecting to {} ({:02X?})", ap_ssid.to_string_not_redactable(), ap_bssid),
|event| {
handle_connect_events(
&event,
&phy,
ap_ssid,
ap_bssid,
protection,
authenticator,
update_sink,
);
},
connect_fut,
)
.await
}
pub async fn connect_with_security_type(
helper: &mut test_utils::TestHelper,
ssid: &Ssid,
bssid: &Bssid,
password: Option<&str>,
bss_protection: Protection,
policy_security_type: fidl_policy::SecurityType,
) {
let credential = password_to_policy_credential(password);
let connect_fut = save_network_and_wait_until_connected(ssid, policy_security_type, credential);
pin_mut!(connect_fut);
// Create the authenticator
let (mut authenticator, mut update_sink) = match bss_protection {
Protection::Wpa3Personal | Protection::Wpa2Wpa3Personal => (
password.map(|p| {
create_authenticator(
bssid,
ssid,
p,
CIPHER_CCMP_128,
bss_protection,
Protection::Wpa3Personal,
)
}),
Some(wlan_rsn::rsna::UpdateSink::default()),
),
Protection::Wpa2Personal | Protection::Wpa1Wpa2Personal => (
password.map(|p| {
create_authenticator(
bssid,
ssid,
p,
CIPHER_CCMP_128,
bss_protection,
Protection::Wpa2Personal,
)
}),
Some(wlan_rsn::rsna::UpdateSink::default()),
),
Protection::Wpa2PersonalTkipOnly | Protection::Wpa1Wpa2PersonalTkipOnly => {
panic!("need tkip support")
}
Protection::Wpa1 => (
password.map(|p| {
create_authenticator(bssid, ssid, p, CIPHER_TKIP, bss_protection, Protection::Wpa1)
}),
Some(wlan_rsn::rsna::UpdateSink::default()),
),
Protection::Open => (None, None),
_ => {
panic!("This helper doesn't yet support {}", bss_protection)
}
};
connect_to_ap(
connect_fut,
helper,
ssid,
bssid,
&bss_protection,
&mut authenticator,
&mut update_sink,
None,
)
.await;
}
pub fn rx_wlan_data_frame(
channel: &fidl_common::WlanChannel,
addr1: &[u8; 6],
addr2: &[u8; 6],
addr3: &[u8; 6],
payload: &[u8],
ether_type: u16,
phy: &WlantapPhyProxy,
) -> Result<(), anyhow::Error> {
let (mut buf, bytes_written) = write_frame_with_dynamic_buf!(vec![], {
headers: {
mac::FixedDataHdrFields: &mac::FixedDataHdrFields {
frame_ctrl: mac::FrameControl(0)
.with_frame_type(mac::FrameType::DATA)
.with_data_subtype(mac::DataSubtype(0))
.with_from_ds(true),
duration: 0,
addr1: *addr1,
addr2: *addr2,
addr3: *addr3,
seq_ctrl: mac::SequenceControl(0).with_seq_num(3),
},
mac::LlcHdr: &data_writer::make_snap_llc_hdr(ether_type),
},
payload: payload,
})?;
buf.truncate(bytes_written);
phy.rx(0, &buf, &mut create_rx_info(channel, 0))?;
Ok(())
}
pub async fn loop_until_iface_is_found() {
// Connect to the client policy service and get a client controller.
let policy_provider = connect_to_protocol::<fidl_policy::ClientProviderMarker>()
.expect("connecting to wlan policy");
let (client_controller, server_end) = create_proxy().expect("creating client controller");
let (update_client_end, _update_server_end) =
create_endpoints().expect("creating client listener");
let () =
policy_provider.get_controller(server_end, update_client_end).expect("getting controller");
// Attempt to issue a scan command until the request succeeds. Scanning will fail until a
// client interface is available. A successful response to a scan request indicates that the
// client policy layer is ready to use.
// TODO(fxbug.dev/57415): Figure out a new way to signal that the client policy layer is ready to go.
let mut retry = test_utils::RetryWithBackoff::infinite_with_max_interval(10.seconds());
loop {
let (scan_proxy, server_end) = create_proxy().unwrap();
client_controller.scan_for_networks(server_end).expect("requesting scan");
match scan_proxy.get_next().await.expect("getting scan results") {
Err(_) => {
retry.sleep_unless_after_deadline().await.unwrap_or_else(|_| {
panic!("Wlanstack did not recognize the interface in time")
});
}
Ok(_) => return,
}
}
}
pub fn init_syslog() {
syslog::init().unwrap();
// Change the severity to DEBUG in order to increase the verbosity of hw-sim logging.
// For example, DEBUG level logging prints more information about the internal state of
// the main_future polled by TestHelper::run_until_complete_or_timeout during a test.
syslog::set_severity(syslog::levels::INFO);
}