diff --git a/Cargo.toml b/Cargo.toml index 7e30d31..b63423b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ getopts = "0.2" [features] std = ["managed/std"] alloc = ["managed/alloc"] -collections = ["managed/collections"] +collections = ["alloc", "managed/collections"] verbose = [] raw_socket = ["libc"] tap_interface = ["libc"] @@ -42,3 +42,6 @@ name = "client" [[example]] name = "ping" + +[[example]] +name = "loopback" diff --git a/README.md b/README.md index d7311c3..0410c76 100644 --- a/README.md +++ b/README.md @@ -125,8 +125,8 @@ or `BufWriter` is used, which are of course not available on heap-less systems. This feature is disabled by default. -Usage example -------------- +Hosted usage examples +--------------------- _smoltcp_, being a freestanding networking stack, needs to be able to transmit and receive raw frames. For testing purposes, we will use a regular OS, and run _smoltcp_ in @@ -240,6 +240,26 @@ Currently, netmasks are not implemented, and so the only address this example ca is the other endpoint of the tap interface, `192.168.1.100`. It cannot reach itself because packets entering a tap interface do not loop back. +Bare-metal usage examples +------------------------- + +Examples that use no services from the host OS are necessarily less illustrative than examples +that do. Because of this, only one such example is provided. + +### examples/loopback.rs + +_examples/loopback.rs_ performs a simple exchange between two TCP socket via a loopback interface. +This example requires the `collections` feature to run. + +Read its [source code](/examples/loopback.rs), then run it as: + +```sh +cargo run --example loopback +``` + +If the `std` feature is enabled, it will print logs and packet dumps; otherwise, nothing at all +will be displayed. + License ------- diff --git a/examples/loopback.rs b/examples/loopback.rs new file mode 100644 index 0000000..fa3806c --- /dev/null +++ b/examples/loopback.rs @@ -0,0 +1,118 @@ +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(unused_mut)] + +#[macro_use] +extern crate log; +#[cfg(feature = "std")] +extern crate env_logger; +#[cfg(feature = "std")] +extern crate getopts; +extern crate smoltcp; + +#[cfg(feature = "std")] +mod utils; + +use smoltcp::Error; +use smoltcp::phy::Loopback; +#[cfg(feature = "std")] +use smoltcp::phy::Tracer; +use smoltcp::wire::{EthernetAddress, IpAddress}; +#[cfg(feature = "std")] +use smoltcp::wire::EthernetFrame; +use smoltcp::iface::{ArpCache, SliceArpCache, EthernetInterface}; +use smoltcp::socket::{AsSocket, SocketSet}; +use smoltcp::socket::{TcpSocket, TcpSocketBuffer}; + +fn main() { + #[cfg(feature = "std")] + utils::setup_logging(); + + let mut device = Loopback::new(); + #[cfg(feature = "std")] + let mut device = Tracer::<_, EthernetFrame<&'static [u8]>>::new(device, utils::trace_writer); + + let mut arp_cache_entries: [_; 8] = Default::default(); + let mut arp_cache = SliceArpCache::new(&mut arp_cache_entries[..]); + + let hardware_addr = EthernetAddress::default(); + let mut protocol_addrs = [IpAddress::v4(127, 0, 0, 1)]; + let mut iface = EthernetInterface::new( + &mut device, &mut arp_cache as &mut ArpCache, + hardware_addr, &mut protocol_addrs[..]); + + let server_socket = { + // It is not strictly necessary to use a `static mut` and unsafe code here, but + // on embedded systems that smoltcp targets it is far better to allocate the data + // statically to verify that it fits into RAM rather than get undefined behavior + // when stack overflows. + static mut TCP_SERVER_RX_DATA: [u8; 1024] = [0; 1024]; + static mut TCP_SERVER_TX_DATA: [u8; 1024] = [0; 1024]; + let tcp_rx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_RX_DATA[..] }); + let tcp_tx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_SERVER_TX_DATA[..] }); + TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer) + }; + + let client_socket = { + static mut TCP_CLIENT_RX_DATA: [u8; 1024] = [0; 1024]; + static mut TCP_CLIENT_TX_DATA: [u8; 1024] = [0; 1024]; + let tcp_rx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_CLIENT_RX_DATA[..] }); + let tcp_tx_buffer = TcpSocketBuffer::new(unsafe { &mut TCP_CLIENT_TX_DATA[..] }); + TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer) + }; + + let mut socket_set_entries: [_; 2] = Default::default(); + let mut socket_set = SocketSet::new(&mut socket_set_entries[..]); + let server_handle = socket_set.add(server_socket); + let client_handle = socket_set.add(client_socket); + + let mut did_listen = false; + let mut did_connect = false; + let mut done = false; + let mut timestamp_ms = 0; + while !done && timestamp_ms < 500 { + { + let socket: &mut TcpSocket = socket_set.get_mut(server_handle).as_socket(); + if !socket.is_active() && !socket.is_listening() { + if !did_listen { + socket.listen(1234).unwrap(); + did_listen = true; + } + } + + if socket.can_recv() { + debug!("got {:?}", socket.recv(32).unwrap()); + socket.close(); + done = true; + } + } + + { + let socket: &mut TcpSocket = socket_set.get_mut(client_handle).as_socket(); + if !socket.is_open() { + if !did_connect { + socket.connect((IpAddress::v4(127, 0, 0, 1), 1234), + (IpAddress::v4(127, 0, 0, 1), 65000)).unwrap(); + did_connect = true; + } + } + + if socket.can_send() { + socket.send_slice(b"0123456789abcdef").unwrap(); + socket.close(); + } + } + + match iface.poll(&mut socket_set, timestamp_ms) { + Ok(()) | Err(Error::Exhausted) => (), + Err(e) => debug!("poll error: {}", e) + } + + const DELAY: u64 = 20; + debug!("{}ms pass", DELAY); + timestamp_ms += DELAY; + } + + if !done { + error!("this is taking too long, bailing out"); + } +} diff --git a/examples/utils.rs b/examples/utils.rs index 9617e9c..cac2a73 100644 --- a/examples/utils.rs +++ b/examples/utils.rs @@ -1,9 +1,11 @@ +#![allow(dead_code)] + use std::str::{self, FromStr}; use std::env; use std::time::{Instant, Duration, SystemTime, UNIX_EPOCH}; use std::process; use log::{LogLevelFilter, LogRecord}; -use env_logger::{LogBuilder}; +use env_logger::LogBuilder; use getopts; use smoltcp::phy::{Tracer, FaultInjector, TapInterface}; @@ -35,6 +37,10 @@ pub fn setup_logging() { .unwrap(); } +pub fn trace_writer(printer: PrettyPrinter>) { + trace!("{}", printer) +} + pub fn setup_device(more_args: &[&str]) -> (FaultInjector>>, Vec) { @@ -72,10 +78,6 @@ pub fn setup_device(more_args: &[&str]) let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().subsec_nanos(); - fn trace_writer(printer: PrettyPrinter>) { - trace!("{}", printer) - } - let device = TapInterface::new(&matches.free[0]).unwrap(); let device = Tracer::<_, EthernetFrame<&'static [u8]>>::new(device, trace_writer); let mut device = FaultInjector::new(device, seed);