Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: smoltcp-rs/smoltcp
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 869b088f68be
Choose a base ref
...
head repository: smoltcp-rs/smoltcp
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 4083ee8ea8c1
Choose a head ref
  • 5 commits
  • 7 files changed
  • 1 contributor

Commits on Mar 5, 2017

  1. Copy the full SHA
    11c8751 View commit details
  2. Copy the full SHA
    1a32b98 View commit details
  3. Copy the full SHA
    b88204c View commit details
  4. Add a TCP client example.

    whitequark committed Mar 5, 2017
    Copy the full SHA
    a0f2c62 View commit details
  5. Copy the full SHA
    4083ee8 View commit details
Showing with 356 additions and 91 deletions.
  1. +9 −0 Cargo.toml
  2. +86 −0 examples/client.rs
  3. +7 −52 examples/server.rs
  4. +67 −0 examples/utils.rs
  5. +36 −31 src/parsing.rs
  6. +145 −8 src/socket/tcp.rs
  7. +6 −0 src/wire/ip.rs
9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -28,3 +28,12 @@ use_collections = ["managed/use_collections"]
use_log = ["log"]
verbose = []
default = ["use_std", "use_log", "verbose"]

[[example]]
name = "tcpdump"

[[example]]
name = "server"

[[example]]
name = "client"
86 changes: 86 additions & 0 deletions examples/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#[macro_use]
extern crate log;
extern crate env_logger;
extern crate getopts;
extern crate smoltcp;

mod utils;

use std::str::{self, FromStr};
use std::time::Instant;
use smoltcp::wire::{EthernetAddress, IpAddress};
use smoltcp::iface::{ArpCache, SliceArpCache, EthernetInterface};
use smoltcp::socket::{AsSocket, SocketSet};
use smoltcp::socket::{TcpSocket, TcpSocketBuffer};

fn main() {
utils::setup_logging();
let (device, args) = utils::setup_device(&["ADDRESS", "PORT"]);
let address = IpAddress::from_str(&args[0]).expect("invalid address format");
let port = u16::from_str(&args[1]).expect("invalid port format");

let startup_time = Instant::now();

let arp_cache = SliceArpCache::new(vec![Default::default(); 8]);

let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; 64]);
let tcp_tx_buffer = TcpSocketBuffer::new(vec![0; 128]);
let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);

let hardware_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
let protocol_addr = IpAddress::v4(192, 168, 69, 1);
let mut iface = EthernetInterface::new(
Box::new(device), Box::new(arp_cache) as Box<ArpCache>,
hardware_addr, [protocol_addr]);

let mut sockets = SocketSet::new(vec![]);
let tcp_handle = sockets.add(tcp_socket);

{
let socket: &mut TcpSocket = sockets.get_mut(tcp_handle).as_socket();
socket.connect((address, port), (protocol_addr, 49500)).unwrap();
}

let mut tcp_active = false;
loop {
{
let socket: &mut TcpSocket = sockets.get_mut(tcp_handle).as_socket();
if socket.is_active() && !tcp_active {
debug!("connected");
} else if !socket.is_active() && tcp_active {
debug!("disconnected");
}
tcp_active = socket.is_active();

if socket.may_recv() {
let data = {
let mut data = socket.recv(128).unwrap().to_owned();
if data.len() > 0 {
debug!("recv data: {:?}",
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)"));
data = data.split(|&b| b == b'\n').collect::<Vec<_>>().concat();
data.reverse();
data.extend(b"\n");
}
data
};
if socket.can_send() && data.len() > 0 {
debug!("send data: {:?}",
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)"));
socket.send_slice(&data[..]).unwrap();
}
} else if socket.may_send() {
debug!("close");
socket.close();
}
}

let timestamp = Instant::now().duration_since(startup_time);
let timestamp_ms = (timestamp.as_secs() * 1000) +
(timestamp.subsec_nanos() / 1000000) as u64;
match iface.poll(&mut sockets, timestamp_ms) {
Ok(()) => (),
Err(e) => debug!("poll error: {}", e)
}
}
}
59 changes: 7 additions & 52 deletions examples/server.rs
Original file line number Diff line number Diff line change
@@ -4,66 +4,21 @@ extern crate env_logger;
extern crate getopts;
extern crate smoltcp;

use std::str::{self, FromStr};
use std::env;
use std::time::{Instant, SystemTime, UNIX_EPOCH};
use log::{LogLevelFilter, LogRecord};
use env_logger::{LogBuilder};

use smoltcp::phy::{Tracer, FaultInjector, TapInterface};
use smoltcp::wire::{EthernetFrame, EthernetAddress, IpAddress};
use smoltcp::wire::PrettyPrinter;
mod utils;

use std::str;
use std::time::Instant;
use smoltcp::wire::{EthernetAddress, IpAddress};
use smoltcp::iface::{ArpCache, SliceArpCache, EthernetInterface};
use smoltcp::socket::{AsSocket, SocketSet};
use smoltcp::socket::{UdpSocket, UdpSocketBuffer, UdpPacketBuffer};
use smoltcp::socket::{TcpSocket, TcpSocketBuffer};

fn main() {
let mut opts = getopts::Options::new();
opts.optopt("", "drop-chance", "Chance of dropping a packet (%)", "CHANCE");
opts.optopt("", "corrupt-chance", "Chance of corrupting a packet (%)", "CHANCE");
opts.optflag("h", "help", "print this help menu");

let matches = opts.parse(env::args().skip(1)).unwrap();
if matches.opt_present("h") || matches.free.len() != 1 {
let brief = format!("Usage: {} FILE [options]", env::args().nth(0).unwrap());
print!("{}", opts.usage(&brief));
return
}
let drop_chance = u8::from_str(&matches.opt_str("drop-chance")
.unwrap_or("0".to_string())).unwrap();
let corrupt_chance = u8::from_str(&matches.opt_str("corrupt-chance")
.unwrap_or("0".to_string())).unwrap();

let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().subsec_nanos();
utils::setup_logging();
let (device, _args) = utils::setup_device(&[]);

let startup_time = Instant::now();
LogBuilder::new()
.format(move |record: &LogRecord| {
let elapsed = Instant::now().duration_since(startup_time);
if record.target().starts_with("smoltcp::") {
format!("\x1b[0m[{:6}.{:03}s] ({}): {}\x1b[0m",
elapsed.as_secs(), elapsed.subsec_nanos() / 1000000,
record.target().replace("smoltcp::", ""), record.args())
} else {
format!("\x1b[32m[{:6}.{:03}s] ({}): {}\x1b[0m",
elapsed.as_secs(), elapsed.subsec_nanos() / 1000000,
record.target(), record.args())
}
})
.filter(None, LogLevelFilter::Trace)
.init()
.unwrap();

fn trace_writer(printer: PrettyPrinter<EthernetFrame<&[u8]>>) {
print!("\x1b[37m{}\x1b[0m", printer)
}

let device = TapInterface::new(&matches.free[0]).unwrap();
let mut device = FaultInjector::new(device, seed);
device.set_drop_chance(drop_chance);
device.set_corrupt_chance(corrupt_chance);
let device = Tracer::<_, EthernetFrame<&[u8]>>::new(device, trace_writer);

let arp_cache = SliceArpCache::new(vec![Default::default(); 8]);

67 changes: 67 additions & 0 deletions examples/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use std::str::{self, FromStr};
use std::env;
use std::time::{Instant, SystemTime, UNIX_EPOCH};
use std::process;
use log::{LogLevelFilter, LogRecord};
use env_logger::{LogBuilder};
use getopts;

use smoltcp::phy::{Tracer, FaultInjector, TapInterface};
use smoltcp::wire::EthernetFrame;
use smoltcp::wire::PrettyPrinter;

pub fn setup_logging() {
let startup_time = Instant::now();
LogBuilder::new()
.format(move |record: &LogRecord| {
let elapsed = Instant::now().duration_since(startup_time);
if record.target().starts_with("smoltcp::") {
format!("\x1b[0m[{:6}.{:03}s] ({}): {}\x1b[0m",
elapsed.as_secs(), elapsed.subsec_nanos() / 1000000,
record.target().replace("smoltcp::", ""), record.args())
} else {
format!("\x1b[32m[{:6}.{:03}s] ({}): {}\x1b[0m",
elapsed.as_secs(), elapsed.subsec_nanos() / 1000000,
record.target(), record.args())
}
})
.filter(None, LogLevelFilter::Trace)
.init()
.unwrap();
}

pub fn setup_device(more_args: &[&str])
-> (Tracer<FaultInjector<TapInterface>, EthernetFrame<&'static [u8]>>,
Vec<String>) {
let mut opts = getopts::Options::new();
opts.optopt("", "drop-chance", "Chance of dropping a packet (%)", "CHANCE");
opts.optopt("", "corrupt-chance", "Chance of corrupting a packet (%)", "CHANCE");
opts.optflag("h", "help", "print this help menu");

let matches = opts.parse(env::args().skip(1)).unwrap();
if matches.opt_present("h") || matches.free.len() != more_args.len() + 1 {
let brief = format!("Usage: {} INTERFACE {} [options]",
env::args().nth(0).unwrap(),
more_args.join(" "));
print!("{}", opts.usage(&brief));
process::exit(if matches.free.len() != more_args.len() + 1 { 1 } else { 0 });
}
let drop_chance = u8::from_str(&matches.opt_str("drop-chance")
.unwrap_or("0".to_string())).unwrap();
let corrupt_chance = u8::from_str(&matches.opt_str("corrupt-chance")
.unwrap_or("0".to_string())).unwrap();

let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().subsec_nanos();

fn trace_writer(printer: PrettyPrinter<EthernetFrame<&[u8]>>) {
print!("\x1b[37m{}\x1b[0m", printer)
}

let device = TapInterface::new(&matches.free[0]).unwrap();
let mut device = FaultInjector::new(device, seed);
device.set_drop_chance(drop_chance);
device.set_corrupt_chance(corrupt_chance);
let device = Tracer::<_, EthernetFrame<&'static [u8]>>::new(device, trace_writer);

(device, matches.free[1..].to_owned())
}
67 changes: 36 additions & 31 deletions src/parsing.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#![allow(dead_code)]

use core::str::FromStr;
use core::result;
use wire::{EthernetAddress, IpAddress, Ipv4Address};

@@ -138,23 +137,29 @@ impl<'a> Parser<'a> {
}
}

impl EthernetAddress {
impl FromStr for EthernetAddress {
type Err = ();

/// Parse a string representation of an Ethernet address.
pub fn parse(s: &str) -> Result<EthernetAddress> {
fn from_str(s: &str) -> Result<EthernetAddress> {
Parser::new(s).until_eof(|p| p.accept_mac())
}
}

impl Ipv4Address {
impl FromStr for Ipv4Address {
type Err = ();

/// Parse a string representation of an IPv4 address.
pub fn parse(s: &str) -> Result<Ipv4Address> {
fn from_str(s: &str) -> Result<Ipv4Address> {
Parser::new(s).until_eof(|p| p.accept_ipv4())
}
}

impl IpAddress {
impl FromStr for IpAddress {
type Err = ();

/// Parse a string representation of an IPv4 address.
pub fn parse(s: &str) -> Result<IpAddress> {
fn from_str(s: &str) -> Result<IpAddress> {
Parser::new(s).until_eof(|p| p.accept_ip())
}
}
@@ -165,46 +170,46 @@ mod test {

#[test]
fn test_mac() {
assert_eq!(EthernetAddress::parse(""), Err(()));
assert_eq!(EthernetAddress::parse("02:00:00:00:00:00"),
assert_eq!(EthernetAddress::from_str(""), Err(()));
assert_eq!(EthernetAddress::from_str("02:00:00:00:00:00"),
Ok(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x00])));
assert_eq!(EthernetAddress::parse("01:23:45:67:89:ab"),
assert_eq!(EthernetAddress::from_str("01:23:45:67:89:ab"),
Ok(EthernetAddress([0x01, 0x23, 0x45, 0x67, 0x89, 0xab])));
assert_eq!(EthernetAddress::parse("cd:ef:10:00:00:00"),
assert_eq!(EthernetAddress::from_str("cd:ef:10:00:00:00"),
Ok(EthernetAddress([0xcd, 0xef, 0x10, 0x00, 0x00, 0x00])));
assert_eq!(EthernetAddress::parse("00:00:00:ab:cd:ef"),
assert_eq!(EthernetAddress::from_str("00:00:00:ab:cd:ef"),
Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef])));
assert_eq!(EthernetAddress::parse("00-00-00-ab-cd-ef"),
assert_eq!(EthernetAddress::from_str("00-00-00-ab-cd-ef"),
Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef])));
assert_eq!(EthernetAddress::parse("AB-CD-EF-00-00-00"),
assert_eq!(EthernetAddress::from_str("AB-CD-EF-00-00-00"),
Ok(EthernetAddress([0xab, 0xcd, 0xef, 0x00, 0x00, 0x00])));
assert_eq!(EthernetAddress::parse("100:00:00:00:00:00"), Err(()));
assert_eq!(EthernetAddress::parse("002:00:00:00:00:00"), Err(()));
assert_eq!(EthernetAddress::parse("02:00:00:00:00:000"), Err(()));
assert_eq!(EthernetAddress::parse("02:00:00:00:00:0x"), Err(()));
assert_eq!(EthernetAddress::from_str("100:00:00:00:00:00"), Err(()));
assert_eq!(EthernetAddress::from_str("002:00:00:00:00:00"), Err(()));
assert_eq!(EthernetAddress::from_str("02:00:00:00:00:000"), Err(()));
assert_eq!(EthernetAddress::from_str("02:00:00:00:00:0x"), Err(()));
}

#[test]
fn test_ipv4() {
assert_eq!(Ipv4Address::parse(""), Err(()));
assert_eq!(Ipv4Address::parse("1.2.3.4"),
assert_eq!(Ipv4Address::from_str(""), Err(()));
assert_eq!(Ipv4Address::from_str("1.2.3.4"),
Ok(Ipv4Address([1, 2, 3, 4])));
assert_eq!(Ipv4Address::parse("001.2.3.4"),
assert_eq!(Ipv4Address::from_str("001.2.3.4"),
Ok(Ipv4Address([1, 2, 3, 4])));
assert_eq!(Ipv4Address::parse("0001.2.3.4"), Err(()));
assert_eq!(Ipv4Address::parse("999.2.3.4"), Err(()));
assert_eq!(Ipv4Address::parse("1.2.3.4.5"), Err(()));
assert_eq!(Ipv4Address::parse("1.2.3"), Err(()));
assert_eq!(Ipv4Address::parse("1.2.3."), Err(()));
assert_eq!(Ipv4Address::parse("1.2.3.4."), Err(()));
assert_eq!(Ipv4Address::from_str("0001.2.3.4"), Err(()));
assert_eq!(Ipv4Address::from_str("999.2.3.4"), Err(()));
assert_eq!(Ipv4Address::from_str("1.2.3.4.5"), Err(()));
assert_eq!(Ipv4Address::from_str("1.2.3"), Err(()));
assert_eq!(Ipv4Address::from_str("1.2.3."), Err(()));
assert_eq!(Ipv4Address::from_str("1.2.3.4."), Err(()));
}

#[test]
fn test_ip() {
assert_eq!(IpAddress::parse(""),
assert_eq!(IpAddress::from_str(""),
Ok(IpAddress::Unspecified));
assert_eq!(IpAddress::parse("1.2.3.4"),
assert_eq!(IpAddress::from_str("1.2.3.4"),
Ok(IpAddress::Ipv4(Ipv4Address([1, 2, 3, 4]))));
assert_eq!(IpAddress::parse("x"), Err(()));
assert_eq!(IpAddress::from_str("x"), Err(()));
}
}
Loading