Skip to content

Commit

Permalink
Merge branch 'master' into fix-ringbuffer
Browse files Browse the repository at this point in the history
  • Loading branch information
whitequark committed May 24, 2018
2 parents 8c4e2ec + 9c1900f commit 6e2da5e
Show file tree
Hide file tree
Showing 23 changed files with 1,792 additions and 240 deletions.
7 changes: 5 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ readme = "README.md"
keywords = ["ip", "tcp", "udp", "ethernet", "network"]
categories = ["embedded"]
license = "0BSD"
# Each example should have an explicit `[[example]]` section here to
# ensure that the correct features are enabled.
autoexamples = false

[dependencies]
managed = { version = "0.5", default-features = false, features = ["map"] }
Expand Down Expand Up @@ -54,11 +57,11 @@ required-features = ["std", "phy-raw_socket", "proto-ipv4"]

[[example]]
name = "httpclient"
required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-tcp"]
required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-ipv6", "socket-tcp"]

[[example]]
name = "ping"
required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-icmp"]
required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-ipv6", "socket-icmp"]

[[example]]
name = "server"
Expand Down
40 changes: 38 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ The only supported medium is Ethernet.

### IP layer

The only supported internetworking protocol is IPv4.
#### IPv4

* IPv4 header checksum is generated and validated.
* IPv4 time-to-live value is configurable per socket, set to 64 by default.
Expand All @@ -41,8 +41,16 @@ The only supported internetworking protocol is IPv4.
* IPv4 options are **not** supported and are silently ignored.
* IPv4 routes (other than the default one) are **not** supported.

#### IPv6

* IPv6 hop-limit value is configurable per socket, set to 64 by default.
* IPv6 default gateway is **not** supported.
* IPv6 extension headers are **not** supported.

### ICMP layer

#### ICMPv4

The ICMPv4 protocol is supported, and ICMP sockets are available.

* ICMPv4 header checksum is supported.
Expand All @@ -52,6 +60,21 @@ The ICMPv4 protocol is supported, and ICMP sockets are available.
* ICMPv4 protocol unreachable messages are **not** passed to higher layers when received.
* ICMPv4 parameter problem messages are **not** generated.

#### ICMPv6

The ICMPv6 protocol is supported, but is **not** available via ICMP sockets.

* ICMPv6 header checksum is supported.
* ICMPv6 echo replies are generated in response to echo requests.
* ICMPv6 protocol unreachable messages are **not** passed to higher layers when received.

#### NDISC

* Neighbor Advertisement messages are generated in response to Neighbor Solicitations.
* Router Advertisement messages are **not** generated or read.
* Router Solicitation messages are **not** generated or read.
* Redirected Header messages are **not** generated or read.

### UDP layer

The UDP protocol is supported over IPv4, and UDP sockets are available.
Expand Down Expand Up @@ -80,6 +103,7 @@ The TCP protocol is supported over IPv4, and server and client TCP sockets are a
* Congestion control is **not** implemented.
* Timestamping is **not** supported.
* Urgent pointer is **ignored**.
* Probing Zero Windows is **not** implemented.

## Installation

Expand Down Expand Up @@ -166,13 +190,19 @@ a specific user:
sudo ip tuntap add name tap0 mode tap user $USER
sudo ip link set tap0 up
sudo ip addr add 192.168.69.100/24 dev tap0
sudo ip -6 addr add fe80::100/64 dev tap0
sudo ip -6 addr add fdaa::100/64 dev tap0
sudo ip -6 route add fe80::/64 dev tap0
sudo ip -6 route add fdaa::/64 dev tap0
```

It's possible to let _smoltcp_ access Internet by enabling routing for the tap interface:

```sh
sudo iptables -t nat -A POSTROUTING -s 192.168.69.0/24 -j MASQUERADE
sudo sysctl net.ipv4.ip_forward=1
sudo ip6tables -t nat -A POSTROUTING -s fdaa::/64 -j MASQUERADE
sudo sysctl -w net.ipv6.conf.all.forwarding=1
```

### Fault injection
Expand Down Expand Up @@ -221,7 +251,7 @@ sudo ./target/debug/examples/tcpdump eth0

_examples/httpclient.rs_ emulates a network host that can initiate HTTP requests.

The host is assigned the hardware address `02-00-00-00-00-02` and IPv4 address `192.168.69.1`.
The host is assigned the hardware address `02-00-00-00-00-02`, IPv4 address `192.168.69.1`, and IPv6 address `fdaa::1`.

Read its [source code](/examples/httpclient.rs), then run it as:

Expand All @@ -235,6 +265,12 @@ For example:
cargo run --example httpclient -- tap0 93.184.216.34 http://example.org/
```

or:

```sh
cargo run --example httpclient -- tap0 2606:2800:220:1:248:1893:25c8:1946 http://example.org/
```

It connects to the given address (not a hostname) and URL, and prints any returned response data.
The TCP socket buffers are limited to 1024 bytes to make packet traces more interesting.

Expand Down
8 changes: 6 additions & 2 deletions examples/httpclient.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::collections::BTreeMap;
use std::os::unix::io::AsRawFd;
use url::Url;
use smoltcp::phy::wait as phy_wait;
use smoltcp::wire::{EthernetAddress, Ipv4Address, IpAddress, IpCidr};
use smoltcp::wire::{EthernetAddress, Ipv4Address, Ipv6Address, IpAddress, IpCidr};
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder};
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
use smoltcp::time::Instant;
Expand Down Expand Up @@ -42,13 +42,17 @@ fn main() {
let tcp_socket = TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer);

let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]);
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)];
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24),
IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64),
IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)];
let default_v4_gw = Ipv4Address::new(192, 168, 69, 100);
let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100);
let mut iface = EthernetInterfaceBuilder::new(device)
.ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache)
.ip_addrs(ip_addrs)
.ipv4_gateway(default_v4_gw)
.ipv6_gateway(default_v6_gw)
.finalize();

let mut sockets = SocketSet::new(vec![]);
Expand Down
101 changes: 74 additions & 27 deletions examples/ping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,47 @@ use smoltcp::time::{Duration, Instant};
use smoltcp::phy::Device;
use smoltcp::phy::wait as phy_wait;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr,
Ipv6Address, Icmpv6Repr, Icmpv6Packet,
Ipv4Address, Icmpv4Repr, Icmpv4Packet};
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder};
use smoltcp::socket::{SocketSet, IcmpSocket, IcmpSocketBuffer, IcmpPacketMetadata, IcmpEndpoint};
use std::collections::HashMap;
use byteorder::{ByteOrder, NetworkEndian};

macro_rules! send_icmp_ping {
( $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr,
$echo_payload:expr, $socket:expr, $remote_addr:expr ) => {{
let icmp_repr = $repr_type::EchoRequest {
ident: $ident,
seq_no: $seq_no,
data: &$echo_payload,
};

let icmp_payload = $socket
.send(icmp_repr.buffer_len(), $remote_addr)
.unwrap();

let mut icmp_packet = $packet_type::new(icmp_payload);
(icmp_repr, icmp_packet)
}}
}

macro_rules! get_icmp_pong {
( $repr_type:ident, $repr:expr, $payload:expr, $waiting_queue:expr, $remote_addr:expr,
$timestamp:expr, $received:expr ) => {{
if let $repr_type::EchoReply { seq_no, data, .. } = $repr {
if let Some(_) = $waiting_queue.get(&seq_no) {
let packet_timestamp_ms = NetworkEndian::read_i64(data);
println!("{} bytes from {}: icmp_seq={}, time={}ms",
data.len(), $remote_addr, seq_no,
$timestamp.total_millis() - packet_timestamp_ms);
$waiting_queue.remove(&seq_no);
$received += 1;
}
}
}}
}

fn main() {
utils::setup_logging("warn");

Expand All @@ -40,7 +75,7 @@ fn main() {
let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
let device_caps = device.capabilities();
let address = Ipv4Address::from_str(&matches.free[0]).expect("invalid address format");
let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format");
let count = matches.opt_str("count").map(|s| usize::from_str(&s).unwrap()).unwrap_or(4);
let interval = matches.opt_str("interval")
.map(|s| Duration::from_secs(u64::from_str(&s).unwrap()))
Expand All @@ -52,19 +87,23 @@ fn main() {
let neighbor_cache = NeighborCache::new(BTreeMap::new());

let remote_addr = address;
let local_addr = Ipv4Address::new(192, 168, 69, 1);

let icmp_rx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY], vec![0; 256]);
let icmp_tx_buffer = IcmpSocketBuffer::new(vec![IcmpPacketMetadata::EMPTY], vec![0; 256]);
let icmp_socket = IcmpSocket::new(icmp_rx_buffer, icmp_tx_buffer);

let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]);
let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24);
let src_ipv6 = IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1);
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24),
IpCidr::new(src_ipv6, 64),
IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)];
let default_v4_gw = Ipv4Address::new(192, 168, 69, 100);
let default_v6_gw = Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0x100);
let mut iface = EthernetInterfaceBuilder::new(device)
.ethernet_addr(ethernet_addr)
.ip_addrs([ip_addr])
.ip_addrs(ip_addrs)
.ipv4_gateway(default_v4_gw)
.ipv6_gateway(default_v6_gw)
.neighbor_cache(neighbor_cache)
.finalize();

Expand All @@ -77,7 +116,6 @@ fn main() {
let mut echo_payload = [0xffu8; 40];
let mut waiting_queue = HashMap::new();
let ident = 0x22b;
let endpoint = IpAddress::Ipv4(remote_addr);

loop {
iface.poll(&mut sockets, Instant::now()).unwrap();
Expand All @@ -93,18 +131,23 @@ fn main() {
if socket.can_send() && seq_no < count as u16 &&
send_at <= timestamp {
NetworkEndian::write_i64(&mut echo_payload, timestamp.total_millis());
let icmp_repr = Icmpv4Repr::EchoRequest {
ident: ident,
seq_no,
data: &echo_payload,
};

let icmp_payload = socket
.send(icmp_repr.buffer_len(), endpoint)
.unwrap();

let mut icmp_packet = Icmpv4Packet::new(icmp_payload);
icmp_repr.emit(&mut icmp_packet, &device_caps.checksum);
match remote_addr {
IpAddress::Ipv4(_) => {
let (icmp_repr, mut icmp_packet) = send_icmp_ping!(
Icmpv4Repr, Icmpv4Packet, ident, seq_no,
echo_payload, socket, remote_addr);
icmp_repr.emit(&mut icmp_packet, &device_caps.checksum);
},
IpAddress::Ipv6(_) => {
let (icmp_repr, mut icmp_packet) = send_icmp_ping!(
Icmpv6Repr, Icmpv6Packet, ident, seq_no,
echo_payload, socket, remote_addr);
icmp_repr.emit(&src_ipv6, &remote_addr,
&mut icmp_packet, &device_caps.checksum);
},
_ => unimplemented!()
}

waiting_queue.insert(seq_no, timestamp);
seq_no += 1;
Expand All @@ -113,18 +156,22 @@ fn main() {

if socket.can_recv() {
let (payload, _) = socket.recv().unwrap();
let icmp_packet = Icmpv4Packet::new(&payload);
let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &device_caps.checksum).unwrap();

if let Icmpv4Repr::EchoReply { seq_no, data, .. } = icmp_repr {
if let Some(_) = waiting_queue.get(&seq_no) {
let packet_timestamp_ms = NetworkEndian::read_i64(data);
println!("{} bytes from {}: icmp_seq={}, time={}ms",
data.len(), remote_addr, seq_no,
timestamp.total_millis() - packet_timestamp_ms);
waiting_queue.remove(&seq_no);
received += 1;

match remote_addr {
IpAddress::Ipv4(_) => {
let icmp_packet = Icmpv4Packet::new(&payload);
let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &device_caps.checksum).unwrap();
get_icmp_pong!(Icmpv4Repr, icmp_repr, payload,
waiting_queue, remote_addr, timestamp, received);
}
IpAddress::Ipv6(_) => {
let icmp_packet = Icmpv6Packet::new(&payload);
let icmp_repr = Icmpv6Repr::parse(&remote_addr, &src_ipv6,
&icmp_packet, &device_caps.checksum).unwrap();
get_icmp_pong!(Icmpv6Repr, icmp_repr, payload,
waiting_queue, remote_addr, timestamp, received);
},
_ => unimplemented!()
}
}

Expand Down
6 changes: 5 additions & 1 deletion examples/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,11 @@ fn main() {
let tcp4_socket = TcpSocket::new(tcp4_rx_buffer, tcp4_tx_buffer);

let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)];
let ip_addrs = [
IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24),
IpCidr::new(IpAddress::v6(0xfdaa, 0, 0, 0, 0, 0, 0, 1), 64),
IpCidr::new(IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64)
];
let mut iface = EthernetInterfaceBuilder::new(device)
.ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache)
Expand Down

0 comments on commit 6e2da5e

Please sign in to comment.