Skip to content

Commit

Permalink
xous: net: initial commit of network support
Browse files Browse the repository at this point in the history
This is an initial commit of network support for Xous.

On hardware, is backed by smoltcp running via a Xous server in a
separate process space.

This patch adds TCP and UDP client and server support as well as DNS
resolution support using the dns Xous server.

Signed-off-by: Sean Cross <sean@xobs.io>
  • Loading branch information
xobs committed Jan 13, 2024
1 parent ef4f722 commit aa8acc2
Show file tree
Hide file tree
Showing 9 changed files with 1,493 additions and 1 deletion.
6 changes: 6 additions & 0 deletions library/std/src/os/xous/services.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
use crate::os::xous::ffi::Connection;
use core::sync::atomic::{AtomicU32, Ordering};

mod dns;
pub(crate) use dns::*;

mod log;
pub(crate) use log::*;

mod net;
pub(crate) use net::*;

mod systime;
pub(crate) use systime::*;

Expand Down
28 changes: 28 additions & 0 deletions library/std/src/os/xous/services/dns.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use crate::os::xous::ffi::Connection;
use crate::os::xous::services::connect;
use core::sync::atomic::{AtomicU32, Ordering};

#[repr(usize)]
pub(crate) enum DnsLendMut {
RawLookup = 6,
}

impl Into<usize> for DnsLendMut {
fn into(self) -> usize {
self as usize
}
}

/// Return a `Connection` to the DNS lookup server. This server is used for
/// querying domain name values.
pub(crate) fn dns_server() -> Connection {
static DNS_CONNECTION: AtomicU32 = AtomicU32::new(0);
let cid = DNS_CONNECTION.load(Ordering::Relaxed);
if cid != 0 {
return cid.into();
}

let cid = connect("_DNS Resolver Middleware_").unwrap();
DNS_CONNECTION.store(cid.into(), Ordering::Relaxed);
cid
}
95 changes: 95 additions & 0 deletions library/std/src/os/xous/services/net.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
use crate::os::xous::ffi::Connection;
use crate::os::xous::services::connect;
use core::sync::atomic::{AtomicU32, Ordering};

pub(crate) enum NetBlockingScalar {
StdGetTtlUdp(u16 /* fd */), /* 36 */
StdSetTtlUdp(u16 /* fd */, u32 /* ttl */), /* 37 */
StdGetTtlTcp(u16 /* fd */), /* 36 */
StdSetTtlTcp(u16 /* fd */, u32 /* ttl */), /* 37 */
StdGetNodelay(u16 /* fd */), /* 38 */
StdSetNodelay(u16 /* fd */, bool), /* 39 */
StdTcpClose(u16 /* fd */), /* 34 */
StdUdpClose(u16 /* fd */), /* 41 */
StdTcpStreamShutdown(u16 /* fd */, crate::net::Shutdown /* how */), /* 46 */
}

pub(crate) enum NetLendMut {
StdTcpConnect, /* 30 */
StdTcpTx(u16 /* fd */), /* 31 */
StdTcpPeek(u16 /* fd */, bool /* nonblocking */), /* 32 */
StdTcpRx(u16 /* fd */, bool /* nonblocking */), /* 33 */
StdGetAddress(u16 /* fd */), /* 35 */
StdUdpBind, /* 40 */
StdUdpRx(u16 /* fd */), /* 42 */
StdUdpTx(u16 /* fd */), /* 43 */
StdTcpListen, /* 44 */
StdTcpAccept(u16 /* fd */), /* 45 */
}

impl Into<usize> for NetLendMut {
fn into(self) -> usize {
match self {
NetLendMut::StdTcpConnect => 30,
NetLendMut::StdTcpTx(fd) => 31 | ((fd as usize) << 16),
NetLendMut::StdTcpPeek(fd, blocking) => {
32 | ((fd as usize) << 16) | if blocking { 0x8000 } else { 0 }
}
NetLendMut::StdTcpRx(fd, blocking) => {
33 | ((fd as usize) << 16) | if blocking { 0x8000 } else { 0 }
}
NetLendMut::StdGetAddress(fd) => 35 | ((fd as usize) << 16),
NetLendMut::StdUdpBind => 40,
NetLendMut::StdUdpRx(fd) => 42 | ((fd as usize) << 16),
NetLendMut::StdUdpTx(fd) => 43 | ((fd as usize) << 16),
NetLendMut::StdTcpListen => 44,
NetLendMut::StdTcpAccept(fd) => 45 | ((fd as usize) << 16),
}
}
}

impl<'a> Into<[usize; 5]> for NetBlockingScalar {
fn into(self) -> [usize; 5] {
match self {
NetBlockingScalar::StdGetTtlTcp(fd) => [36 | ((fd as usize) << 16), 0, 0, 0, 0],
NetBlockingScalar::StdGetTtlUdp(fd) => [36 | ((fd as usize) << 16), 0, 0, 0, 1],
NetBlockingScalar::StdSetTtlTcp(fd, ttl) => {
[37 | ((fd as usize) << 16), ttl as _, 0, 0, 0]
}
NetBlockingScalar::StdSetTtlUdp(fd, ttl) => {
[37 | ((fd as usize) << 16), ttl as _, 0, 0, 1]
}
NetBlockingScalar::StdGetNodelay(fd) => [38 | ((fd as usize) << 16), 0, 0, 0, 0],
NetBlockingScalar::StdSetNodelay(fd, enabled) => {
[39 | ((fd as usize) << 16), if enabled { 1 } else { 0 }, 0, 0, 1]
}
NetBlockingScalar::StdTcpClose(fd) => [34 | ((fd as usize) << 16), 0, 0, 0, 0],
NetBlockingScalar::StdUdpClose(fd) => [41 | ((fd as usize) << 16), 0, 0, 0, 0],
NetBlockingScalar::StdTcpStreamShutdown(fd, how) => [
46 | ((fd as usize) << 16),
match how {
crate::net::Shutdown::Read => 1,
crate::net::Shutdown::Write => 2,
crate::net::Shutdown::Both => 3,
},
0,
0,
0,
],
}
}
}

/// Return a `Connection` to the Network server. This server provides all
/// OS-level networking functions.
pub(crate) fn net_server() -> Connection {
static NET_CONNECTION: AtomicU32 = AtomicU32::new(0);
let cid = NET_CONNECTION.load(Ordering::Relaxed);
if cid != 0 {
return cid.into();
}

let cid = connect("_Middleware Network Server_").unwrap();
NET_CONNECTION.store(cid.into(), Ordering::Relaxed);
cid
}
1 change: 0 additions & 1 deletion library/std/src/sys/pal/xous/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ pub mod fs;
#[path = "../unsupported/io.rs"]
pub mod io;
pub mod locks;
#[path = "../unsupported/net.rs"]
pub mod net;
pub mod os;
#[path = "../unix/os_str.rs"]
Expand Down
127 changes: 127 additions & 0 deletions library/std/src/sys/pal/xous/net/dns.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use crate::io;
use crate::net::{Ipv4Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use crate::os::xous::ffi::lend_mut;
use crate::os::xous::services::{dns_server, DnsLendMut};
use core::convert::{TryFrom, TryInto};

pub struct DnsError {
pub code: u8,
}

#[repr(C, align(4096))]
struct LookupHostQuery([u8; 4096]);

pub struct LookupHost {
data: LookupHostQuery,
port: u16,
offset: usize,
count: usize,
}

impl LookupHost {
pub fn port(&self) -> u16 {
self.port
}
}

impl Iterator for LookupHost {
type Item = SocketAddr;
fn next(&mut self) -> Option<SocketAddr> {
if self.offset >= self.data.0.len() {
return None;
}
match self.data.0.get(self.offset) {
Some(&4) => {
self.offset += 1;
if self.offset + 4 > self.data.0.len() {
return None;
}
let result = Some(SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::new(
self.data.0[self.offset],
self.data.0[self.offset + 1],
self.data.0[self.offset + 2],
self.data.0[self.offset + 3],
),
self.port,
)));
self.offset += 4;
result
}
Some(&6) => {
self.offset += 1;
if self.offset + 16 > self.data.0.len() {
return None;
}
let mut new_addr = [0u8; 16];
for (src, octet) in self.data.0[(self.offset + 1)..(self.offset + 16 + 1)]
.iter()
.zip(new_addr.iter_mut())
{
*octet = *src;
}
let result =
Some(SocketAddr::V6(SocketAddrV6::new(new_addr.into(), self.port, 0, 0)));
self.offset += 16;
result
}
_ => None,
}
}
}

pub fn lookup(query: &str, port: u16) -> Result<LookupHost, DnsError> {
let mut result = LookupHost { data: LookupHostQuery([0u8; 4096]), offset: 0, count: 0, port };

// Copy the query into the message that gets sent to the DNS server
for (query_byte, result_byte) in query.as_bytes().iter().zip(result.data.0.iter_mut()) {
*result_byte = *query_byte;
}

lend_mut(
dns_server(),
DnsLendMut::RawLookup.into(),
&mut result.data.0,
0,
query.as_bytes().len(),
)
.unwrap();
if result.data.0[0] != 0 {
return Err(DnsError { code: result.data.0[1] });
}
assert_eq!(result.offset, 0);
result.count = result.data.0[1] as usize;

// Advance the offset to the first record
result.offset = 2;
Ok(result)
}

impl TryFrom<&str> for LookupHost {
type Error = io::Error;

fn try_from(s: &str) -> io::Result<LookupHost> {
macro_rules! try_opt {
($e:expr, $msg:expr) => {
match $e {
Some(r) => r,
None => return Err(io::const_io_error!(io::ErrorKind::InvalidInput, &$msg)),
}
};
}

// split the string by ':' and convert the second part to u16
let (host, port_str) = try_opt!(s.rsplit_once(':'), "invalid socket address");
let port: u16 = try_opt!(port_str.parse().ok(), "invalid port value");
(host, port).try_into()
}
}

impl TryFrom<(&str, u16)> for LookupHost {
type Error = io::Error;

fn try_from(v: (&str, u16)) -> io::Result<LookupHost> {
lookup(v.0, v.1)
.map_err(|_e| io::const_io_error!(io::ErrorKind::InvalidInput, &"DNS failure"))
}
}
84 changes: 84 additions & 0 deletions library/std/src/sys/pal/xous/net/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
mod dns;

mod tcpstream;
pub use tcpstream::*;

mod tcplistener;
pub use tcplistener::*;

mod udp;
pub use udp::*;

// this structure needs to be synchronized with what's in net/src/api.rs
#[repr(C)]
#[derive(Debug)]
enum NetError {
// Ok = 0,
Unaddressable = 1,
SocketInUse = 2,
// AccessDenied = 3,
Invalid = 4,
// Finished = 5,
LibraryError = 6,
// AlreadyUsed = 7,
TimedOut = 8,
WouldBlock = 9,
}

#[repr(C, align(4096))]
struct ConnectRequest {
raw: [u8; 4096],
}

#[repr(C, align(4096))]
struct SendData {
raw: [u8; 4096],
}

#[repr(C, align(4096))]
pub struct ReceiveData {
raw: [u8; 4096],
}

#[repr(C, align(4096))]
pub struct GetAddress {
raw: [u8; 4096],
}

pub use dns::LookupHost;

#[allow(nonstandard_style)]
pub mod netc {
pub const AF_INET: u8 = 0;
pub const AF_INET6: u8 = 1;
pub type sa_family_t = u8;

#[derive(Copy, Clone)]
pub struct in_addr {
pub s_addr: u32,
}

#[derive(Copy, Clone)]
pub struct sockaddr_in {
pub sin_family: sa_family_t,
pub sin_port: u16,
pub sin_addr: in_addr,
}

#[derive(Copy, Clone)]
pub struct in6_addr {
pub s6_addr: [u8; 16],
}

#[derive(Copy, Clone)]
pub struct sockaddr_in6 {
pub sin6_family: sa_family_t,
pub sin6_port: u16,
pub sin6_addr: in6_addr,
pub sin6_flowinfo: u32,
pub sin6_scope_id: u32,
}

#[derive(Copy, Clone)]
pub struct sockaddr {}
}
Loading

0 comments on commit aa8acc2

Please sign in to comment.