From a8c6e8047f2cbbfd650b923e205c9b8d64f3a8e1 Mon Sep 17 00:00:00 2001 From: bunnie Date: Mon, 18 Oct 2021 05:55:42 +0800 Subject: [PATCH] simple end-to-end demo of receiving UDP packets asynchronously --- Cargo.lock | 1 + services/net/src/api.rs | 3 +- services/net/src/backend/udp.rs | 12 +-- services/net/src/lib.rs | 1 + services/net/src/main.rs | 35 ++++++-- services/shellchat/Cargo.toml | 1 + services/shellchat/src/cmds.rs | 4 + services/shellchat/src/cmds/net_cmd.rs | 111 +++++++++++++++++++++++++ 8 files changed, 153 insertions(+), 15 deletions(-) create mode 100644 services/shellchat/src/cmds/net_cmd.rs diff --git a/Cargo.lock b/Cargo.lock index 299efe93a..aa7bb57e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2010,6 +2010,7 @@ dependencies = [ "llio", "log", "log-server", + "net", "num-derive", "num-traits", "oqc-test", diff --git a/services/net/src/api.rs b/services/net/src/api.rs index 43a1ce2dc..cf8542a2d 100644 --- a/services/net/src/api.rs +++ b/services/net/src/api.rs @@ -70,6 +70,7 @@ impl From for NetIpAddr { } } +pub(crate) const UDP_RESPONSE_MAX_LEN: usize = 1800; /// The data field for a UDP response is limited to less than the theoretical /// size of 64k. While UDP allows for a 64k packet, it seems no protoctols /// in practice utilize such a length (about 512 bytes is the biggest), due @@ -84,7 +85,7 @@ pub(crate) struct NetUdpResponse { pub endpoint_ip_addr: NetIpAddr, pub len: u16, pub endpoint_port: u16, - pub data: [u8; 1800], + pub data: [u8; UDP_RESPONSE_MAX_LEN], } #[derive(Debug, Archive, Serialize, Deserialize, Copy, Clone)] diff --git a/services/net/src/backend/udp.rs b/services/net/src/backend/udp.rs index 68aeaf1e1..0be58809f 100644 --- a/services/net/src/backend/udp.rs +++ b/services/net/src/backend/udp.rs @@ -38,8 +38,8 @@ pub struct UdpSocket{ // next steps: build this stub, and figure out how to clean up the error handling code. impl UdpSocket { - pub fn bind(maybe_socket: Result<&SocketAddr>, max_payload: Option) -> Result { - let socket = maybe_socket?; + pub fn bind(socket: SocketAddr, max_payload: Option) -> Result { + //let socket = maybe_socket.unwrap(); let xns = xous_names::XousNames::new().unwrap(); let net = NetConn::new(&xns).unwrap(); let cb_sid = xous::create_server().unwrap(); @@ -76,8 +76,8 @@ impl UdpSocket { endpoint, data: Vec::new(), }; - for (&src, dst) in incoming.data.iter().zip(rx.data.iter_mut()) { - *dst = src; + for &d in incoming.data[..incoming.len as usize].iter() { + rx.data.push(d); } rx_buf.lock().unwrap().push(rx); notify.lock().unwrap().notify(); // this will only notify if a destination has been set @@ -96,7 +96,7 @@ impl UdpSocket { }); let request = NetUdpBind { - ip_addr: NetIpAddr::from(*socket), + ip_addr: NetIpAddr::from(socket), port: socket.port(), cb_sid: cb_sid.to_array(), max_payload, @@ -110,7 +110,7 @@ impl UdpSocket { Ok(UdpSocket { net, cb_sid, - socket_addr: *socket, + socket_addr: socket, rx_buf, handle: Some(handle), notify, diff --git a/services/net/src/lib.rs b/services/net/src/lib.rs index 3bff4d26a..228a1113c 100644 --- a/services/net/src/lib.rs +++ b/services/net/src/lib.rs @@ -8,6 +8,7 @@ use num_traits::*; pub mod backend; pub use backend::*; +pub use smoltcp::time::Duration; /// NetConn is a crate-level structure that just counts the number of connections from this process to /// the Net server. It's not mean to be created by user-facing code, so the visibility is (crate). diff --git a/services/net/src/main.rs b/services/net/src/main.rs index 2a5c36656..f0bceb88b 100644 --- a/services/net/src/main.rs +++ b/services/net/src/main.rs @@ -97,6 +97,11 @@ enum WaitOp { Quit, } +pub struct UdpState { + handle: SocketHandle, + cid: xous::CID, +} + #[xous::xous_main] fn xmain() -> ! { log_server::init_wait().unwrap(); @@ -170,7 +175,7 @@ fn xmain() -> ! { let mut net_config: Option = None; // ping-specific storage - let ping_remote_addr = IpAddress::from_str("10.0.245.243").expect("invalid address format"); + let ping_remote_addr = IpAddress::from_str("10.0.245.1").expect("invalid address format"); let icmp_rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY], vec![0; 256]); let icmp_tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY], vec![0; 256]); @@ -178,7 +183,7 @@ fn xmain() -> ! { let mut sockets = SocketSet::new(vec![]); let icmp_handle = sockets.add(icmp_socket); - let mut udp_handles = HashMap::::new(); + let mut udp_handles = HashMap::::new(); let mut send_at = Instant::from_millis(0); let mut seq_no = 0; @@ -236,7 +241,11 @@ fn xmain() -> ! { let mut udp_socket = UdpSocket::new(udp_rx_buffer, udp_tx_buffer); match udp_socket.bind(udpspec.port) { Ok(_) => { - udp_handles.insert(udpspec.port, sockets.add(udp_socket)); + let udpstate = UdpState { + handle: sockets.add(udp_socket), + cid: xous::connect(xous::SID::from_array(udpspec.cb_sid)).unwrap(), + }; + udp_handles.insert(udpspec.port, udpstate); buf.replace(NetMemResponse::Ok).unwrap(); } Err(e) => { @@ -250,8 +259,8 @@ fn xmain() -> ! { let mut buf = unsafe{Buffer::from_memory_message_mut(msg.body.memory_message_mut().unwrap())}; let udpspec = buf.to_original::().unwrap(); match udp_handles.remove(&udpspec.port) { - Some(handle) => { - sockets.get::(handle).close(); + Some(udpstate) => { + sockets.get::(udpstate.handle).close(); buf.replace(NetMemResponse::Ok).unwrap() } _ => { @@ -358,7 +367,8 @@ fn xmain() -> ! { } { - for (port, &handle) in udp_handles.iter() { + for (port, udpstate) in udp_handles.iter() { + let handle = udpstate.handle; let mut socket = sockets.get::(handle); match socket.recv() { Ok((data, endpoint)) => { @@ -369,8 +379,17 @@ fn xmain() -> ! { endpoint ); // return the data/endpoint tuple to the caller - // TODO - + let mut response = NetUdpResponse { + endpoint_ip_addr: NetIpAddr::from(endpoint.addr), + len: data.len() as u16, + endpoint_port: endpoint.port, + data: [0; UDP_RESPONSE_MAX_LEN], + }; + for (&src, dst) in data.iter().zip(response.data.iter_mut()) { + *dst = src; + } + let buf = Buffer::into_buf(response).expect("couldn't convert UDP response to memory message"); + buf.send(udpstate.cid, NetUdpCallback::RxData.to_u32().unwrap()).expect("couldn't send UDP response"); } Err(_) => { // do nothing diff --git a/services/shellchat/Cargo.toml b/services/shellchat/Cargo.toml index 5ff9222df..dc4d2601a 100644 --- a/services/shellchat/Cargo.toml +++ b/services/shellchat/Cargo.toml @@ -32,6 +32,7 @@ engine-25519 = {path="../engine-25519"} spinor = {path="../spinor"} root-keys = {path="../root-keys"} jtag = {path="../jtag"} +net = {path="../net"} trng = {path = "../trng"} diff --git a/services/shellchat/src/cmds.rs b/services/shellchat/src/cmds.rs index 1afe1f9e6..1a3186462 100644 --- a/services/shellchat/src/cmds.rs +++ b/services/shellchat/src/cmds.rs @@ -99,6 +99,7 @@ mod memtest; use memtest::*; mod keys; use keys::*; mod wlan; use wlan::*; mod jtag_cmd; use jtag_cmd::*; +mod net_cmd; use net_cmd::*; //mod fcc; use fcc::*; //mod pds; // dependency of the FCC file @@ -124,6 +125,7 @@ pub struct CmdEnv { memtest_cmd: Memtest, keys_cmd: Keys, jtag_cmd: JtagCmd, + net_cmd: NetCmd, //fcc_cmd: Fcc, } @@ -176,6 +178,7 @@ impl CmdEnv { memtest_cmd: memtest, keys_cmd: Keys::new(&xns), jtag_cmd: JtagCmd::new(&xns), + net_cmd: NetCmd::new(&xns), //fcc_cmd: fcc, } @@ -214,6 +217,7 @@ impl CmdEnv { &mut self.keys_cmd, &mut wlan_cmd, &mut self.jtag_cmd, + &mut self.net_cmd, //&mut self.fcc_cmd, ]; diff --git a/services/shellchat/src/cmds/net_cmd.rs b/services/shellchat/src/cmds/net_cmd.rs new file mode 100644 index 000000000..07855f6b2 --- /dev/null +++ b/services/shellchat/src/cmds/net_cmd.rs @@ -0,0 +1,111 @@ +use crate::{ShellCmdApi, CommonEnv}; +use xous_ipc::String; +use net::Duration; +use xous::MessageEnvelope; +use num_traits::*; +use std::net::ToSocketAddrs; + +#[derive(Debug)] +pub struct NetCmd { + udp: Option, + callback_id: Option, + callback_conn: u32, +} +impl NetCmd { + pub fn new(xns: &xous_names::XousNames) -> Self { + NetCmd { + udp: None, + callback_id: None, + callback_conn: xns.request_connection_blocking(crate::SERVER_NAME_SHELLCHAT).unwrap(), + } + } +} + +#[derive(Debug, num_derive::FromPrimitive, num_derive::ToPrimitive)] +pub(crate) enum NetCmdDispatch { + UdpTest1, +} + +pub const UDP_TEST_SIZE: usize = 64; +impl<'a> ShellCmdApi<'a> for NetCmd { + cmd_api!(net); // inserts boilerplate for command API + + fn process(&mut self, args: String::<1024>, env: &mut CommonEnv) -> Result>, xous::Error> { + if self.callback_id.is_none() { + let cb_id = env.register_handler(String::<256>::from_str(self.verb())); + log::info!("hooking net callback with ID {}", cb_id); + self.callback_id = Some(cb_id); + } + + use core::fmt::Write; + let mut ret = String::<1024>::new(); + let helpstring = "net [udp]"; + + let mut tokens = args.as_str().unwrap().split(' '); + + if let Some(sub_cmd) = tokens.next() { + match sub_cmd { + "udp" => { + if self.udp.is_none() { + let mut udp = net::UdpSocket::bind( + "127.0.0.1:6969".to_socket_addrs().unwrap().into_iter().next().unwrap(), + Some(UDP_TEST_SIZE as u16) + ).unwrap(); + udp.set_read_timeout(Some(Duration::from_millis(1000))).unwrap(); + udp.set_scalar_notification( + self.callback_conn, + self.callback_id.unwrap() as usize, // this is guaranteed in the prelude + [Some(NetCmdDispatch::UdpTest1.to_usize().unwrap()), None, None, None] + ); + self.udp = Some(udp); + write!(ret, "Created UDP socket listener on port 6969").unwrap(); + } else { + write!(ret, "Socket listener already installed on 6969").unwrap(); + } + } + _ => { + write!(ret, "{}", helpstring).unwrap(); + } + } + + } else { + write!(ret, "{}", helpstring).unwrap(); + } + Ok(Some(ret)) + } + + + fn callback(&mut self, msg: &MessageEnvelope, _env: &mut CommonEnv) -> Result>, xous::Error> { + use core::fmt::Write; + + log::info!("net callback"); + let mut ret = String::<1024>::new(); + xous::msg_scalar_unpack!(msg, dispatch, _, _, _, { + match FromPrimitive::from_usize(dispatch) { + Some(NetCmdDispatch::UdpTest1) => { + if let Some(udp_socket) = &self.udp { + let mut pkt: [u8; UDP_TEST_SIZE] = [0; UDP_TEST_SIZE]; + match udp_socket.recv_from(&mut pkt) { + Ok((len, addr)) => { + write!(ret, "UDP received {} bytes: {:?}: {}", len, addr, std::str::from_utf8(&pkt[..len]).unwrap()).unwrap(); + log::info!("UDP received {} bytes: {:?}: {:?}", len, addr, &pkt[..len]); + }, + Err(e) => { + log::error!("Net UDP error: {:?}", e); + write!(ret, "UDP receive error: {:?}", e).unwrap(); + } + } + } else { + log::error!("Got NetCmd callback from uninitialized socket"); + write!(ret, "Got NetCmd callback from uninitialized socket").unwrap(); + } + } + None => { + log::error!("NetCmd callback with unrecognized dispatch ID"); + write!(ret, "NetCmd callback with unrecognized dispatch ID").unwrap(); + } + } + }); + Ok(Some(ret)) + } +}