From 6c31682924367fc793a25d7ffe4bb9e00c9ab58c Mon Sep 17 00:00:00 2001 From: Mathias Date: Thu, 18 Jan 2024 11:21:46 +0100 Subject: [PATCH] Fix TlsSocket and module restart with EDM --- Cargo.toml | 13 ++---- src/asynch/control.rs | 50 ++++++++++++++++++++--- src/asynch/runner.rs | 63 ++++++++++++++-------------- src/asynch/ublox_stack/dns.rs | 10 +---- src/asynch/ublox_stack/mod.rs | 77 ++--------------------------------- src/asynch/ublox_stack/tcp.rs | 2 - src/asynch/ublox_stack/tls.rs | 8 ++++ src/asynch/ublox_stack/udp.rs | 1 - src/command/custom_digest.rs | 3 +- src/command/data_mode/mod.rs | 2 +- src/command/edm/mod.rs | 16 ++++---- src/command/security/mod.rs | 2 +- src/fmt.rs | 2 +- src/lib.rs | 1 + src/peer_builder.rs | 9 ++-- 15 files changed, 109 insertions(+), 150 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 681f4b9..33cd4f2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,7 +20,8 @@ atat = { version = "0.21", features = ["derive", "bytes"] } heapless = { version = "^0.8", features = ["serde"] } no-std-net = { version = "0.6", features = ["serde"] } serde = { version = "^1", default-features = false, features = ["derive"] } -ublox-sockets = { version = "0.5", features = ["edm"], optional = true } +# ublox-sockets = { version = "0.5", features = ["edm"], optional = true } +ublox-sockets = { git = "https://github.com/BlackbirdHQ/ublox-sockets", rev = "9f7fe54", features = ["edm"], optional = true } postcard = "1.0.4" portable-atomic = "1.5" @@ -78,11 +79,5 @@ exclude = ["examples"] [patch.crates-io] -atat = { path = "../atat/atat" } -ublox-sockets = { path = "../ublox-sockets" } -no-std-net = { path = "../no-std-net" } - -embassy-time = { path = "../embassy/embassy-time" } -embassy-sync = { path = "../embassy/embassy-sync" } -embassy-futures = { path = "../embassy/embassy-futures" } -embassy-net-driver = { path = "../embassy/embassy-net-driver" } +no-std-net = { git = "https://github.com/rushmorem/no-std-net", branch = "issue-15" } +atat = { path = "../atat/atat" } \ No newline at end of file diff --git a/src/asynch/control.rs b/src/asynch/control.rs index be438d9..e826bf5 100644 --- a/src/asynch/control.rs +++ b/src/asynch/control.rs @@ -4,16 +4,21 @@ use core::task::Poll; use atat::asynch::AtatClient; use embassy_time::{with_timeout, Duration}; -use crate::command::gpio::{ - types::{GPIOId, GPIOValue}, - WriteGPIO, -}; use crate::command::network::SetNetworkHostName; +use crate::command::security::types::SecurityDataType; +use crate::command::security::SendSecurityDataImport; use crate::command::wifi::types::{ Authentication, StatusId, WifiStationAction, WifiStationConfig, WifiStatus, WifiStatusVal, }; use crate::command::wifi::{ExecWifiStationAction, GetWifiStatus, SetWifiStationConfig}; use crate::command::OnOff; +use crate::command::{ + gpio::{ + types::{GPIOId, GPIOValue}, + WriteGPIO, + }, + security::PrepareSecurityDataImport, +}; use crate::error::Error; use super::state::LinkState; @@ -223,7 +228,7 @@ impl<'a, AT: AtatClient> Control<'a, AT> { }) .await?; - with_timeout(Duration::from_secs(10), self.wait_for_join(ssid)) + with_timeout(Duration::from_secs(20), self.wait_for_join(ssid)) .await .map_err(|_| Error::Timeout)??; @@ -275,4 +280,39 @@ impl<'a, AT: AtatClient> Control<'a, AT> { self.at.send_edm(WriteGPIO { id, value }).await?; Ok(()) } + + // FIXME: This could probably be improved + pub async fn import_credentials( + &mut self, + data_type: SecurityDataType, + name: &str, + data: &[u8], + md5_sum: Option<&str>, + ) -> Result<(), atat::Error> { + assert!(name.len() < 16); + + info!("Importing {:?} bytes as {:?}", data.len(), name); + + self.at + .send_edm(PrepareSecurityDataImport { + data_type, + data_size: data.len(), + internal_name: name, + password: None, + }) + .await?; + + let import_data = self + .at + .send_edm(SendSecurityDataImport { + data: atat::serde_bytes::Bytes::new(data), + }) + .await?; + + if let Some(hash) = md5_sum { + assert_eq!(import_data.md5_string.as_str(), hash); + } + + Ok(()) + } } diff --git a/src/asynch/runner.rs b/src/asynch/runner.rs index d366869..bf3d6f3 100644 --- a/src/asynch/runner.rs +++ b/src/asynch/runner.rs @@ -4,6 +4,7 @@ use super::state::{self, LinkState}; use crate::{ command::{ edm::{urc::EdmEvent, SwitchToEdmCommand}, + general::SoftwareVersion, network::{ responses::NetworkStatusResponse, types::{InterfaceType, NetworkStatus, NetworkStatusParameter}, @@ -11,8 +12,8 @@ use crate::{ GetNetworkStatus, }, system::{ - types::{EchoOn}, - RebootDCE, SetEcho, StoreCurrentConfig, + types::{BaudRate, ChangeAfterConfirm, EchoOn, FlowControl, Parity, StopBits}, + RebootDCE, SetEcho, SetRS232Settings, StoreCurrentConfig, }, wifi::{ types::DisconnectReason, @@ -88,26 +89,21 @@ impl< // parameter. Instead, the // parameter must be set to 0 and the serial settings will take effect // when the module is reset. - // let baud_rate = BaudRate::B3000000; - // self.at - // .send_edm(SetRS232Settings { - // baud_rate, - // flow_control: FlowControl::On, - // data_bits: 8, - // stop_bits: StopBits::One, - // parity: Parity::None, - // change_after_confirm: ChangeAfterConfirm::StoreAndReset, - // }) - // .await?; - - // self.restart(true).await?; - - // self.at - // .0 - // .lock() - // .await - // .set_baudrate(baud_rate as u32) - // .map_err(|_| Error::BaudDetection)?; + let baud_rate = BaudRate::B115200; + self.at + .send_edm(SetRS232Settings { + baud_rate, + flow_control: FlowControl::On, + data_bits: 8, + stop_bits: StopBits::One, + parity: Parity::None, + change_after_confirm: ChangeAfterConfirm::StoreAndReset, + }) + .await?; + + self.restart(true).await?; + + self.at.send_edm(SoftwareVersion).await?; // Move to control // if let Some(size) = self.config.tls_in_buffer_size { @@ -133,7 +129,7 @@ impl< let fut = async { loop { match self.urc_subscription.next_message_pure().await { - EdmEvent::ATEvent(Urc::StartUp) | EdmEvent::StartUp => return, + EdmEvent::ATEvent(Urc::StartUp) => return, _ => {} } } @@ -163,28 +159,29 @@ impl< self.at.send_edm(RebootDCE).await?; - Timer::after(Duration::from_millis(3500)).await; + self.wait_startup(Duration::from_secs(10)).await?; + info!("Module started again"); self.enter_edm(Duration::from_secs(4)).await?; Ok(()) } pub async fn enter_edm(&mut self, timeout: Duration) -> Result<(), Error> { + info!("Entering EDM mode"); + // Switch to EDM on Init. If in EDM, fail and check with autosense let fut = async { loop { // Ignore AT results until we are successful in EDM mode - self.at.send(SwitchToEdmCommand).await.ok(); - - if let Ok(EdmEvent::StartUp) = with_timeout( - Duration::from_millis(300), - self.urc_subscription.next_message_pure(), - ) - .await - { + if let Ok(_) = self.at.send(SwitchToEdmCommand).await { + // After executing the data mode command or the extended data + // mode command, a delay of 50 ms is required before start of + // data transmission. + Timer::after(Duration::from_millis(50)).await; break; } + Timer::after(Duration::from_millis(10)).await; } }; @@ -192,7 +189,7 @@ impl< .await .map_err(|_| Error::Timeout)?; - self.at.send_edm(SetEcho { on: EchoOn::Off }).await?; + self.at.send_edm(SetEcho { on: EchoOn::On }).await?; Ok(()) } diff --git a/src/asynch/ublox_stack/dns.rs b/src/asynch/ublox_stack/dns.rs index a71e18c..ab28ee2 100644 --- a/src/asynch/ublox_stack/dns.rs +++ b/src/asynch/ublox_stack/dns.rs @@ -147,18 +147,12 @@ impl<'a> DnsSocket<'a> { s.waker.wake(); } - - poll_fn(|cx| { let mut s = self.stack.borrow_mut(); let query = s.dns_table.get_mut(&name_string).unwrap(); match query.state { - DnsState::Resolved(ip) => { - Poll::Ready(Ok(ip)) - } - DnsState::Error(_e) => { - Poll::Ready(Err(Error::Failed)) - } + DnsState::Resolved(ip) => Poll::Ready(Ok(ip)), + DnsState::Error(_e) => Poll::Ready(Err(Error::Failed)), _ => { query.waker.register(cx.waker()); Poll::Pending diff --git a/src/asynch/ublox_stack/mod.rs b/src/asynch/ublox_stack/mod.rs index 89ff839..5074181 100644 --- a/src/asynch/ublox_stack/mod.rs +++ b/src/asynch/ublox_stack/mod.rs @@ -22,8 +22,6 @@ use crate::command::edm::EdmDataCommand; use crate::command::ping::types::PingError; use crate::command::ping::urc::{PingErrorResponse, PingResponse}; use crate::command::ping::Ping; -use crate::command::security::types::SecurityDataType; -use crate::command::security::{PrepareSecurityDataImport, SendSecurityDataImport}; use crate::command::Urc; use crate::peer_builder::{PeerUrlBuilder, SecurityCredentials}; @@ -81,7 +79,7 @@ struct SocketStack { waker: WakerRegistration, dns_table: DnsTable, dropped_sockets: heapless::Vec, - credential_map: heapless::FnvIndexMap, + credential_map: heapless::FnvIndexMap, } impl UbloxStack { @@ -170,76 +168,6 @@ impl UbloxStack Result<(), atat::Error> { - let mut device = self.device.borrow_mut(); - - assert!(root_ca.0.len() < 16); - assert!(cert.0.len() < 16); - assert!(priv_key.0.len() < 16); - - device - .at - .send_edm(PrepareSecurityDataImport { - data_type: SecurityDataType::TrustedRootCA, - data_size: root_ca.1.len(), - internal_name: root_ca.0, - password: None, - }) - .await?; - - device - .at - .send_edm(SendSecurityDataImport { - data: atat::serde_bytes::Bytes::new(root_ca.1), - }) - .await?; - - device - .at - .send_edm(PrepareSecurityDataImport { - data_type: SecurityDataType::ClientCertificate, - data_size: cert.1.len(), - internal_name: cert.0, - password: None, - }) - .await?; - - device - .at - .send_edm(SendSecurityDataImport { - data: atat::serde_bytes::Bytes::new(cert.1), - }) - .await?; - - device - .at - .send_edm(PrepareSecurityDataImport { - data_type: SecurityDataType::ClientPrivateKey, - data_size: priv_key.1.len(), - internal_name: priv_key.0, - password: None, - }) - .await?; - - device - .at - .send_edm(SendSecurityDataImport { - data: atat::serde_bytes::Bytes::new(priv_key.1), - }) - .await?; - - // FIXME: - // self.socket.borrow_mut().credential_map.insert(key, value); - - Ok(()) - } - /// Make a query for a given name and return the corresponding IP addresses. // #[cfg(feature = "dns")] pub async fn dns_query( @@ -426,7 +354,8 @@ impl UbloxStack TlsSocket<'a> { let TcpIo { stack, handle } = tcp_socket.io; let s = &mut *stack.borrow_mut(); + info!("Associating credentials {} with {}", credentials, handle); s.credential_map.insert(handle, credentials); Self { inner: tcp_socket } @@ -217,6 +218,13 @@ impl<'a> TlsSocket<'a> { } } +impl<'a> Drop for TlsSocket<'a> { + fn drop(&mut self) { + let mut stack = self.inner.io.stack.borrow_mut(); + stack.credential_map.remove(&self.inner.io.handle); + } +} + mod embedded_io_impls { use super::*; diff --git a/src/asynch/ublox_stack/udp.rs b/src/asynch/ublox_stack/udp.rs index 285a880..8348b3d 100644 --- a/src/asynch/ublox_stack/udp.rs +++ b/src/asynch/ublox_stack/udp.rs @@ -3,7 +3,6 @@ use core::cell::RefCell; use core::mem; - use atat::asynch::AtatClient; use embedded_nal_async::SocketAddr; use ublox_sockets::{udp, SocketHandle, UdpState}; diff --git a/src/command/custom_digest.rs b/src/command/custom_digest.rs index 888c06f..d591a08 100644 --- a/src/command/custom_digest.rs +++ b/src/command/custom_digest.rs @@ -84,8 +84,7 @@ impl Digester for EdmDigester { }; (return_val, edm_len) } - PayloadType::StartEvent => (DigestResult::Urc(&buf[..edm_len]), edm_len), - // PayloadType::StartEvent => (DigestResult::Response(Ok(&buf[..edm_len])), edm_len), + PayloadType::StartEvent => (DigestResult::Response(Ok(&buf[..edm_len])), edm_len), PayloadType::ATEvent | PayloadType::ConnectEvent | PayloadType::DataEvent diff --git a/src/command/data_mode/mod.rs b/src/command/data_mode/mod.rs index 9221b24..fa9da8d 100644 --- a/src/command/data_mode/mod.rs +++ b/src/command/data_mode/mod.rs @@ -29,7 +29,7 @@ pub struct ChangeMode { /// service on a remote device, it implicitly registers to receive the "Connection Closed" /// event. #[derive(Clone, AtatCmd)] -#[at_cmd("+UDCP", ConnectPeerResponse, timeout_ms = 3000)] +#[at_cmd("+UDCP", ConnectPeerResponse, timeout_ms = 5000)] pub struct ConnectPeer<'a> { #[at_arg(position = 0, len = 128)] pub url: &'a str, diff --git a/src/command/edm/mod.rs b/src/command/edm/mod.rs index c1dd312..691c47b 100644 --- a/src/command/edm/mod.rs +++ b/src/command/edm/mod.rs @@ -172,16 +172,14 @@ impl atat::AtatCmd for SwitchToEdmCommand { fn parse( &self, - _resp: Result<&[u8], atat::InternalError>, + resp: Result<&[u8], atat::InternalError>, ) -> core::result::Result { - // let resp = resp?; - // // Parse EDM startup command - // let correct = &[0xAA, 0x00, 0x02, 0x00, 0x71, 0x55]; // &[0xAA,0x00,0x06,0x00,0x45,0x4f,0x4b,0x0D,0x0a,0x55]; // AA 00 06 00 44 41 54 0D 0A 0D 0A 4F 4B 0D 0A 55 ? - // if resp.len() != correct.len() - // || resp[.. correct.len()] != *correct { - // // TODO: check this - // return Err(atat::Error::InvalidResponse); - // } + let resp = resp?; + // Parse EDM startup command + let correct = &[0xAA, 0x00, 0x02, 0x00, 0x71, 0x55]; + if resp.len() != correct.len() || resp[..correct.len()] != *correct { + return Err(atat::Error::InvalidResponse); + } Ok(NoResponse) } } diff --git a/src/command/security/mod.rs b/src/command/security/mod.rs index 388057a..81fd730 100644 --- a/src/command/security/mod.rs +++ b/src/command/security/mod.rs @@ -41,7 +41,7 @@ pub struct PrepareSecurityDataImport<'a> { "", SecurityDataImport, value_sep = false, - timeout_ms = 1000, + timeout_ms = 3000, cmd_prefix = "", termination = "" )] diff --git a/src/fmt.rs b/src/fmt.rs index 3f3ed3c..81c9940 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -1,7 +1,7 @@ #![macro_use] #![allow(unused_macros)] -use core::fmt::{Debug}; +use core::fmt::Debug; #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); diff --git a/src/lib.rs b/src/lib.rs index 6262ebd..c1adaa5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,6 +21,7 @@ pub use atat; pub mod command; pub mod error; // pub mod wifi; +pub use peer_builder::SecurityCredentials; // TODO: // - UDP stack diff --git a/src/peer_builder.rs b/src/peer_builder.rs index 7c5e6d3..2e65114 100644 --- a/src/peer_builder.rs +++ b/src/peer_builder.rs @@ -3,7 +3,8 @@ use core::fmt::Write; use heapless::String; use no_std_net::{IpAddr, SocketAddr}; -#[derive(PartialEq, Clone, Default)] +#[derive(Debug, Clone, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct SecurityCredentials { pub ca_cert_name: heapless::String<16>, pub c_cert_name: heapless::String<16>, @@ -15,7 +16,7 @@ pub(crate) struct PeerUrlBuilder<'a> { hostname: Option<&'a str>, ip_addr: Option, port: Option, - creds: Option, + creds: Option<&'a SecurityCredentials>, local_port: Option, } @@ -115,7 +116,7 @@ impl<'a> PeerUrlBuilder<'a> { self } - pub fn creds(&mut self, creds: SecurityCredentials) -> &mut Self { + pub fn creds(&mut self, creds: &'a SecurityCredentials) -> &mut Self { self.creds.replace(creds); self } @@ -173,7 +174,7 @@ mod test { let url = PeerUrlBuilder::new() .hostname("example.org") .port(2000) - .creds(SecurityCredentials { + .creds(&SecurityCredentials { c_cert_name: heapless::String::try_from("client.crt").unwrap(), ca_cert_name: heapless::String::try_from("ca.crt").unwrap(), c_key_name: heapless::String::try_from("client.key").unwrap(),