Compare commits

..

28 Commits

Author SHA1 Message Date
Dario Nieuwenhuis b8a262cec2 Merge pull request #505 from theli-ua/v0.7.x
Emit dns servers in DHCPv4 repr. Fixes #504
2021-06-27 08:42:34 +02:00
Anton Romanov 0974b3c6be map -> inspect 2021-06-24 18:18:26 +00:00
Anton Romanov 4c05c3a9b9 Make clippy happy 2021-06-23 19:35:48 +00:00
Anton Romanov 5211338f57 remove unwrap 2021-06-23 19:33:36 +00:00
Anton Romanov c07cbfea0e Get rid of vec 2021-06-23 18:33:32 +00:00
Anton Romanov d0a7921cf6 Emit dns servers in DHCPv4 repr. Fixes #504 2021-06-23 18:19:54 +00:00
Dario Nieuwenhuis 450c5d8f87 Bump to v0.7.4, update changelog. 2021-06-11 22:56:44 +02:00
Dario Nieuwenhuis 3baa5fd28f tcp: use nonzero initial sequence number. 2021-06-11 22:55:52 +02:00
Dario Nieuwenhuis 0e7f78f47d tcp: fix substract with overflow when receiving a SYNACK with unincremented ACK number. 2021-06-11 22:55:32 +02:00
Dario Nieuwenhuis 1b3344bc52 Fix "subtract sequence numbers with underflow" on remote window shrink.
Fixes #489
2021-06-11 22:54:42 +02:00
Dario Nieuwenhuis ab6d383db8 Bump to v0.7.3, update changelog. 2021-05-29 04:40:25 +02:00
Dario Nieuwenhuis 3433523a7c Remove unused attribute. 2021-05-29 04:33:54 +02:00
Dario Nieuwenhuis 9c8df9f1f3 Bump to v0.7.2, add CHANGELOG 2021-05-29 04:29:32 +02:00
Dario Nieuwenhuis e8fe034b61 Fix u32::MAX 2021-05-29 04:26:25 +02:00
Anton Romanov 6dca868aed Account for lease time, router and subnet options in DhcpRepr::buffer_len 2021-05-29 04:26:25 +02:00
Dario Nieuwenhuis a2302412e7 tcp: LastAck should only change to Closed on ack of fin.
Fixes #470
2021-05-29 04:26:15 +02:00
Dario Nieuwenhuis cefbed6e52 tcp rtte: fix "attempt to multiply with overflow". Fixes #468 2021-05-29 04:25:11 +02:00
Dario Nieuwenhuis b693333125 phy: fix FaultInjector returning a too big buffer when simulating a drop on tx 2021-05-29 04:24:41 +02:00
Dario Nieuwenhuis b7c589e371 dhcp: Clear expiration time on reset. 2021-05-29 04:22:53 +02:00
Dario Nieuwenhuis 4047b9f75a dhcp: always send parameter_request_list. Fixes #445. 2021-05-29 04:21:48 +02:00
Dario Nieuwenhuis c7861455e1 iface: check for ipv4 subnet broadcast addrs everywhere 2021-05-29 04:21:16 +02:00
Dario Nieuwenhuis 3dea658b5e Fix 0.7.1 release date 2021-03-27 15:01:31 +01:00
Dario Nieuwenhuis 402bb578f7 Bump version to 0.7.1, add changelog. 2021-03-27 15:00:34 +01:00
Dario Nieuwenhuis 22af77e140 Run tests on backport branches too. 2021-03-25 17:39:28 +01:00
Dario Nieuwenhuis 4e314089d6 Fix "leftover tokens" macro error 2021-03-25 03:21:54 +01:00
Dario Nieuwenhuis a6a28957b3 Fix feature-related compilation issues. 2021-03-25 03:21:54 +01:00
Dario Nieuwenhuis 99f318f86e Fix timeval in phy_wait for times greater than 1 second 2021-03-25 03:21:54 +01:00
Dario Nieuwenhuis 5967c9aa7e Remove support table from docs. Fixes #361
The table is no longer very informative since it's all "yes" now.
2021-03-25 03:21:54 +01:00
93 changed files with 9937 additions and 19099 deletions

6
.github/bors.toml vendored
View File

@ -1,6 +0,0 @@
status = [
"tests",
"fuzz",
"clippy",
"fmt",
]

View File

@ -1,6 +1,6 @@
on:
push:
branches: [ staging, trying ]
branches: [ staging, trying, master, v0.* ]
pull_request_target:
name: Clippy check
@ -17,7 +17,7 @@ jobs:
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: 1.53.0
toolchain: 1.49.0
override: true
components: clippy
- uses: actions-rs/clippy-check@v1

View File

@ -1,6 +1,6 @@
on:
push:
branches: [ staging, trying ]
branches: [ staging, trying, master, v0.* ]
pull_request:
name: Fuzz
@ -16,9 +16,6 @@ jobs:
toolchain: nightly
override: true
- name: Install cargo-fuzz
# Fix for cargo-fuzz on latest nightly: https://github.com/rust-fuzz/cargo-fuzz/issues/276
# Switch back to installing from crates.io when it's released.
#run: cargo install cargo-fuzz
run: cargo install --git https://github.com/rust-fuzz/cargo-fuzz --rev b4df3e58f767b5cad8d1aa6753961003f56f3609
run: cargo install cargo-fuzz
- name: Fuzz
run: cargo fuzz run packet_parser -- -max_len=1536 -max_total_time=30

View File

@ -1,44 +0,0 @@
name: Matrix bot
on:
pull_request_target:
types: [opened, closed]
jobs:
new-pr:
if: github.event.action == 'opened' && github.repository == 'smoltcp-rs/smoltcp'
runs-on: ubuntu-latest
continue-on-error: true
steps:
- name: send message
uses: s3krit/matrix-message-action@v0.0.3
with:
room_id: ${{ secrets.MATRIX_ROOM_ID }}
access_token: ${{ secrets.MATRIX_ACCESS_TOKEN }}
message: "New PR: [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }})"
server: "matrix.org"
merged-pr:
if: github.event.action == 'closed' && github.event.pull_request.merged == true && github.repository == 'smoltcp-rs/smoltcp'
runs-on: ubuntu-latest
continue-on-error: true
steps:
- name: send message
uses: s3krit/matrix-message-action@v0.0.3
with:
room_id: ${{ secrets.MATRIX_ROOM_ID }}
access_token: ${{ secrets.MATRIX_ACCESS_TOKEN }}
message: "PR merged: [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }})"
server: "matrix.org"
abandoned-pr:
if: github.event.action == 'closed' && github.event.pull_request.merged == false && github.repository == 'smoltcp-rs/smoltcp'
runs-on: ubuntu-latest
continue-on-error: true
steps:
- name: send message
uses: s3krit/matrix-message-action@v0.0.3
with:
room_id: ${{ secrets.MATRIX_ROOM_ID }}
access_token: ${{ secrets.MATRIX_ACCESS_TOKEN }}
message: "PR closed without merging: [${{ github.event.pull_request.title }}](${{ github.event.pull_request.html_url }})"
server: "matrix.org"

View File

@ -1,18 +0,0 @@
on:
push:
branches: [ staging, trying ]
pull_request:
name: Rustfmt check
jobs:
fmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
components: rustfmt
- name: Check fmt
run: cargo fmt -- --check

View File

@ -1,27 +1,21 @@
on:
push:
branches: [ staging, trying ]
branches: [ staging, trying, master, v0.* ]
pull_request:
name: Test
jobs:
tests:
runs-on: ubuntu-20.04
needs: [test, check]
steps:
- name: Done
run: exit 0
test:
runs-on: ubuntu-20.04
continue-on-error: ${{ matrix.rust == 'nightly' }}
strategy:
matrix:
# Test on stable, MSRV 1.46, and nightly.
# Test on stable, MSRV 1.40, and nightly.
# Failure is permitted on nightly.
rust:
- stable
- 1.56.0
- 1.40.0
- nightly
features:
@ -32,23 +26,25 @@ jobs:
- std proto-ipv4
# Test features chosen to be as orthogonal as possible.
- std medium-ethernet phy-raw_socket proto-ipv6 socket-udp
- std medium-ethernet phy-tuntap_interface proto-ipv6 socket-udp
- std medium-ethernet proto-ipv4 proto-igmp socket-raw
- std medium-ethernet proto-ipv4 socket-udp socket-tcp
- std medium-ethernet proto-ipv4 proto-dhcpv4 socket-udp
- std medium-ethernet proto-ipv6 socket-udp
- std medium-ethernet proto-ipv6 socket-tcp
- std medium-ethernet proto-ipv4 socket-icmp socket-tcp
- std medium-ethernet proto-ipv6 socket-icmp socket-tcp
- std ethernet phy-raw_socket proto-ipv6 socket-udp
- std ethernet phy-tap_interface proto-ipv6 socket-udp
- std ethernet proto-ipv4 proto-igmp socket-raw
- std ethernet proto-ipv4 socket-udp socket-tcp
- std ethernet proto-ipv4 proto-dhcpv4 socket-udp
- std ethernet proto-ipv6 socket-udp
- std ethernet proto-ipv6 socket-tcp
- std ethernet proto-ipv4 socket-icmp socket-tcp
- std ethernet proto-ipv6 socket-icmp socket-tcp
# Test features chosen to be as aggressive as possible.
- std medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp async
- std ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp async
include:
# Test alloc feature which requires nightly.
- rust: nightly
features: alloc rand-custom-impl medium-ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp
features: alloc ethernet proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp
- rust: nightly
features: alloc proto-ipv4 proto-ipv6 socket-raw socket-udp socket-tcp socket-icmp
steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
@ -64,17 +60,16 @@ jobs:
continue-on-error: ${{ matrix.rust == 'nightly' }}
strategy:
matrix:
# Test on stable, MSRV 1.46, and nightly.
# Test on stable, MSRV 1.40, and nightly.
# Failure is permitted on nightly.
rust:
- stable
- 1.56.0
- 1.40.0
- nightly
features:
# These feature sets cannot run tests, so we only check they build.
- rand-custom-impl medium-ip medium-ethernet medium-ieee802154 proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async
- rand-custom-impl defmt medium-ip medium-ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async
- ethernet proto-ipv6 proto-ipv6 proto-igmp proto-dhcpv4 socket-raw socket-udp socket-tcp socket-icmp async
steps:
- uses: actions/checkout@v2

View File

@ -6,14 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
- Version bumped to 0.8
- Minimum Supported Rust Version (MSRV) **bumped** from 1.40 to 1.46
- Update `managed` from 0.7 to 0.8 ([442](https://github.com/smoltcp-rs/smoltcp/pull/442))
- udp: Add `close()` method to unbind socket.
## [0.7.5] - 2021-06-28
- dhcpv4: emit DNS servers in repr (#505)
## [0.7.4] - 2021-06-11
@ -47,7 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [0.7.0] - 2021-01-20
- Minimum Supported Rust Version (MSRV) **bumped** from 1.36 to 1.40
Minimum Supported Rust Version (MSRV) **bumped** from 1.36 to 1.40
### New features
- tcp: Allow distinguishing between graceful (FIN) and ungraceful (RST) close. On graceful close, `recv()` now returns `Error::Finished`. On ungraceful close, `Error::Illegal` is returned, as before. ([351](https://github.com/smoltcp-rs/smoltcp/pull/351))
@ -84,7 +77,6 @@ only processed when directed to the 255.255.255.255 address. ([377](https://gith
- Simplify lifetime parameters of sockets, SocketSet, EthernetInterface ([410](https://github.com/smoltcp-rs/smoltcp/pull/410), [413](https://github.com/smoltcp-rs/smoltcp/pull/413))
[Unreleased]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.0...HEAD
[0.7.5]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.4...v0.7.5
[0.7.4]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.3...v0.7.4
[0.7.3]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.2...v0.7.3
[0.7.2]: https://github.com/smoltcp-rs/smoltcp/compare/v0.7.1...v0.7.2

View File

@ -1,6 +1,6 @@
[package]
name = "smoltcp"
version = "0.8.0"
version = "0.7.4"
edition = "2018"
authors = ["whitequark <whitequark@whitequark.org>"]
description = "A TCP/IP stack designed for bare-metal, real-time systems without a heap."
@ -16,13 +16,11 @@ license = "0BSD"
autoexamples = false
[dependencies]
managed = { version = "0.8", default-features = false, features = ["map"] }
managed = { version = "0.7", default-features = false, features = ["map"] }
byteorder = { version = "1.0", default-features = false }
log = { version = "0.4.4", default-features = false, optional = true }
libc = { version = "0.2.18", optional = true }
bitflags = { version = "1.0", default-features = false }
defmt = { version = "0.3", optional = true }
rand_core = { version = "0.6.3", optional = true, default-features = false }
[dev-dependencies]
env_logger = "0.5"
@ -31,38 +29,28 @@ rand = "0.3"
url = "1.0"
[features]
std = ["managed/std", "rand_core/std"]
std = ["managed/std"]
alloc = ["managed/alloc"]
verbose = []
rand-custom-impl = []
"medium-ethernet" = ["socket"]
"medium-ip" = ["socket"]
"medium-ieee802154" = ["socket", "proto-sixlowpan"]
"phy-raw_socket" = ["std", "libc"]
"phy-tuntap_interface" = ["std", "libc", "medium-ethernet"]
ethernet = ["socket"]
"phy-raw_socket" = ["std", "libc", "ethernet"]
"phy-tap_interface" = ["std", "libc", "ethernet"]
"proto-ipv4" = []
"proto-igmp" = ["proto-ipv4"]
"proto-dhcpv4" = ["proto-ipv4"]
"proto-dhcpv4" = ["proto-ipv4", "socket-raw", "ethernet"]
"proto-ipv6" = []
"proto-sixlowpan" = ["proto-ipv6"]
"socket" = []
"socket-raw" = ["socket"]
"socket-udp" = ["socket"]
"socket-tcp" = ["socket"]
"socket-icmp" = ["socket"]
"socket-dhcpv4" = ["socket", "medium-ethernet", "proto-dhcpv4"]
"async" = []
default = [
"std", "log", # needed for `cargo test --no-default-features --features default` :/
"medium-ethernet", "medium-ip", "medium-ieee802154",
"phy-raw_socket", "phy-tuntap_interface",
"proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6", "proto-sixlowpan",
"socket-raw", "socket-icmp", "socket-udp", "socket-tcp", "socket-dhcpv4",
"ethernet",
"phy-raw_socket", "phy-tap_interface",
"proto-ipv4", "proto-igmp", "proto-dhcpv4", "proto-ipv6",
"socket-raw", "socket-icmp", "socket-udp", "socket-tcp",
"async"
]
@ -80,39 +68,35 @@ required-features = ["std", "phy-raw_socket", "proto-ipv4"]
[[example]]
name = "httpclient"
required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-ipv6", "socket-tcp"]
required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-ipv6", "socket-tcp"]
[[example]]
name = "ping"
required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-ipv6", "socket-icmp"]
required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-ipv6", "socket-icmp"]
[[example]]
name = "server"
required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "socket-tcp", "socket-udp"]
required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-tcp", "socket-udp"]
[[example]]
name = "client"
required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "socket-tcp", "socket-udp"]
required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-tcp", "socket-udp"]
[[example]]
name = "loopback"
required-features = ["log", "medium-ethernet", "proto-ipv4", "socket-tcp"]
required-features = ["log", "proto-ipv4", "socket-tcp"]
[[example]]
name = "multicast"
required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-igmp", "socket-udp"]
required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-igmp", "socket-udp"]
[[example]]
name = "benchmark"
required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "socket-raw", "socket-udp"]
required-features = ["std", "phy-tap_interface", "proto-ipv4", "socket-raw", "socket-udp"]
[[example]]
name = "dhcp_client"
required-features = ["std", "medium-ethernet", "medium-ip", "phy-tuntap_interface", "proto-ipv4", "proto-dhcpv4", "socket-raw"]
[[example]]
name = "sixlowpan"
required-features = ["std", "medium-ieee802154", "phy-raw_socket", "proto-sixlowpan", "socket-udp"]
required-features = ["std", "phy-tap_interface", "proto-ipv4", "proto-dhcpv4", "socket-raw"]
[profile.release]
debug = 2

View File

@ -1,17 +1,12 @@
# smoltcp
[![docs.rs](https://docs.rs/smoltcp/badge.svg)](https://docs.rs/smoltcp)
[![crates.io](https://img.shields.io/crates/v/smoltcp.svg)](https://crates.io/crates/smoltcp)
[![crates.io](https://img.shields.io/crates/d/smoltcp.svg)](https://crates.io/crates/smoltcp)
[![crates.io](https://img.shields.io/matrix/smoltcp:matrix.org)](https://matrix.to/#/#smoltcp:matrix.org)
_smoltcp_ is a standalone, event-driven TCP/IP stack that is designed for bare-metal,
real-time systems. Its design goals are simplicity and robustness. Its design anti-goals
include complicated compile-time computations, such as macro or type tricks, even
at cost of performance degradation.
_smoltcp_ does not need heap allocation *at all*, is [extensively documented][docs],
and compiles on stable Rust 1.53 and later.
and compiles on stable Rust 1.40 and later.
_smoltcp_ achieves [~Gbps of throughput](#examplesbenchmarkrs) when tested against
the Linux TCP stack in loopback mode.
@ -130,7 +125,7 @@ To use the _smoltcp_ library in your project, add the following to `Cargo.toml`:
```toml
[dependencies]
smoltcp = "0.7.5"
smoltcp = "0.5"
```
The default configuration assumes a hosted environment, for ease of evaluation.
@ -138,7 +133,7 @@ You probably want to disable default features and configure them one by one:
```toml
[dependencies]
smoltcp = { version = "0.7.5", default-features = false, features = ["log"] }
smoltcp = { version = "0.5", default-features = false, features = ["log"] }
```
### Feature `std`
@ -175,9 +170,9 @@ or `BufWriter` is used, which are of course not available on heap-less systems.
This feature is disabled by default.
### Features `phy-raw_socket` and `phy-tuntap_interface`
### Features `phy-raw_socket` and `phy-tap_interface`
Enable `smoltcp::phy::RawSocket` and `smoltcp::phy::TunTapInterface`, respectively.
Enable `smoltcp::phy::RawSocket` and `smoltcp::phy::TapInterface`, respectively.
These features are enabled by default.
@ -222,45 +217,6 @@ 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
# Some distros have a default policy of DROP. This allows the traffic.
sudo iptables -A FORWARD -i tap0 -s 192.168.69.0/24 -j ACCEPT
sudo iptables -A FORWARD -o tap0 -d 192.168.69.0/24 -j ACCEPT
```
### Bridged connection
Instead of the routed connection above, you may also set up a bridged (switched)
connection. This will make smoltcp speak directly to your LAN, with real ARP, etc.
It is needed to run the DHCP example.
NOTE: In this case, the examples' IP configuration must match your LAN's!
NOTE: this ONLY works with actual wired Ethernet connections. It
will NOT work on a WiFi connection.
```sh
# Replace with your wired Ethernet interface name
ETH=enp0s20f0u1u1
sudo modprobe bridge
sudo modprobe br_netfilter
sudo sysctl -w net.bridge.bridge-nf-call-arptables=0
sudo sysctl -w net.bridge.bridge-nf-call-ip6tables=0
sudo sysctl -w net.bridge.bridge-nf-call-iptables=0
sudo ip tuntap add name tap0 mode tap user $USER
sudo brctl addbr br0
sudo brctl addif br0 tap0
sudo brctl addif br0 $ETH
sudo ip link set tap0 up
sudo ip link set $ETH up
sudo ip link set br0 up
# This connects your host system to the internet, so you can use it
# at the same time you run the examples.
sudo dhcpcd br0
```
### Fault injection
@ -314,19 +270,19 @@ The host is assigned the hardware address `02-00-00-00-00-02`, IPv4 address `192
Read its [source code](/examples/httpclient.rs), then run it as:
```sh
cargo run --example httpclient -- --tap tap0 ADDRESS URL
cargo run --example httpclient -- tap0 ADDRESS URL
```
For example:
```sh
cargo run --example httpclient -- --tap tap0 93.184.216.34 http://example.org/
cargo run --example httpclient -- tap0 93.184.216.34 http://example.org/
```
or:
```sh
cargo run --example httpclient -- --tap tap0 2606:2800:220:1:248:1893:25c8:1946 http://example.org/
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.
@ -341,7 +297,7 @@ The host is assigned the hardware address `02-00-00-00-00-02` and IPv4 address `
Read its [source code](/examples/ping.rs), then run it as:
```sh
cargo run --example ping -- --tap tap0 ADDRESS
cargo run --example ping -- tap0 ADDRESS
```
It sends a series of 4 ICMP ECHO\_REQUEST packets to the given address at one second intervals and
@ -363,7 +319,7 @@ The host is assigned the hardware address `02-00-00-00-00-01` and IPv4 address `
Read its [source code](/examples/server.rs), then run it as:
```sh
cargo run --example server -- --tap tap0
cargo run --example server -- tap0
```
It responds to:
@ -393,7 +349,7 @@ The host is assigned the hardware address `02-00-00-00-00-02` and IPv4 address `
Read its [source code](/examples/client.rs), then run it as:
```sh
cargo run --example client -- --tap tap0 ADDRESS PORT
cargo run --example client -- tap0 ADDRESS PORT
```
It connects to the given address (not a hostname) and port (e.g. `socat stdio tcp4-listen:1234`),
@ -406,7 +362,7 @@ _examples/benchmark.rs_ implements a simple throughput benchmark.
Read its [source code](/examples/benchmark.rs), then run it as:
```sh
cargo run --release --example benchmark -- --tap tap0 [reader|writer]
cargo run --release --example benchmark -- tap0 [reader|writer]
```
It establishes a connection to itself from a different thread and reads or writes a large amount
@ -416,9 +372,9 @@ A typical result (achieved on a Intel Core i7-7500U CPU and a Linux 4.9.65 x86_6
on a Dell XPS 13 9360 laptop) is as follows:
```
$ cargo run -q --release --example benchmark -- --tap tap0 reader
$ cargo run -q --release --example benchmark tap0 reader
throughput: 2.556 Gbps
$ cargo run -q --release --example benchmark -- --tap tap0 writer
$ cargo run -q --release --example benchmark tap0 writer
throughput: 5.301 Gbps
```
@ -435,7 +391,7 @@ Although it does not require `std`, this example still requires the `alloc` feat
Read its [source code](/examples/loopback.rs), then run it without `std`:
```sh
cargo run --example loopback --no-default-features --features="log proto-ipv4 socket-tcp alloc"
cargo run --example loopback --no-default-features --features="log proto-ipv4 socket-tcp alloc"
```
... or with `std` (in this case the features don't have to be explicitly listed):

View File

@ -1,25 +1,22 @@
#![feature(test)]
mod wire {
use smoltcp::phy::ChecksumCapabilities;
use smoltcp::wire::{IpAddress, IpProtocol};
use test;
#[cfg(feature = "proto-ipv6")]
use smoltcp::wire::{Ipv6Address, Ipv6Repr, Ipv6Packet};
#[cfg(feature = "proto-ipv4")]
use smoltcp::wire::{Ipv4Address, Ipv4Packet, Ipv4Repr};
#[cfg(feature = "proto-ipv6")]
use smoltcp::wire::{Ipv6Address, Ipv6Packet, Ipv6Repr};
use smoltcp::wire::{TcpControl, TcpPacket, TcpRepr, TcpSeqNumber};
use smoltcp::wire::{UdpPacket, UdpRepr};
extern crate test;
use smoltcp::wire::{Ipv4Address, Ipv4Repr, Ipv4Packet};
use smoltcp::phy::{ChecksumCapabilities};
use smoltcp::wire::{IpAddress, IpProtocol};
use smoltcp::wire::{TcpRepr, TcpPacket, TcpSeqNumber, TcpControl};
use smoltcp::wire::{UdpRepr, UdpPacket};
#[cfg(feature = "proto-ipv6")]
const SRC_ADDR: IpAddress = IpAddress::Ipv6(Ipv6Address([
0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
]));
const SRC_ADDR: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1]));
#[cfg(feature = "proto-ipv6")]
const DST_ADDR: IpAddress = IpAddress::Ipv6(Ipv6Address([
0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2,
]));
const DST_ADDR: IpAddress = IpAddress::Ipv6(Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 2]));
#[cfg(all(not(feature = "proto-ipv6"), feature = "proto-ipv4"))]
const SRC_ADDR: IpAddress = IpAddress::Ipv4(Ipv4Address([192, 168, 1, 1]));
@ -29,53 +26,42 @@ mod wire {
#[bench]
#[cfg(any(feature = "proto-ipv6", feature = "proto-ipv4"))]
fn bench_emit_tcp(b: &mut test::Bencher) {
static PAYLOAD_BYTES: [u8; 400] = [0x2a; 400];
static PAYLOAD_BYTES: [u8; 400] =
[0x2a; 400];
let repr = TcpRepr {
src_port: 48896,
dst_port: 80,
control: TcpControl::Syn,
seq_number: TcpSeqNumber(0x01234567),
ack_number: None,
window_len: 0x0123,
window_scale: None,
src_port: 48896,
dst_port: 80,
seq_number: TcpSeqNumber(0x01234567),
ack_number: None,
window_len: 0x0123,
control: TcpControl::Syn,
max_seg_size: None,
sack_permitted: false,
sack_ranges: [None, None, None],
payload: &PAYLOAD_BYTES,
window_scale: None,
payload: &PAYLOAD_BYTES
};
let mut bytes = vec![0xa5; repr.buffer_len()];
b.iter(|| {
let mut packet = TcpPacket::new_unchecked(&mut bytes);
repr.emit(
&mut packet,
&SRC_ADDR,
&DST_ADDR,
&ChecksumCapabilities::default(),
);
let mut packet = TcpPacket::new(&mut bytes);
repr.emit(&mut packet, &SRC_ADDR, &DST_ADDR, &ChecksumCapabilities::default());
});
}
#[bench]
#[cfg(any(feature = "proto-ipv6", feature = "proto-ipv4"))]
fn bench_emit_udp(b: &mut test::Bencher) {
static PAYLOAD_BYTES: [u8; 400] = [0x2a; 400];
static PAYLOAD_BYTES: [u8; 400] =
[0x2a; 400];
let repr = UdpRepr {
src_port: 48896,
dst_port: 80,
payload: &PAYLOAD_BYTES
};
let mut bytes = vec![0xa5; repr.header_len() + PAYLOAD_BYTES.len()];
let mut bytes = vec![0xa5; repr.buffer_len()];
b.iter(|| {
let mut packet = UdpPacket::new_unchecked(&mut bytes);
repr.emit(
&mut packet,
&SRC_ADDR,
&DST_ADDR,
PAYLOAD_BYTES.len(),
|buf| buf.copy_from_slice(&PAYLOAD_BYTES),
&ChecksumCapabilities::default(),
);
let mut packet = UdpPacket::new(&mut bytes);
repr.emit(&mut packet, &SRC_ADDR, &DST_ADDR, &ChecksumCapabilities::default());
});
}
@ -83,16 +69,16 @@ mod wire {
#[cfg(feature = "proto-ipv4")]
fn bench_emit_ipv4(b: &mut test::Bencher) {
let repr = Ipv4Repr {
src_addr: Ipv4Address([192, 168, 1, 1]),
dst_addr: Ipv4Address([192, 168, 1, 2]),
protocol: IpProtocol::Tcp,
src_addr: Ipv4Address([192, 168, 1, 1]),
dst_addr: Ipv4Address([192, 168, 1, 2]),
protocol: IpProtocol::Tcp,
payload_len: 100,
hop_limit: 64,
hop_limit: 64
};
let mut bytes = vec![0xa5; repr.buffer_len()];
b.iter(|| {
let mut packet = Ipv4Packet::new_unchecked(&mut bytes);
let mut packet = Ipv4Packet::new(&mut bytes);
repr.emit(&mut packet, &ChecksumCapabilities::default());
});
}
@ -101,16 +87,18 @@ mod wire {
#[cfg(feature = "proto-ipv6")]
fn bench_emit_ipv6(b: &mut test::Bencher) {
let repr = Ipv6Repr {
src_addr: Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]),
dst_addr: Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2]),
src_addr: Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1]),
dst_addr: Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 2]),
next_header: IpProtocol::Tcp,
payload_len: 100,
hop_limit: 64,
hop_limit: 64
};
let mut bytes = vec![0xa5; repr.buffer_len()];
b.iter(|| {
let mut packet = Ipv6Packet::new_unchecked(&mut bytes);
let mut packet = Ipv6Packet::new(&mut bytes);
repr.emit(&mut packet);
});
}

View File

@ -2,33 +2,28 @@
mod utils;
use log::debug;
use std::cmp;
use std::collections::BTreeMap;
use std::sync::atomic::{Ordering, AtomicBool};
use std::thread;
use std::io::{Read, Write};
use std::net::TcpStream;
use std::os::unix::io::AsRawFd;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;
use log::debug;
use smoltcp::iface::{InterfaceBuilder, NeighborCache};
use smoltcp::phy::{wait as phy_wait, Device, Medium};
use smoltcp::phy::wait as phy_wait;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder};
use smoltcp::socket::SocketSet;
use smoltcp::socket::{TcpSocket, TcpSocketBuffer};
use smoltcp::time::{Duration, Instant};
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
const AMOUNT: usize = 1_000_000_000;
enum Client {
Reader,
Writer,
}
enum Client { Reader, Writer }
fn client(kind: Client) {
let port = match kind {
Client::Reader => 1234,
Client::Writer => 1235,
};
let port = match kind { Client::Reader => 1234, Client::Writer => 1235 };
let mut stream = TcpStream::connect(("192.168.69.1", port)).unwrap();
let mut buffer = vec![0; 1_000_000];
@ -47,7 +42,7 @@ fn client(kind: Client) {
// print!("(P:{})", result);
processed += result
}
Err(err) => panic!("cannot process: {}", err),
Err(err) => panic!("cannot process: {}", err)
}
}
@ -67,18 +62,18 @@ fn main() {
utils::setup_logging("info");
let (mut opts, mut free) = utils::create_options();
utils::add_tuntap_options(&mut opts, &mut free);
utils::add_tap_options(&mut opts, &mut free);
utils::add_middleware_options(&mut opts, &mut free);
free.push("MODE");
let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tuntap_options(&mut matches);
let device = utils::parse_tap_options(&mut matches);
let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
let mode = match matches.free[0].as_ref() {
"reader" => Client::Reader,
"writer" => Client::Writer,
_ => panic!("invalid mode"),
_ => panic!("invalid mode")
};
thread::spawn(move || client(mode));
@ -95,69 +90,68 @@ fn main() {
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
let ip_addrs = [IpCidr::new(IpAddress::v4(192, 168, 69, 1), 24)];
let medium = device.capabilities().medium;
let mut builder = InterfaceBuilder::new(device, vec![]).ip_addrs(ip_addrs);
if medium == Medium::Ethernet {
builder = builder
.hardware_addr(ethernet_addr.into())
.neighbor_cache(neighbor_cache);
}
let mut iface = builder.finalize();
let mut iface = EthernetInterfaceBuilder::new(device)
.ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache)
.ip_addrs(ip_addrs)
.finalize();
let tcp1_handle = iface.add_socket(tcp1_socket);
let tcp2_handle = iface.add_socket(tcp2_socket);
let mut sockets = SocketSet::new(vec![]);
let tcp1_handle = sockets.add(tcp1_socket);
let tcp2_handle = sockets.add(tcp2_socket);
let default_timeout = Some(Duration::from_millis(1000));
let mut processed = 0;
while !CLIENT_DONE.load(Ordering::SeqCst) {
let timestamp = Instant::now();
match iface.poll(timestamp) {
Ok(_) => {}
match iface.poll(&mut sockets, timestamp) {
Ok(_) => {},
Err(e) => {
debug!("poll error: {}", e);
debug!("poll error: {}",e);
}
}
// tcp:1234: emit data
let socket = iface.get_socket::<TcpSocket>(tcp1_handle);
if !socket.is_open() {
socket.listen(1234).unwrap();
}
if socket.can_send() {
if processed < AMOUNT {
let length = socket
.send(|buffer| {
// tcp:1234: emit data
{
let mut socket = sockets.get::<TcpSocket>(tcp1_handle);
if !socket.is_open() {
socket.listen(1234).unwrap();
}
if socket.can_send() {
if processed < AMOUNT {
let length = socket.send(|buffer| {
let length = cmp::min(buffer.len(), AMOUNT - processed);
(length, length)
})
.unwrap();
processed += length;
}).unwrap();
processed += length;
}
}
}
// tcp:1235: sink data
let socket = iface.get_socket::<TcpSocket>(tcp2_handle);
if !socket.is_open() {
socket.listen(1235).unwrap();
}
{
let mut socket = sockets.get::<TcpSocket>(tcp2_handle);
if !socket.is_open() {
socket.listen(1235).unwrap();
}
if socket.can_recv() {
if processed < AMOUNT {
let length = socket
.recv(|buffer| {
if socket.can_recv() {
if processed < AMOUNT {
let length = socket.recv(|buffer| {
let length = cmp::min(buffer.len(), AMOUNT - processed);
(length, length)
})
.unwrap();
processed += length;
}).unwrap();
processed += length;
}
}
}
match iface.poll_at(timestamp) {
match iface.poll_at(&sockets, timestamp) {
Some(poll_at) if timestamp < poll_at => {
phy_wait(fd, Some(poll_at - timestamp)).expect("wait error");
}
},
Some(_) => (),
None => {
phy_wait(fd, default_timeout).expect("wait error");

View File

@ -1,30 +1,29 @@
mod utils;
use log::debug;
use std::str::{self, FromStr};
use std::collections::BTreeMap;
use std::os::unix::io::AsRawFd;
use std::str::{self, FromStr};
use log::debug;
use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes};
use smoltcp::phy::{wait as phy_wait, Device, Medium};
use smoltcp::socket::{TcpSocket, TcpSocketBuffer};
use smoltcp::phy::wait as phy_wait;
use smoltcp::wire::{EthernetAddress, Ipv4Address, IpAddress, IpCidr};
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes};
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address};
fn main() {
utils::setup_logging("");
let (mut opts, mut free) = utils::create_options();
utils::add_tuntap_options(&mut opts, &mut free);
utils::add_tap_options(&mut opts, &mut free);
utils::add_middleware_options(&mut opts, &mut free);
free.push("ADDRESS");
free.push("PORT");
let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tuntap_options(&mut matches);
let device = utils::parse_tap_options(&mut matches);
let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format");
let port = u16::from_str(&matches.free[1]).expect("invalid port format");
@ -40,70 +39,64 @@ fn main() {
let mut routes_storage = [None; 1];
let mut routes = Routes::new(&mut routes_storage[..]);
routes.add_default_ipv4_route(default_v4_gw).unwrap();
let mut iface = EthernetInterfaceBuilder::new(device)
.ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache)
.ip_addrs(ip_addrs)
.routes(routes)
.finalize();
let medium = device.capabilities().medium;
let mut builder = InterfaceBuilder::new(device, vec![])
.ip_addrs(ip_addrs)
.routes(routes);
if medium == Medium::Ethernet {
builder = builder
.hardware_addr(ethernet_addr.into())
.neighbor_cache(neighbor_cache);
let mut sockets = SocketSet::new(vec![]);
let tcp_handle = sockets.add(tcp_socket);
{
let mut socket = sockets.get::<TcpSocket>(tcp_handle);
socket.connect((address, port), 49500).unwrap();
}
let mut iface = builder.finalize();
let tcp_handle = iface.add_socket(tcp_socket);
let socket = iface.get_socket::<TcpSocket>(tcp_handle);
socket.connect((address, port), 49500).unwrap();
let mut tcp_active = false;
loop {
let timestamp = Instant::now();
match iface.poll(timestamp) {
Ok(_) => {}
match iface.poll(&mut sockets, timestamp) {
Ok(_) => {},
Err(e) => {
debug!("poll error: {}", e);
}
}
let socket = iface.get_socket::<TcpSocket>(tcp_handle);
if socket.is_active() && !tcp_active {
debug!("connected");
} else if !socket.is_active() && tcp_active {
debug!("disconnected");
break;
}
tcp_active = socket.is_active();
{
let mut socket = sockets.get::<TcpSocket>(tcp_handle);
if socket.is_active() && !tcp_active {
debug!("connected");
} else if !socket.is_active() && tcp_active {
debug!("disconnected");
break
}
tcp_active = socket.is_active();
if socket.may_recv() {
let data = socket
.recv(|data| {
if socket.may_recv() {
let data = socket.recv(|data| {
let mut data = data.to_owned();
if !data.is_empty() {
debug!(
"recv data: {:?}",
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")
);
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.len(), data)
})
.unwrap();
if socket.can_send() && !data.is_empty() {
debug!(
"send data: {:?}",
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")
);
socket.send_slice(&data[..]).unwrap();
}).unwrap();
if socket.can_send() && !data.is_empty() {
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();
}
} else if socket.may_send() {
debug!("close");
socket.close();
}
phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error");
phy_wait(fd, iface.poll_delay(&sockets, timestamp)).expect("wait error");
}
}

View File

@ -1,105 +1,100 @@
#![allow(clippy::option_map_unit_fn)]
mod utils;
use log::*;
use std::collections::BTreeMap;
use std::os::unix::io::AsRawFd;
use smoltcp::iface::{Interface, InterfaceBuilder, NeighborCache, Routes};
use smoltcp::socket::{Dhcpv4Event, Dhcpv4Socket};
use smoltcp::phy::wait as phy_wait;
use smoltcp::wire::{EthernetAddress, Ipv4Address, IpCidr, Ipv4Cidr};
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes};
use smoltcp::socket::{SocketSet, RawSocketBuffer, RawPacketMetadata};
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetAddress, IpCidr, Ipv4Address, Ipv4Cidr};
use smoltcp::{
phy::{wait as phy_wait, Device, Medium},
time::Duration,
};
use smoltcp::dhcp::Dhcpv4Client;
fn main() {
#[cfg(feature = "log")]
utils::setup_logging("");
let (mut opts, mut free) = utils::create_options();
utils::add_tuntap_options(&mut opts, &mut free);
utils::add_tap_options(&mut opts, &mut free);
utils::add_middleware_options(&mut opts, &mut free);
let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tuntap_options(&mut matches);
let device = utils::parse_tap_options(&mut matches);
let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
let neighbor_cache = NeighborCache::new(BTreeMap::new());
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]);
let ip_addrs = [IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0)];
let mut routes_storage = [None; 1];
let routes = Routes::new(&mut routes_storage[..]);
let mut iface = EthernetInterfaceBuilder::new(device)
.ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache)
.ip_addrs(ip_addrs)
.routes(routes)
.finalize();
let medium = device.capabilities().medium;
let mut builder = InterfaceBuilder::new(device, vec![])
.ip_addrs(ip_addrs)
.routes(routes);
if medium == Medium::Ethernet {
builder = builder
.hardware_addr(ethernet_addr.into())
.neighbor_cache(neighbor_cache);
}
let mut iface = builder.finalize();
let mut dhcp_socket = Dhcpv4Socket::new();
// Set a ridiculously short max lease time to show DHCP renews work properly.
// This will cause the DHCP client to start renewing after 5 seconds, and give up the
// lease after 10 seconds if renew hasn't succeeded.
// IMPORTANT: This should be removed in production.
dhcp_socket.set_max_lease_duration(Some(Duration::from_secs(10)));
let dhcp_handle = iface.add_socket(dhcp_socket);
let mut sockets = SocketSet::new(vec![]);
let dhcp_rx_buffer = RawSocketBuffer::new(
[RawPacketMetadata::EMPTY; 1],
vec![0; 900]
);
let dhcp_tx_buffer = RawSocketBuffer::new(
[RawPacketMetadata::EMPTY; 1],
vec![0; 600]
);
let mut dhcp = Dhcpv4Client::new(&mut sockets, dhcp_rx_buffer, dhcp_tx_buffer, Instant::now());
let mut prev_cidr = Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0);
loop {
let timestamp = Instant::now();
if let Err(e) = iface.poll(timestamp) {
debug!("poll error: {}", e);
}
let event = iface.get_socket::<Dhcpv4Socket>(dhcp_handle).poll();
match event {
None => {}
Some(Dhcpv4Event::Configured(config)) => {
debug!("DHCP config acquired!");
debug!("IP address: {}", config.address);
set_ipv4_addr(&mut iface, config.address);
if let Some(router) = config.router {
debug!("Default gateway: {}", router);
iface.routes_mut().add_default_ipv4_route(router).unwrap();
} else {
debug!("Default gateway: None");
iface.routes_mut().remove_default_ipv4_route();
}
for (i, s) in config.dns_servers.iter().enumerate() {
if let Some(s) = s {
debug!("DNS server {}: {}", i, s);
}
iface.poll(&mut sockets, timestamp)
.map(|_| ())
.unwrap_or_else(|e| println!("Poll: {:?}", e));
let config = dhcp.poll(&mut iface, &mut sockets, timestamp)
.unwrap_or_else(|e| {
println!("DHCP: {:?}", e);
None
});
config.map(|config| {
println!("DHCP config: {:?}", config);
if let Some(cidr) = config.address {
if cidr != prev_cidr {
iface.update_ip_addrs(|addrs| {
addrs.iter_mut().next()
.map(|addr| {
*addr = IpCidr::Ipv4(cidr);
});
});
prev_cidr = cidr;
println!("Assigned a new IPv4 address: {}", cidr);
}
}
Some(Dhcpv4Event::Deconfigured) => {
debug!("DHCP lost config!");
set_ipv4_addr(&mut iface, Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0));
iface.routes_mut().remove_default_ipv4_route();
}
}
phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error");
config.router.map(|router| iface.routes_mut()
.add_default_ipv4_route(router)
.unwrap()
);
iface.routes_mut()
.update(|routes_map| {
routes_map.get(&IpCidr::new(Ipv4Address::UNSPECIFIED.into(), 0))
.map(|default_route| {
println!("Default gateway: {}", default_route.via_router);
});
});
if config.dns_servers.iter().any(|s| s.is_some()) {
println!("DNS servers:");
for dns_server in config.dns_servers.iter().filter_map(|s| *s) {
println!("- {}", dns_server);
}
}
});
let mut timeout = dhcp.next_poll(timestamp);
iface.poll_delay(&sockets, timestamp)
.map(|sockets_timeout| timeout = sockets_timeout);
phy_wait(fd, Some(timeout))
.unwrap_or_else(|e| println!("Wait: {:?}", e));
}
}
fn set_ipv4_addr<DeviceT>(iface: &mut Interface<'_, DeviceT>, cidr: Ipv4Cidr)
where
DeviceT: for<'d> Device<'d>,
{
iface.update_ip_addrs(|addrs| {
let dest = addrs.iter_mut().next().unwrap();
*dest = IpCidr::Ipv4(cidr);
});
}

View File

@ -1,33 +1,34 @@
mod utils;
use log::debug;
use std::str::{self, FromStr};
use std::collections::BTreeMap;
use std::os::unix::io::AsRawFd;
use std::str::{self, FromStr};
use url::Url;
use log::debug;
use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes};
use smoltcp::phy::{wait as phy_wait, Device, Medium};
use smoltcp::socket::{TcpSocket, TcpSocketBuffer};
use smoltcp::phy::wait as phy_wait;
use smoltcp::wire::{EthernetAddress, Ipv4Address, Ipv6Address, IpAddress, IpCidr};
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes};
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr, Ipv4Address, Ipv6Address};
fn main() {
utils::setup_logging("");
let (mut opts, mut free) = utils::create_options();
utils::add_tuntap_options(&mut opts, &mut free);
utils::add_tap_options(&mut opts, &mut free);
utils::add_middleware_options(&mut opts, &mut free);
free.push("ADDRESS");
free.push("URL");
let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tuntap_options(&mut matches);
let device = utils::parse_tap_options(&mut matches);
let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
let address = IpAddress::from_str(&matches.free[0]).expect("invalid address format");
let url = Url::parse(&matches.free[1]).expect("invalid url format");
let neighbor_cache = NeighborCache::new(BTreeMap::new());
let tcp_rx_buffer = TcpSocketBuffer::new(vec![0; 1024]);
@ -35,86 +36,72 @@ 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),
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 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 routes_storage = [None; 2];
let mut routes = Routes::new(&mut routes_storage[..]);
routes.add_default_ipv4_route(default_v4_gw).unwrap();
routes.add_default_ipv6_route(default_v6_gw).unwrap();
let mut iface = EthernetInterfaceBuilder::new(device)
.ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache)
.ip_addrs(ip_addrs)
.routes(routes)
.finalize();
let medium = device.capabilities().medium;
let mut builder = InterfaceBuilder::new(device, vec![])
.ip_addrs(ip_addrs)
.routes(routes);
if medium == Medium::Ethernet {
builder = builder
.hardware_addr(ethernet_addr.into())
.neighbor_cache(neighbor_cache);
}
let mut iface = builder.finalize();
let mut sockets = SocketSet::new(vec![]);
let tcp_handle = sockets.add(tcp_socket);
let tcp_handle = iface.add_socket(tcp_socket);
enum State {
Connect,
Request,
Response,
}
enum State { Connect, Request, Response }
let mut state = State::Connect;
loop {
let timestamp = Instant::now();
match iface.poll(timestamp) {
Ok(_) => {}
match iface.poll(&mut sockets, timestamp) {
Ok(_) => {},
Err(e) => {
debug!("poll error: {}", e);
debug!("poll error: {}",e);
}
}
let socket = iface.get_socket::<TcpSocket>(tcp_handle);
{
let mut socket = sockets.get::<TcpSocket>(tcp_handle);
state = match state {
State::Connect if !socket.is_active() => {
debug!("connecting");
let local_port = 49152 + rand::random::<u16>() % 16384;
socket
.connect((address, url.port().unwrap_or(80)), local_port)
.unwrap();
State::Request
}
State::Request if socket.may_send() => {
debug!("sending request");
let http_get = "GET ".to_owned() + url.path() + " HTTP/1.1\r\n";
socket.send_slice(http_get.as_ref()).expect("cannot send");
let http_host = "Host: ".to_owned() + url.host_str().unwrap() + "\r\n";
socket.send_slice(http_host.as_ref()).expect("cannot send");
socket
.send_slice(b"Connection: close\r\n")
.expect("cannot send");
socket.send_slice(b"\r\n").expect("cannot send");
State::Response
}
State::Response if socket.can_recv() => {
socket
.recv(|data| {
state = match state {
State::Connect if !socket.is_active() => {
debug!("connecting");
let local_port = 49152 + rand::random::<u16>() % 16384;
socket.connect((address, url.port().unwrap_or(80)), local_port).unwrap();
State::Request
}
State::Request if socket.may_send() => {
debug!("sending request");
let http_get = "GET ".to_owned() + url.path() + " HTTP/1.1\r\n";
socket.send_slice(http_get.as_ref()).expect("cannot send");
let http_host = "Host: ".to_owned() + url.host_str().unwrap() + "\r\n";
socket.send_slice(http_host.as_ref()).expect("cannot send");
socket.send_slice(b"Connection: close\r\n").expect("cannot send");
socket.send_slice(b"\r\n").expect("cannot send");
State::Response
}
State::Response if socket.can_recv() => {
socket.recv(|data| {
println!("{}", str::from_utf8(data).unwrap_or("(invalid utf8)"));
(data.len(), ())
})
.unwrap();
State::Response
}).unwrap();
State::Response
}
State::Response if !socket.may_recv() => {
debug!("received complete response");
break
}
_ => state
}
State::Response if !socket.may_recv() => {
debug!("received complete response");
break;
}
_ => state,
};
}
phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error");
phy_wait(fd, iface.poll_delay(&sockets, timestamp)).expect("wait error");
}
}

View File

@ -7,21 +7,20 @@
mod utils;
use core::str;
use log::{debug, error, info};
use log::{info, debug, error};
use smoltcp::iface::{InterfaceBuilder, NeighborCache};
use smoltcp::phy::{Loopback, Medium};
use smoltcp::socket::{TcpSocket, TcpSocketBuffer};
use smoltcp::time::{Duration, Instant};
use smoltcp::phy::Loopback;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder};
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
use smoltcp::time::{Duration, Instant};
#[cfg(not(feature = "std"))]
mod mock {
use core::cell::Cell;
use smoltcp::time::{Duration, Instant};
use core::cell::Cell;
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Clock(Cell<Instant>);
impl Clock {
@ -37,25 +36,16 @@ mod mock {
self.0.get()
}
}
struct Rand;
smoltcp::rand_custom_impl!(Rand);
impl smoltcp::Rand for Rand {
fn rand_bytes(buf: &mut [u8]) {
buf.fill(0x42);
}
}
}
#[cfg(feature = "std")]
mod mock {
use smoltcp::time::{Duration, Instant};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::sync::atomic::{Ordering, AtomicUsize};
use smoltcp::time::{Duration, Instant};
// should be AtomicU64 but that's unstable
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Clock(Arc<AtomicUsize>);
impl Clock {
@ -64,8 +54,7 @@ mod mock {
}
pub fn advance(&self, duration: Duration) {
self.0
.fetch_add(duration.total_millis() as usize, Ordering::SeqCst);
self.0.fetch_add(duration.total_millis() as usize, Ordering::SeqCst);
}
pub fn elapsed(&self) -> Instant {
@ -76,7 +65,7 @@ mod mock {
fn main() {
let clock = mock::Clock::new();
let device = Loopback::new(Medium::Ethernet);
let device = Loopback::new();
#[cfg(feature = "std")]
let device = {
@ -87,19 +76,18 @@ fn main() {
utils::add_middleware_options(&mut opts, &mut free);
let mut matches = utils::parse_options(&opts, free);
utils::parse_middleware_options(&mut matches, device, /*loopback=*/ true)
utils::parse_middleware_options(&mut matches, device, /*loopback=*/true)
};
let mut neighbor_cache_entries = [None; 8];
let mut neighbor_cache = NeighborCache::new(&mut neighbor_cache_entries[..]);
let mut ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)];
let mut sockets: [_; 2] = Default::default();
let mut iface = InterfaceBuilder::new(device, &mut sockets[..])
.hardware_addr(EthernetAddress::default().into())
.neighbor_cache(neighbor_cache)
.ip_addrs(ip_addrs)
.finalize();
let mut iface = EthernetInterfaceBuilder::new(device)
.ethernet_addr(EthernetAddress::default())
.neighbor_cache(neighbor_cache)
.ip_addrs(ip_addrs)
.finalize();
let server_socket = {
// It is not strictly necessary to use a `static mut` and unsafe code here, but
@ -121,65 +109,66 @@ fn main() {
TcpSocket::new(tcp_rx_buffer, tcp_tx_buffer)
};
let server_handle = iface.add_socket(server_socket);
let client_handle = iface.add_socket(client_socket);
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_listen = false;
let mut did_connect = false;
let mut done = false;
while !done && clock.elapsed() < Instant::from_millis(10_000) {
match iface.poll(clock.elapsed()) {
Ok(_) => {}
match iface.poll(&mut socket_set, clock.elapsed()) {
Ok(_) => {},
Err(e) => {
debug!("poll error: {}", e);
}
}
let mut socket = iface.get_socket::<TcpSocket>(server_handle);
if !socket.is_active() && !socket.is_listening() {
if !did_listen {
debug!("listening");
socket.listen(1234).unwrap();
did_listen = true;
{
let mut socket = socket_set.get::<TcpSocket>(server_handle);
if !socket.is_active() && !socket.is_listening() {
if !did_listen {
debug!("listening");
socket.listen(1234).unwrap();
did_listen = true;
}
}
if socket.can_recv() {
debug!("got {:?}", socket.recv(|buffer| {
(buffer.len(), str::from_utf8(buffer).unwrap())
}));
socket.close();
done = true;
}
}
if socket.can_recv() {
debug!(
"got {:?}",
socket.recv(|buffer| { (buffer.len(), str::from_utf8(buffer).unwrap()) })
);
socket.close();
done = true;
}
{
let mut socket = socket_set.get::<TcpSocket>(client_handle);
if !socket.is_open() {
if !did_connect {
debug!("connecting");
socket.connect((IpAddress::v4(127, 0, 0, 1), 1234),
(IpAddress::Unspecified, 65000)).unwrap();
did_connect = true;
}
}
let mut socket = iface.get_socket::<TcpSocket>(client_handle);
if !socket.is_open() {
if !did_connect {
debug!("connecting");
socket
.connect(
(IpAddress::v4(127, 0, 0, 1), 1234),
(IpAddress::Unspecified, 65000),
)
.unwrap();
did_connect = true;
if socket.can_send() {
debug!("sending");
socket.send_slice(b"0123456789abcdef").unwrap();
socket.close();
}
}
if socket.can_send() {
debug!("sending");
socket.send_slice(b"0123456789abcdef").unwrap();
socket.close();
}
match iface.poll_delay(clock.elapsed()) {
Some(Duration::ZERO) => debug!("resuming"),
match iface.poll_delay(&socket_set, clock.elapsed()) {
Some(Duration { millis: 0 }) => debug!("resuming"),
Some(delay) => {
debug!("sleeping for {} ms", delay);
clock.advance(delay)
}
None => clock.advance(Duration::from_millis(1)),
},
None => clock.advance(Duration::from_millis(1))
}
}

View File

@ -1,19 +1,17 @@
mod utils;
use log::debug;
use std::collections::BTreeMap;
use std::os::unix::io::AsRawFd;
use log::debug;
use smoltcp::iface::{InterfaceBuilder, NeighborCache};
use smoltcp::phy::wait as phy_wait;
use smoltcp::socket::{
RawPacketMetadata, RawSocket, RawSocketBuffer, UdpPacketMetadata, UdpSocket, UdpSocketBuffer,
};
use smoltcp::wire::{EthernetAddress, IpVersion, IpProtocol, IpAddress, IpCidr, Ipv4Address,
Ipv4Packet, IgmpPacket, IgmpRepr};
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder};
use smoltcp::socket::{SocketSet,
RawSocket, RawSocketBuffer, RawPacketMetadata,
UdpSocket, UdpSocketBuffer, UdpPacketMetadata};
use smoltcp::time::Instant;
use smoltcp::wire::{
EthernetAddress, IgmpPacket, IgmpRepr, IpAddress, IpCidr, IpProtocol, IpVersion, Ipv4Address,
Ipv4Packet,
};
const MDNS_PORT: u16 = 5353;
const MDNS_GROUP: [u8; 4] = [224, 0, 0, 251];
@ -22,13 +20,16 @@ fn main() {
utils::setup_logging("warn");
let (mut opts, mut free) = utils::create_options();
utils::add_tuntap_options(&mut opts, &mut free);
utils::add_tap_options(&mut opts, &mut free);
utils::add_middleware_options(&mut opts, &mut free);
let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tuntap_options(&mut matches);
let device = utils::parse_tap_options(&mut matches);
let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
let device = utils::parse_middleware_options(&mut matches,
device,
/*loopback=*/
false);
let neighbor_cache = NeighborCache::new(BTreeMap::new());
let local_addr = Ipv4Address::new(192, 168, 69, 2);
@ -36,75 +37,72 @@ fn main() {
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]);
let ip_addr = IpCidr::new(IpAddress::from(local_addr), 24);
let mut ipv4_multicast_storage = [None; 1];
let mut iface = InterfaceBuilder::new(device, vec![])
.hardware_addr(ethernet_addr.into())
.neighbor_cache(neighbor_cache)
.ip_addrs([ip_addr])
.ipv4_multicast_groups(&mut ipv4_multicast_storage[..])
.finalize();
let mut iface = EthernetInterfaceBuilder::new(device)
.ethernet_addr(ethernet_addr)
.neighbor_cache(neighbor_cache)
.ip_addrs([ip_addr])
.ipv4_multicast_groups(&mut ipv4_multicast_storage[..])
.finalize();
let now = Instant::now();
// Join a multicast group to receive mDNS traffic
iface
.join_multicast_group(Ipv4Address::from_bytes(&MDNS_GROUP), now)
.unwrap();
iface.join_multicast_group(Ipv4Address::from_bytes(&MDNS_GROUP), now).unwrap();
let mut sockets = SocketSet::new(vec![]);
// Must fit at least one IGMP packet
let raw_rx_buffer = RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; 2], vec![0; 512]);
// Will not send IGMP
let raw_tx_buffer = RawSocketBuffer::new(vec![], vec![]);
let raw_socket = RawSocket::new(
IpVersion::Ipv4,
IpProtocol::Igmp,
raw_rx_buffer,
raw_tx_buffer,
IpVersion::Ipv4, IpProtocol::Igmp,
raw_rx_buffer, raw_tx_buffer
);
let raw_handle = iface.add_socket(raw_socket);
let raw_handle = sockets.add(raw_socket);
// Must fit mDNS payload of at least one packet
let udp_rx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY; 4], vec![0; 1024]);
// Will not send mDNS
let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 0]);
let udp_socket = UdpSocket::new(udp_rx_buffer, udp_tx_buffer);
let udp_handle = iface.add_socket(udp_socket);
let udp_handle = sockets.add(udp_socket);
loop {
let timestamp = Instant::now();
match iface.poll(timestamp) {
Ok(_) => {}
match iface.poll(&mut sockets, timestamp) {
Ok(_) => {},
Err(e) => {
debug!("poll error: {}", e);
debug!("poll error: {}",e);
}
}
let socket = iface.get_socket::<RawSocket>(raw_handle);
{
let mut socket = sockets.get::<RawSocket>(raw_handle);
if socket.can_recv() {
// For display purposes only - normally we wouldn't process incoming IGMP packets
// in the application layer
socket
.recv()
.and_then(Ipv4Packet::new_checked)
.and_then(|ipv4_packet| IgmpPacket::new_checked(ipv4_packet.payload()))
.and_then(|igmp_packet| IgmpRepr::parse(&igmp_packet))
.map(|igmp_repr| println!("IGMP packet: {:?}", igmp_repr))
.unwrap_or_else(|e| println!("Recv IGMP error: {:?}", e));
if socket.can_recv() {
// For display purposes only - normally we wouldn't process incoming IGMP packets
// in the application layer
socket.recv()
.and_then(Ipv4Packet::new_checked)
.and_then(|ipv4_packet| IgmpPacket::new_checked(ipv4_packet.payload()))
.and_then(|igmp_packet| IgmpRepr::parse(&igmp_packet))
.map(|igmp_repr| println!("IGMP packet: {:?}", igmp_repr))
.unwrap_or_else(|e| println!("Recv IGMP error: {:?}", e));
}
}
{
let mut socket = sockets.get::<UdpSocket>(udp_handle);
if !socket.is_open() {
socket.bind(MDNS_PORT).unwrap()
}
if socket.can_recv() {
socket.recv()
.map(|(data, sender)| println!("mDNS traffic: {} UDP bytes from {}", data.len(), sender))
.unwrap_or_else(|e| println!("Recv UDP error: {:?}", e));
}
}
let socket = iface.get_socket::<UdpSocket>(udp_handle);
if !socket.is_open() {
socket.bind(MDNS_PORT).unwrap()
}
if socket.can_recv() {
socket
.recv()
.map(|(data, sender)| {
println!("mDNS traffic: {} UDP bytes from {}", data.len(), sender)
})
.unwrap_or_else(|e| println!("Recv UDP error: {:?}", e));
}
phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error");
phy_wait(fd, iface.poll_delay(&sockets, timestamp)).expect("wait error");
}
}

View File

@ -1,25 +1,21 @@
mod utils;
use byteorder::{ByteOrder, NetworkEndian};
use log::debug;
use std::cmp;
use std::collections::BTreeMap;
use std::collections::HashMap;
use std::os::unix::io::AsRawFd;
use std::str::FromStr;
use std::collections::BTreeMap;
use std::cmp;
use std::os::unix::io::AsRawFd;
use std::collections::HashMap;
use log::debug;
use byteorder::{ByteOrder, NetworkEndian};
use smoltcp::iface::{InterfaceBuilder, NeighborCache, Routes};
use smoltcp::phy::wait as phy_wait;
use smoltcp::time::{Duration, Instant};
use smoltcp::phy::Device;
use smoltcp::socket::{IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer};
use smoltcp::wire::{
EthernetAddress, Icmpv4Packet, Icmpv4Repr, Icmpv6Packet, Icmpv6Repr, IpAddress, IpCidr,
Ipv4Address, Ipv6Address,
};
use smoltcp::{
phy::Medium,
time::{Duration, Instant},
};
use smoltcp::phy::wait as phy_wait;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr,
Ipv6Address, Icmpv6Repr, Icmpv6Packet,
Ipv4Address, Icmpv4Repr, Icmpv4Packet};
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder, Routes};
use smoltcp::socket::{SocketSet, IcmpSocket, IcmpSocketBuffer, IcmpPacketMetadata, IcmpEndpoint};
macro_rules! send_icmp_ping {
( $repr_type:ident, $packet_type:ident, $ident:expr, $seq_no:expr,
@ -30,11 +26,13 @@ macro_rules! send_icmp_ping {
data: &$echo_payload,
};
let icmp_payload = $socket.send(icmp_repr.buffer_len(), $remote_addr).unwrap();
let icmp_payload = $socket
.send(icmp_repr.buffer_len(), $remote_addr)
.unwrap();
let icmp_packet = $packet_type::new_unchecked(icmp_payload);
(icmp_repr, icmp_packet)
}};
}}
}
macro_rules! get_icmp_pong {
@ -43,65 +41,42 @@ macro_rules! get_icmp_pong {
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
);
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");
let (mut opts, mut free) = utils::create_options();
utils::add_tuntap_options(&mut opts, &mut free);
utils::add_tap_options(&mut opts, &mut free);
utils::add_middleware_options(&mut opts, &mut free);
opts.optopt(
"c",
"count",
"Amount of echo request packets to send (default: 4)",
"COUNT",
);
opts.optopt(
"i",
"interval",
"Interval between successive packets sent (seconds) (default: 1)",
"INTERVAL",
);
opts.optopt(
"",
"timeout",
"Maximum wait duration for an echo response packet (seconds) (default: 5)",
"TIMEOUT",
);
opts.optopt("c", "count", "Amount of echo request packets to send (default: 4)", "COUNT");
opts.optopt("i", "interval",
"Interval between successive packets sent (seconds) (default: 1)", "INTERVAL");
opts.optopt("", "timeout",
"Maximum wait duration for an echo response packet (seconds) (default: 5)",
"TIMEOUT");
free.push("ADDRESS");
let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tuntap_options(&mut matches);
let device = utils::parse_tap_options(&mut matches);
let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
let device_caps = device.capabilities();
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")
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()))
.unwrap_or_else(|| Duration::from_secs(1));
let timeout = Duration::from_secs(
matches
.opt_str("timeout")
.map(|s| u64::from_str(&s).unwrap())
.unwrap_or(5),
let timeout = Duration::from_secs(
matches.opt_str("timeout").map(|s| u64::from_str(&s).unwrap()).unwrap_or(5)
);
let neighbor_cache = NeighborCache::new(BTreeMap::new());
@ -114,30 +89,24 @@ fn main() {
let ethernet_addr = EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]);
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 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 routes_storage = [None; 2];
let mut routes = Routes::new(&mut routes_storage[..]);
routes.add_default_ipv4_route(default_v4_gw).unwrap();
routes.add_default_ipv6_route(default_v6_gw).unwrap();
let mut iface = EthernetInterfaceBuilder::new(device)
.ethernet_addr(ethernet_addr)
.ip_addrs(ip_addrs)
.routes(routes)
.neighbor_cache(neighbor_cache)
.finalize();
let medium = device.capabilities().medium;
let mut builder = InterfaceBuilder::new(device, vec![])
.ip_addrs(ip_addrs)
.routes(routes);
if medium == Medium::Ethernet {
builder = builder
.hardware_addr(ethernet_addr.into())
.neighbor_cache(neighbor_cache);
}
let mut iface = builder.finalize();
let icmp_handle = iface.add_socket(icmp_socket);
let mut sockets = SocketSet::new(vec![]);
let icmp_handle = sockets.add(icmp_socket);
let mut send_at = Instant::from_millis(0);
let mut seq_no = 0;
@ -148,120 +117,89 @@ fn main() {
loop {
let timestamp = Instant::now();
match iface.poll(timestamp) {
Ok(_) => {}
match iface.poll(&mut sockets, timestamp) {
Ok(_) => {},
Err(e) => {
debug!("poll error: {}", e);
}
}
let timestamp = Instant::now();
let socket = iface.get_socket::<IcmpSocket>(icmp_handle);
if !socket.is_open() {
socket.bind(IcmpEndpoint::Ident(ident)).unwrap();
send_at = timestamp;
}
if socket.can_send() && seq_no < count as u16 && send_at <= timestamp {
NetworkEndian::write_i64(&mut echo_payload, timestamp.total_millis());
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!(),
{
let timestamp = Instant::now();
let mut socket = sockets.get::<IcmpSocket>(icmp_handle);
if !socket.is_open() {
socket.bind(IcmpEndpoint::Ident(ident)).unwrap();
send_at = timestamp;
}
waiting_queue.insert(seq_no, timestamp);
seq_no += 1;
send_at += interval;
}
if socket.can_send() && seq_no < count as u16 &&
send_at <= timestamp {
NetworkEndian::write_i64(&mut echo_payload, timestamp.total_millis());
if socket.can_recv() {
let (payload, _) = socket.recv().unwrap();
match remote_addr {
IpAddress::Ipv4(_) => {
let icmp_packet = Icmpv4Packet::new_checked(&payload).unwrap();
let icmp_repr = Icmpv4Repr::parse(&icmp_packet, &device_caps.checksum).unwrap();
get_icmp_pong!(
Icmpv4Repr,
icmp_repr,
payload,
waiting_queue,
remote_addr,
timestamp,
received
);
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!()
}
IpAddress::Ipv6(_) => {
let icmp_packet = Icmpv6Packet::new_checked(&payload).unwrap();
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
);
waiting_queue.insert(seq_no, timestamp);
seq_no += 1;
send_at += interval;
}
if socket.can_recv() {
let (payload, _) = socket.recv().unwrap();
match remote_addr {
IpAddress::Ipv4(_) => {
let icmp_packet = Icmpv4Packet::new_checked(&payload).unwrap();
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_checked(&payload).unwrap();
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!()
}
_ => unimplemented!(),
}
}
waiting_queue.retain(|seq, from| {
if timestamp - *from < timeout {
true
} else {
println!("From {} icmp_seq={} timeout", remote_addr, seq);
false
waiting_queue.retain(|seq, from| {
if timestamp - *from < timeout {
true
} else {
println!("From {} icmp_seq={} timeout", remote_addr, seq);
false
}
});
if seq_no == count as u16 && waiting_queue.is_empty() {
break
}
});
if seq_no == count as u16 && waiting_queue.is_empty() {
break;
}
let timestamp = Instant::now();
match iface.poll_at(timestamp) {
match iface.poll_at(&sockets, timestamp) {
Some(poll_at) if timestamp < poll_at => {
let resume_at = cmp::min(poll_at, send_at);
phy_wait(fd, Some(resume_at - timestamp)).expect("wait error");
}
},
Some(_) => (),
None => {
phy_wait(fd, Some(send_at - timestamp)).expect("wait error");
@ -270,10 +208,6 @@ fn main() {
}
println!("--- {} ping statistics ---", remote_addr);
println!(
"{} packets transmitted, {} received, {:.0}% packet loss",
seq_no,
received,
100.0 * (seq_no - received) as f64 / seq_no as f64
);
println!("{} packets transmitted, {} received, {:.0}% packet loss",
seq_no, received, 100.0 * (seq_no - received) as f64 / seq_no as f64);
}

View File

@ -1,29 +1,30 @@
mod utils;
use log::debug;
use std::str;
use std::collections::BTreeMap;
use std::fmt::Write;
use std::os::unix::io::AsRawFd;
use std::str;
use log::debug;
use smoltcp::iface::{InterfaceBuilder, NeighborCache};
use smoltcp::phy::{wait as phy_wait, Device, Medium};
use smoltcp::socket::{TcpSocket, TcpSocketBuffer};
use smoltcp::socket::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer};
use smoltcp::time::{Duration, Instant};
use smoltcp::phy::wait as phy_wait;
use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr};
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder};
use smoltcp::socket::SocketSet;
use smoltcp::socket::{UdpSocket, UdpSocketBuffer, UdpPacketMetadata};
use smoltcp::socket::{TcpSocket, TcpSocketBuffer};
use smoltcp::time::{Duration, Instant};
fn main() {
utils::setup_logging("");
let (mut opts, mut free) = utils::create_options();
utils::add_tuntap_options(&mut opts, &mut free);
utils::add_tap_options(&mut opts, &mut free);
utils::add_middleware_options(&mut opts, &mut free);
let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_tuntap_options(&mut matches);
let device = utils::parse_tap_options(&mut matches);
let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/false);
let neighbor_cache = NeighborCache::new(BTreeMap::new());
@ -51,145 +52,137 @@ fn main() {
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),
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)
.ip_addrs(ip_addrs)
.finalize();
let medium = device.capabilities().medium;
let mut builder = InterfaceBuilder::new(device, vec![]).ip_addrs(ip_addrs);
if medium == Medium::Ethernet {
builder = builder
.hardware_addr(ethernet_addr.into())
.neighbor_cache(neighbor_cache);
}
let mut iface = builder.finalize();
let udp_handle = iface.add_socket(udp_socket);
let tcp1_handle = iface.add_socket(tcp1_socket);
let tcp2_handle = iface.add_socket(tcp2_socket);
let tcp3_handle = iface.add_socket(tcp3_socket);
let tcp4_handle = iface.add_socket(tcp4_socket);
let mut sockets = SocketSet::new(vec![]);
let udp_handle = sockets.add(udp_socket);
let tcp1_handle = sockets.add(tcp1_socket);
let tcp2_handle = sockets.add(tcp2_socket);
let tcp3_handle = sockets.add(tcp3_socket);
let tcp4_handle = sockets.add(tcp4_socket);
let mut tcp_6970_active = false;
loop {
let timestamp = Instant::now();
match iface.poll(timestamp) {
Ok(_) => {}
match iface.poll(&mut sockets, timestamp) {
Ok(_) => {},
Err(e) => {
debug!("poll error: {}", e);
}
}
// udp:6969: respond "hello"
let socket = iface.get_socket::<UdpSocket>(udp_handle);
if !socket.is_open() {
socket.bind(6969).unwrap()
}
let client = match socket.recv() {
Ok((data, endpoint)) => {
debug!(
"udp:6969 recv data: {:?} from {}",
str::from_utf8(data).unwrap(),
endpoint
);
Some(endpoint)
{
let mut socket = sockets.get::<UdpSocket>(udp_handle);
if !socket.is_open() {
socket.bind(6969).unwrap()
}
let client = match socket.recv() {
Ok((data, endpoint)) => {
debug!("udp:6969 recv data: {:?} from {}",
str::from_utf8(data).unwrap(), endpoint);
Some(endpoint)
}
Err(_) => None
};
if let Some(endpoint) = client {
let data = b"hello\n";
debug!("udp:6969 send data: {:?}",
str::from_utf8(data.as_ref()).unwrap());
socket.send_slice(data, endpoint).unwrap();
}
Err(_) => None,
};
if let Some(endpoint) = client {
let data = b"hello\n";
debug!(
"udp:6969 send data: {:?}",
str::from_utf8(data.as_ref()).unwrap()
);
socket.send_slice(data, endpoint).unwrap();
}
// tcp:6969: respond "hello"
let socket = iface.get_socket::<TcpSocket>(tcp1_handle);
if !socket.is_open() {
socket.listen(6969).unwrap();
}
{
let mut socket = sockets.get::<TcpSocket>(tcp1_handle);
if !socket.is_open() {
socket.listen(6969).unwrap();
}
if socket.can_send() {
debug!("tcp:6969 send greeting");
writeln!(socket, "hello").unwrap();
debug!("tcp:6969 close");
socket.close();
if socket.can_send() {
debug!("tcp:6969 send greeting");
writeln!(socket, "hello").unwrap();
debug!("tcp:6969 close");
socket.close();
}
}
// tcp:6970: echo with reverse
let socket = iface.get_socket::<TcpSocket>(tcp2_handle);
if !socket.is_open() {
socket.listen(6970).unwrap()
}
{
let mut socket = sockets.get::<TcpSocket>(tcp2_handle);
if !socket.is_open() {
socket.listen(6970).unwrap()
}
if socket.is_active() && !tcp_6970_active {
debug!("tcp:6970 connected");
} else if !socket.is_active() && tcp_6970_active {
debug!("tcp:6970 disconnected");
}
tcp_6970_active = socket.is_active();
if socket.is_active() && !tcp_6970_active {
debug!("tcp:6970 connected");
} else if !socket.is_active() && tcp_6970_active {
debug!("tcp:6970 disconnected");
}
tcp_6970_active = socket.is_active();
if socket.may_recv() {
let data = socket
.recv(|buffer| {
if socket.may_recv() {
let data = socket.recv(|buffer| {
let recvd_len = buffer.len();
let mut data = buffer.to_owned();
if !data.is_empty() {
debug!(
"tcp:6970 recv data: {:?}",
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")
);
debug!("tcp:6970 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");
}
(recvd_len, data)
})
.unwrap();
if socket.can_send() && !data.is_empty() {
debug!(
"tcp:6970 send data: {:?}",
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)")
);
socket.send_slice(&data[..]).unwrap();
}).unwrap();
if socket.can_send() && !data.is_empty() {
debug!("tcp:6970 send data: {:?}",
str::from_utf8(data.as_ref()).unwrap_or("(invalid utf8)"));
socket.send_slice(&data[..]).unwrap();
}
} else if socket.may_send() {
debug!("tcp:6970 close");
socket.close();
}
} else if socket.may_send() {
debug!("tcp:6970 close");
socket.close();
}
// tcp:6971: sinkhole
let socket = iface.get_socket::<TcpSocket>(tcp3_handle);
if !socket.is_open() {
socket.listen(6971).unwrap();
socket.set_keep_alive(Some(Duration::from_millis(1000)));
socket.set_timeout(Some(Duration::from_millis(2000)));
}
{
let mut socket = sockets.get::<TcpSocket>(tcp3_handle);
if !socket.is_open() {
socket.listen(6971).unwrap();
socket.set_keep_alive(Some(Duration::from_millis(1000)));
socket.set_timeout(Some(Duration::from_millis(2000)));
}
if socket.may_recv() {
socket
.recv(|buffer| {
if socket.may_recv() {
socket.recv(|buffer| {
if !buffer.is_empty() {
debug!("tcp:6971 recv {:?} octets", buffer.len());
}
(buffer.len(), ())
})
.unwrap();
} else if socket.may_send() {
socket.close();
}).unwrap();
} else if socket.may_send() {
socket.close();
}
}
// tcp:6972: fountain
let socket = iface.get_socket::<TcpSocket>(tcp4_handle);
if !socket.is_open() {
socket.listen(6972).unwrap()
}
{
let mut socket = sockets.get::<TcpSocket>(tcp4_handle);
if !socket.is_open() {
socket.listen(6972).unwrap()
}
if socket.may_send() {
socket
.send(|data| {
if socket.may_send() {
socket.send(|data| {
if !data.is_empty() {
debug!("tcp:6972 send {:?} octets", data.len());
for (i, b) in data.iter_mut().enumerate() {
@ -197,10 +190,10 @@ fn main() {
}
}
(data.len(), ())
})
.unwrap();
}).unwrap();
}
}
phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error");
phy_wait(fd, iface.poll_delay(&sockets, timestamp)).expect("wait error");
}
}

View File

@ -1,130 +0,0 @@
//! 6lowpan exmaple
//!
//! This example is designed to run using the Linux ieee802154/6lowpan support,
//! using mac802154_hwsim.
//!
//! mac802154_hwsim allows you to create multiple "virtual" radios and specify
//! which is in range with which. This is very useful for testing without
//! needing real hardware. By default it creates two interfaces `wpan0` and
//! `wpan1` that are in range with each other. You can customize this with
//! the `wpan-hwsim` tool.
//!
//! We'll configure Linux to speak 6lowpan on `wpan0`, and leave `wpan1`
//! unconfigured so smoltcp can use it with a raw socket.
//!
//! # Setup
//!
//! modprobe mac802154_hwsim
//!
//! ip link set wpan0 down
//! ip link set wpan1 down
//! iwpan dev wpan0 set pan_id 0xbeef
//! iwpan dev wpan1 set pan_id 0xbeef
//! ip link add link wpan0 name lowpan0 type lowpan
//! ip link set wpan0 up
//! ip link set wpan1 up
//! ip link set lowpan0 up
//!
//! # Running
//!
//! Run it with `sudo ./target/debug/examples/sixlowpan`.
//!
//! You can set wireshark to sniff on interface `wpan0` to see the packets.
//!
//! Ping it with `ping fe80::180b:4242:4242:4242%lowpan0`.
//!
//! Speak UDP with `nc -uv fe80::180b:4242:4242:4242%lowpan0 6969`.
//!
//! # Teardown
//!
//! rmmod mac802154_hwsim
//!
mod utils;
use log::debug;
use std::collections::BTreeMap;
use std::os::unix::io::AsRawFd;
use std::str;
use smoltcp::iface::{InterfaceBuilder, NeighborCache};
use smoltcp::phy::{wait as phy_wait, Medium, RawSocket};
use smoltcp::socket::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer};
use smoltcp::time::Instant;
use smoltcp::wire::{Ieee802154Pan, IpAddress, IpCidr};
fn main() {
utils::setup_logging("");
let (mut opts, mut free) = utils::create_options();
utils::add_middleware_options(&mut opts, &mut free);
let mut matches = utils::parse_options(&opts, free);
let device = RawSocket::new("wpan1", Medium::Ieee802154).unwrap();
let fd = device.as_raw_fd();
let device = utils::parse_middleware_options(&mut matches, device, /*loopback=*/ false);
let neighbor_cache = NeighborCache::new(BTreeMap::new());
let udp_rx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 64]);
let udp_tx_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY], vec![0; 128]);
let udp_socket = UdpSocket::new(udp_rx_buffer, udp_tx_buffer);
let ieee802154_addr = smoltcp::wire::Ieee802154Address::Extended([
0x1a, 0x0b, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42,
]);
let ip_addrs = [IpCidr::new(
IpAddress::v6(0xfe80, 0, 0, 0, 0x180b, 0x4242, 0x4242, 0x4242),
64,
)];
let mut builder = InterfaceBuilder::new(device, vec![])
.ip_addrs(ip_addrs)
.pan_id(Ieee802154Pan(0xbeef));
builder = builder
.hardware_addr(ieee802154_addr.into())
.neighbor_cache(neighbor_cache);
let mut iface = builder.finalize();
let udp_handle = iface.add_socket(udp_socket);
loop {
let timestamp = Instant::now();
match iface.poll(timestamp) {
Ok(_) => {}
Err(e) => {
debug!("poll error: {}", e);
}
}
// udp:6969: respond "hello"
let socket = iface.get_socket::<UdpSocket>(udp_handle);
if !socket.is_open() {
socket.bind(6969).unwrap()
}
let client = match socket.recv() {
Ok((data, endpoint)) => {
debug!(
"udp:6969 recv data: {:?} from {}",
str::from_utf8(data).unwrap(),
endpoint
);
Some(endpoint)
}
Err(_) => None,
};
if let Some(endpoint) = client {
let data = b"hello\n";
debug!(
"udp:6969 send data: {:?}",
str::from_utf8(data.as_ref()).unwrap()
);
socket.send_slice(data, endpoint).unwrap();
}
phy_wait(fd, iface.poll_delay(timestamp)).expect("wait error");
}
}

View File

@ -1,24 +1,19 @@
use smoltcp::phy::wait as phy_wait;
use smoltcp::phy::{Device, RawSocket, RxToken};
use smoltcp::time::Instant;
use smoltcp::wire::{EthernetFrame, PrettyPrinter};
use std::env;
use std::os::unix::io::AsRawFd;
use smoltcp::phy::wait as phy_wait;
use smoltcp::phy::{Device, RxToken, RawSocket};
use smoltcp::wire::{PrettyPrinter, EthernetFrame};
use smoltcp::time::Instant;
fn main() {
let ifname = env::args().nth(1).unwrap();
let mut socket = RawSocket::new(ifname.as_ref(), smoltcp::phy::Medium::Ethernet).unwrap();
let mut socket = RawSocket::new(ifname.as_ref()).unwrap();
loop {
phy_wait(socket.as_raw_fd(), None).unwrap();
let (rx_token, _) = socket.receive().unwrap();
rx_token
.consume(Instant::now(), |buffer| {
println!(
"{}",
PrettyPrinter::<EthernetFrame<&[u8]>>::new("", &buffer)
);
Ok(())
})
.unwrap();
rx_token.consume(Instant::now(), |buffer| {
println!("{}", PrettyPrinter::<EthernetFrame<&[u8]>>::new("", &buffer));
Ok(())
}).unwrap();
}
}

View File

@ -1,56 +1,43 @@
#![allow(dead_code)]
use std::cell::RefCell;
use std::str::{self, FromStr};
use std::rc::Rc;
use std::io::{self, Write};
use std::fs::File;
use std::time::{SystemTime, UNIX_EPOCH};
use std::env;
use std::process;
#[cfg(feature = "log")]
use log::{Level, LevelFilter, trace};
#[cfg(feature = "log")]
use env_logger::Builder;
use getopts::{Matches, Options};
#[cfg(feature = "log")]
use log::{trace, Level, LevelFilter};
use std::env;
use std::fs::File;
use std::io::{self, Write};
use std::process;
use std::str::{self, FromStr};
use std::time::{SystemTime, UNIX_EPOCH};
use getopts::{Options, Matches};
#[cfg(feature = "phy-tuntap_interface")]
use smoltcp::phy::TunTapInterface;
use smoltcp::phy::{Device, FaultInjector, Medium, Tracer};
use smoltcp::phy::{PcapMode, PcapWriter};
use smoltcp::phy::{Device, EthernetTracer, FaultInjector};
#[cfg(feature = "phy-tap_interface")]
use smoltcp::phy::TapInterface;
use smoltcp::phy::{PcapWriter, PcapSink, PcapMode, PcapLinkType};
use smoltcp::phy::RawSocket;
use smoltcp::time::{Duration, Instant};
#[cfg(feature = "log")]
pub fn setup_logging_with_clock<F>(filter: &str, since_startup: F)
where
F: Fn() -> Instant + Send + Sync + 'static,
{
where F: Fn() -> Instant + Send + Sync + 'static {
Builder::new()
.format(move |buf, record| {
let elapsed = since_startup();
let timestamp = format!("[{}]", elapsed);
if record.target().starts_with("smoltcp::") {
writeln!(
buf,
"\x1b[0m{} ({}): {}\x1b[0m",
timestamp,
record.target().replace("smoltcp::", ""),
record.args()
)
writeln!(buf, "\x1b[0m{} ({}): {}\x1b[0m", timestamp,
record.target().replace("smoltcp::", ""), record.args())
} else if record.level() == Level::Trace {
let message = format!("{}", record.args());
writeln!(
buf,
"\x1b[37m{} {}\x1b[0m",
timestamp,
message.replace("\n", "\n ")
)
writeln!(buf, "\x1b[37m{} {}\x1b[0m", timestamp,
message.replace("\n", "\n "))
} else {
writeln!(
buf,
"\x1b[32m{} ({}): {}\x1b[0m",
timestamp,
record.target(),
record.args()
)
writeln!(buf, "\x1b[32m{} ({}): {}\x1b[0m", timestamp,
record.target(), record.args())
}
})
.filter(None, LevelFilter::Trace)
@ -61,7 +48,9 @@ where
#[cfg(feature = "log")]
pub fn setup_logging(filter: &str) {
setup_logging_with_clock(filter, Instant::now)
setup_logging_with_clock(filter, move || {
Instant::now()
})
}
pub fn create_options() -> (Options, Vec<&'static str>) {
@ -78,113 +67,59 @@ pub fn parse_options(options: &Options, free: Vec<&str>) -> Matches {
}
Ok(matches) => {
if matches.opt_present("h") || matches.free.len() != free.len() {
let brief = format!(
"Usage: {} [OPTION]... {}",
env::args().next().unwrap(),
free.join(" ")
);
let brief = format!("Usage: {} [OPTION]... {}",
env::args().next().unwrap(), free.join(" "));
print!("{}", options.usage(&brief));
process::exit(if matches.free.len() != free.len() {
1
} else {
0
})
process::exit(if matches.free.len() != free.len() { 1 } else { 0 })
}
matches
}
}
}
pub fn add_tuntap_options(opts: &mut Options, _free: &mut Vec<&str>) {
opts.optopt("", "tun", "TUN interface to use", "tun0");
opts.optopt("", "tap", "TAP interface to use", "tap0");
pub fn add_tap_options(_opts: &mut Options, free: &mut Vec<&str>) {
free.push("INTERFACE");
}
#[cfg(feature = "phy-tuntap_interface")]
pub fn parse_tuntap_options(matches: &mut Matches) -> TunTapInterface {
let tun = matches.opt_str("tun");
let tap = matches.opt_str("tap");
match (tun, tap) {
(Some(tun), None) => TunTapInterface::new(&tun, Medium::Ip).unwrap(),
(None, Some(tap)) => TunTapInterface::new(&tap, Medium::Ethernet).unwrap(),
_ => panic!("You must specify exactly one of --tun or --tap"),
}
#[cfg(feature = "phy-tap_interface")]
pub fn parse_tap_options(matches: &mut Matches) -> TapInterface {
let interface = matches.free.remove(0);
TapInterface::new(&interface).unwrap()
}
pub fn parse_raw_socket_options(matches: &mut Matches) -> RawSocket {
let interface = matches.free.remove(0);
RawSocket::new(&interface).unwrap()
}
pub fn add_middleware_options(opts: &mut Options, _free: &mut Vec<&str>) {
opts.optopt("", "pcap", "Write a packet capture file", "FILE");
opts.optopt(
"",
"drop-chance",
"Chance of dropping a packet (%)",
"CHANCE",
);
opts.optopt(
"",
"corrupt-chance",
"Chance of corrupting a packet (%)",
"CHANCE",
);
opts.optopt(
"",
"size-limit",
"Drop packets larger than given size (octets)",
"SIZE",
);
opts.optopt(
"",
"tx-rate-limit",
"Drop packets after transmit rate exceeds given limit \
(packets per interval)",
"RATE",
);
opts.optopt(
"",
"rx-rate-limit",
"Drop packets after transmit rate exceeds given limit \
(packets per interval)",
"RATE",
);
opts.optopt(
"",
"shaping-interval",
"Sets the interval for rate limiting (ms)",
"RATE",
);
opts.optopt("", "drop-chance", "Chance of dropping a packet (%)", "CHANCE");
opts.optopt("", "corrupt-chance", "Chance of corrupting a packet (%)", "CHANCE");
opts.optopt("", "size-limit", "Drop packets larger than given size (octets)", "SIZE");
opts.optopt("", "tx-rate-limit", "Drop packets after transmit rate exceeds given limit \
(packets per interval)", "RATE");
opts.optopt("", "rx-rate-limit", "Drop packets after transmit rate exceeds given limit \
(packets per interval)", "RATE");
opts.optopt("", "shaping-interval", "Sets the interval for rate limiting (ms)", "RATE");
}
pub fn parse_middleware_options<D>(
matches: &mut Matches,
device: D,
loopback: bool,
) -> FaultInjector<Tracer<PcapWriter<D, Box<dyn io::Write>>>>
where
D: for<'a> Device<'a>,
pub fn parse_middleware_options<D>(matches: &mut Matches, device: D, loopback: bool)
-> FaultInjector<EthernetTracer<PcapWriter<D, Rc<dyn PcapSink>>>>
where D: for<'a> Device<'a>
{
let drop_chance = matches
.opt_str("drop-chance")
.map(|s| u8::from_str(&s).unwrap())
.unwrap_or(0);
let corrupt_chance = matches
.opt_str("corrupt-chance")
.map(|s| u8::from_str(&s).unwrap())
.unwrap_or(0);
let size_limit = matches
.opt_str("size-limit")
.map(|s| usize::from_str(&s).unwrap())
.unwrap_or(0);
let tx_rate_limit = matches
.opt_str("tx-rate-limit")
.map(|s| u64::from_str(&s).unwrap())
.unwrap_or(0);
let rx_rate_limit = matches
.opt_str("rx-rate-limit")
.map(|s| u64::from_str(&s).unwrap())
.unwrap_or(0);
let shaping_interval = matches
.opt_str("shaping-interval")
.map(|s| u64::from_str(&s).unwrap())
.unwrap_or(0);
let drop_chance = matches.opt_str("drop-chance").map(|s| u8::from_str(&s).unwrap())
.unwrap_or(0);
let corrupt_chance = matches.opt_str("corrupt-chance").map(|s| u8::from_str(&s).unwrap())
.unwrap_or(0);
let size_limit = matches.opt_str("size-limit").map(|s| usize::from_str(&s).unwrap())
.unwrap_or(0);
let tx_rate_limit = matches.opt_str("tx-rate-limit").map(|s| u64::from_str(&s).unwrap())
.unwrap_or(0);
let rx_rate_limit = matches.opt_str("rx-rate-limit").map(|s| u64::from_str(&s).unwrap())
.unwrap_or(0);
let shaping_interval = matches.opt_str("shaping-interval").map(|s| u64::from_str(&s).unwrap())
.unwrap_or(0);
let pcap_writer: Box<dyn io::Write>;
if let Some(pcap_filename) = matches.opt_str("pcap") {
@ -193,26 +128,15 @@ where
pcap_writer = Box::new(io::sink())
}
let seed = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.subsec_nanos();
let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().subsec_nanos();
let device = PcapWriter::new(
device,
pcap_writer,
if loopback {
PcapMode::TxOnly
} else {
PcapMode::Both
},
);
let device = Tracer::new(device, |_timestamp, _printer| {
let device = PcapWriter::new(device, Rc::new(RefCell::new(pcap_writer)) as Rc<dyn PcapSink>,
if loopback { PcapMode::TxOnly } else { PcapMode::Both },
PcapLinkType::Ethernet);
let device = EthernetTracer::new(device, |_timestamp, _printer| {
#[cfg(feature = "log")]
trace!("{}", _printer);
});
let mut device = FaultInjector::new(device, seed);
device.set_drop_chance(drop_chance);
device.set_corrupt_chance(corrupt_chance);

View File

@ -3,16 +3,21 @@ name = "smoltcp-fuzz"
version = "0.0.1"
authors = ["Automatically generated"]
publish = false
edition = "2018"
[package.metadata]
cargo-fuzz = true
[dependencies]
libfuzzer-sys = "0.4"
arbitrary = { version = "1", features = ["derive"] }
getopts = "0.2"
smoltcp = { path = "..", features = [ "medium-ethernet" ] }
[dependencies.smoltcp]
path = ".."
[dependencies.libfuzzer-sys]
git = "https://github.com/rust-fuzz/libfuzzer-sys.git"
[profile.release]
codegen-units = 1 # needed to prevent weird linker error about sancov guards
# Prevent this from interfering with workspaces
[workspace]
@ -21,35 +26,7 @@ members = ["."]
[[bin]]
name = "packet_parser"
path = "fuzz_targets/packet_parser.rs"
test = false
doc = false
[[bin]]
name = "tcp_headers"
path = "fuzz_targets/tcp_headers.rs"
test = false
doc = false
[[bin]]
name = "dhcp_header"
path = "fuzz_targets/dhcp_header.rs"
test = false
doc = false
[[bin]]
name = "ieee802154_header"
path = "fuzz_targets/ieee802154_header.rs"
test = false
doc = false
[[bin]]
name = "sixlowpan_udp_header"
path = "fuzz_targets/sixlowpan_udp_header.rs"
test = false
doc = false
[[bin]]
name = "sixlowpan_iphc_header"
path = "fuzz_targets/sixlowpan_iphc_header.rs"
test = false
doc = false

View File

@ -1,19 +0,0 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use smoltcp::wire::{DhcpPacket, DhcpRepr};
fuzz_target!(|data: &[u8]| {
let _ = match DhcpPacket::new_checked(data) {
Ok(ref packet) => match DhcpRepr::parse(packet) {
Ok(dhcp_repr) => {
let mut dhcp_payload = vec![0; dhcp_repr.buffer_len()];
match DhcpPacket::new_checked(&mut dhcp_payload[..]) {
Ok(mut dhcp_packet) => Some(dhcp_repr.emit(&mut dhcp_packet)),
Err(_) => None,
}
}
Err(_) => None,
},
Err(_) => None,
};
});

View File

@ -1,19 +0,0 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use smoltcp::wire::{Ieee802154Frame, Ieee802154Repr};
fuzz_target!(|data: &[u8]| {
if let Ok(ref frame) = Ieee802154Frame::new_checked(data) {
if let Ok(repr) = Ieee802154Repr::parse(frame) {
// The buffer len returns only the lenght required for emitting the header
// and does not take into account the length of the payload.
let mut buffer = vec![0; repr.buffer_len()];
// NOTE: unchecked because the checked version checks if the addressing mode field
// is valid or not. The addressing mode field is required for calculating the length of
// the header, which is used in `check_len`.
let mut frame = Ieee802154Frame::new_unchecked(&mut buffer[..]);
repr.emit(&mut frame);
}
};
});

View File

@ -1,10 +1,8 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use smoltcp::wire::*;
#[macro_use] extern crate libfuzzer_sys;
extern crate smoltcp;
fuzz_target!(|data: &[u8]| {
format!(
"{}",
PrettyPrinter::<EthernetFrame<&'static [u8]>>::new("", &data)
);
use smoltcp::wire::*;
format!("{}", PrettyPrinter::<EthernetFrame<&'static [u8]>>::new("", &data));
});

View File

@ -1,42 +0,0 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use smoltcp::wire::{Ieee802154Address, SixlowpanIphcPacket, SixlowpanIphcRepr};
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, arbitrary::Arbitrary)]
pub enum AddressFuzzer {
Absent,
Short([u8; 2]),
Extended([u8; 8]),
}
impl From<AddressFuzzer> for Ieee802154Address {
fn from(val: AddressFuzzer) -> Self {
match val {
AddressFuzzer::Absent => Ieee802154Address::Absent,
AddressFuzzer::Short(b) => Ieee802154Address::Short(b),
AddressFuzzer::Extended(b) => Ieee802154Address::Extended(b),
}
}
}
#[derive(Debug, arbitrary::Arbitrary)]
struct SixlowpanIphcPacketFuzzer<'a> {
data: &'a [u8],
ll_src_addr: Option<AddressFuzzer>,
ll_dst_addr: Option<AddressFuzzer>,
}
fuzz_target!(|fuzz: SixlowpanIphcPacketFuzzer| {
if let Ok(ref frame) = SixlowpanIphcPacket::new_checked(fuzz.data) {
if let Ok(repr) = SixlowpanIphcRepr::parse(
frame,
fuzz.ll_src_addr.map(Into::into),
fuzz.ll_dst_addr.map(Into::into),
) {
let mut buffer = vec![0; repr.buffer_len()];
let mut frame = SixlowpanIphcPacket::new_unchecked(&mut buffer[..]);
repr.emit(&mut frame);
}
};
});

View File

@ -1,43 +0,0 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use smoltcp::wire::{Ipv6Address, SixlowpanUdpPacket, SixlowpanUdpRepr};
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, arbitrary::Arbitrary)]
pub struct AddressFuzzer(pub [u8; 16]);
impl From<AddressFuzzer> for Ipv6Address {
fn from(val: AddressFuzzer) -> Self {
Ipv6Address(val.0)
}
}
#[derive(Debug, arbitrary::Arbitrary)]
struct SixlowpanUdpPacketFuzzer<'a> {
data: &'a [u8],
src_addr: AddressFuzzer,
dst_addr: AddressFuzzer,
checksum: Option<u16>,
}
fuzz_target!(|fuzz: SixlowpanUdpPacketFuzzer| {
if let Ok(ref frame) = SixlowpanUdpPacket::new_checked(fuzz.data) {
if let Ok(repr) = SixlowpanUdpRepr::parse(
frame,
&fuzz.src_addr.into(),
&fuzz.dst_addr.into(),
fuzz.checksum,
) {
let payload = frame.payload();
let mut buffer = vec![0; repr.header_len() + payload.len()];
let mut frame = SixlowpanUdpPacket::new_unchecked(&mut buffer[..]);
repr.emit(
&mut frame,
&fuzz.src_addr.into(),
&fuzz.dst_addr.into(),
payload.len(),
|b| b.copy_from_slice(payload),
);
}
};
});

View File

@ -1,24 +1,29 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use smoltcp::iface::{InterfaceBuilder, NeighborCache};
use smoltcp::phy::{Loopback, Medium};
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
use smoltcp::time::{Duration, Instant};
#[macro_use] extern crate libfuzzer_sys;
extern crate smoltcp;
use std as core;
extern crate getopts;
use core::cmp;
use smoltcp::phy::Loopback;
use smoltcp::wire::{EthernetAddress, EthernetFrame, EthernetProtocol};
use smoltcp::wire::{IpAddress, IpCidr, Ipv4Packet, Ipv6Packet, TcpPacket};
use std::cmp;
use smoltcp::iface::{NeighborCache, EthernetInterfaceBuilder};
use smoltcp::socket::{SocketSet, TcpSocket, TcpSocketBuffer};
use smoltcp::time::{Duration, Instant};
#[path = "../utils.rs"]
mod utils;
mod utils {
include!("../utils.rs");
}
mod mock {
use smoltcp::time::{Duration, Instant};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::sync::atomic::{Ordering, AtomicUsize};
use smoltcp::time::{Duration, Instant};
// should be AtomicU64 but that's unstable
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Clock(Arc<AtomicUsize>);
impl Clock {
@ -27,8 +32,7 @@ mod mock {
}
pub fn advance(&self, duration: Duration) {
self.0
.fetch_add(duration.total_millis() as usize, Ordering::SeqCst);
self.0.fetch_add(duration.total_millis() as usize, Ordering::SeqCst);
}
pub fn elapsed(&self) -> Instant {
@ -47,10 +51,7 @@ impl TcpHeaderFuzzer {
//
// Otherwise, it replaces the entire rest of the TCP header with the fuzzer's output.
pub fn new(data: &[u8]) -> TcpHeaderFuzzer {
let copy_len = cmp::min(
data.len(),
56, /* max TCP header length without port numbers*/
);
let copy_len = cmp::min(data.len(), 56 /* max TCP header length without port numbers*/);
let mut fuzzer = TcpHeaderFuzzer([0; 56], copy_len);
fuzzer.0[..copy_len].copy_from_slice(&data[..copy_len]);
@ -66,16 +67,13 @@ impl smoltcp::phy::Fuzzer for TcpHeaderFuzzer {
let tcp_packet_offset = {
let eth_frame = EthernetFrame::new_unchecked(&frame_data);
EthernetFrame::<&mut [u8]>::header_len()
+ match eth_frame.ethertype() {
EthernetProtocol::Ipv4 => {
Ipv4Packet::new_unchecked(eth_frame.payload()).header_len() as usize
}
EthernetProtocol::Ipv6 => {
Ipv6Packet::new_unchecked(eth_frame.payload()).header_len() as usize
}
_ => return,
}
EthernetFrame::<&mut [u8]>::header_len() + match eth_frame.ethertype() {
EthernetProtocol::Ipv4 =>
Ipv4Packet::new_unchecked(eth_frame.payload()).header_len() as usize,
EthernetProtocol::Ipv6 =>
Ipv6Packet::new_unchecked(eth_frame.payload()).header_len() as usize,
_ => return
}
};
let tcp_is_syn = {
@ -96,7 +94,7 @@ impl smoltcp::phy::Fuzzer for TcpHeaderFuzzer {
(tcp_packet[12] as usize >> 4) * 4
};
let tcp_packet = &mut frame_data[tcp_packet_offset + 4..];
let tcp_packet = &mut frame_data[tcp_packet_offset+4..];
let replacement_data = &self.0[..self.1];
let copy_len = cmp::min(replacement_data.len(), tcp_header_len);
@ -115,28 +113,28 @@ fuzz_target!(|data: &[u8]| {
let clock = mock::Clock::new();
let device = {
let (mut opts, mut free) = utils::create_options();
utils::add_middleware_options(&mut opts, &mut free);
let mut matches = utils::parse_options(&opts, free);
let device = utils::parse_middleware_options(
&mut matches,
Loopback::new(Medium::Ethernet),
/*loopback=*/ true,
);
let device = utils::parse_middleware_options(&mut matches, Loopback::new(),
/*loopback=*/true);
smoltcp::phy::FuzzInjector::new(device, EmptyFuzzer(), TcpHeaderFuzzer::new(data))
smoltcp::phy::FuzzInjector::new(device,
EmptyFuzzer(),
TcpHeaderFuzzer::new(data))
};
let mut neighbor_cache_entries = [None; 8];
let neighbor_cache = NeighborCache::new(&mut neighbor_cache_entries[..]);
let ip_addrs = [IpCidr::new(IpAddress::v4(127, 0, 0, 1), 8)];
let mut iface = InterfaceBuilder::new(device)
.ethernet_addr(EthernetAddress::default())
.neighbor_cache(neighbor_cache)
.ip_addrs(ip_addrs)
.finalize();
let mut iface = EthernetInterfaceBuilder::new(device)
.ethernet_addr(EthernetAddress::default())
.neighbor_cache(neighbor_cache)
.ip_addrs(ip_addrs)
.finalize();
let server_socket = {
// It is not strictly necessary to use a `static mut` and unsafe code here, but
@ -163,7 +161,7 @@ fuzz_target!(|data: &[u8]| {
let server_handle = socket_set.add(server_socket);
let client_handle = socket_set.add(client_socket);
let mut did_listen = false;
let mut did_listen = false;
let mut did_connect = false;
let mut done = false;
while !done && clock.elapsed() < Instant::from_millis(4_000) {
@ -188,28 +186,24 @@ fuzz_target!(|data: &[u8]| {
let mut socket = socket_set.get::<TcpSocket>(client_handle);
if !socket.is_open() {
if !did_connect {
socket
.connect(
(IpAddress::v4(127, 0, 0, 1), 1234),
(IpAddress::Unspecified, 65000),
)
.unwrap();
socket.connect((IpAddress::v4(127, 0, 0, 1), 1234),
(IpAddress::Unspecified, 65000)).unwrap();
did_connect = true;
}
}
if socket.can_send() {
socket
.send_slice(b"0123456789abcdef0123456789abcdef0123456789abcdef")
.unwrap();
socket.send_slice(b"0123456789abcdef0123456789abcdef0123456789abcdef").unwrap();
socket.close();
}
}
match iface.poll_delay(&socket_set, clock.elapsed()) {
Some(Duration::ZERO) => {}
Some(delay) => clock.advance(delay),
None => clock.advance(Duration::from_millis(1)),
Some(Duration { millis: 0 }) => {},
Some(delay) => {
clock.advance(delay)
},
None => clock.advance(Duration::from_millis(1))
}
}
});

View File

@ -1,17 +1,18 @@
// TODO: this is literally a copy of examples/utils.rs, but without an allow dead code attribute.
// The include logic does not allow having attributes in included files.
use getopts::{Matches, Options};
use std::env;
use std::fs::File;
use std::io;
use std::io::Write;
use std::process;
use std::cell::RefCell;
use std::str::{self, FromStr};
use std::rc::Rc;
use std::io;
use std::fs::File;
use std::time::{SystemTime, UNIX_EPOCH};
use std::env;
use std::process;
use getopts::{Options, Matches};
use smoltcp::phy::{Device, FaultInjector, Tracer};
use smoltcp::phy::{PcapMode, PcapWriter};
use smoltcp::phy::{Device, EthernetTracer, FaultInjector};
use smoltcp::phy::{PcapWriter, PcapSink, PcapMode, PcapLinkType};
use smoltcp::time::Duration;
pub fn create_options() -> (Options, Vec<&'static str>) {
@ -28,17 +29,10 @@ pub fn parse_options(options: &Options, free: Vec<&str>) -> Matches {
}
Ok(matches) => {
if matches.opt_present("h") || matches.free.len() != free.len() {
let brief = format!(
"Usage: {} [OPTION]... {}",
env::args().nth(0).unwrap(),
free.join(" ")
);
let brief = format!("Usage: {} [OPTION]... {}",
env::args().nth(0).unwrap(), free.join(" "));
print!("{}", options.usage(&brief));
process::exit(if matches.free.len() != free.len() {
1
} else {
0
})
process::exit(if matches.free.len() != free.len() { 1 } else { 0 })
}
matches
}
@ -47,102 +41,46 @@ pub fn parse_options(options: &Options, free: Vec<&str>) -> Matches {
pub fn add_middleware_options(opts: &mut Options, _free: &mut Vec<&str>) {
opts.optopt("", "pcap", "Write a packet capture file", "FILE");
opts.optopt(
"",
"drop-chance",
"Chance of dropping a packet (%)",
"CHANCE",
);
opts.optopt(
"",
"corrupt-chance",
"Chance of corrupting a packet (%)",
"CHANCE",
);
opts.optopt(
"",
"size-limit",
"Drop packets larger than given size (octets)",
"SIZE",
);
opts.optopt(
"",
"tx-rate-limit",
"Drop packets after transmit rate exceeds given limit \
(packets per interval)",
"RATE",
);
opts.optopt(
"",
"rx-rate-limit",
"Drop packets after transmit rate exceeds given limit \
(packets per interval)",
"RATE",
);
opts.optopt(
"",
"shaping-interval",
"Sets the interval for rate limiting (ms)",
"RATE",
);
opts.optopt("", "drop-chance", "Chance of dropping a packet (%)", "CHANCE");
opts.optopt("", "corrupt-chance", "Chance of corrupting a packet (%)", "CHANCE");
opts.optopt("", "size-limit", "Drop packets larger than given size (octets)", "SIZE");
opts.optopt("", "tx-rate-limit", "Drop packets after transmit rate exceeds given limit \
(packets per interval)", "RATE");
opts.optopt("", "rx-rate-limit", "Drop packets after transmit rate exceeds given limit \
(packets per interval)", "RATE");
opts.optopt("", "shaping-interval", "Sets the interval for rate limiting (ms)", "RATE");
}
pub fn parse_middleware_options<D>(
matches: &mut Matches,
device: D,
loopback: bool,
) -> FaultInjector<Tracer<PcapWriter<D, Box<dyn Write>>>>
where
D: for<'a> Device<'a>,
pub fn parse_middleware_options<D>(matches: &mut Matches, device: D, loopback: bool)
-> FaultInjector<EthernetTracer<PcapWriter<D, Rc<PcapSink>>>>
where D: for<'a> Device<'a>
{
let drop_chance = matches
.opt_str("drop-chance")
.map(|s| u8::from_str(&s).unwrap())
.unwrap_or(0);
let corrupt_chance = matches
.opt_str("corrupt-chance")
.map(|s| u8::from_str(&s).unwrap())
.unwrap_or(0);
let size_limit = matches
.opt_str("size-limit")
.map(|s| usize::from_str(&s).unwrap())
.unwrap_or(0);
let tx_rate_limit = matches
.opt_str("tx-rate-limit")
.map(|s| u64::from_str(&s).unwrap())
.unwrap_or(0);
let rx_rate_limit = matches
.opt_str("rx-rate-limit")
.map(|s| u64::from_str(&s).unwrap())
.unwrap_or(0);
let shaping_interval = matches
.opt_str("shaping-interval")
.map(|s| u64::from_str(&s).unwrap())
.unwrap_or(0);
let drop_chance = matches.opt_str("drop-chance").map(|s| u8::from_str(&s).unwrap())
.unwrap_or(0);
let corrupt_chance = matches.opt_str("corrupt-chance").map(|s| u8::from_str(&s).unwrap())
.unwrap_or(0);
let size_limit = matches.opt_str("size-limit").map(|s| usize::from_str(&s).unwrap())
.unwrap_or(0);
let tx_rate_limit = matches.opt_str("tx-rate-limit").map(|s| u64::from_str(&s).unwrap())
.unwrap_or(0);
let rx_rate_limit = matches.opt_str("rx-rate-limit").map(|s| u64::from_str(&s).unwrap())
.unwrap_or(0);
let shaping_interval = matches.opt_str("shaping-interval").map(|s| u64::from_str(&s).unwrap())
.unwrap_or(0);
let pcap_writer: Box<dyn io::Write>;
let pcap_writer: Box<io::Write>;
if let Some(pcap_filename) = matches.opt_str("pcap") {
pcap_writer = Box::new(File::create(pcap_filename).expect("cannot open file"))
} else {
pcap_writer = Box::new(io::sink())
}
let seed = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.subsec_nanos();
let seed = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().subsec_nanos();
let device = PcapWriter::new(
device,
pcap_writer,
if loopback {
PcapMode::TxOnly
} else {
PcapMode::Both
},
);
let device = Tracer::new(device, |_timestamp, _printer| {
let device = PcapWriter::new(device, Rc::new(RefCell::new(pcap_writer)) as Rc<PcapSink>,
if loopback { PcapMode::TxOnly } else { PcapMode::Both },
PcapLinkType::Ethernet);
let device = EthernetTracer::new(device, |_timestamp, _printer| {
#[cfg(feature = "log")]
trace!("{}", _printer);
});

424
src/dhcp/clientv4.rs Normal file
View File

@ -0,0 +1,424 @@
use crate::{Result, Error};
use crate::wire::{IpVersion, IpProtocol, IpEndpoint, IpAddress,
Ipv4Cidr, Ipv4Address, Ipv4Packet, Ipv4Repr,
UdpPacket, UdpRepr,
DhcpPacket, DhcpRepr, DhcpMessageType};
use crate::wire::dhcpv4::field as dhcpv4_field;
use crate::socket::{SocketSet, SocketHandle, RawSocket, RawSocketBuffer};
use crate::phy::{Device, ChecksumCapabilities};
use crate::iface::EthernetInterface as Interface;
use crate::time::{Instant, Duration};
use super::{UDP_SERVER_PORT, UDP_CLIENT_PORT};
const DISCOVER_TIMEOUT: u64 = 10;
const REQUEST_TIMEOUT: u64 = 1;
const REQUEST_RETRIES: u16 = 15;
const DEFAULT_RENEW_INTERVAL: u32 = 60;
const PARAMETER_REQUEST_LIST: &[u8] = &[
dhcpv4_field::OPT_SUBNET_MASK,
dhcpv4_field::OPT_ROUTER,
dhcpv4_field::OPT_DOMAIN_NAME_SERVER,
];
/// IPv4 configuration data returned by `client.poll()`
#[derive(Debug)]
pub struct Config {
pub address: Option<Ipv4Cidr>,
pub router: Option<Ipv4Address>,
pub dns_servers: [Option<Ipv4Address>; 3],
}
#[derive(Debug)]
struct RequestState {
retry: u16,
endpoint_ip: Ipv4Address,
server_identifier: Ipv4Address,
requested_ip: Ipv4Address,
}
#[derive(Debug)]
struct RenewState {
endpoint_ip: Ipv4Address,
server_identifier: Ipv4Address,
}
#[derive(Debug)]
enum ClientState {
/// Discovering the DHCP server
Discovering,
/// Requesting an address
Requesting(RequestState),
/// Having an address, refresh it periodically
Renew(RenewState),
}
pub struct Client {
state: ClientState,
raw_handle: SocketHandle,
/// When to send next request
next_egress: Instant,
/// When any existing DHCP address will expire.
lease_expiration: Option<Instant>,
transaction_id: u32,
}
/// DHCP client with a RawSocket.
///
/// To provide memory for the dynamic IP address, configure your
/// `Interface` with one of `ip_addrs` and the `ipv4_gateway` being
/// `Ipv4Address::UNSPECIFIED`. You must also assign this `0.0.0.0/0`
/// while the client's state is `Discovering`. Hence, the `poll()`
/// method returns a corresponding `Config` struct in this case.
///
/// You must call `dhcp_client.poll()` after `iface.poll()` to send
/// and receive DHCP packets.
impl Client {
/// # Usage
/// ```rust
/// use smoltcp::socket::{SocketSet, RawSocketBuffer, RawPacketMetadata};
/// use smoltcp::dhcp::Dhcpv4Client;
/// use smoltcp::time::Instant;
///
/// let mut sockets = SocketSet::new(vec![]);
/// let dhcp_rx_buffer = RawSocketBuffer::new(
/// [RawPacketMetadata::EMPTY; 1],
/// vec![0; 600]
/// );
/// let dhcp_tx_buffer = RawSocketBuffer::new(
/// [RawPacketMetadata::EMPTY; 1],
/// vec![0; 600]
/// );
/// let mut dhcp = Dhcpv4Client::new(
/// &mut sockets,
/// dhcp_rx_buffer, dhcp_tx_buffer,
/// Instant::now()
/// );
/// ```
pub fn new<'a>(sockets: &mut SocketSet<'a>, rx_buffer: RawSocketBuffer<'a>, tx_buffer: RawSocketBuffer<'a>, now: Instant) -> Self
{
let raw_socket = RawSocket::new(IpVersion::Ipv4, IpProtocol::Udp, rx_buffer, tx_buffer);
let raw_handle = sockets.add(raw_socket);
Client {
state: ClientState::Discovering,
raw_handle,
next_egress: now,
transaction_id: 1,
lease_expiration: None,
}
}
/// When to send next packet
///
/// Useful for suspending execution after polling.
pub fn next_poll(&self, now: Instant) -> Duration {
self.next_egress - now
}
/// Process incoming packets on the contained RawSocket, and send
/// DHCP requests when timeouts are ready.
///
/// Applying the obtained network configuration is left to the
/// user.
///
/// A Config can be returned from any valid DHCP reply. The client
/// performs no bookkeeping on configuration or their changes.
pub fn poll<DeviceT>(&mut self,
iface: &mut Interface<DeviceT>, sockets: &mut SocketSet,
now: Instant
) -> Result<Option<Config>>
where
DeviceT: for<'d> Device<'d>,
{
let checksum_caps = iface.device().capabilities().checksum;
let mut raw_socket = sockets.get::<RawSocket>(self.raw_handle);
// Process incoming
let config = {
match raw_socket.recv()
.and_then(|packet| parse_udp(packet, &checksum_caps)) {
Ok((IpEndpoint {
addr: IpAddress::Ipv4(src_ip),
port: UDP_SERVER_PORT,
}, IpEndpoint {
addr: _,
port: UDP_CLIENT_PORT,
}, payload)) =>
self.ingress(iface, now, payload, &src_ip),
Ok(_) =>
return Err(Error::Unrecognized),
Err(Error::Exhausted) =>
None,
Err(e) =>
return Err(e),
}
};
if config.is_some() {
// Return a new config immediately so that addresses can
// be configured that are required by egress().
Ok(config)
} else {
// Send requests
if raw_socket.can_send() && now >= self.next_egress {
self.egress(iface, &mut *raw_socket, &checksum_caps, now)
} else {
Ok(None)
}
}
}
fn ingress<DeviceT>(&mut self,
iface: &mut Interface<DeviceT>, now: Instant,
data: &[u8], src_ip: &Ipv4Address
) -> Option<Config>
where
DeviceT: for<'d> Device<'d>,
{
let dhcp_packet = match DhcpPacket::new_checked(data) {
Ok(dhcp_packet) => dhcp_packet,
Err(e) => {
net_debug!("DHCP invalid pkt from {}: {:?}", src_ip, e);
return None;
}
};
let dhcp_repr = match DhcpRepr::parse(&dhcp_packet) {
Ok(dhcp_repr) => dhcp_repr,
Err(e) => {
net_debug!("DHCP error parsing pkt from {}: {:?}", src_ip, e);
return None;
}
};
let mac = iface.ethernet_addr();
if dhcp_repr.client_hardware_address != mac { return None }
if dhcp_repr.transaction_id != self.transaction_id { return None }
let server_identifier = match dhcp_repr.server_identifier {
Some(server_identifier) => server_identifier,
None => return None,
};
net_debug!("DHCP recv {:?} from {} ({})", dhcp_repr.message_type, src_ip, server_identifier);
// once we receive the ack, we can pass the config to the user
let config = if dhcp_repr.message_type == DhcpMessageType::Ack {
let lease_duration = dhcp_repr.lease_duration.unwrap_or(DEFAULT_RENEW_INTERVAL * 2);
self.lease_expiration = Some(now + Duration::from_secs(lease_duration.into()));
// RFC 2131 indicates clients should renew a lease halfway through its expiration.
self.next_egress = now + Duration::from_secs((lease_duration / 2).into());
let address = dhcp_repr.subnet_mask
.and_then(|mask| IpAddress::Ipv4(mask).to_prefix_len())
.map(|prefix_len| Ipv4Cidr::new(dhcp_repr.your_ip, prefix_len));
let router = dhcp_repr.router;
let dns_servers = dhcp_repr.dns_servers
.unwrap_or([None; 3]);
Some(Config { address, router, dns_servers })
} else {
None
};
match self.state {
ClientState::Discovering
if dhcp_repr.message_type == DhcpMessageType::Offer =>
{
self.next_egress = now;
let r_state = RequestState {
retry: 0,
endpoint_ip: *src_ip,
server_identifier,
requested_ip: dhcp_repr.your_ip // use the offered ip
};
Some(ClientState::Requesting(r_state))
}
ClientState::Requesting(ref r_state)
if dhcp_repr.message_type == DhcpMessageType::Ack &&
server_identifier == r_state.server_identifier =>
{
let p_state = RenewState {
endpoint_ip: *src_ip,
server_identifier,
};
Some(ClientState::Renew(p_state))
}
_ => None
}.map(|new_state| self.state = new_state);
config
}
fn egress<DeviceT: for<'d> Device<'d>>(&mut self, iface: &mut Interface<DeviceT>, raw_socket: &mut RawSocket, checksum_caps: &ChecksumCapabilities, now: Instant) -> Result<Option<Config>> {
// Reset after maximum amount of retries
let retries_exceeded = match self.state {
ClientState::Requesting(ref mut r_state) if r_state.retry >= REQUEST_RETRIES => {
net_debug!("DHCP request retries exceeded, restarting discovery");
true
}
_ => false
};
let lease_expired = self.lease_expiration.map_or(false, |expiration| now >= expiration);
if lease_expired || retries_exceeded {
self.reset(now);
// Return a config now so that user code assigns the
// 0.0.0.0/0 address, which will be used sending a DHCP
// discovery packet in the next call to egress().
return Ok(Some(Config {
address: Some(Ipv4Cidr::new(Ipv4Address::UNSPECIFIED, 0)),
router: None,
dns_servers: [None; 3],
}));
}
// Prepare sending next packet
self.transaction_id += 1;
let mac = iface.ethernet_addr();
let mut dhcp_repr = DhcpRepr {
message_type: DhcpMessageType::Discover,
transaction_id: self.transaction_id,
client_hardware_address: mac,
client_ip: Ipv4Address::UNSPECIFIED,
your_ip: Ipv4Address::UNSPECIFIED,
server_ip: Ipv4Address::UNSPECIFIED,
router: None,
subnet_mask: None,
relay_agent_ip: Ipv4Address::UNSPECIFIED,
broadcast: true,
requested_ip: None,
client_identifier: Some(mac),
server_identifier: None,
parameter_request_list: Some(PARAMETER_REQUEST_LIST),
max_size: Some(raw_socket.payload_recv_capacity() as u16),
lease_duration: None,
dns_servers: None,
};
let mut send_packet = |iface, endpoint, dhcp_repr| {
send_packet(iface, raw_socket, &endpoint, &dhcp_repr, checksum_caps)
.map(|()| None)
};
match self.state {
ClientState::Discovering => {
self.next_egress = now + Duration::from_secs(DISCOVER_TIMEOUT);
let endpoint = IpEndpoint {
addr: Ipv4Address::BROADCAST.into(),
port: UDP_SERVER_PORT,
};
net_trace!("DHCP send discover to {}: {:?}", endpoint, dhcp_repr);
send_packet(iface, endpoint, dhcp_repr)
}
ClientState::Requesting(ref mut r_state) => {
r_state.retry += 1;
self.next_egress = now + Duration::from_secs(REQUEST_TIMEOUT);
let endpoint = IpEndpoint {
addr: Ipv4Address::BROADCAST.into(),
port: UDP_SERVER_PORT,
};
dhcp_repr.message_type = DhcpMessageType::Request;
dhcp_repr.broadcast = false;
dhcp_repr.requested_ip = Some(r_state.requested_ip);
dhcp_repr.server_identifier = Some(r_state.server_identifier);
net_trace!("DHCP send request to {} = {:?}", endpoint, dhcp_repr);
send_packet(iface, endpoint, dhcp_repr)
}
ClientState::Renew(ref mut p_state) => {
self.next_egress = now + Duration::from_secs(DEFAULT_RENEW_INTERVAL.into());
let endpoint = IpEndpoint {
addr: p_state.endpoint_ip.into(),
port: UDP_SERVER_PORT,
};
let client_ip = iface.ipv4_addr().unwrap_or(Ipv4Address::UNSPECIFIED);
dhcp_repr.message_type = DhcpMessageType::Request;
dhcp_repr.client_ip = client_ip;
dhcp_repr.broadcast = false;
net_trace!("DHCP send renew to {}: {:?}", endpoint, dhcp_repr);
send_packet(iface, endpoint, dhcp_repr)
}
}
}
/// Reset state and restart discovery phase.
///
/// Use this to speed up acquisition of an address in a new
/// network if a link was down and it is now back up.
///
/// You *must* configure a `0.0.0.0` address on your interface
/// before the next call to `poll()`!
pub fn reset(&mut self, now: Instant) {
net_trace!("DHCP reset");
self.state = ClientState::Discovering;
self.next_egress = now;
self.lease_expiration = None;
}
}
fn send_packet<DeviceT: for<'d> Device<'d>>(iface: &mut Interface<DeviceT>, raw_socket: &mut RawSocket, endpoint: &IpEndpoint, dhcp_repr: &DhcpRepr, checksum_caps: &ChecksumCapabilities) -> Result<()> {
let mut dhcp_payload_buf = [0; 320];
assert!(dhcp_repr.buffer_len() <= dhcp_payload_buf.len());
let dhcp_payload = &mut dhcp_payload_buf[0..dhcp_repr.buffer_len()];
{
let mut dhcp_packet = DhcpPacket::new_checked(&mut dhcp_payload[..])?;
dhcp_repr.emit(&mut dhcp_packet)?;
}
let udp_repr = UdpRepr {
src_port: UDP_CLIENT_PORT,
dst_port: endpoint.port,
payload: dhcp_payload,
};
let src_addr = iface.ipv4_addr().unwrap();
let dst_addr = match endpoint.addr {
IpAddress::Ipv4(addr) => addr,
_ => return Err(Error::Illegal),
};
let ipv4_repr = Ipv4Repr {
src_addr,
dst_addr,
protocol: IpProtocol::Udp,
payload_len: udp_repr.buffer_len(),
hop_limit: 64,
};
let mut packet = raw_socket.send(
ipv4_repr.buffer_len() + udp_repr.buffer_len()
)?;
{
let mut ipv4_packet = Ipv4Packet::new_unchecked(&mut packet);
ipv4_repr.emit(&mut ipv4_packet, &checksum_caps);
}
{
let mut udp_packet = UdpPacket::new_unchecked(
&mut packet[ipv4_repr.buffer_len()..]
);
udp_repr.emit(&mut udp_packet,
&src_addr.into(), &dst_addr.into(),
checksum_caps);
}
Ok(())
}
fn parse_udp<'a>(data: &'a [u8], checksum_caps: &ChecksumCapabilities) -> Result<(IpEndpoint, IpEndpoint, &'a [u8])> {
let ipv4_packet = Ipv4Packet::new_checked(data)?;
let ipv4_repr = Ipv4Repr::parse(&ipv4_packet, &checksum_caps)?;
let udp_packet = UdpPacket::new_checked(ipv4_packet.payload())?;
let udp_repr = UdpRepr::parse(
&udp_packet,
&ipv4_repr.src_addr.into(), &ipv4_repr.dst_addr.into(),
checksum_caps
)?;
let src = IpEndpoint {
addr: ipv4_repr.src_addr.into(),
port: udp_repr.src_port,
};
let dst = IpEndpoint {
addr: ipv4_repr.dst_addr.into(),
port: udp_repr.dst_port,
};
let data = udp_repr.payload;
Ok((src, dst, data))
}

5
src/dhcp/mod.rs Normal file
View File

@ -0,0 +1,5 @@
pub const UDP_SERVER_PORT: u16 = 67;
pub const UDP_CLIENT_PORT: u16 = 68;
mod clientv4;
pub use self::clientv4::{Client as Dhcpv4Client, Config as Dhcpv4Config};

2815
src/iface/ethernet.rs Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -4,27 +4,19 @@ The `iface` module deals with the *network interfaces*. It filters incoming fram
provides lookup and caching of hardware addresses, and handles management packets.
*/
#[cfg(any(
feature = "medium-ethernet",
feature = "medium-ip",
feature = "medium-ieee802154"
))]
mod interface;
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
#[cfg(feature = "ethernet")]
mod neighbor;
mod route;
#[cfg(feature = "ethernet")]
mod ethernet;
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
#[cfg(feature = "ethernet")]
pub use self::neighbor::Neighbor as Neighbor;
#[cfg(feature = "ethernet")]
pub(crate) use self::neighbor::Answer as NeighborAnswer;
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
#[cfg(feature = "ethernet")]
pub use self::neighbor::Cache as NeighborCache;
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
pub use self::neighbor::Neighbor;
pub use self::route::{Route, Routes};
#[cfg(any(
feature = "medium-ethernet",
feature = "medium-ip",
feature = "medium-ieee802154"
))]
pub use self::interface::{Interface, InterfaceBuilder};
#[cfg(feature = "ethernet")]
pub use self::ethernet::{Interface as EthernetInterface,
InterfaceBuilder as EthernetInterfaceBuilder};

View File

@ -3,31 +3,29 @@
use managed::ManagedMap;
use crate::wire::{EthernetAddress, IpAddress};
use crate::time::{Duration, Instant};
use crate::wire::{HardwareAddress, IpAddress};
/// A cached neighbor.
///
/// A neighbor mapping translates from a protocol address to a hardware address,
/// and contains the timestamp past which the mapping should be discarded.
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Neighbor {
hardware_addr: HardwareAddress,
expires_at: Instant,
hardware_addr: EthernetAddress,
expires_at: Instant,
}
/// An answer to a neighbor cache lookup.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) enum Answer {
/// The neighbor address is in the cache and not expired.
Found(HardwareAddress),
Found(EthernetAddress),
/// The neighbor address is not in the cache, or has expired.
NotFound,
/// The neighbor address is not in the cache, or has expired,
/// and a lookup has been made recently.
RateLimited,
RateLimited
}
impl Answer {
@ -61,21 +59,20 @@ impl Answer {
/// ```
#[derive(Debug)]
pub struct Cache<'a> {
storage: ManagedMap<'a, IpAddress, Neighbor>,
storage: ManagedMap<'a, IpAddress, Neighbor>,
silent_until: Instant,
#[cfg(any(feature = "std", feature = "alloc"))]
gc_threshold: usize,
gc_threshold: usize
}
impl<'a> Cache<'a> {
/// Minimum delay between discovery requests, in milliseconds.
pub(crate) const SILENT_TIME: Duration = Duration::from_millis(1_000);
pub(crate) const SILENT_TIME: Duration = Duration { millis: 1_000 };
/// Neighbor entry lifetime, in milliseconds.
pub(crate) const ENTRY_LIFETIME: Duration = Duration::from_millis(60_000);
pub(crate) const ENTRY_LIFETIME: Duration = Duration { millis: 60_000 };
/// Default number of entries in the cache before GC kicks in
#[cfg(any(feature = "std", feature = "alloc"))]
pub(crate) const GC_THRESHOLD: usize = 1024;
/// Create a cache. The backing storage is cleared upon creation.
@ -83,41 +80,21 @@ impl<'a> Cache<'a> {
/// # Panics
/// This function panics if `storage.len() == 0`.
pub fn new<T>(storage: T) -> Cache<'a>
where
T: Into<ManagedMap<'a, IpAddress, Neighbor>>,
{
let mut storage = storage.into();
storage.clear();
where T: Into<ManagedMap<'a, IpAddress, Neighbor>> {
Cache {
storage,
#[cfg(any(feature = "std", feature = "alloc"))]
gc_threshold: Self::GC_THRESHOLD,
silent_until: Instant::from_millis(0),
}
Cache::new_with_limit(storage, Cache::GC_THRESHOLD)
}
#[cfg(any(feature = "std", feature = "alloc"))]
pub fn new_with_limit<T>(storage: T, gc_threshold: usize) -> Cache<'a>
where
T: Into<ManagedMap<'a, IpAddress, Neighbor>>,
{
where T: Into<ManagedMap<'a, IpAddress, Neighbor>> {
let mut storage = storage.into();
storage.clear();
Cache {
storage,
gc_threshold,
silent_until: Instant::from_millis(0),
}
Cache { storage, gc_threshold, silent_until: Instant::from_millis(0) }
}
pub fn fill(
&mut self,
protocol_addr: IpAddress,
hardware_addr: HardwareAddress,
timestamp: Instant,
) {
pub fn fill(&mut self, protocol_addr: IpAddress, hardware_addr: EthernetAddress,
timestamp: Instant) {
debug_assert!(protocol_addr.is_unicast());
debug_assert!(hardware_addr.is_unicast());
@ -125,12 +102,11 @@ impl<'a> Cache<'a> {
let current_storage_size = self.storage.len();
match self.storage {
ManagedMap::Borrowed(_) => (),
ManagedMap::Borrowed(_) => (),
#[cfg(any(feature = "std", feature = "alloc"))]
ManagedMap::Owned(ref mut map) => {
if current_storage_size >= self.gc_threshold {
let new_btree_map = map
.iter_mut()
let new_btree_map = map.iter_mut()
.map(|(key, value)| (*key, *value))
.filter(|(_, v)| timestamp < v.expires_at)
.collect();
@ -140,18 +116,13 @@ impl<'a> Cache<'a> {
}
};
let neighbor = Neighbor {
expires_at: timestamp + Self::ENTRY_LIFETIME,
hardware_addr,
expires_at: timestamp + Self::ENTRY_LIFETIME, hardware_addr
};
match self.storage.insert(protocol_addr, neighbor) {
Ok(Some(old_neighbor)) => {
if old_neighbor.hardware_addr != hardware_addr {
net_trace!(
"replaced {} => {} (was {})",
protocol_addr,
hardware_addr,
old_neighbor.hardware_addr
);
net_trace!("replaced {} => {} (was {})",
protocol_addr, hardware_addr, old_neighbor.hardware_addr);
}
}
Ok(None) => {
@ -174,37 +145,34 @@ impl<'a> Cache<'a> {
}
// Owned maps can extend themselves.
#[cfg(any(feature = "std", feature = "alloc"))]
ManagedMap::Owned(_) => unreachable!(),
ManagedMap::Owned(_) => unreachable!()
};
let _old_neighbor = self.storage.remove(&old_protocol_addr).unwrap();
let _old_neighbor =
self.storage.remove(&old_protocol_addr).unwrap();
match self.storage.insert(protocol_addr, neighbor) {
Ok(None) => {
net_trace!(
"filled {} => {} (evicted {} => {})",
protocol_addr,
hardware_addr,
old_protocol_addr,
_old_neighbor.hardware_addr
);
net_trace!("filled {} => {} (evicted {} => {})",
protocol_addr, hardware_addr,
old_protocol_addr, _old_neighbor.hardware_addr);
}
// We've covered everything else above.
_ => unreachable!(),
_ => unreachable!()
}
}
}
}
pub(crate) fn lookup(&self, protocol_addr: &IpAddress, timestamp: Instant) -> Answer {
assert!(protocol_addr.is_unicast());
if protocol_addr.is_broadcast() {
return Answer::Found(EthernetAddress::BROADCAST);
}
if let Some(&Neighbor {
expires_at,
hardware_addr,
}) = self.storage.get(protocol_addr)
{
if let Some(&Neighbor { expires_at, hardware_addr }) =
self.storage.get(protocol_addr) {
if timestamp < expires_at {
return Answer::Found(hardware_addr);
return Answer::Found(hardware_addr)
}
}
@ -218,56 +186,36 @@ impl<'a> Cache<'a> {
pub(crate) fn limit_rate(&mut self, timestamp: Instant) {
self.silent_until = timestamp + Self::SILENT_TIME;
}
pub(crate) fn flush(&mut self) {
self.storage.clear()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_IP_ADDR_4};
use std::collections::BTreeMap;
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3, MOCK_IP_ADDR_4};
use crate::wire::EthernetAddress;
const HADDR_A: HardwareAddress = HardwareAddress::Ethernet(EthernetAddress([0, 0, 0, 0, 0, 1]));
const HADDR_B: HardwareAddress = HardwareAddress::Ethernet(EthernetAddress([0, 0, 0, 0, 0, 2]));
const HADDR_C: HardwareAddress = HardwareAddress::Ethernet(EthernetAddress([0, 0, 0, 0, 0, 3]));
const HADDR_D: HardwareAddress = HardwareAddress::Ethernet(EthernetAddress([0, 0, 0, 0, 0, 4]));
const HADDR_A: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 1]);
const HADDR_B: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 2]);
const HADDR_C: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 3]);
const HADDR_D: EthernetAddress = EthernetAddress([0, 0, 0, 0, 0, 4]);
#[test]
fn test_fill() {
let mut cache_storage = [Default::default(); 3];
let mut cache = Cache::new(&mut cache_storage[..]);
assert!(!cache
.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0))
.found());
assert!(!cache
.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0))
.found());
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)).found(), false);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false);
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
Answer::Found(HADDR_A)
);
assert!(!cache
.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0))
.found());
assert!(!cache
.lookup(
&MOCK_IP_ADDR_1,
Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2
)
.found(),);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2).found(),
false);
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
assert!(!cache
.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0))
.found());
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0)).found(), false);
}
#[test]
@ -276,16 +224,9 @@ mod test {
let mut cache = Cache::new(&mut cache_storage[..]);
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
Answer::Found(HADDR_A)
);
assert!(!cache
.lookup(
&MOCK_IP_ADDR_1,
Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2
)
.found(),);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0) + Cache::ENTRY_LIFETIME * 2).found(),
false);
}
#[test]
@ -294,15 +235,9 @@ mod test {
let mut cache = Cache::new(&mut cache_storage[..]);
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
Answer::Found(HADDR_A)
);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_A));
cache.fill(MOCK_IP_ADDR_1, HADDR_B, Instant::from_millis(0));
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
Answer::Found(HADDR_B)
);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::Found(HADDR_B));
}
#[test]
@ -312,20 +247,10 @@ mod test {
cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50));
// Adding third item after the expiration of the previous
// two should garbage collect
cache.fill(
MOCK_IP_ADDR_3,
HADDR_C,
Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2,
);
cache.fill(MOCK_IP_ADDR_3, HADDR_C, Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2);
assert_eq!(cache.storage.len(), 1);
assert_eq!(
cache.lookup(
&MOCK_IP_ADDR_3,
Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2
),
Answer::Found(HADDR_C)
);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_3, Instant::from_millis(50) + Cache::ENTRY_LIFETIME * 2), Answer::Found(HADDR_C));
}
#[test]
@ -336,22 +261,12 @@ mod test {
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(100));
cache.fill(MOCK_IP_ADDR_2, HADDR_B, Instant::from_millis(50));
cache.fill(MOCK_IP_ADDR_3, HADDR_C, Instant::from_millis(200));
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)),
Answer::Found(HADDR_B)
);
assert!(!cache
.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000))
.found());
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)), Answer::Found(HADDR_B));
assert_eq!(cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)).found(), false);
cache.fill(MOCK_IP_ADDR_4, HADDR_D, Instant::from_millis(300));
assert!(!cache
.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000))
.found());
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)),
Answer::Found(HADDR_D)
);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(1000)).found(), false);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_4, Instant::from_millis(1000)), Answer::Found(HADDR_D));
}
#[test]
@ -359,42 +274,10 @@ mod test {
let mut cache_storage = [Default::default(); 3];
let mut cache = Cache::new(&mut cache_storage[..]);
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
Answer::NotFound
);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)), Answer::NotFound);
cache.limit_rate(Instant::from_millis(0));
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(100)),
Answer::RateLimited
);
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(2000)),
Answer::NotFound
);
}
#[test]
fn test_flush() {
let mut cache_storage = [Default::default(); 3];
let mut cache = Cache::new(&mut cache_storage[..]);
cache.fill(MOCK_IP_ADDR_1, HADDR_A, Instant::from_millis(0));
assert_eq!(
cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0)),
Answer::Found(HADDR_A)
);
assert!(!cache
.lookup(&MOCK_IP_ADDR_2, Instant::from_millis(0))
.found());
cache.flush();
assert!(!cache
.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0))
.found());
assert!(!cache
.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(0))
.found());
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(100)), Answer::RateLimited);
assert_eq!(cache.lookup(&MOCK_IP_ADDR_1, Instant::from_millis(2000)), Answer::NotFound);
}
}

View File

@ -1,17 +1,16 @@
use managed::ManagedMap;
use crate::time::Instant;
use core::ops::Bound;
use managed::ManagedMap;
use crate::wire::{IpAddress, IpCidr};
use crate::{Error, Result};
use crate::wire::{IpCidr, IpAddress};
#[cfg(feature = "proto-ipv4")]
use crate::wire::{Ipv4Address, Ipv4Cidr};
#[cfg(feature = "proto-ipv6")]
use crate::wire::{Ipv6Address, Ipv6Cidr};
use crate::{Error, Result};
/// A prefix of addresses that should be routed via a router
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Route {
pub via_router: IpAddress,
/// `None` means "forever".
@ -70,9 +69,7 @@ impl<'a> Routes<'a> {
/// Creates a routing tables. The backing storage is **not** cleared
/// upon creation.
pub fn new<T>(storage: T) -> Routes<'a>
where
T: Into<ManagedMap<'a, IpCidr, Route>>,
{
where T: Into<ManagedMap<'a, IpCidr, Route>> {
let storage = storage.into();
Routes { storage }
}
@ -91,7 +88,7 @@ impl<'a> Routes<'a> {
let route = Route::new_ipv4_gateway(gateway);
match self.storage.insert(cidr, route) {
Ok(route) => Ok(route),
Err((_cidr, _route)) => Err(Error::Exhausted),
Err((_cidr, _route)) => Err(Error::Exhausted)
}
}
@ -104,29 +101,12 @@ impl<'a> Routes<'a> {
let route = Route::new_ipv6_gateway(gateway);
match self.storage.insert(cidr, route) {
Ok(route) => Ok(route),
Err((_cidr, _route)) => Err(Error::Exhausted),
Err((_cidr, _route)) => Err(Error::Exhausted)
}
}
/// Remove the default ipv4 gateway
///
/// On success, returns the previous default route, if any.
#[cfg(feature = "proto-ipv4")]
pub fn remove_default_ipv4_route(&mut self) -> Option<Route> {
let cidr = IpCidr::new(IpAddress::v4(0, 0, 0, 0), 0);
self.storage.remove(&cidr)
}
/// Remove the default ipv6 gateway
///
/// On success, returns the previous default route, if any.
#[cfg(feature = "proto-ipv6")]
pub fn remove_default_ipv6_route(&mut self) -> Option<Route> {
let cidr = IpCidr::new(IpAddress::v6(0, 0, 0, 0, 0, 0, 0, 0), 0);
self.storage.remove(&cidr)
}
pub(crate) fn lookup(&self, addr: &IpAddress, timestamp: Instant) -> Option<IpAddress> {
pub(crate) fn lookup(&self, addr: &IpAddress, timestamp: Instant) ->
Option<IpAddress> {
assert!(addr.is_unicast());
let cidr = match addr {
@ -134,14 +114,10 @@ impl<'a> Routes<'a> {
IpAddress::Ipv4(addr) => IpCidr::Ipv4(Ipv4Cidr::new(*addr, 32)),
#[cfg(feature = "proto-ipv6")]
IpAddress::Ipv6(addr) => IpCidr::Ipv6(Ipv6Cidr::new(*addr, 128)),
_ => unimplemented!(),
_ => unimplemented!()
};
for (prefix, route) in self
.storage
.range((Bound::Unbounded::<IpCidr>, Bound::Included(cidr)))
.rev()
{
for (prefix, route) in self.storage.range((Bound::Unbounded::<IpCidr>, Bound::Included(cidr))).rev() {
// TODO: do something with route.preferred_until
if let Some(expires_at) = route.expires_at {
if timestamp > expires_at {
@ -164,28 +140,24 @@ mod test {
#[cfg(feature = "proto-ipv6")]
mod mock {
use super::super::*;
pub const ADDR_1A: Ipv6Address =
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1]);
pub const ADDR_1B: Ipv6Address =
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 13]);
pub const ADDR_1C: Ipv6Address =
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 42]);
pub const ADDR_1A: Ipv6Address = Ipv6Address(
[0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1]);
pub const ADDR_1B: Ipv6Address = Ipv6Address(
[0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 13]);
pub const ADDR_1C: Ipv6Address = Ipv6Address(
[0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 42]);
pub fn cidr_1() -> Ipv6Cidr {
Ipv6Cidr::new(
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]),
64,
)
Ipv6Cidr::new(Ipv6Address(
[0xfe, 0x80, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0]), 64)
}
pub const ADDR_2A: Ipv6Address =
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 1]);
pub const ADDR_2B: Ipv6Address =
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 21]);
pub const ADDR_2A: Ipv6Address = Ipv6Address(
[0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 1]);
pub const ADDR_2B: Ipv6Address = Ipv6Address(
[0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 21]);
pub fn cidr_2() -> Ipv6Cidr {
Ipv6Cidr::new(
Ipv6Address([0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 0]),
64,
)
Ipv6Cidr::new(Ipv6Address(
[0xfe, 0x80, 0, 0, 0, 0, 51, 100, 0, 0, 0, 0, 0, 0, 0, 0]), 64)
}
}
@ -213,56 +185,25 @@ mod test {
let mut routes_storage = [None, None, None];
let mut routes = Routes::new(&mut routes_storage[..]);
assert_eq!(
routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)),
None
);
assert_eq!(
routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)),
None
);
assert_eq!(
routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)),
None
);
assert_eq!(
routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)),
None
);
assert_eq!(
routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)),
None
);
assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), None);
assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), None);
assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), None);
assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), None);
assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), None);
let route = Route {
via_router: ADDR_1A.into(),
preferred_until: None,
expires_at: None,
preferred_until: None, expires_at: None,
};
routes.update(|storage| {
storage.insert(cidr_1().into(), route).unwrap();
});
assert_eq!(
routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)),
Some(ADDR_1A.into())
);
assert_eq!(
routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)),
Some(ADDR_1A.into())
);
assert_eq!(
routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)),
Some(ADDR_1A.into())
);
assert_eq!(
routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)),
None
);
assert_eq!(
routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)),
None
);
assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), None);
assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), None);
let route2 = Route {
via_router: ADDR_2A.into(),
@ -273,46 +214,16 @@ mod test {
storage.insert(cidr_2().into(), route2).unwrap();
});
assert_eq!(
routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)),
Some(ADDR_1A.into())
);
assert_eq!(
routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)),
Some(ADDR_1A.into())
);
assert_eq!(
routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)),
Some(ADDR_1A.into())
);
assert_eq!(
routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)),
Some(ADDR_2A.into())
);
assert_eq!(
routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)),
Some(ADDR_2A.into())
);
assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(0)), Some(ADDR_1A.into()));
assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(0)), Some(ADDR_2A.into()));
assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(0)), Some(ADDR_2A.into()));
assert_eq!(
routes.lookup(&ADDR_1A.into(), Instant::from_millis(10)),
Some(ADDR_1A.into())
);
assert_eq!(
routes.lookup(&ADDR_1B.into(), Instant::from_millis(10)),
Some(ADDR_1A.into())
);
assert_eq!(
routes.lookup(&ADDR_1C.into(), Instant::from_millis(10)),
Some(ADDR_1A.into())
);
assert_eq!(
routes.lookup(&ADDR_2A.into(), Instant::from_millis(10)),
Some(ADDR_2A.into())
);
assert_eq!(
routes.lookup(&ADDR_2B.into(), Instant::from_millis(10)),
Some(ADDR_2A.into())
);
assert_eq!(routes.lookup(&ADDR_1A.into(), Instant::from_millis(10)), Some(ADDR_1A.into()));
assert_eq!(routes.lookup(&ADDR_1B.into(), Instant::from_millis(10)), Some(ADDR_1A.into()));
assert_eq!(routes.lookup(&ADDR_1C.into(), Instant::from_millis(10)), Some(ADDR_1A.into()));
assert_eq!(routes.lookup(&ADDR_2A.into(), Instant::from_millis(10)), Some(ADDR_2A.into()));
assert_eq!(routes.lookup(&ADDR_2B.into(), Instant::from_millis(10)), Some(ADDR_2A.into()));
}
}

View File

@ -1,12 +1,6 @@
#![cfg_attr(not(any(test, feature = "std")), no_std)]
#![deny(unsafe_code)]
#![cfg_attr(
all(
any(feature = "proto-ipv4", feature = "proto-ipv6"),
feature = "medium-ethernet"
),
deny(unused)
)]
#![cfg_attr(all(any(feature = "proto-ipv4", feature = "proto-ipv6"), feature = "ethernet"), deny(unused))]
//! The _smoltcp_ library is built in a layered structure, with the layers corresponding
//! to the levels of API abstraction. Only the highest layers would be used by a typical
@ -70,14 +64,6 @@
//! feature ever defined, to ensure that, when the representation layer is unable to make sense
//! of a packet, it is still logged correctly and in full.
//!
//! # Minimum Supported Rust Version (MSRV)
//!
//! This crate is guaranteed to compile on stable Rust 1.46 and up with any valid set of features.
//! It *might* compile on older versions but that may change in any new patch release.
//!
//! The exception is when using the `defmt` feature, in which case `defmt`'s MSRV applies, which
//! is higher than 1.46.
//!
//! [wire]: wire/index.html
//! [osi]: https://en.wikipedia.org/wiki/OSI_model
//! [berk]: https://en.wikipedia.org/wiki/Berkeley_sockets
@ -94,15 +80,11 @@ compile_error!("at least one socket needs to be enabled"); */
#![allow(clippy::option_map_unit_fn)]
#![allow(clippy::unit_arg)]
#[cfg(any(feature = "std", feature = "alloc"))]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(not(any(
feature = "proto-ipv4",
feature = "proto-ipv6",
feature = "proto-sixlowpan"
)))]
compile_error!("You must enable at least one of the following features: proto-ipv4, proto-ipv6, proto-sixlowpan");
#[cfg(not(any(feature = "proto-ipv4", feature = "proto-ipv6")))]
compile_error!("You must enable at least one of the following features: proto-ipv4, proto-ipv6");
#[cfg(all(
feature = "socket",
@ -115,41 +97,25 @@ compile_error!("You must enable at least one of the following features: proto-ip
))]
compile_error!("If you enable the socket feature, you must enable at least one of the following features: socket-raw, socket-udp, socket-tcp, socket-icmp");
#[cfg(all(
feature = "socket",
not(any(
feature = "medium-ethernet",
feature = "medium-ip",
feature = "medium-ieee802154",
))
))]
compile_error!("If you enable the socket feature, you must enable at least one of the following features: medium-ip, medium-ethernet, medium-ieee802154");
#[cfg(all(feature = "defmt", feature = "log"))]
compile_error!("You must enable at most one of the following features: defmt, log");
use core::fmt;
#[macro_use]
mod macros;
mod parsers;
mod rand;
#[cfg(feature = "rand-custom-impl")]
pub use crate::rand::Rand;
pub mod iface;
pub mod storage;
pub mod phy;
pub mod wire;
pub mod iface;
#[cfg(feature = "socket")]
pub mod socket;
pub mod storage;
pub mod time;
pub mod wire;
#[cfg(feature = "proto-dhcpv4")]
pub mod dhcp;
/// The error type for the networking stack.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
/// An operation cannot proceed because a buffer is empty or full.
Exhausted,
@ -182,32 +148,24 @@ pub enum Error {
/// An incoming packet was recognized but contradicted internal state.
/// E.g. a TCP packet addressed to a socket that doesn't exist.
Dropped,
/// An incoming packet was recognized but some parts are not supported by smoltcp.
/// E.g. some bit configuration in a packet header is not supported, but is defined in an RFC.
NotSupported,
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
/// The result type for the networking stack.
pub type Result<T> = core::result::Result<T, Error>;
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Exhausted => write!(f, "buffer space exhausted"),
Error::Illegal => write!(f, "illegal operation"),
Error::Exhausted => write!(f, "buffer space exhausted"),
Error::Illegal => write!(f, "illegal operation"),
Error::Unaddressable => write!(f, "unaddressable destination"),
Error::Finished => write!(f, "operation finished"),
Error::Truncated => write!(f, "truncated packet"),
Error::Checksum => write!(f, "checksum error"),
Error::Unrecognized => write!(f, "unrecognized packet"),
Error::Fragmented => write!(f, "fragmented packet"),
Error::Malformed => write!(f, "malformed packet"),
Error::Dropped => write!(f, "dropped by socket"),
Error::NotSupported => write!(f, "not supported by smoltcp"),
Error::Finished => write!(f, "operation finished"),
Error::Truncated => write!(f, "truncated packet"),
Error::Checksum => write!(f, "checksum error"),
Error::Unrecognized => write!(f, "unrecognized packet"),
Error::Fragmented => write!(f, "fragmented packet"),
Error::Malformed => write!(f, "malformed packet"),
Error::Dropped => write!(f, "dropped by socket"),
}
}
}

View File

@ -1,26 +1,13 @@
#[cfg(not(test))]
#[cfg(feature = "log")]
macro_rules! net_log {
(trace, $($arg:expr),*) => { log::trace!($($arg),*) };
(debug, $($arg:expr),*) => { log::debug!($($arg),*) };
(trace, $($arg:expr),*) => { log::trace!($($arg),*); };
(debug, $($arg:expr),*) => { log::debug!($($arg),*); };
}
#[cfg(test)]
#[cfg(feature = "log")]
#[cfg(not(feature = "log"))]
macro_rules! net_log {
(trace, $($arg:expr),*) => { println!($($arg),*) };
(debug, $($arg:expr),*) => { println!($($arg),*) };
}
#[cfg(feature = "defmt")]
macro_rules! net_log {
(trace, $($arg:expr),*) => { defmt::trace!($($arg),*) };
(debug, $($arg:expr),*) => { defmt::debug!($($arg),*) };
}
#[cfg(not(any(feature = "log", feature = "defmt")))]
macro_rules! net_log {
($level:ident, $($arg:expr),*) => {{ $( let _ = $arg; )* }}
($level:ident, $($arg:expr),*) => { $( let _ = $arg; )* }
}
macro_rules! net_trace {
@ -42,7 +29,6 @@ macro_rules! enum_with_unknown {
}
) => {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
$( #[$enum_attr] )*
pub enum $name {
$(

View File

@ -1,12 +1,9 @@
#![cfg_attr(
not(all(feature = "proto-ipv6", feature = "proto-ipv4")),
allow(dead_code)
)]
#![cfg_attr(not(all(feature = "proto-ipv6", feature = "proto-ipv4")), allow(dead_code))]
use core::result;
use core::str::FromStr;
use core::result;
#[cfg(feature = "medium-ethernet")]
#[cfg(feature = "ethernet")]
use crate::wire::EthernetAddress;
use crate::wire::{IpAddress, IpCidr, IpEndpoint};
#[cfg(feature = "proto-ipv4")]
@ -18,14 +15,14 @@ type Result<T> = result::Result<T, ()>;
struct Parser<'a> {
data: &'a [u8],
pos: usize,
pos: usize
}
impl<'a> Parser<'a> {
fn new(data: &'a str) -> Parser<'a> {
Parser {
data: data.as_bytes(),
pos: 0,
pos: 0
}
}
@ -43,14 +40,12 @@ impl<'a> Parser<'a> {
self.pos += 1;
Ok(chr)
}
None => Err(()),
None => Err(())
}
}
fn try_do<F, T>(&mut self, f: F) -> Option<T>
where
F: FnOnce(&mut Parser<'a>) -> Result<T>,
{
where F: FnOnce(&mut Parser<'a>) -> Result<T> {
let pos = self.pos;
match f(self) {
Ok(res) => Some(res),
@ -70,9 +65,7 @@ impl<'a> Parser<'a> {
}
fn until_eof<F, T>(&mut self, f: F) -> Result<T>
where
F: FnOnce(&mut Parser<'a>) -> Result<T>,
{
where F: FnOnce(&mut Parser<'a>) -> Result<T> {
let res = f(self)?;
self.accept_eof()?;
Ok(res)
@ -106,7 +99,8 @@ impl<'a> Parser<'a> {
}
}
fn accept_number(&mut self, max_digits: usize, max_value: u32, hex: bool) -> Result<u32> {
fn accept_number(&mut self, max_digits: usize, max_value: u32,
hex: bool) -> Result<u32> {
let mut value = self.accept_digit(hex)? as u32;
for _ in 1..max_digits {
match self.try_do(|p| p.accept_digit(hex)) {
@ -114,7 +108,7 @@ impl<'a> Parser<'a> {
value *= if hex { 16 } else { 10 };
value += digit as u32;
}
None => break,
None => break
}
}
if value < max_value {
@ -124,7 +118,7 @@ impl<'a> Parser<'a> {
}
}
#[cfg(feature = "medium-ethernet")]
#[cfg(feature = "ethernet")]
fn accept_mac_joined_with(&mut self, separator: u8) -> Result<EthernetAddress> {
let mut octets = [0u8; 6];
for (n, octet) in octets.iter_mut().enumerate() {
@ -136,13 +130,13 @@ impl<'a> Parser<'a> {
Ok(EthernetAddress(octets))
}
#[cfg(feature = "medium-ethernet")]
#[cfg(feature = "ethernet")]
fn accept_mac(&mut self) -> Result<EthernetAddress> {
if let Some(mac) = self.try_do(|p| p.accept_mac_joined_with(b'-')) {
return Ok(mac);
return Ok(mac)
}
if let Some(mac) = self.try_do(|p| p.accept_mac_joined_with(b':')) {
return Ok(mac);
return Ok(mac)
}
Err(())
}
@ -160,13 +154,9 @@ impl<'a> Parser<'a> {
}
#[cfg(feature = "proto-ipv6")]
fn accept_ipv6_part(
&mut self,
(head, tail): (&mut [u16; 8], &mut [u16; 6]),
(head_idx, tail_idx): (&mut usize, &mut usize),
mut use_tail: bool,
is_cidr: bool,
) -> Result<()> {
fn accept_ipv6_part(&mut self, (head, tail): (&mut [u16; 8], &mut [u16; 6]),
(head_idx, tail_idx): (&mut usize, &mut usize),
mut use_tail: bool, is_cidr: bool) -> Result<()> {
let double_colon = match self.try_do(|p| p.accept_str(b"::")) {
Some(_) if !use_tail && *head_idx < 7 => {
// Found a double colon. Start filling out the
@ -174,7 +164,7 @@ impl<'a> Parser<'a> {
// this is the last character we can parse.
use_tail = true;
true
}
},
Some(_) => {
// This is a bad address. Only one double colon is
// allowed and an address is only 128 bits.
@ -203,20 +193,21 @@ impl<'a> Parser<'a> {
});
}
Ok(())
}
},
Some(part) if *tail_idx < 6 => {
// Valid u16 to be added to the address
tail[*tail_idx] = part as u16;
*tail_idx += 1;
if *tail_idx == 1 && tail[0] == 0xffff && head[0..8] == [0, 0, 0, 0, 0, 0, 0, 0] {
if *tail_idx == 1 && tail[0] == 0xffff
&& head[0..8] == [0, 0, 0, 0, 0, 0, 0, 0] {
self.try_do(|p| {
p.accept_char(b':')?;
p.accept_ipv4_mapped_ipv6_part(tail, tail_idx)
});
}
Ok(())
}
},
Some(_) => {
// Tail or head section is too long
Err(())
@ -268,12 +259,7 @@ impl<'a> Parser<'a> {
let (mut addr, mut tail) = ([0u16; 8], [0u16; 6]);
let (mut head_idx, mut tail_idx) = (0, 0);
self.accept_ipv6_part(
(&mut addr, &mut tail),
(&mut head_idx, &mut tail_idx),
false,
is_cidr,
)?;
self.accept_ipv6_part((&mut addr, &mut tail), (&mut head_idx, &mut tail_idx), false, is_cidr)?;
// We need to copy the tail portion (the portion following the "::") to the
// end of the address.
@ -304,14 +290,14 @@ impl<'a> Parser<'a> {
#[allow(clippy::single_match)]
match self.try_do(|p| p.accept_ipv4()) {
Some(ipv4) => return Ok(IpAddress::Ipv4(ipv4)),
None => (),
None => ()
}
#[cfg(feature = "proto-ipv6")]
#[allow(clippy::single_match)]
match self.try_do(|p| p.accept_ipv6(false)) {
Some(ipv6) => return Ok(IpAddress::Ipv6(ipv6)),
None => (),
None => ()
}
Err(())
@ -328,10 +314,7 @@ impl<'a> Parser<'a> {
self.accept_number(5, 65535, false)?
};
Ok(IpEndpoint {
addr: IpAddress::Ipv4(ip),
port: port as u16,
})
Ok(IpEndpoint { addr: IpAddress::Ipv4(ip), port: port as u16 })
}
#[cfg(feature = "proto-ipv6")]
@ -343,16 +326,10 @@ impl<'a> Parser<'a> {
self.accept_char(b':')?;
let port = self.accept_number(5, 65535, false)?;
Ok(IpEndpoint {
addr: IpAddress::Ipv6(ip),
port: port as u16,
})
Ok(IpEndpoint { addr: IpAddress::Ipv6(ip), port: port as u16 })
} else {
let ip = self.accept_ipv6(false)?;
Ok(IpEndpoint {
addr: IpAddress::Ipv6(ip),
port: 0,
})
Ok(IpEndpoint { addr: IpAddress::Ipv6(ip), port: 0 })
}
}
@ -361,21 +338,21 @@ impl<'a> Parser<'a> {
#[allow(clippy::single_match)]
match self.try_do(|p| p.accept_ipv4_endpoint()) {
Some(ipv4) => return Ok(ipv4),
None => (),
None => ()
}
#[cfg(feature = "proto-ipv6")]
#[allow(clippy::single_match)]
match self.try_do(|p| p.accept_ipv6_endpoint()) {
Some(ipv6) => return Ok(ipv6),
None => (),
None => ()
}
Err(())
}
}
#[cfg(feature = "medium-ethernet")]
#[cfg(feature = "ethernet")]
impl FromStr for EthernetAddress {
type Err = ();
@ -454,14 +431,14 @@ impl FromStr for IpCidr {
#[allow(clippy::single_match)]
match Ipv4Cidr::from_str(s) {
Ok(cidr) => return Ok(IpCidr::Ipv4(cidr)),
Err(_) => (),
Err(_) => ()
}
#[cfg(feature = "proto-ipv6")]
#[allow(clippy::single_match)]
match Ipv6Cidr::from_str(s) {
Ok(cidr) => return Ok(IpCidr::Ipv6(cidr)),
Err(_) => (),
Err(_) => ()
}
Err(())
@ -472,7 +449,7 @@ impl FromStr for IpEndpoint {
type Err = ();
fn from_str(s: &str) -> Result<IpEndpoint> {
Parser::new(s).until_eof(|p| p.accept_ip_endpoint())
Parser::new(s).until_eof(|p| Ok(p.accept_ip_endpoint()?))
}
}
@ -488,40 +465,29 @@ mod test {
if let Ok(cidr) = cidr {
assert_eq!($from_str(&format!("{}", cidr)), Ok(cidr));
assert_eq!(IpCidr::from_str(&format!("{}", cidr)), Ok($variant(cidr)));
assert_eq!(IpCidr::from_str(&format!("{}", cidr)),
Ok($variant(cidr)));
}
}
};
}
}
#[test]
#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
#[cfg(all(feature = "proto-ipv4", feature = "ethernet"))]
fn test_mac() {
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::from_str("01:23:45:67:89:ab"),
Ok(EthernetAddress([0x01, 0x23, 0x45, 0x67, 0x89, 0xab]))
);
assert_eq!(
EthernetAddress::from_str("cd:ef:10:00:00:00"),
Ok(EthernetAddress([0xcd, 0xef, 0x10, 0x00, 0x00, 0x00]))
);
assert_eq!(
EthernetAddress::from_str("00:00:00:ab:cd:ef"),
Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef]))
);
assert_eq!(
EthernetAddress::from_str("00-00-00-ab-cd-ef"),
Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef]))
);
assert_eq!(
EthernetAddress::from_str("AB-CD-EF-00-00-00"),
Ok(EthernetAddress([0xab, 0xcd, 0xef, 0x00, 0x00, 0x00]))
);
assert_eq!(EthernetAddress::from_str("02:00:00:00:00:00"),
Ok(EthernetAddress([0x02, 0x00, 0x00, 0x00, 0x00, 0x00])));
assert_eq!(EthernetAddress::from_str("01:23:45:67:89:ab"),
Ok(EthernetAddress([0x01, 0x23, 0x45, 0x67, 0x89, 0xab])));
assert_eq!(EthernetAddress::from_str("cd:ef:10:00:00:00"),
Ok(EthernetAddress([0xcd, 0xef, 0x10, 0x00, 0x00, 0x00])));
assert_eq!(EthernetAddress::from_str("00:00:00:ab:cd:ef"),
Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef])));
assert_eq!(EthernetAddress::from_str("00-00-00-ab-cd-ef"),
Ok(EthernetAddress([0x00, 0x00, 0x00, 0xab, 0xcd, 0xef])));
assert_eq!(EthernetAddress::from_str("AB-CD-EF-00-00-00"),
Ok(EthernetAddress([0xab, 0xcd, 0xef, 0x00, 0x00, 0x00])));
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(()));
@ -532,14 +498,10 @@ mod test {
#[cfg(feature = "proto-ipv4")]
fn test_ipv4() {
assert_eq!(Ipv4Address::from_str(""), Err(()));
assert_eq!(
Ipv4Address::from_str("1.2.3.4"),
Ok(Ipv4Address([1, 2, 3, 4]))
);
assert_eq!(
Ipv4Address::from_str("001.2.3.4"),
Ok(Ipv4Address([1, 2, 3, 4]))
);
assert_eq!(Ipv4Address::from_str("1.2.3.4"),
Ok(Ipv4Address([1, 2, 3, 4])));
assert_eq!(Ipv4Address::from_str("001.2.3.4"),
Ok(Ipv4Address([1, 2, 3, 4])));
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(()));
@ -553,87 +515,73 @@ mod test {
fn test_ipv6() {
// Obviously not valid
assert_eq!(Ipv6Address::from_str(""), Err(()));
assert_eq!(
Ipv6Address::from_str("fe80:0:0:0:0:0:0:1"),
Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1))
);
assert_eq!(Ipv6Address::from_str("::1"), Ok(Ipv6Address::LOOPBACK));
assert_eq!(Ipv6Address::from_str("::"), Ok(Ipv6Address::UNSPECIFIED));
assert_eq!(
Ipv6Address::from_str("fe80::1"),
Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1))
);
assert_eq!(
Ipv6Address::from_str("1234:5678::"),
Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0, 0))
);
assert_eq!(
Ipv6Address::from_str("1234:5678::8765:4321"),
Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0x8765, 0x4321))
);
assert_eq!(Ipv6Address::from_str("fe80:0:0:0:0:0:0:1"),
Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)));
assert_eq!(Ipv6Address::from_str("::1"),
Ok(Ipv6Address::LOOPBACK));
assert_eq!(Ipv6Address::from_str("::"),
Ok(Ipv6Address::UNSPECIFIED));
assert_eq!(Ipv6Address::from_str("fe80::1"),
Ok(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)));
assert_eq!(Ipv6Address::from_str("1234:5678::"),
Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0, 0)));
assert_eq!(Ipv6Address::from_str("1234:5678::8765:4321"),
Ok(Ipv6Address::new(0x1234, 0x5678, 0, 0, 0, 0, 0x8765, 0x4321)));
// Two double colons in address
assert_eq!(Ipv6Address::from_str("1234:5678::1::1"), Err(()));
assert_eq!(
Ipv6Address::from_str("4444:333:22:1::4"),
Ok(Ipv6Address::new(0x4444, 0x0333, 0x0022, 0x0001, 0, 0, 0, 4))
);
assert_eq!(
Ipv6Address::from_str("1:1:1:1:1:1::"),
Ok(Ipv6Address::new(1, 1, 1, 1, 1, 1, 0, 0))
);
assert_eq!(
Ipv6Address::from_str("::1:1:1:1:1:1"),
Ok(Ipv6Address::new(0, 0, 1, 1, 1, 1, 1, 1))
);
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"), Err(()));
assert_eq!(Ipv6Address::from_str("1234:5678::1::1"),
Err(()));
assert_eq!(Ipv6Address::from_str("4444:333:22:1::4"),
Ok(Ipv6Address::new(0x4444, 0x0333, 0x0022, 0x0001, 0, 0, 0, 4)));
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1::"),
Ok(Ipv6Address::new(1, 1, 1, 1, 1, 1, 0, 0)));
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1"),
Ok(Ipv6Address::new(0, 0, 1, 1, 1, 1, 1, 1)));
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"),
Err(()));
// Double colon appears too late indicating an address that is too long
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1::"), Err(()));
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1::"),
Err(()));
// Section after double colon is too long for a valid address
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"), Err(()));
assert_eq!(Ipv6Address::from_str("::1:1:1:1:1:1:1"),
Err(()));
// Obviously too long
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1:1:1"), Err(()));
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1:1:1"),
Err(()));
// Address is too short
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1"), Err(()));
assert_eq!(Ipv6Address::from_str("1:1:1:1:1:1:1"),
Err(()));
// Long number
assert_eq!(Ipv6Address::from_str("::000001"), Err(()));
assert_eq!(Ipv6Address::from_str("::000001"),
Err(()));
// IPv4-Mapped address
assert_eq!(
Ipv6Address::from_str("::ffff:192.168.1.1"),
Ok(Ipv6Address([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1
]))
);
assert_eq!(
Ipv6Address::from_str("0:0:0:0:0:ffff:192.168.1.1"),
Ok(Ipv6Address([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1
]))
);
assert_eq!(
Ipv6Address::from_str("0::ffff:192.168.1.1"),
Ok(Ipv6Address([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1
]))
);
assert_eq!(Ipv6Address::from_str("::ffff:192.168.1.1"),
Ok(Ipv6Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1])));
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:192.168.1.1"),
Ok(Ipv6Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1])));
assert_eq!(Ipv6Address::from_str("0::ffff:192.168.1.1"),
Ok(Ipv6Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1])));
// Only ffff is allowed in position 6 when IPv4 mapped
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:eeee:192.168.1.1"), Err(()));
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:eeee:192.168.1.1"),
Err(()));
// Positions 1-5 must be 0 when IPv4 mapped
assert_eq!(Ipv6Address::from_str("0:0:0:0:1:ffff:192.168.1.1"), Err(()));
assert_eq!(Ipv6Address::from_str("1::ffff:192.168.1.1"), Err(()));
assert_eq!(Ipv6Address::from_str("0:0:0:0:1:ffff:192.168.1.1"),
Err(()));
assert_eq!(Ipv6Address::from_str("1::ffff:192.168.1.1"),
Err(()));
// Out of range ipv4 octet
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:256.168.1.1"), Err(()));
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:256.168.1.1"),
Err(()));
// Invalid hex in ipv4 octet
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:c0.168.1.1"), Err(()));
assert_eq!(Ipv6Address::from_str("0:0:0:0:0:ffff:c0.168.1.1"),
Err(()));
}
#[test]
#[cfg(feature = "proto-ipv4")]
fn test_ip_ipv4() {
assert_eq!(IpAddress::from_str(""), Err(()));
assert_eq!(
IpAddress::from_str("1.2.3.4"),
Ok(IpAddress::Ipv4(Ipv4Address([1, 2, 3, 4])))
);
assert_eq!(IpAddress::from_str("1.2.3.4"),
Ok(IpAddress::Ipv4(Ipv4Address([1, 2, 3, 4]))));
assert_eq!(IpAddress::from_str("x"), Err(()));
}
@ -641,12 +589,8 @@ mod test {
#[cfg(feature = "proto-ipv6")]
fn test_ip_ipv6() {
assert_eq!(IpAddress::from_str(""), Err(()));
assert_eq!(
IpAddress::from_str("fe80::1"),
Ok(IpAddress::Ipv6(Ipv6Address::new(
0xfe80, 0, 0, 0, 0, 0, 0, 1
)))
);
assert_eq!(IpAddress::from_str("fe80::1"),
Ok(IpAddress::Ipv6(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1))));
assert_eq!(IpAddress::from_str("x"), Err(()));
}
@ -654,22 +598,14 @@ mod test {
#[cfg(feature = "proto-ipv4")]
fn test_cidr_ipv4() {
let tests = [
(
"127.0.0.1/8",
Ok(Ipv4Cidr::new(Ipv4Address([127, 0, 0, 1]), 8u8)),
),
(
"192.168.1.1/24",
Ok(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 1]), 24u8)),
),
(
"8.8.8.8/32",
Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 32u8)),
),
(
"8.8.8.8/0",
Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 0u8)),
),
("127.0.0.1/8",
Ok(Ipv4Cidr::new(Ipv4Address([127, 0, 0, 1]), 8u8))),
("192.168.1.1/24",
Ok(Ipv4Cidr::new(Ipv4Address([192, 168, 1, 1]), 24u8))),
("8.8.8.8/32",
Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 32u8))),
("8.8.8.8/0",
Ok(Ipv4Cidr::new(Ipv4Address([8, 8, 8, 8]), 0u8))),
("", Err(())),
("1", Err(())),
("127.0.0.1", Err(())),
@ -686,32 +622,22 @@ mod test {
#[cfg(feature = "proto-ipv6")]
fn test_cidr_ipv6() {
let tests = [
(
"fe80::1/64",
Ok(Ipv6Cidr::new(
Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
64u8,
)),
),
(
"fe80::/64",
Ok(Ipv6Cidr::new(
Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0),
64u8,
)),
),
("::1/128", Ok(Ipv6Cidr::new(Ipv6Address::LOOPBACK, 128u8))),
("::/128", Ok(Ipv6Cidr::new(Ipv6Address::UNSPECIFIED, 128u8))),
(
"fe80:0:0:0:0:0:0:1/64",
Ok(Ipv6Cidr::new(
Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
64u8,
)),
),
("fe80:0:0:0:0:0:0:1|64", Err(())),
("fe80::|64", Err(())),
("fe80::1::/64", Err(())),
("fe80::1/64",
Ok(Ipv6Cidr::new(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64u8))),
("fe80::/64",
Ok(Ipv6Cidr::new(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 0), 64u8))),
("::1/128",
Ok(Ipv6Cidr::new(Ipv6Address::LOOPBACK, 128u8))),
("::/128",
Ok(Ipv6Cidr::new(Ipv6Address::UNSPECIFIED, 128u8))),
("fe80:0:0:0:0:0:0:1/64",
Ok(Ipv6Cidr::new(Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), 64u8))),
("fe80:0:0:0:0:0:0:1|64",
Err(())),
("fe80::|64",
Err(())),
("fe80::1::/64",
Err(()))
];
check_cidr_test_array!(tests, Ipv6Cidr::from_str, IpCidr::Ipv6);
}
@ -723,17 +649,11 @@ mod test {
assert_eq!(IpEndpoint::from_str("x"), Err(()));
assert_eq!(
IpEndpoint::from_str("127.0.0.1"),
Ok(IpEndpoint {
addr: IpAddress::v4(127, 0, 0, 1),
port: 0
})
Ok(IpEndpoint { addr: IpAddress::v4(127, 0, 0, 1), port: 0 })
);
assert_eq!(
IpEndpoint::from_str("127.0.0.1:12345"),
Ok(IpEndpoint {
addr: IpAddress::v4(127, 0, 0, 1),
port: 12345
})
Ok(IpEndpoint { addr: IpAddress::v4(127, 0, 0, 1), port: 12345 })
);
}
@ -744,17 +664,11 @@ mod test {
assert_eq!(IpEndpoint::from_str("x"), Err(()));
assert_eq!(
IpEndpoint::from_str("fe80::1"),
Ok(IpEndpoint {
addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1),
port: 0
})
Ok(IpEndpoint { addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), port: 0 })
);
assert_eq!(
IpEndpoint::from_str("[fe80::1]:12345"),
Ok(IpEndpoint {
addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1),
port: 12345
})
Ok(IpEndpoint { addr: IpAddress::v6(0xfe80, 0, 0, 0, 0, 0, 0, 1), port: 12345 })
);
}
}

View File

@ -1,8 +1,8 @@
use core::cell::RefCell;
use crate::phy::{self, Device, DeviceCapabilities};
use crate::time::{Duration, Instant};
use crate::{Error, Result};
use crate::phy::{self, DeviceCapabilities, Device};
use crate::time::{Duration, Instant};
// We use our own RNG to stay compatible with #![no_std].
// The use of the RNG below has a slight bias, but it doesn't matter.
@ -19,23 +19,22 @@ fn xorshift32(state: &mut u32) -> u32 {
const MTU: usize = 1536;
#[derive(Debug, Default, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct Config {
corrupt_pct: u8,
drop_pct: u8,
max_size: usize,
drop_pct: u8,
reorder_pct: u8,
max_size: usize,
max_tx_rate: u64,
max_rx_rate: u64,
interval: Duration,
interval: Duration,
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct State {
rng_seed: u32,
rng_seed: u32,
refilled_at: Instant,
tx_bucket: u64,
rx_bucket: u64,
tx_bucket: u64,
rx_bucket: u64,
}
impl State {
@ -47,7 +46,7 @@ impl State {
let buffer = buffer.as_mut();
// We introduce a single bitflip, as the most likely, and the hardest to detect, error.
let index = (xorshift32(&mut self.rng_seed) as usize) % buffer.len();
let bit = 1 << (xorshift32(&mut self.rng_seed) % 8) as u8;
let bit = 1 << (xorshift32(&mut self.rng_seed) % 8) as u8;
buffer[index] ^= bit;
}
@ -60,9 +59,7 @@ impl State {
}
fn maybe_transmit(&mut self, config: &Config, timestamp: Instant) -> bool {
if config.max_tx_rate == 0 {
return true;
}
if config.max_tx_rate == 0 { return true }
self.refill(config, timestamp);
if self.tx_bucket > 0 {
@ -74,9 +71,7 @@ impl State {
}
fn maybe_receive(&mut self, config: &Config, timestamp: Instant) -> bool {
if config.max_rx_rate == 0 {
return true;
}
if config.max_rx_rate == 0 { return true }
self.refill(config, timestamp);
if self.rx_bucket > 0 {
@ -95,22 +90,22 @@ impl State {
/// or hardware limitations (such as a limited number or size of usable network buffers).
#[derive(Debug)]
pub struct FaultInjector<D: for<'a> Device<'a>> {
inner: D,
state: RefCell<State>,
config: Config,
inner: D,
state: RefCell<State>,
config: Config,
}
impl<D: for<'a> Device<'a>> FaultInjector<D> {
/// Create a fault injector device, using the given random number generator seed.
pub fn new(inner: D, seed: u32) -> FaultInjector<D> {
let state = State {
rng_seed: seed,
rng_seed: seed,
refilled_at: Instant::from_millis(0),
tx_bucket: 0,
rx_bucket: 0,
tx_bucket: 0,
rx_bucket: 0,
};
FaultInjector {
inner,
inner: inner,
state: RefCell::new(state),
config: Config::default(),
}
@ -156,9 +151,7 @@ impl<D: for<'a> Device<'a>> FaultInjector<D> {
/// # Panics
/// This function panics if the probability is not between 0% and 100%.
pub fn set_corrupt_chance(&mut self, pct: u8) {
if pct > 100 {
panic!("percentage out of range")
}
if pct > 100 { panic!("percentage out of range") }
self.config.corrupt_pct = pct
}
@ -167,9 +160,7 @@ impl<D: for<'a> Device<'a>> FaultInjector<D> {
/// # Panics
/// This function panics if the probability is not between 0% and 100%.
pub fn set_drop_chance(&mut self, pct: u8) {
if pct > 100 {
panic!("percentage out of range")
}
if pct > 100 { panic!("percentage out of range") }
self.config.drop_pct = pct
}
@ -196,8 +187,7 @@ impl<D: for<'a> Device<'a>> FaultInjector<D> {
}
impl<'a, D> Device<'a> for FaultInjector<D>
where
D: for<'b> Device<'b>,
where D: for<'b> Device<'b>,
{
type RxToken = RxToken<'a, <D as Device<'a>>::RxToken>;
type TxToken = TxToken<'a, <D as Device<'a>>::TxToken>;
@ -211,85 +201,67 @@ where
}
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
let &mut Self {
ref mut inner,
ref state,
config,
} = self;
let &mut Self { ref mut inner, ref state, config } = self;
inner.receive().map(|(rx_token, tx_token)| {
let rx = RxToken {
state,
config,
token: rx_token,
state: &state,
config: config,
token: rx_token,
corrupt: [0; MTU],
};
let tx = TxToken {
state,
config,
token: tx_token,
junk: [0; MTU],
state: &state,
config: config,
token: tx_token,
junk: [0; MTU],
};
(rx, tx)
})
}
fn transmit(&'a mut self) -> Option<Self::TxToken> {
let &mut Self {
ref mut inner,
ref state,
config,
} = self;
let &mut Self { ref mut inner, ref state, config } = self;
inner.transmit().map(|token| TxToken {
state,
config,
token,
junk: [0; MTU],
state: &state,
config: config,
token: token,
junk: [0; MTU],
})
}
}
#[doc(hidden)]
pub struct RxToken<'a, Rx: phy::RxToken> {
state: &'a RefCell<State>,
config: Config,
token: Rx,
state: &'a RefCell<State>,
config: Config,
token: Rx,
corrupt: [u8; MTU],
}
impl<'a, Rx: phy::RxToken> phy::RxToken for RxToken<'a, Rx> {
fn consume<R, F>(self, timestamp: Instant, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
where F: FnOnce(&mut [u8]) -> Result<R>
{
if self.state.borrow_mut().maybe(self.config.drop_pct) {
net_trace!("rx: randomly dropping a packet");
return Err(Error::Exhausted);
return Err(Error::Exhausted)
}
if !self
.state
.borrow_mut()
.maybe_receive(&self.config, timestamp)
{
if !self.state.borrow_mut().maybe_receive(&self.config, timestamp) {
net_trace!("rx: dropping a packet because of rate limiting");
return Err(Error::Exhausted);
return Err(Error::Exhausted)
}
let Self {
token,
config,
state,
mut corrupt,
} = self;
let Self { token, config, state, mut corrupt } = self;
token.consume(timestamp, |buffer| {
if config.max_size > 0 && buffer.as_ref().len() > config.max_size {
net_trace!("rx: dropping a packet that is too large");
return Err(Error::Exhausted);
return Err(Error::Exhausted)
}
if state.borrow_mut().maybe(config.corrupt_pct) {
net_trace!("rx: randomly corrupting a packet");
let mut corrupt = &mut corrupt[..buffer.len()];
corrupt.copy_from_slice(buffer);
state.borrow_mut().corrupt(&mut corrupt);
f(corrupt)
f(&mut corrupt)
} else {
f(buffer)
}
@ -299,16 +271,15 @@ impl<'a, Rx: phy::RxToken> phy::RxToken for RxToken<'a, Rx> {
#[doc(hidden)]
pub struct TxToken<'a, Tx: phy::TxToken> {
state: &'a RefCell<State>,
state: &'a RefCell<State>,
config: Config,
token: Tx,
junk: [u8; MTU],
token: Tx,
junk: [u8; MTU],
}
impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> {
fn consume<R, F>(mut self, timestamp: Instant, len: usize, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
where F: FnOnce(&mut [u8]) -> Result<R>
{
let drop = if self.state.borrow_mut().maybe(self.config.drop_pct) {
net_trace!("tx: randomly dropping a packet");
@ -316,11 +287,7 @@ impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> {
} else if self.config.max_size > 0 && len > self.config.max_size {
net_trace!("tx: dropping a packet that is too large");
true
} else if !self
.state
.borrow_mut()
.maybe_transmit(&self.config, timestamp)
{
} else if !self.state.borrow_mut().maybe_transmit(&self.config, timestamp) {
net_trace!("tx: dropping a packet because of rate limiting");
true
} else {
@ -331,12 +298,7 @@ impl<'a, Tx: phy::TxToken> phy::TxToken for TxToken<'a, Tx> {
return f(&mut self.junk[..len]);
}
let Self {
token,
state,
config,
..
} = self;
let Self { token, state, config, .. } = self;
token.consume(timestamp, len, |mut buf| {
if state.borrow_mut().maybe(config.corrupt_pct) {
net_trace!("tx: corrupting a packet");

View File

@ -1,6 +1,6 @@
use crate::phy::{self, Device, DeviceCapabilities};
use crate::time::Instant;
use crate::Result;
use crate::phy::{self, DeviceCapabilities, Device};
use crate::time::Instant;
// This could be fixed once associated consts are stable.
const MTU: usize = 1536;
@ -18,9 +18,8 @@ pub trait Fuzzer {
/// smoltcp, and is not for production use.
#[allow(unused)]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FuzzInjector<D: for<'a> Device<'a>, FTx: Fuzzer, FRx: Fuzzer> {
inner: D,
inner: D,
fuzz_tx: FTx,
fuzz_rx: FRx,
}
@ -29,11 +28,7 @@ pub struct FuzzInjector<D: for<'a> Device<'a>, FTx: Fuzzer, FRx: Fuzzer> {
impl<D: for<'a> Device<'a>, FTx: Fuzzer, FRx: Fuzzer> FuzzInjector<D, FTx, FRx> {
/// Create a fuzz injector device.
pub fn new(inner: D, fuzz_tx: FTx, fuzz_rx: FRx) -> FuzzInjector<D, FTx, FRx> {
FuzzInjector {
inner,
fuzz_tx,
fuzz_rx,
}
FuzzInjector { inner, fuzz_tx, fuzz_rx }
}
/// Return the underlying device, consuming the fuzz injector.
@ -43,10 +38,9 @@ impl<D: for<'a> Device<'a>, FTx: Fuzzer, FRx: Fuzzer> FuzzInjector<D, FTx, FRx>
}
impl<'a, D, FTx, FRx> Device<'a> for FuzzInjector<D, FTx, FRx>
where
D: for<'b> Device<'b>,
FTx: Fuzzer + 'a,
FRx: Fuzzer + 'a,
where D: for<'b> Device<'b>,
FTx: Fuzzer + 'a,
FRx: Fuzzer + 'a
{
type RxToken = RxToken<'a, <D as Device<'a>>::RxToken, FRx>;
type TxToken = TxToken<'a, <D as Device<'a>>::TxToken, FTx>;
@ -60,47 +54,38 @@ where
}
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
let &mut Self {
ref mut inner,
ref fuzz_rx,
ref fuzz_tx,
} = self;
let &mut Self { ref mut inner, ref fuzz_rx, ref fuzz_tx } = self;
inner.receive().map(|(rx_token, tx_token)| {
let rx = RxToken {
fuzzer: fuzz_rx,
token: rx_token,
token: rx_token,
};
let tx = TxToken {
fuzzer: fuzz_tx,
token: tx_token,
token: tx_token,
};
(rx, tx)
})
}
fn transmit(&'a mut self) -> Option<Self::TxToken> {
let &mut Self {
ref mut inner,
fuzz_rx: _,
ref fuzz_tx,
} = self;
let &mut Self { ref mut inner, fuzz_rx: _, ref fuzz_tx } = self;
inner.transmit().map(|token| TxToken {
fuzzer: fuzz_tx,
token: token,
token: token,
})
}
}
#[doc(hidden)]
pub struct RxToken<'a, Rx: phy::RxToken, F: Fuzzer + 'a> {
pub struct RxToken<'a, Rx: phy::RxToken, F: Fuzzer + 'a>{
fuzzer: &'a F,
token: Rx,
token: Rx,
}
impl<'a, Rx: phy::RxToken, FRx: Fuzzer> phy::RxToken for RxToken<'a, Rx, FRx> {
fn consume<R, F>(self, timestamp: Instant, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
where F: FnOnce(&mut [u8]) -> Result<R>
{
let Self { fuzzer, token } = self;
token.consume(timestamp, |buffer| {
@ -113,19 +98,17 @@ impl<'a, Rx: phy::RxToken, FRx: Fuzzer> phy::RxToken for RxToken<'a, Rx, FRx> {
#[doc(hidden)]
pub struct TxToken<'a, Tx: phy::TxToken, F: Fuzzer + 'a> {
fuzzer: &'a F,
token: Tx,
token: Tx,
}
impl<'a, Tx: phy::TxToken, FTx: Fuzzer> phy::TxToken for TxToken<'a, Tx, FTx> {
fn consume<R, F>(self, timestamp: Instant, len: usize, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
where F: FnOnce(&mut [u8]) -> Result<R>
{
let Self { fuzzer, token } = self;
token.consume(timestamp, len, |buf| {
let result = f(buf);
fuzzer.fuzz_packet(buf);
result
token.consume(timestamp, len, |mut buf| {
fuzzer.fuzz_packet(&mut buf);
f(buf)
})
}
}

View File

@ -1,18 +1,22 @@
#[cfg(not(feature = "rust-1_28"))]
use alloc::collections::VecDeque;
#[cfg(feature = "std")]
use std::vec::Vec;
#[cfg(feature = "std")]
use std::collections::VecDeque;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "rust-1_28")]
#[cfg(all(feature = "alloc", not(feature = "rust-1_28")))]
use alloc::collections::VecDeque;
#[cfg(all(feature = "alloc", feature = "rust-1_28"))]
use alloc::VecDeque;
use crate::phy::{self, Device, DeviceCapabilities, Medium};
use crate::time::Instant;
use crate::Result;
use crate::phy::{self, Device, DeviceCapabilities};
use crate::time::Instant;
/// A loopback device.
#[derive(Debug)]
pub struct Loopback {
queue: VecDeque<Vec<u8>>,
medium: Medium,
}
#[allow(clippy::new_without_default)]
@ -21,10 +25,9 @@ impl Loopback {
///
/// Every packet transmitted through this device will be received through it
/// in FIFO order.
pub fn new(medium: Medium) -> Loopback {
pub fn new() -> Loopback {
Loopback {
queue: VecDeque::new(),
medium,
}
}
}
@ -36,7 +39,6 @@ impl<'a> Device<'a> for Loopback {
fn capabilities(&self) -> DeviceCapabilities {
DeviceCapabilities {
max_transmission_unit: 65535,
medium: self.medium,
..DeviceCapabilities::default()
}
}
@ -44,9 +46,7 @@ impl<'a> Device<'a> for Loopback {
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
self.queue.pop_front().map(move |buffer| {
let rx = RxToken { buffer };
let tx = TxToken {
queue: &mut self.queue,
};
let tx = TxToken { queue: &mut self.queue };
(rx, tx)
})
}
@ -65,8 +65,7 @@ pub struct RxToken {
impl phy::RxToken for RxToken {
fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
where F: FnOnce(&mut [u8]) -> Result<R>
{
f(&mut self.buffer)
}
@ -79,8 +78,7 @@ pub struct TxToken<'a> {
impl<'a> phy::TxToken for TxToken<'a> {
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
where F: FnOnce(&mut [u8]) -> Result<R>
{
let mut buffer = Vec::new();
buffer.resize(len, 0);

View File

@ -8,12 +8,9 @@ and implementations of it:
* _middleware_ [Tracer](struct.Tracer.html) and
[FaultInjector](struct.FaultInjector.html), to facilitate debugging;
* _adapters_ [RawSocket](struct.RawSocket.html) and
[TunTapInterface](struct.TunTapInterface.html), to transmit and receive frames
[TapInterface](struct.TapInterface.html), to transmit and receive frames
on the host OS.
*/
#![cfg_attr(
feature = "medium-ethernet",
doc = r##"
# Examples
An implementation of the [Device](trait.Device.html) trait for a simple hardware
@ -21,7 +18,7 @@ Ethernet controller could look as follows:
```rust
use smoltcp::Result;
use smoltcp::phy::{self, DeviceCapabilities, Device, Medium};
use smoltcp::phy::{self, DeviceCapabilities, Device};
use smoltcp::time::Instant;
struct StmPhy {
@ -55,7 +52,6 @@ impl<'a> phy::Device<'a> for StmPhy {
let mut caps = DeviceCapabilities::default();
caps.max_transmission_unit = 1536;
caps.max_burst_size = Some(1);
caps.medium = Medium::Ethernet;
caps
}
}
@ -86,55 +82,45 @@ impl<'a> phy::TxToken for StmPhyTxToken<'a> {
}
}
```
"##
)]
*/
use crate::time::Instant;
use crate::Result;
use crate::time::Instant;
#[cfg(all(
any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"),
unix
))]
#[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tap_interface"), unix))]
mod sys;
mod tracer;
mod fault_injector;
mod fuzz_injector;
mod pcap_writer;
#[cfg(any(feature = "std", feature = "alloc"))]
mod loopback;
mod pcap_writer;
#[cfg(all(feature = "phy-raw_socket", unix))]
mod raw_socket;
mod tracer;
#[cfg(all(
feature = "phy-tuntap_interface",
any(target_os = "linux", target_os = "android")
))]
mod tuntap_interface;
#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))]
mod tap_interface;
#[cfg(all(
any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"),
unix
))]
#[cfg(all(any(feature = "phy-raw_socket", feature = "phy-tap_interface"), unix))]
pub use self::sys::wait;
pub use self::tracer::Tracer;
pub use self::fault_injector::FaultInjector;
pub use self::fuzz_injector::{FuzzInjector, Fuzzer};
pub use self::fuzz_injector::{Fuzzer, FuzzInjector};
pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter};
#[cfg(any(feature = "std", feature = "alloc"))]
pub use self::loopback::Loopback;
pub use self::pcap_writer::{PcapLinkType, PcapMode, PcapSink, PcapWriter};
#[cfg(all(feature = "phy-raw_socket", unix))]
pub use self::raw_socket::RawSocket;
pub use self::tracer::Tracer;
#[cfg(all(
feature = "phy-tuntap_interface",
any(target_os = "linux", target_os = "android")
))]
pub use self::tuntap_interface::TunTapInterface;
#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))]
pub use self::tap_interface::TapInterface;
#[cfg(feature = "ethernet")]
/// A tracer device for Ethernet frames.
pub type EthernetTracer<T> = Tracer<T, super::wire::EthernetFrame<&'static [u8]>>;
/// A description of checksum behavior for a particular protocol.
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Checksum {
/// Verify checksum when receiving and compute checksum when sending.
Both,
@ -157,7 +143,7 @@ impl Checksum {
pub fn rx(&self) -> bool {
match *self {
Checksum::Both | Checksum::Rx => true,
_ => false,
_ => false
}
}
@ -165,14 +151,13 @@ impl Checksum {
pub fn tx(&self) -> bool {
match *self {
Checksum::Both | Checksum::Tx => true,
_ => false,
_ => false
}
}
}
/// A description of checksum behavior for every supported protocol.
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct ChecksumCapabilities {
pub ipv4: Checksum,
@ -205,16 +190,8 @@ impl ChecksumCapabilities {
/// Higher-level protocols may achieve higher throughput or lower latency if they consider
/// the bandwidth or packet size limitations.
#[derive(Debug, Clone, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct DeviceCapabilities {
/// Medium of the device.
///
/// This indicates what kind of packet the sent/received bytes are, and determines
/// some behaviors of Interface. For example, ARP/NDISC address resolution is only done
/// for Ethernet mediums.
pub medium: Medium,
/// Maximum transmission unit.
///
/// The network device is unable to send or receive frames larger than the value returned
@ -245,68 +222,6 @@ pub struct DeviceCapabilities {
pub checksum: ChecksumCapabilities,
}
impl DeviceCapabilities {
pub fn ip_mtu(&self) -> usize {
match self.medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => {
self.max_transmission_unit - crate::wire::EthernetFrame::<&[u8]>::header_len()
}
#[cfg(feature = "medium-ip")]
Medium::Ip => self.max_transmission_unit,
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => self.max_transmission_unit, // TODO(thvdveld): what is the MTU for Medium::IEEE802
}
}
}
/// Type of medium of a device.
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Medium {
/// Ethernet medium. Devices of this type send and receive Ethernet frames,
/// and interfaces using it must do neighbor discovery via ARP or NDISC.
///
/// Examples of devices of this type are Ethernet, WiFi (802.11), Linux `tap`, and VPNs in tap (layer 2) mode.
#[cfg(feature = "medium-ethernet")]
Ethernet,
/// IP medium. Devices of this type send and receive IP frames, without an
/// Ethernet header. MAC addresses are not used, and no neighbor discovery (ARP, NDISC) is done.
///
/// Examples of devices of this type are the Linux `tun`, PPP interfaces, VPNs in tun (layer 3) mode.
#[cfg(feature = "medium-ip")]
Ip,
#[cfg(feature = "medium-ieee802154")]
Ieee802154,
}
impl Default for Medium {
fn default() -> Medium {
#[cfg(feature = "medium-ethernet")]
return Medium::Ethernet;
#[cfg(all(
feature = "medium-ip",
not(feature = "medium-ethernet"),
not(feature = "medium-ieee802154")
))]
return Medium::Ip;
#[cfg(all(
feature = "medium-ieee802154",
not(feature = "medium-ip"),
not(feature = "medium-ethernet")
))]
return Medium::Ieee802154;
#[cfg(all(
not(feature = "medium-ip"),
not(feature = "medium-ethernet"),
not(feature = "medium-ieee802154")
))]
panic!("No medium enabled");
}
}
/// An interface for sending and receiving raw network frames.
///
/// The interface is based on _tokens_, which are types that allow to receive/transmit a
@ -341,8 +256,7 @@ pub trait RxToken {
/// The timestamp must be a number of milliseconds, monotonically increasing since an
/// arbitrary moment in time, such as system startup.
fn consume<R, F>(self, timestamp: Instant, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>;
where F: FnOnce(&mut [u8]) -> Result<R>;
}
/// A token to transmit a single network packet.
@ -357,6 +271,5 @@ pub trait TxToken {
/// The timestamp must be a number of milliseconds, monotonically increasing since an
/// arbitrary moment in time, such as system startup.
fn consume<R, F>(self, timestamp: Instant, len: usize, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>;
where F: FnOnce(&mut [u8]) -> Result<R>;
}

View File

@ -1,12 +1,12 @@
use byteorder::{ByteOrder, NativeEndian};
use core::cell::RefCell;
use phy::Medium;
#[cfg(feature = "std")]
use std::cell::RefCell;
#[cfg(feature = "std")]
use std::io::Write;
use byteorder::{ByteOrder, NativeEndian};
use crate::phy::{self, Device, DeviceCapabilities};
use crate::time::Instant;
use crate::Result;
use crate::phy::{self, DeviceCapabilities, Device};
use crate::time::Instant;
enum_with_unknown! {
/// Captured packet header type.
@ -14,41 +14,35 @@ enum_with_unknown! {
/// Ethernet frames
Ethernet = 1,
/// IPv4 or IPv6 packets (depending on the version field)
Ip = 101,
/// IEEE 802.15.4 packets with FCS included.
Ieee802154WithFcs = 195,
Ip = 101
}
}
/// Packet capture mode.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PcapMode {
/// Capture both received and transmitted packets.
Both,
/// Capture only received packets.
RxOnly,
/// Capture only transmitted packets.
TxOnly,
TxOnly
}
/// A packet capture sink.
pub trait PcapSink {
/// Write data into the sink.
fn write(&mut self, data: &[u8]);
/// Flush data written into the sync.
fn flush(&mut self) {}
fn write(&self, data: &[u8]);
/// Write an `u16` into the sink, in native byte order.
fn write_u16(&mut self, value: u16) {
fn write_u16(&self, value: u16) {
let mut bytes = [0u8; 2];
NativeEndian::write_u16(&mut bytes, value);
self.write(&bytes[..])
}
/// Write an `u32` into the sink, in native byte order.
fn write_u32(&mut self, value: u32) {
fn write_u32(&self, value: u32) {
let mut bytes = [0u8; 4];
NativeEndian::write_u32(&mut bytes, value);
self.write(&bytes[..])
@ -57,13 +51,13 @@ pub trait PcapSink {
/// Write the libpcap global header into the sink.
///
/// This method may be overridden e.g. if special synchronization is necessary.
fn global_header(&mut self, link_type: PcapLinkType) {
self.write_u32(0xa1b2c3d4); // magic number
self.write_u16(2); // major version
self.write_u16(4); // minor version
self.write_u32(0); // timezone (= UTC)
self.write_u32(0); // accuracy (not used)
self.write_u32(65535); // maximum packet length
fn global_header(&self, link_type: PcapLinkType) {
self.write_u32(0xa1b2c3d4); // magic number
self.write_u16(2); // major version
self.write_u16(4); // minor version
self.write_u32(0); // timezone (= UTC)
self.write_u32(0); // accuracy (not used)
self.write_u32(65535); // maximum packet length
self.write_u32(link_type.into()); // link-layer header type
}
@ -73,33 +67,40 @@ pub trait PcapSink {
///
/// # Panics
/// This function panics if `length` is greater than 65535.
fn packet_header(&mut self, timestamp: Instant, length: usize) {
fn packet_header(&self, timestamp: Instant, length: usize) {
assert!(length <= 65535);
self.write_u32(timestamp.secs() as u32); // timestamp seconds
self.write_u32(timestamp.micros() as u32); // timestamp microseconds
self.write_u32(length as u32); // captured length
self.write_u32(length as u32); // original length
self.write_u32(timestamp.secs() as u32); // timestamp seconds
self.write_u32(timestamp.millis() as u32); // timestamp microseconds
self.write_u32(length as u32); // captured length
self.write_u32(length as u32); // original length
}
/// Write the libpcap packet header followed by packet data into the sink.
///
/// See also the note for [global_header](#method.global_header).
fn packet(&mut self, timestamp: Instant, packet: &[u8]) {
fn packet(&self, timestamp: Instant, packet: &[u8]) {
self.packet_header(timestamp, packet.len());
self.write(packet);
self.flush();
self.write(packet)
}
}
impl<T: AsRef<dyn PcapSink>> PcapSink for T {
fn write(&self, data: &[u8]) {
self.as_ref().write(data)
}
}
#[cfg(feature = "std")]
impl<T: Write> PcapSink for T {
fn write(&mut self, data: &[u8]) {
T::write_all(self, data).expect("cannot write")
impl<T: Write> PcapSink for RefCell<T> {
fn write(&self, data: &[u8]) {
self.borrow_mut().write_all(data).expect("cannot write")
}
fn flush(&mut self) {
T::flush(self).expect("cannot flush")
fn packet(&self, timestamp: Instant, packet: &[u8]) {
self.packet_header(timestamp, packet.len());
PcapSink::write(self, packet);
self.borrow_mut().flush().expect("cannot flush")
}
}
@ -117,106 +118,63 @@ impl<T: Write> PcapSink for T {
/// [sink]: trait.PcapSink.html
#[derive(Debug)]
pub struct PcapWriter<D, S>
where
D: for<'a> Device<'a>,
S: PcapSink,
where D: for<'a> Device<'a>,
S: PcapSink + Clone,
{
lower: D,
sink: RefCell<S>,
mode: PcapMode,
sink: S,
mode: PcapMode,
}
impl<D: for<'a> Device<'a>, S: PcapSink> PcapWriter<D, S> {
impl<D: for<'a> Device<'a>, S: PcapSink + Clone> PcapWriter<D, S> {
/// Creates a packet capture writer.
pub fn new(lower: D, mut sink: S, mode: PcapMode) -> PcapWriter<D, S> {
let medium = lower.capabilities().medium;
let link_type = match medium {
#[cfg(feature = "medium-ip")]
Medium::Ip => PcapLinkType::Ip,
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => PcapLinkType::Ethernet,
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => PcapLinkType::Ieee802154WithFcs,
};
pub fn new(lower: D, sink: S, mode: PcapMode, link_type: PcapLinkType) -> PcapWriter<D, S> {
sink.global_header(link_type);
PcapWriter {
lower,
sink: RefCell::new(sink),
mode,
}
}
/// Get a reference to the underlying device.
///
/// Even if the device offers reading through a standard reference, it is inadvisable to
/// directly read from the device as doing so will circumvent the packet capture.
pub fn get_ref(&self) -> &D {
&self.lower
}
/// Get a mutable reference to the underlying device.
///
/// It is inadvisable to directly read from the device as doing so will circumvent the packet capture.
pub fn get_mut(&mut self) -> &mut D {
&mut self.lower
PcapWriter { lower, sink, mode }
}
}
impl<'a, D, S> Device<'a> for PcapWriter<D, S>
where
D: for<'b> Device<'b>,
S: PcapSink + 'a,
where D: for<'b> Device<'b>,
S: PcapSink + Clone + 'a,
{
type RxToken = RxToken<'a, <D as Device<'a>>::RxToken, S>;
type TxToken = TxToken<'a, <D as Device<'a>>::TxToken, S>;
type RxToken = RxToken<<D as Device<'a>>::RxToken, S>;
type TxToken = TxToken<<D as Device<'a>>::TxToken, S>;
fn capabilities(&self) -> DeviceCapabilities {
self.lower.capabilities()
}
fn capabilities(&self) -> DeviceCapabilities { self.lower.capabilities() }
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
let sink = &self.sink;
let mode = self.mode;
self.lower.receive().map(move |(rx_token, tx_token)| {
let rx = RxToken {
token: rx_token,
sink,
mode,
};
let tx = TxToken {
token: tx_token,
sink,
mode,
};
let &mut Self { ref mut lower, ref sink, mode, .. } = self;
lower.receive().map(|(rx_token, tx_token)| {
let rx = RxToken { token: rx_token, sink: sink.clone(), mode };
let tx = TxToken { token: tx_token, sink: sink.clone(), mode };
(rx, tx)
})
}
fn transmit(&'a mut self) -> Option<Self::TxToken> {
let sink = &self.sink;
let mode = self.mode;
self.lower
.transmit()
.map(move |token| TxToken { token, sink, mode })
let &mut Self { ref mut lower, ref sink, mode } = self;
lower.transmit().map(|token| {
TxToken { token, sink: sink.clone(), mode }
})
}
}
#[doc(hidden)]
pub struct RxToken<'a, Rx: phy::RxToken, S: PcapSink> {
pub struct RxToken<Rx: phy::RxToken, S: PcapSink> {
token: Rx,
sink: &'a RefCell<S>,
mode: PcapMode,
sink: S,
mode: PcapMode,
}
impl<'a, Rx: phy::RxToken, S: PcapSink> phy::RxToken for RxToken<'a, Rx, S> {
impl<Rx: phy::RxToken, S: PcapSink> phy::RxToken for RxToken<Rx, S> {
fn consume<R, F: FnOnce(&mut [u8]) -> Result<R>>(self, timestamp: Instant, f: F) -> Result<R> {
let Self { token, sink, mode } = self;
token.consume(timestamp, |buffer| {
match mode {
PcapMode::Both | PcapMode::RxOnly => {
sink.borrow_mut().packet(timestamp, buffer.as_ref())
}
PcapMode::TxOnly => (),
PcapMode::Both | PcapMode::RxOnly =>
sink.packet(timestamp, buffer.as_ref()),
PcapMode::TxOnly => ()
}
f(buffer)
})
@ -224,23 +182,23 @@ impl<'a, Rx: phy::RxToken, S: PcapSink> phy::RxToken for RxToken<'a, Rx, S> {
}
#[doc(hidden)]
pub struct TxToken<'a, Tx: phy::TxToken, S: PcapSink> {
pub struct TxToken<Tx: phy::TxToken, S: PcapSink> {
token: Tx,
sink: &'a RefCell<S>,
mode: PcapMode,
sink: S,
mode: PcapMode
}
impl<'a, Tx: phy::TxToken, S: PcapSink> phy::TxToken for TxToken<'a, Tx, S> {
impl<Tx: phy::TxToken, S: PcapSink> phy::TxToken for TxToken<Tx, S> {
fn consume<R, F>(self, timestamp: Instant, len: usize, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
where F: FnOnce(&mut [u8]) -> Result<R>
{
let Self { token, sink, mode } = self;
token.consume(timestamp, len, |buffer| {
let result = f(buffer);
match mode {
PcapMode::Both | PcapMode::TxOnly => sink.borrow_mut().packet(timestamp, buffer),
PcapMode::RxOnly => (),
PcapMode::Both | PcapMode::TxOnly =>
sink.packet(timestamp, &buffer),
PcapMode::RxOnly => ()
};
result
})

View File

@ -1,19 +1,18 @@
use std::cell::RefCell;
use std::io;
use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::Rc;
use std::vec::Vec;
use std::rc::Rc;
use std::io;
use std::os::unix::io::{RawFd, AsRawFd};
use crate::phy::{self, sys, Device, DeviceCapabilities, Medium};
use crate::time::Instant;
use crate::Result;
use crate::phy::{self, sys, DeviceCapabilities, Device};
use crate::time::Instant;
/// A socket that captures or transmits the complete frame.
#[derive(Debug)]
pub struct RawSocket {
medium: Medium,
lower: Rc<RefCell<sys::RawSocketDesc>>,
mtu: usize,
lower: Rc<RefCell<sys::RawSocketDesc>>,
mtu: usize
}
impl AsRawFd for RawSocket {
@ -27,23 +26,13 @@ impl RawSocket {
///
/// This requires superuser privileges or a corresponding capability bit
/// set on the executable.
pub fn new(name: &str, medium: Medium) -> io::Result<RawSocket> {
let mut lower = sys::RawSocketDesc::new(name, medium)?;
pub fn new(name: &str) -> io::Result<RawSocket> {
let mut lower = sys::RawSocketDesc::new(name)?;
lower.bind_interface()?;
let mut mtu = lower.interface_mtu()?;
#[cfg(feature = "medium-ethernet")]
if medium == Medium::Ethernet {
// SIOCGIFMTU returns the IP MTU (typically 1500 bytes.)
// smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it.
mtu += crate::wire::EthernetFrame::<&[u8]>::header_len()
}
let mtu = lower.interface_mtu()?;
Ok(RawSocket {
medium,
lower: Rc::new(RefCell::new(lower)),
mtu: mtu,
mtu: mtu
})
}
}
@ -55,7 +44,6 @@ impl<'a> Device<'a> for RawSocket {
fn capabilities(&self) -> DeviceCapabilities {
DeviceCapabilities {
max_transmission_unit: self.mtu,
medium: self.medium,
..DeviceCapabilities::default()
}
}
@ -67,13 +55,13 @@ impl<'a> Device<'a> for RawSocket {
Ok(size) => {
buffer.resize(size, 0);
let rx = RxToken { buffer };
let tx = TxToken {
lower: self.lower.clone(),
};
let tx = TxToken { lower: self.lower.clone() };
Some((rx, tx))
}
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => None,
Err(err) => panic!("{}", err),
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
None
}
Err(err) => panic!("{}", err)
}
}
@ -86,13 +74,12 @@ impl<'a> Device<'a> for RawSocket {
#[doc(hidden)]
pub struct RxToken {
buffer: Vec<u8>,
buffer: Vec<u8>
}
impl phy::RxToken for RxToken {
fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
where F: FnOnce(&mut [u8]) -> Result<R>
{
f(&mut self.buffer[..])
}
@ -100,13 +87,12 @@ impl phy::RxToken for RxToken {
#[doc(hidden)]
pub struct TxToken {
lower: Rc<RefCell<sys::RawSocketDesc>>,
lower: Rc<RefCell<sys::RawSocketDesc>>,
}
impl phy::TxToken for TxToken {
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
where F: FnOnce(&mut [u8]) -> Result<R>
{
let mut lower = self.lower.borrow_mut();
let mut buffer = vec![0; len];

View File

@ -4,9 +4,8 @@ use std::os::unix::io::{AsRawFd, RawFd};
use libc;
use super::{ifreq, ifreq_for};
use crate::phy::Medium;
use crate::wire::ETHERNET_HEADER_LEN;
use super::{ifreq, ifreq_for};
/// set interface
#[cfg(any(target_os = "macos", target_os = "openbsd"))]
@ -68,7 +67,7 @@ fn open_device() -> io::Result<libc::c_int> {
}
impl BpfDevice {
pub fn new(name: &str, _medium: Medium) -> io::Result<BpfDevice> {
pub fn new(name: &str) -> io::Result<BpfDevice> {
Ok(BpfDevice {
fd: open_device()?,
ifreq: ifreq_for(name),

View File

@ -1,11 +1,14 @@
#![allow(unused)]
pub const SIOCGIFMTU: libc::c_ulong = 0x8921;
#[cfg(any(feature = "phy-raw_socket",
feature = "phy-tap_interface"))]
pub const SIOCGIFMTU: libc::c_ulong = 0x8921;
#[cfg(any(feature = "phy-raw_socket"))]
pub const SIOCGIFINDEX: libc::c_ulong = 0x8933;
pub const ETH_P_ALL: libc::c_short = 0x0003;
pub const ETH_P_IEEE802154: libc::c_short = 0x00F6;
#[cfg(any(feature = "phy-raw_socket"))]
pub const ETH_P_ALL: libc::c_short = 0x0003;
pub const TUNSETIFF: libc::c_ulong = 0x400454CA;
pub const IFF_TUN: libc::c_int = 0x0001;
pub const IFF_TAP: libc::c_int = 0x0002;
pub const IFF_NO_PI: libc::c_int = 0x1000;
#[cfg(feature = "phy-tap_interface")]
pub const TUNSETIFF: libc::c_ulong = 0x400454CA;
#[cfg(feature = "phy-tap_interface")]
pub const IFF_TAP: libc::c_int = 0x0002;
#[cfg(feature = "phy-tap_interface")]
pub const IFF_NO_PI: libc::c_int = 0x1000;

View File

@ -1,46 +1,26 @@
#![allow(unsafe_code)]
use crate::time::Duration;
use std::{mem, ptr, io};
use std::os::unix::io::RawFd;
use std::{io, mem, ptr};
use crate::time::Duration;
#[cfg(any(target_os = "linux", target_os = "android"))]
#[path = "linux.rs"]
mod imp;
#[cfg(all(
feature = "phy-raw_socket",
not(any(target_os = "linux", target_os = "android")),
unix
))]
pub mod bpf;
#[cfg(all(
feature = "phy-raw_socket",
any(target_os = "linux", target_os = "android")
))]
#[cfg(all(feature = "phy-raw_socket", any(target_os = "linux", target_os = "android")))]
pub mod raw_socket;
#[cfg(all(
feature = "phy-tuntap_interface",
any(target_os = "linux", target_os = "android")
))]
pub mod tuntap_interface;
#[cfg(all(feature = "phy-raw_socket", not(any(target_os = "linux", target_os = "android")), unix))]
pub mod bpf;
#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))]
pub mod tap_interface;
#[cfg(all(
feature = "phy-raw_socket",
not(any(target_os = "linux", target_os = "android")),
unix
))]
pub use self::bpf::BpfDevice as RawSocketDesc;
#[cfg(all(
feature = "phy-raw_socket",
any(target_os = "linux", target_os = "android")
))]
#[cfg(all(feature = "phy-raw_socket", any(target_os = "linux", target_os = "android")))]
pub use self::raw_socket::RawSocketDesc;
#[cfg(all(
feature = "phy-tuntap_interface",
any(target_os = "linux", target_os = "android")
))]
pub use self::tuntap_interface::TunTapInterfaceDesc;
#[cfg(all(feature = "phy-raw_socket", not(any(target_os = "linux", target_os = "android")), unix))]
pub use self::bpf::BpfDevice as RawSocketDesc;
#[cfg(all(feature = "phy-tap_interface", any(target_os = "linux", target_os = "android")))]
pub use self::tap_interface::TapInterfaceDesc;
/// Wait until given file descriptor becomes readable, but no longer than given timeout.
pub fn wait(fd: RawFd, duration: Option<Duration>) -> io::Result<()> {
@ -64,51 +44,35 @@ pub fn wait(fd: RawFd, duration: Option<Duration>) -> io::Result<()> {
exceptfds.assume_init()
};
let mut timeout = libc::timeval {
tv_sec: 0,
tv_usec: 0,
};
let timeout_ptr = if let Some(duration) = duration {
timeout.tv_sec = duration.secs() as libc::time_t;
timeout.tv_usec = (duration.millis() * 1_000) as libc::suseconds_t;
&mut timeout as *mut _
} else {
ptr::null_mut()
};
let mut timeout = libc::timeval { tv_sec: 0, tv_usec: 0 };
let timeout_ptr =
if let Some(duration) = duration {
timeout.tv_sec = duration.secs() as libc::time_t;
timeout.tv_usec = (duration.millis() * 1_000) as libc::suseconds_t;
&mut timeout as *mut _
} else {
ptr::null_mut()
};
let res = libc::select(
fd + 1,
&mut readfds,
&mut writefds,
&mut exceptfds,
timeout_ptr,
);
if res == -1 {
return Err(io::Error::last_os_error());
}
let res = libc::select(fd + 1, &mut readfds, &mut writefds, &mut exceptfds, timeout_ptr);
if res == -1 { return Err(io::Error::last_os_error()) }
Ok(())
}
}
#[cfg(all(
any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"),
unix
))]
#[cfg(all(any(feature = "phy-tap_interface", feature = "phy-raw_socket"), unix))]
#[repr(C)]
#[derive(Debug)]
struct ifreq {
ifr_name: [libc::c_char; libc::IF_NAMESIZE],
ifr_data: libc::c_int, /* ifr_ifindex or ifr_mtu */
ifr_data: libc::c_int /* ifr_ifindex or ifr_mtu */
}
#[cfg(all(
any(feature = "phy-tuntap_interface", feature = "phy-raw_socket"),
unix
))]
#[cfg(all(any(feature = "phy-tap_interface", feature = "phy-raw_socket"), unix))]
fn ifreq_for(name: &str) -> ifreq {
let mut ifreq = ifreq {
ifr_name: [0; libc::IF_NAMESIZE],
ifr_data: 0,
ifr_data: 0
};
for (i, byte) in name.as_bytes().iter().enumerate() {
ifreq.ifr_name[i] = *byte as libc::c_char
@ -116,20 +80,13 @@ fn ifreq_for(name: &str) -> ifreq {
ifreq
}
#[cfg(all(
any(target_os = "linux", target_os = "android"),
any(feature = "phy-tuntap_interface", feature = "phy-raw_socket")
))]
fn ifreq_ioctl(
lower: libc::c_int,
ifreq: &mut ifreq,
cmd: libc::c_ulong,
) -> io::Result<libc::c_int> {
#[cfg(all(any(target_os = "linux", target_os = "android"),
any(feature = "phy-tap_interface", feature = "phy-raw_socket")))]
fn ifreq_ioctl(lower: libc::c_int, ifreq: &mut ifreq,
cmd: libc::c_ulong) -> io::Result<libc::c_int> {
unsafe {
let res = libc::ioctl(lower, cmd as _, ifreq as *mut ifreq);
if res == -1 {
return Err(io::Error::last_os_error());
}
if res == -1 { return Err(io::Error::last_os_error()) }
}
Ok(ifreq.ifr_data)

View File

@ -1,14 +1,12 @@
use std::{mem, io};
use std::os::unix::io::{RawFd, AsRawFd};
use super::*;
use crate::phy::Medium;
use crate::wire::EthernetFrame;
use std::os::unix::io::{AsRawFd, RawFd};
use std::{io, mem};
#[derive(Debug)]
pub struct RawSocketDesc {
protocol: libc::c_short,
lower: libc::c_int,
ifreq: ifreq,
ifreq: ifreq
}
impl AsRawFd for RawSocketDesc {
@ -18,63 +16,43 @@ impl AsRawFd for RawSocketDesc {
}
impl RawSocketDesc {
pub fn new(name: &str, medium: Medium) -> io::Result<RawSocketDesc> {
let protocol = match medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => imp::ETH_P_ALL,
#[cfg(feature = "medium-ip")]
Medium::Ip => imp::ETH_P_ALL,
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => imp::ETH_P_IEEE802154,
};
pub fn new(name: &str) -> io::Result<RawSocketDesc> {
let lower = unsafe {
let lower = libc::socket(
libc::AF_PACKET,
libc::SOCK_RAW | libc::SOCK_NONBLOCK,
protocol.to_be() as i32,
);
if lower == -1 {
return Err(io::Error::last_os_error());
}
let lower = libc::socket(libc::AF_PACKET, libc::SOCK_RAW | libc::SOCK_NONBLOCK,
imp::ETH_P_ALL.to_be() as i32);
if lower == -1 { return Err(io::Error::last_os_error()) }
lower
};
Ok(RawSocketDesc {
protocol,
lower,
ifreq: ifreq_for(name),
lower: lower,
ifreq: ifreq_for(name)
})
}
pub fn interface_mtu(&mut self) -> io::Result<usize> {
// SIOCGIFMTU returns the IP MTU (typically 1500 bytes.)
// smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it.
let ip_mtu =
ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize)?;
let ip_mtu = ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize)?;
Ok(ip_mtu + EthernetFrame::<&[u8]>::header_len())
}
pub fn bind_interface(&mut self) -> io::Result<()> {
let sockaddr = libc::sockaddr_ll {
sll_family: libc::AF_PACKET as u16,
sll_protocol: self.protocol.to_be() as u16,
sll_ifindex: ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFINDEX)?,
sll_hatype: 1,
sll_pkttype: 0,
sll_halen: 6,
sll_addr: [0; 8],
sll_family: libc::AF_PACKET as u16,
sll_protocol: imp::ETH_P_ALL.to_be() as u16,
sll_ifindex: ifreq_ioctl(self.lower, &mut self.ifreq, imp::SIOCGIFINDEX)?,
sll_hatype: 1,
sll_pkttype: 0,
sll_halen: 6,
sll_addr: [0; 8]
};
unsafe {
let res = libc::bind(
self.lower,
&sockaddr as *const libc::sockaddr_ll as *const libc::sockaddr,
mem::size_of::<libc::sockaddr_ll>() as u32,
);
if res == -1 {
return Err(io::Error::last_os_error());
}
let res = libc::bind(self.lower,
&sockaddr as *const libc::sockaddr_ll as *const libc::sockaddr,
mem::size_of::<libc::sockaddr_ll>() as u32);
if res == -1 { return Err(io::Error::last_os_error()) }
}
Ok(())
@ -82,30 +60,18 @@ impl RawSocketDesc {
pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
unsafe {
let len = libc::recv(
self.lower,
buffer.as_mut_ptr() as *mut libc::c_void,
buffer.len(),
0,
);
if len == -1 {
return Err(io::Error::last_os_error());
}
let len = libc::recv(self.lower, buffer.as_mut_ptr() as *mut libc::c_void,
buffer.len(), 0);
if len == -1 { return Err(io::Error::last_os_error()) }
Ok(len as usize)
}
}
pub fn send(&mut self, buffer: &[u8]) -> io::Result<usize> {
unsafe {
let len = libc::send(
self.lower,
buffer.as_ptr() as *const libc::c_void,
buffer.len(),
0,
);
if len == -1 {
Err(io::Error::last_os_error()).unwrap()
}
let len = libc::send(self.lower, buffer.as_ptr() as *const libc::c_void,
buffer.len(), 0);
if len == -1 { Err(io::Error::last_os_error()).unwrap() }
Ok(len as usize)
}
}
@ -113,8 +79,6 @@ impl RawSocketDesc {
impl Drop for RawSocketDesc {
fn drop(&mut self) {
unsafe {
libc::close(self.lower);
}
unsafe { libc::close(self.lower); }
}
}

View File

@ -0,0 +1,77 @@
use std::io;
use std::os::unix::io::{RawFd, AsRawFd};
use super::*;
use crate::wire::EthernetFrame;
#[derive(Debug)]
pub struct TapInterfaceDesc {
lower: libc::c_int,
ifreq: ifreq
}
impl AsRawFd for TapInterfaceDesc {
fn as_raw_fd(&self) -> RawFd {
self.lower
}
}
impl TapInterfaceDesc {
pub fn new(name: &str) -> io::Result<TapInterfaceDesc> {
let lower = unsafe {
let lower = libc::open("/dev/net/tun\0".as_ptr() as *const libc::c_char,
libc::O_RDWR | libc::O_NONBLOCK);
if lower == -1 { return Err(io::Error::last_os_error()) }
lower
};
Ok(TapInterfaceDesc {
lower: lower,
ifreq: ifreq_for(name)
})
}
pub fn attach_interface(&mut self) -> io::Result<()> {
self.ifreq.ifr_data = imp::IFF_TAP | imp::IFF_NO_PI;
ifreq_ioctl(self.lower, &mut self.ifreq, imp::TUNSETIFF).map(|_| ())
}
pub fn interface_mtu(&mut self) -> io::Result<usize> {
let lower = unsafe {
let lower = libc::socket(libc::AF_INET, libc::SOCK_DGRAM, libc::IPPROTO_IP);
if lower == -1 { return Err(io::Error::last_os_error()) }
lower
};
let ip_mtu = ifreq_ioctl(lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize);
unsafe { libc::close(lower); }
// SIOCGIFMTU returns the IP MTU (typically 1500 bytes.)
// smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it.
Ok(ip_mtu? + EthernetFrame::<&[u8]>::header_len())
}
pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
unsafe {
let len = libc::read(self.lower, buffer.as_mut_ptr() as *mut libc::c_void,
buffer.len());
if len == -1 { return Err(io::Error::last_os_error()) }
Ok(len as usize)
}
}
pub fn send(&mut self, buffer: &[u8]) -> io::Result<usize> {
unsafe {
let len = libc::write(self.lower, buffer.as_ptr() as *const libc::c_void,
buffer.len());
if len == -1 { Err(io::Error::last_os_error()).unwrap() }
Ok(len as usize)
}
}
}
impl Drop for TapInterfaceDesc {
fn drop(&mut self) {
unsafe { libc::close(self.lower); }
}
}

View File

@ -1,119 +0,0 @@
use super::*;
use crate::{phy::Medium, wire::EthernetFrame};
use std::io;
use std::os::unix::io::{AsRawFd, RawFd};
#[derive(Debug)]
pub struct TunTapInterfaceDesc {
lower: libc::c_int,
ifreq: ifreq,
medium: Medium,
}
impl AsRawFd for TunTapInterfaceDesc {
fn as_raw_fd(&self) -> RawFd {
self.lower
}
}
impl TunTapInterfaceDesc {
pub fn new(name: &str, medium: Medium) -> io::Result<TunTapInterfaceDesc> {
let lower = unsafe {
let lower = libc::open(
"/dev/net/tun\0".as_ptr() as *const libc::c_char,
libc::O_RDWR | libc::O_NONBLOCK,
);
if lower == -1 {
return Err(io::Error::last_os_error());
}
lower
};
Ok(TunTapInterfaceDesc {
lower,
ifreq: ifreq_for(name),
medium,
})
}
pub fn attach_interface(&mut self) -> io::Result<()> {
let mode = match self.medium {
#[cfg(feature = "medium-ip")]
Medium::Ip => imp::IFF_TUN,
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => imp::IFF_TAP,
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => todo!(),
};
self.ifreq.ifr_data = mode | imp::IFF_NO_PI;
ifreq_ioctl(self.lower, &mut self.ifreq, imp::TUNSETIFF).map(|_| ())
}
pub fn interface_mtu(&mut self) -> io::Result<usize> {
let lower = unsafe {
let lower = libc::socket(libc::AF_INET, libc::SOCK_DGRAM, libc::IPPROTO_IP);
if lower == -1 {
return Err(io::Error::last_os_error());
}
lower
};
let ip_mtu = ifreq_ioctl(lower, &mut self.ifreq, imp::SIOCGIFMTU).map(|mtu| mtu as usize);
unsafe {
libc::close(lower);
}
// Propagate error after close, to ensure we always close.
let ip_mtu = ip_mtu?;
// SIOCGIFMTU returns the IP MTU (typically 1500 bytes.)
// smoltcp counts the entire Ethernet packet in the MTU, so add the Ethernet header size to it.
let mtu = match self.medium {
#[cfg(feature = "medium-ip")]
Medium::Ip => ip_mtu,
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => ip_mtu + EthernetFrame::<&[u8]>::header_len(),
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => todo!(),
};
Ok(mtu)
}
pub fn recv(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
unsafe {
let len = libc::read(
self.lower,
buffer.as_mut_ptr() as *mut libc::c_void,
buffer.len(),
);
if len == -1 {
return Err(io::Error::last_os_error());
}
Ok(len as usize)
}
}
pub fn send(&mut self, buffer: &[u8]) -> io::Result<usize> {
unsafe {
let len = libc::write(
self.lower,
buffer.as_ptr() as *const libc::c_void,
buffer.len(),
);
if len == -1 {
Err(io::Error::last_os_error()).unwrap()
}
Ok(len as usize)
}
}
}
impl Drop for TunTapInterfaceDesc {
fn drop(&mut self) {
unsafe {
libc::close(self.lower);
}
}
}

View File

@ -1,53 +1,50 @@
use std::cell::RefCell;
use std::io;
use std::os::unix::io::{AsRawFd, RawFd};
use std::rc::Rc;
use std::vec::Vec;
use std::rc::Rc;
use std::io;
use std::os::unix::io::{RawFd, AsRawFd};
use crate::phy::{self, sys, Device, DeviceCapabilities, Medium};
use crate::time::Instant;
use crate::Result;
use crate::phy::{self, sys, DeviceCapabilities, Device};
use crate::time::Instant;
/// A virtual TUN (IP) or TAP (Ethernet) interface.
/// A virtual Ethernet interface.
#[derive(Debug)]
pub struct TunTapInterface {
lower: Rc<RefCell<sys::TunTapInterfaceDesc>>,
mtu: usize,
medium: Medium,
pub struct TapInterface {
lower: Rc<RefCell<sys::TapInterfaceDesc>>,
mtu: usize
}
impl AsRawFd for TunTapInterface {
impl AsRawFd for TapInterface {
fn as_raw_fd(&self) -> RawFd {
self.lower.borrow().as_raw_fd()
}
}
impl TunTapInterface {
/// Attaches to a TUN/TAP interface called `name`, or creates it if it does not exist.
impl TapInterface {
/// Attaches to a TAP interface called `name`, or creates it if it does not exist.
///
/// If `name` is a persistent interface configured with UID of the current user,
/// no special privileges are needed. Otherwise, this requires superuser privileges
/// or a corresponding capability set on the executable.
pub fn new(name: &str, medium: Medium) -> io::Result<TunTapInterface> {
let mut lower = sys::TunTapInterfaceDesc::new(name, medium)?;
pub fn new(name: &str) -> io::Result<TapInterface> {
let mut lower = sys::TapInterfaceDesc::new(name)?;
lower.attach_interface()?;
let mtu = lower.interface_mtu()?;
Ok(TunTapInterface {
Ok(TapInterface {
lower: Rc::new(RefCell::new(lower)),
mtu,
medium,
mtu: mtu
})
}
}
impl<'a> Device<'a> for TunTapInterface {
impl<'a> Device<'a> for TapInterface {
type RxToken = RxToken;
type TxToken = TxToken;
fn capabilities(&self) -> DeviceCapabilities {
DeviceCapabilities {
max_transmission_unit: self.mtu,
medium: self.medium,
..DeviceCapabilities::default()
}
}
@ -59,13 +56,13 @@ impl<'a> Device<'a> for TunTapInterface {
Ok(size) => {
buffer.resize(size, 0);
let rx = RxToken { buffer };
let tx = TxToken {
lower: self.lower.clone(),
};
let tx = TxToken { lower: self.lower.clone() };
Some((rx, tx))
}
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => None,
Err(err) => panic!("{}", err),
Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => {
None
}
Err(err) => panic!("{}", err)
}
}
@ -78,13 +75,12 @@ impl<'a> Device<'a> for TunTapInterface {
#[doc(hidden)]
pub struct RxToken {
buffer: Vec<u8>,
buffer: Vec<u8>
}
impl phy::RxToken for RxToken {
fn consume<R, F>(mut self, _timestamp: Instant, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
where F: FnOnce(&mut [u8]) -> Result<R>
{
f(&mut self.buffer[..])
}
@ -92,13 +88,12 @@ impl phy::RxToken for RxToken {
#[doc(hidden)]
pub struct TxToken {
lower: Rc<RefCell<sys::TunTapInterfaceDesc>>,
lower: Rc<RefCell<sys::TapInterfaceDesc>>,
}
impl phy::TxToken for TxToken {
fn consume<R, F>(self, _timestamp: Instant, len: usize, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
where F: FnOnce(&mut [u8]) -> Result<R>
{
let mut lower = self.lower.borrow_mut();
let mut buffer = vec![0; len];

View File

@ -1,25 +1,21 @@
use core::fmt;
use crate::phy::{self, Device, DeviceCapabilities, Medium};
use crate::Result;
use crate::wire::pretty_print::{PrettyPrint, PrettyPrinter};
use crate::phy::{self, DeviceCapabilities, Device};
use crate::time::Instant;
use crate::{
wire::pretty_print::{PrettyIndent, PrettyPrint},
Result,
};
/// A tracer device.
///
/// A tracer is a device that pretty prints all packets traversing it
/// using the provided writer function, and then passes them to another
/// device.
pub struct Tracer<D: for<'a> Device<'a>> {
inner: D,
writer: fn(Instant, Packet),
pub struct Tracer<D: for<'a> Device<'a>, P: PrettyPrint> {
inner: D,
writer: fn(Instant, PrettyPrinter<P>),
}
impl<D: for<'a> Device<'a>> Tracer<D> {
impl<D: for<'a> Device<'a>, P: PrettyPrint> Tracer<D, P> {
/// Create a tracer device.
pub fn new(inner: D, writer: fn(timestamp: Instant, packet: Packet)) -> Tracer<D> {
pub fn new(inner: D, writer: fn(timestamp: Instant, printer: PrettyPrinter<P>)) -> Tracer<D, P> {
Tracer { inner, writer }
}
@ -44,154 +40,65 @@ impl<D: for<'a> Device<'a>> Tracer<D> {
}
}
impl<'a, D> Device<'a> for Tracer<D>
where
D: for<'b> Device<'b>,
impl<'a, D, P> Device<'a> for Tracer<D, P>
where D: for<'b> Device<'b>,
P: PrettyPrint + 'a,
{
type RxToken = RxToken<<D as Device<'a>>::RxToken>;
type TxToken = TxToken<<D as Device<'a>>::TxToken>;
type RxToken = RxToken<<D as Device<'a>>::RxToken, P>;
type TxToken = TxToken<<D as Device<'a>>::TxToken, P>;
fn capabilities(&self) -> DeviceCapabilities {
self.inner.capabilities()
}
fn capabilities(&self) -> DeviceCapabilities { self.inner.capabilities() }
fn receive(&'a mut self) -> Option<(Self::RxToken, Self::TxToken)> {
let &mut Self {
ref mut inner,
writer,
..
} = self;
let medium = inner.capabilities().medium;
let &mut Self { ref mut inner, writer, .. } = self;
inner.receive().map(|(rx_token, tx_token)| {
let rx = RxToken {
token: rx_token,
writer,
medium,
};
let tx = TxToken {
token: tx_token,
writer,
medium,
};
let rx = RxToken { token: rx_token, writer };
let tx = TxToken { token: tx_token, writer };
(rx, tx)
})
}
fn transmit(&'a mut self) -> Option<Self::TxToken> {
let &mut Self {
ref mut inner,
writer,
} = self;
let medium = inner.capabilities().medium;
inner.transmit().map(|tx_token| TxToken {
token: tx_token,
medium,
writer,
let &mut Self { ref mut inner, writer } = self;
inner.transmit().map(|tx_token| {
TxToken { token: tx_token, writer }
})
}
}
#[doc(hidden)]
pub struct RxToken<Rx: phy::RxToken> {
token: Rx,
writer: fn(Instant, Packet),
medium: Medium,
pub struct RxToken<Rx: phy::RxToken, P: PrettyPrint> {
token: Rx,
writer: fn(Instant, PrettyPrinter<P>)
}
impl<Rx: phy::RxToken> phy::RxToken for RxToken<Rx> {
impl<Rx: phy::RxToken, P: PrettyPrint> phy::RxToken for RxToken<Rx, P> {
fn consume<R, F>(self, timestamp: Instant, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
where F: FnOnce(&mut [u8]) -> Result<R>
{
let Self {
token,
writer,
medium,
} = self;
let Self { token, writer } = self;
token.consume(timestamp, |buffer| {
writer(
timestamp,
Packet {
buffer,
medium,
prefix: "<- ",
},
);
writer(timestamp, PrettyPrinter::<P>::new("<- ", &buffer));
f(buffer)
})
}
}
#[doc(hidden)]
pub struct TxToken<Tx: phy::TxToken> {
token: Tx,
writer: fn(Instant, Packet),
medium: Medium,
pub struct TxToken<Tx: phy::TxToken, P: PrettyPrint> {
token: Tx,
writer: fn(Instant, PrettyPrinter<P>)
}
impl<Tx: phy::TxToken> phy::TxToken for TxToken<Tx> {
impl<Tx: phy::TxToken, P: PrettyPrint> phy::TxToken for TxToken<Tx, P> {
fn consume<R, F>(self, timestamp: Instant, len: usize, f: F) -> Result<R>
where
F: FnOnce(&mut [u8]) -> Result<R>,
where F: FnOnce(&mut [u8]) -> Result<R>
{
let Self {
token,
writer,
medium,
} = self;
let Self { token, writer } = self;
token.consume(timestamp, len, |buffer| {
let result = f(buffer);
writer(
timestamp,
Packet {
buffer,
medium,
prefix: "-> ",
},
);
writer(timestamp, PrettyPrinter::<P>::new("-> ", &buffer));
result
})
}
}
pub struct Packet<'a> {
buffer: &'a [u8],
medium: Medium,
prefix: &'static str,
}
impl<'a> fmt::Display for Packet<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut indent = PrettyIndent::new(self.prefix);
match self.medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => crate::wire::EthernetFrame::<&'static [u8]>::pretty_print(
&self.buffer,
f,
&mut indent,
),
#[cfg(feature = "medium-ip")]
Medium::Ip => match crate::wire::IpVersion::of_packet(self.buffer) {
#[cfg(feature = "proto-ipv4")]
Ok(crate::wire::IpVersion::Ipv4) => {
crate::wire::Ipv4Packet::<&'static [u8]>::pretty_print(
&self.buffer,
f,
&mut indent,
)
}
#[cfg(feature = "proto-ipv6")]
Ok(crate::wire::IpVersion::Ipv6) => {
crate::wire::Ipv6Packet::<&'static [u8]>::pretty_print(
&self.buffer,
f,
&mut indent,
)
}
_ => f.write_str("unrecognized IP version"),
},
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => Ok(()), // XXX
}
}
}

View File

@ -1,67 +0,0 @@
#![allow(unsafe_code)]
#![allow(unused)]
#[cfg(not(any(test, feature = "std", feature = "rand-custom-impl")))]
compile_error!("None of the Cargo features `std` or `rand-custom-impl` is enabled. smoltcp needs a `rand` implementation to work. If your target supports `std`, enable the `std` feature to use the OS's RNG. Otherwise, you must enable the `rand-custom-impl` Cargo feature, and supply your own custom implementation using the `smoltcp::rand_custom_impl!()` macro");
pub fn rand_u32() -> u32 {
let mut val = [0; 4];
rand_bytes(&mut val);
u32::from_ne_bytes(val)
}
/// Fill `buf` with random bytes.
pub fn rand_bytes(buf: &mut [u8]) {
extern "Rust" {
fn _smoltcp_rand(buf: &mut [u8]);
}
unsafe { _smoltcp_rand(buf) }
}
/// Methods required for a custom rand implementation.
///
/// This trait is not intended to be used directly, just to supply a custom rand implementation to smoltcp.
#[cfg(feature = "rand-custom-impl")]
pub trait Rand {
/// Fill `buf` with random bytes.
fn rand_bytes(buf: &mut [u8]);
}
/// Set the custom rand implementation.
///
/// # Example
///
/// ```
/// struct Rand;
/// smoltcp::rand_custom_impl!(Rand);
/// impl smoltcp::Rand for Rand {
/// fn rand_bytes(buf: &mut [u8]) {
/// // TODO
/// }
/// }
///
#[macro_export]
#[cfg(feature = "rand-custom-impl")]
macro_rules! rand_custom_impl {
($t: ty) => {
#[no_mangle]
fn _smoltcp_rand(buf: &mut [u8]) {
<$t as $crate::Rand>::rand_bytes(buf)
}
};
}
#[cfg(all(feature = "std", not(feature = "rand-custom-impl"), not(test)))]
#[no_mangle]
fn _smoltcp_rand(buf: &mut [u8]) {
use rand_core::RngCore;
rand_core::OsRng.fill_bytes(buf)
}
#[cfg(test)]
#[no_mangle]
fn _smoltcp_rand(buf: &mut [u8]) {
panic!("Rand should not be used when testing");
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +1,12 @@
use crate::socket::{PollAt, SocketHandle};
use crate::time::{Duration, Instant};
use crate::wire::IpAddress;
use crate::socket::{SocketHandle, PollAt};
use crate::time::{Duration, Instant};
/// Neighbor dependency.
///
/// This enum tracks whether the socket should be polled based on the neighbor it is
/// going to send packets to.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum NeighborState {
/// Socket can be polled immediately.
Active,
@ -16,7 +15,7 @@ enum NeighborState {
Waiting {
neighbor: IpAddress,
silent_until: Instant,
},
}
}
impl Default for NeighborState {
@ -30,13 +29,12 @@ impl Default for NeighborState {
/// This includes things that only external (to the socket, that is) code
/// is interested in, but which are more conveniently stored inside the socket itself.
#[derive(Debug, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Meta {
/// Handle of this socket within its enclosing `SocketSet`.
/// Mainly useful for debug output.
pub(crate) handle: SocketHandle,
/// See [NeighborState](struct.NeighborState.html).
neighbor_state: NeighborState,
neighbor_state: NeighborState,
}
impl Meta {
@ -44,43 +42,36 @@ impl Meta {
/// in milliseconds.
///
/// See also `iface::NeighborCache::SILENT_TIME`.
pub(crate) const DISCOVERY_SILENT_TIME: Duration = Duration::from_millis(3_000);
pub(crate) const DISCOVERY_SILENT_TIME: Duration = Duration { millis: 3_000 };
pub(crate) fn poll_at<F>(&self, socket_poll_at: PollAt, has_neighbor: F) -> PollAt
where
F: Fn(IpAddress) -> bool,
pub(crate) fn poll_at<F>(&self, socket_poll_at: PollAt, has_neighbor: F) -> PollAt
where F: Fn(IpAddress) -> bool
{
match self.neighbor_state {
NeighborState::Active => socket_poll_at,
NeighborState::Waiting { neighbor, .. } if has_neighbor(neighbor) => socket_poll_at,
NeighborState::Waiting { silent_until, .. } => PollAt::Time(silent_until),
NeighborState::Active =>
socket_poll_at,
NeighborState::Waiting { neighbor, .. }
if has_neighbor(neighbor) =>
socket_poll_at,
NeighborState::Waiting { silent_until, .. } =>
PollAt::Time(silent_until)
}
}
pub(crate) fn egress_permitted<F>(&mut self, timestamp: Instant, has_neighbor: F) -> bool
where
F: Fn(IpAddress) -> bool,
where F: Fn(IpAddress) -> bool
{
match self.neighbor_state {
NeighborState::Active => true,
NeighborState::Waiting {
neighbor,
silent_until,
} => {
NeighborState::Active =>
true,
NeighborState::Waiting { neighbor, silent_until } => {
if has_neighbor(neighbor) {
net_trace!(
"{}: neighbor {} discovered, unsilencing",
self.handle,
neighbor
);
net_trace!("{}: neighbor {} discovered, unsilencing",
self.handle, neighbor);
self.neighbor_state = NeighborState::Active;
true
} else if timestamp >= silent_until {
net_trace!(
"{}: neighbor {} silence timer expired, rediscovering",
self.handle,
neighbor
);
net_trace!("{}: neighbor {} silence timer expired, rediscovering", self.handle, neighbor);
true
} else {
false
@ -90,15 +81,10 @@ impl Meta {
}
pub(crate) fn neighbor_missing(&mut self, timestamp: Instant, neighbor: IpAddress) {
net_trace!(
"{}: neighbor {} missing, silencing until t+{}",
self.handle,
neighbor,
Self::DISCOVERY_SILENT_TIME
);
net_trace!("{}: neighbor {} missing, silencing until t+{}",
self.handle, neighbor, Self::DISCOVERY_SILENT_TIME);
self.neighbor_state = NeighborState::Waiting {
neighbor,
silent_until: timestamp + Self::DISCOVERY_SILENT_TIME,
neighbor, silent_until: timestamp + Self::DISCOVERY_SILENT_TIME
};
}
}

View File

@ -11,24 +11,19 @@ The interface implemented by this module uses explicit buffering: you decide on
size for a buffer, allocate it, and let the networking stack use it.
*/
use crate::phy::DeviceCapabilities;
use crate::time::Instant;
#[cfg(feature = "socket-dhcpv4")]
mod dhcpv4;
#[cfg(all(
feature = "socket-icmp",
any(feature = "proto-ipv4", feature = "proto-ipv6")
))]
mod icmp;
mod meta;
#[cfg(feature = "socket-raw")]
mod raw;
mod set;
#[cfg(feature = "socket-tcp")]
mod tcp;
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
mod icmp;
#[cfg(feature = "socket-udp")]
mod udp;
#[cfg(feature = "socket-tcp")]
mod tcp;
mod set;
mod ref_;
#[cfg(feature = "async")]
mod waker;
@ -38,29 +33,34 @@ pub(crate) use self::meta::Meta as SocketMeta;
pub(crate) use self::waker::WakerRegistration;
#[cfg(feature = "socket-raw")]
pub use self::raw::{RawPacketMetadata, RawSocket, RawSocketBuffer};
pub use self::raw::{RawPacketMetadata,
RawSocketBuffer,
RawSocket};
#[cfg(all(
feature = "socket-icmp",
any(feature = "proto-ipv4", feature = "proto-ipv6")
))]
pub use self::icmp::{Endpoint as IcmpEndpoint, IcmpPacketMetadata, IcmpSocket, IcmpSocketBuffer};
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
pub use self::icmp::{IcmpPacketMetadata,
IcmpSocketBuffer,
Endpoint as IcmpEndpoint,
IcmpSocket};
#[cfg(feature = "socket-udp")]
pub use self::udp::{UdpPacketMetadata, UdpSocket, UdpSocketBuffer};
pub use self::udp::{UdpPacketMetadata,
UdpSocketBuffer,
UdpSocket};
#[cfg(feature = "socket-tcp")]
pub use self::tcp::{SocketBuffer as TcpSocketBuffer, State as TcpState, TcpSocket};
pub use self::tcp::{SocketBuffer as TcpSocketBuffer,
State as TcpState,
TcpSocket};
#[cfg(feature = "socket-dhcpv4")]
pub use self::dhcpv4::{Config as Dhcpv4Config, Dhcpv4Socket, Event as Dhcpv4Event};
pub use self::set::{Handle as SocketHandle, Item as SocketSetItem, Set as SocketSet};
pub use self::set::{Set as SocketSet, Item as SocketSetItem, Handle as SocketHandle};
pub use self::set::{Iter as SocketSetIter, IterMut as SocketSetIterMut};
pub use self::ref_::Ref as SocketRef;
pub(crate) use self::ref_::Session as SocketSession;
/// Gives an indication on the next time the socket should be polled.
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub(crate) enum PollAt {
/// The socket needs to be polled immidiately.
Now,
@ -84,25 +84,20 @@ pub(crate) enum PollAt {
pub enum Socket<'a> {
#[cfg(feature = "socket-raw")]
Raw(RawSocket<'a>),
#[cfg(all(
feature = "socket-icmp",
any(feature = "proto-ipv4", feature = "proto-ipv6")
))]
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
Icmp(IcmpSocket<'a>),
#[cfg(feature = "socket-udp")]
Udp(UdpSocket<'a>),
#[cfg(feature = "socket-tcp")]
Tcp(TcpSocket<'a>),
#[cfg(feature = "socket-dhcpv4")]
Dhcpv4(Dhcpv4Socket),
}
macro_rules! dispatch_socket {
($self_:expr, |$socket:ident| $code:expr) => {
dispatch_socket!(@inner $self_, |$socket| $code)
dispatch_socket!(@inner $self_, |$socket| $code);
};
(mut $self_:expr, |$socket:ident| $code:expr) => {
dispatch_socket!(@inner mut $self_, |$socket| $code)
dispatch_socket!(@inner mut $self_, |$socket| $code);
};
(@inner $( $mut_:ident )* $self_:expr, |$socket:ident| $code:expr) => {
match $self_ {
@ -114,8 +109,6 @@ macro_rules! dispatch_socket {
&$( $mut_ )* Socket::Udp(ref $( $mut_ )* $socket) => $code,
#[cfg(feature = "socket-tcp")]
&$( $mut_ )* Socket::Tcp(ref $( $mut_ )* $socket) => $code,
#[cfg(feature = "socket-dhcpv4")]
&$( $mut_ )* Socket::Dhcpv4(ref $( $mut_ )* $socket) => $code,
}
};
}
@ -135,91 +128,43 @@ impl<'a> Socket<'a> {
dispatch_socket!(mut self, |socket| &mut socket.meta)
}
pub(crate) fn poll_at(&self, cx: &Context) -> PollAt {
dispatch_socket!(self, |socket| socket.poll_at(cx))
pub(crate) fn poll_at(&self) -> PollAt {
dispatch_socket!(self, |socket| socket.poll_at())
}
}
impl<'a> SocketSession for Socket<'a> {
fn finish(&mut self) {
dispatch_socket!(mut self, |socket| socket.finish())
}
}
/// A conversion trait for network sockets.
pub trait AnySocket<'a>: Sized {
fn downcast<'c>(socket: &'c mut Socket<'a>) -> Option<&'c mut Self>;
pub trait AnySocket<'a>: SocketSession + Sized {
fn downcast<'c>(socket_ref: SocketRef<'c, Socket<'a>>) ->
Option<SocketRef<'c, Self>>;
}
macro_rules! from_socket {
($socket:ty, $variant:ident) => {
impl<'a> AnySocket<'a> for $socket {
fn downcast<'c>(socket: &'c mut Socket<'a>) -> Option<&'c mut Self> {
#[allow(unreachable_patterns)]
match socket {
Socket::$variant(socket) => Some(socket),
_ => None,
fn downcast<'c>(ref_: SocketRef<'c, Socket<'a>>) ->
Option<SocketRef<'c, Self>> {
if let Socket::$variant(ref mut socket) = SocketRef::into_inner(ref_) {
Some(SocketRef::new(socket))
} else {
None
}
}
}
};
}
}
#[cfg(feature = "socket-raw")]
from_socket!(RawSocket<'a>, Raw);
#[cfg(all(
feature = "socket-icmp",
any(feature = "proto-ipv4", feature = "proto-ipv6")
))]
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
from_socket!(IcmpSocket<'a>, Icmp);
#[cfg(feature = "socket-udp")]
from_socket!(UdpSocket<'a>, Udp);
#[cfg(feature = "socket-tcp")]
from_socket!(TcpSocket<'a>, Tcp);
#[cfg(feature = "socket-dhcpv4")]
from_socket!(Dhcpv4Socket, Dhcpv4);
/// Data passed to sockets when processing.
#[derive(Clone, Debug)]
pub(crate) struct Context {
pub now: Instant,
#[cfg(all(
any(feature = "medium-ethernet", feature = "medium-ieee802154"),
feature = "socket-dhcpv4"
))]
pub hardware_addr: Option<crate::wire::HardwareAddress>,
#[cfg(feature = "medium-ieee802154")]
pub pan_id: Option<crate::wire::Ieee802154Pan>,
pub caps: DeviceCapabilities,
}
#[cfg(test)]
impl Context {
pub(crate) const DUMMY: Context = Context {
caps: DeviceCapabilities {
#[cfg(feature = "medium-ethernet")]
medium: crate::phy::Medium::Ethernet,
#[cfg(not(feature = "medium-ethernet"))]
medium: crate::phy::Medium::Ip,
checksum: crate::phy::ChecksumCapabilities {
#[cfg(feature = "proto-ipv4")]
icmpv4: crate::phy::Checksum::Both,
#[cfg(feature = "proto-ipv6")]
icmpv6: crate::phy::Checksum::Both,
ipv4: crate::phy::Checksum::Both,
tcp: crate::phy::Checksum::Both,
udp: crate::phy::Checksum::Both,
},
max_burst_size: None,
#[cfg(feature = "medium-ethernet")]
max_transmission_unit: 1514,
#[cfg(not(feature = "medium-ethernet"))]
max_transmission_unit: 1500,
},
#[cfg(all(
any(feature = "medium-ethernet", feature = "medium-ieee802154"),
feature = "socket-dhcpv4"
))]
hardware_addr: Some(crate::wire::HardwareAddress::Ethernet(
crate::wire::EthernetAddress([0x02, 0x02, 0x02, 0x02, 0x02, 0x02]),
)),
now: Instant::from_millis_const(0),
#[cfg(feature = "medium-ieee802154")]
pan_id: Some(crate::wire::Ieee802154Pan(0xabcd)),
};
}

View File

@ -2,18 +2,18 @@ use core::cmp::min;
#[cfg(feature = "async")]
use core::task::Waker;
use crate::{Error, Result};
use crate::phy::ChecksumCapabilities;
use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt};
use crate::storage::{PacketBuffer, PacketMetadata};
#[cfg(feature = "async")]
use crate::socket::WakerRegistration;
use crate::socket::{Context, PollAt, Socket, SocketHandle, SocketMeta};
use crate::storage::{PacketBuffer, PacketMetadata};
use crate::{Error, Result};
use crate::wire::{IpProtocol, IpRepr, IpVersion};
use crate::wire::{IpVersion, IpRepr, IpProtocol};
#[cfg(feature = "proto-ipv4")]
use crate::wire::{Ipv4Packet, Ipv4Repr};
use crate::wire::{Ipv4Repr, Ipv4Packet};
#[cfg(feature = "proto-ipv6")]
use crate::wire::{Ipv6Packet, Ipv6Repr};
use crate::wire::{Ipv6Repr, Ipv6Packet};
/// A UDP packet metadata.
pub type RawPacketMetadata = PacketMetadata<()>;
@ -28,10 +28,10 @@ pub type RawSocketBuffer<'a> = PacketBuffer<'a, ()>;
#[derive(Debug)]
pub struct RawSocket<'a> {
pub(crate) meta: SocketMeta,
ip_version: IpVersion,
ip_version: IpVersion,
ip_protocol: IpProtocol,
rx_buffer: RawSocketBuffer<'a>,
tx_buffer: RawSocketBuffer<'a>,
rx_buffer: RawSocketBuffer<'a>,
tx_buffer: RawSocketBuffer<'a>,
#[cfg(feature = "async")]
rx_waker: WakerRegistration,
#[cfg(feature = "async")]
@ -41,12 +41,9 @@ pub struct RawSocket<'a> {
impl<'a> RawSocket<'a> {
/// Create a raw IP socket bound to the given IP version and datagram protocol,
/// with the given buffers.
pub fn new(
ip_version: IpVersion,
ip_protocol: IpProtocol,
rx_buffer: RawSocketBuffer<'a>,
tx_buffer: RawSocketBuffer<'a>,
) -> RawSocket<'a> {
pub fn new(ip_version: IpVersion, ip_protocol: IpProtocol,
rx_buffer: RawSocketBuffer<'a>,
tx_buffer: RawSocketBuffer<'a>) -> RawSocket<'a> {
RawSocket {
meta: SocketMeta::default(),
ip_version,
@ -64,12 +61,12 @@ impl<'a> RawSocket<'a> {
///
/// The waker is woken on state changes that might affect the return value
/// of `recv` method calls, such as receiving data, or the socket closing.
///
///
/// Notes:
///
/// - Only one waker can be registered at a time. If another waker was previously registered,
/// it is overwritten and will no longer be woken.
/// - The Waker is woken only once. Once woken, you must register it again to receive more wakes.
/// - The Waker is woken only once. Once woken, you must register it again to receive more wakes.
/// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `recv` has
/// necessarily changed.
#[cfg(feature = "async")]
@ -82,12 +79,12 @@ impl<'a> RawSocket<'a> {
/// The waker is woken on state changes that might affect the return value
/// of `send` method calls, such as space becoming available in the transmit
/// buffer, or the socket closing.
///
///
/// Notes:
///
/// - Only one waker can be registered at a time. If another waker was previously registered,
/// it is overwritten and will no longer be woken.
/// - The Waker is woken only once. Once woken, you must register it again to receive more wakes.
/// - The Waker is woken only once. Once woken, you must register it again to receive more wakes.
/// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `send` has
/// necessarily changed.
#[cfg(feature = "async")]
@ -163,13 +160,9 @@ impl<'a> RawSocket<'a> {
pub fn send(&mut self, size: usize) -> Result<&mut [u8]> {
let packet_buf = self.tx_buffer.enqueue(size, ())?;
net_trace!(
"{}:{}:{}: buffer to send {} octets",
self.meta.handle,
self.ip_version,
self.ip_protocol,
packet_buf.len()
);
net_trace!("{}:{}:{}: buffer to send {} octets",
self.meta.handle, self.ip_version, self.ip_protocol,
packet_buf.len());
Ok(packet_buf)
}
@ -190,13 +183,9 @@ impl<'a> RawSocket<'a> {
pub fn recv(&mut self) -> Result<&[u8]> {
let ((), packet_buf) = self.rx_buffer.dequeue()?;
net_trace!(
"{}:{}:{}: receive {} buffered octets",
self.meta.handle,
self.ip_version,
self.ip_protocol,
packet_buf.len()
);
net_trace!("{}:{}:{}: receive {} buffered octets",
self.meta.handle, self.ip_version, self.ip_protocol,
packet_buf.len());
Ok(packet_buf)
}
@ -211,32 +200,25 @@ impl<'a> RawSocket<'a> {
}
pub(crate) fn accepts(&self, ip_repr: &IpRepr) -> bool {
if ip_repr.version() != self.ip_version {
return false;
}
if ip_repr.protocol() != self.ip_protocol {
return false;
}
if ip_repr.version() != self.ip_version { return false }
if ip_repr.protocol() != self.ip_protocol { return false }
true
}
pub(crate) fn process(&mut self, cx: &Context, ip_repr: &IpRepr, payload: &[u8]) -> Result<()> {
pub(crate) fn process(&mut self, ip_repr: &IpRepr, payload: &[u8],
checksum_caps: &ChecksumCapabilities) -> Result<()> {
debug_assert!(self.accepts(ip_repr));
let header_len = ip_repr.buffer_len();
let total_len = header_len + payload.len();
let total_len = header_len + payload.len();
let packet_buf = self.rx_buffer.enqueue(total_len, ())?;
ip_repr.emit(&mut packet_buf[..header_len], &cx.caps.checksum);
ip_repr.emit(&mut packet_buf[..header_len], &checksum_caps);
packet_buf[header_len..].copy_from_slice(payload);
net_trace!(
"{}:{}:{}: receiving {} octets",
self.meta.handle,
self.ip_version,
self.ip_protocol,
packet_buf.len()
);
net_trace!("{}:{}:{}: receiving {} octets",
self.meta.handle, self.ip_version, self.ip_protocol,
packet_buf.len());
#[cfg(feature = "async")]
self.rx_waker.wake();
@ -244,22 +226,16 @@ impl<'a> RawSocket<'a> {
Ok(())
}
pub(crate) fn dispatch<F>(&mut self, cx: &Context, emit: F) -> Result<()>
where
F: FnOnce((IpRepr, &[u8])) -> Result<()>,
{
fn prepare<'a>(
protocol: IpProtocol,
buffer: &'a mut [u8],
_checksum_caps: &ChecksumCapabilities,
) -> Result<(IpRepr, &'a [u8])> {
pub(crate) fn dispatch<F>(&mut self, checksum_caps: &ChecksumCapabilities, emit: F) ->
Result<()>
where F: FnOnce((IpRepr, &[u8])) -> Result<()> {
fn prepare<'a>(protocol: IpProtocol, buffer: &'a mut [u8],
_checksum_caps: &ChecksumCapabilities) -> Result<(IpRepr, &'a [u8])> {
match IpVersion::of_packet(buffer)? {
#[cfg(feature = "proto-ipv4")]
IpVersion::Ipv4 => {
let mut packet = Ipv4Packet::new_checked(buffer)?;
if packet.protocol() != protocol {
return Err(Error::Unaddressable);
}
if packet.protocol() != protocol { return Err(Error::Unaddressable) }
if _checksum_caps.ipv4.tx() {
packet.fill_checksum();
} else {
@ -275,9 +251,7 @@ impl<'a> RawSocket<'a> {
#[cfg(feature = "proto-ipv6")]
IpVersion::Ipv6 => {
let packet = Ipv6Packet::new_checked(buffer)?;
if packet.next_header() != protocol {
return Err(Error::Unaddressable);
}
if packet.next_header() != protocol { return Err(Error::Unaddressable) }
let packet = Ipv6Packet::new_unchecked(&*packet.into_inner());
let ipv6_repr = Ipv6Repr::parse(&packet)?;
Ok((IpRepr::Ipv6(ipv6_repr), packet.payload()))
@ -286,29 +260,21 @@ impl<'a> RawSocket<'a> {
}
}
let handle = self.meta.handle;
let handle = self.meta.handle;
let ip_protocol = self.ip_protocol;
let ip_version = self.ip_version;
let ip_version = self.ip_version;
self.tx_buffer.dequeue_with(|&mut (), packet_buf| {
match prepare(ip_protocol, packet_buf, &cx.caps.checksum) {
match prepare(ip_protocol, packet_buf, &checksum_caps) {
Ok((ip_repr, raw_packet)) => {
net_trace!(
"{}:{}:{}: sending {} octets",
handle,
ip_version,
ip_protocol,
ip_repr.buffer_len() + raw_packet.len()
);
net_trace!("{}:{}:{}: sending {} octets",
handle, ip_version, ip_protocol,
ip_repr.buffer_len() + raw_packet.len());
emit((ip_repr, raw_packet))
}
Err(error) => {
net_debug!(
"{}:{}:{}: dropping outgoing packet ({})",
handle,
ip_version,
ip_protocol,
error
);
net_debug!("{}:{}:{}: dropping outgoing packet ({})",
handle, ip_version, ip_protocol,
error);
// Return Ok(()) so the packet is dequeued.
Ok(())
}
@ -321,7 +287,7 @@ impl<'a> RawSocket<'a> {
Ok(())
}
pub(crate) fn poll_at(&self, _cx: &Context) -> PollAt {
pub(crate) fn poll_at(&self) -> PollAt {
if self.tx_buffer.is_empty() {
PollAt::Ingress
} else {
@ -330,42 +296,34 @@ impl<'a> RawSocket<'a> {
}
}
impl<'a> From<RawSocket<'a>> for Socket<'a> {
fn from(val: RawSocket<'a>) -> Self {
Socket::Raw(val)
impl<'a> Into<Socket<'a>> for RawSocket<'a> {
fn into(self) -> Socket<'a> {
Socket::Raw(self)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::wire::IpRepr;
#[cfg(feature = "proto-ipv4")]
use crate::wire::{Ipv4Address, Ipv4Repr};
#[cfg(feature = "proto-ipv6")]
use crate::wire::{Ipv6Address, Ipv6Repr};
use super::*;
fn buffer(packets: usize) -> RawSocketBuffer<'static> {
RawSocketBuffer::new(
vec![RawPacketMetadata::EMPTY; packets],
vec![0; 48 * packets],
)
RawSocketBuffer::new(vec![RawPacketMetadata::EMPTY; packets], vec![0; 48 * packets])
}
#[cfg(feature = "proto-ipv4")]
mod ipv4_locals {
use super::*;
pub fn socket(
rx_buffer: RawSocketBuffer<'static>,
tx_buffer: RawSocketBuffer<'static>,
) -> RawSocket<'static> {
RawSocket::new(
IpVersion::Ipv4,
IpProtocol::Unknown(IP_PROTO),
rx_buffer,
tx_buffer,
)
pub fn socket(rx_buffer: RawSocketBuffer<'static>,
tx_buffer: RawSocketBuffer<'static>)
-> RawSocket<'static> {
RawSocket::new(IpVersion::Ipv4, IpProtocol::Unknown(IP_PROTO),
rx_buffer, tx_buffer)
}
pub const IP_PROTO: u8 = 63;
@ -374,54 +332,60 @@ mod test {
dst_addr: Ipv4Address([10, 0, 0, 2]),
protocol: IpProtocol::Unknown(IP_PROTO),
payload_len: 4,
hop_limit: 64,
hop_limit: 64
});
pub const PACKET_BYTES: [u8; 24] = [
0x45, 0x00, 0x00, 0x18, 0x00, 0x00, 0x40, 0x00, 0x40, 0x3f, 0x00, 0x00, 0x0a, 0x00,
0x00, 0x01, 0x0a, 0x00, 0x00, 0x02, 0xaa, 0x00, 0x00, 0xff,
0x45, 0x00, 0x00, 0x18,
0x00, 0x00, 0x40, 0x00,
0x40, 0x3f, 0x00, 0x00,
0x0a, 0x00, 0x00, 0x01,
0x0a, 0x00, 0x00, 0x02,
0xaa, 0x00, 0x00, 0xff
];
pub const PACKET_PAYLOAD: [u8; 4] = [
0xaa, 0x00, 0x00, 0xff
];
pub const PACKET_PAYLOAD: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
}
#[cfg(feature = "proto-ipv6")]
mod ipv6_locals {
use super::*;
pub fn socket(
rx_buffer: RawSocketBuffer<'static>,
tx_buffer: RawSocketBuffer<'static>,
) -> RawSocket<'static> {
RawSocket::new(
IpVersion::Ipv6,
IpProtocol::Unknown(IP_PROTO),
rx_buffer,
tx_buffer,
)
pub fn socket(rx_buffer: RawSocketBuffer<'static>,
tx_buffer: RawSocketBuffer<'static>)
-> RawSocket<'static> {
RawSocket::new(IpVersion::Ipv6, IpProtocol::Unknown(IP_PROTO),
rx_buffer, tx_buffer)
}
pub const IP_PROTO: u8 = 63;
pub const HEADER_REPR: IpRepr = IpRepr::Ipv6(Ipv6Repr {
src_addr: Ipv6Address([
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
]),
dst_addr: Ipv6Address([
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02,
]),
src_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]),
dst_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]),
next_header: IpProtocol::Unknown(IP_PROTO),
payload_len: 4,
hop_limit: 64,
hop_limit: 64
});
pub const PACKET_BYTES: [u8; 44] = [
0x60, 0x00, 0x00, 0x00, 0x00, 0x04, 0x3f, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xaa, 0x00,
0x00, 0xff,
0x60, 0x00, 0x00, 0x00,
0x00, 0x04, 0x3f, 0x40,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02,
0xaa, 0x00, 0x00, 0xff
];
pub const PACKET_PAYLOAD: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
pub const PACKET_PAYLOAD: [u8; 4] = [
0xaa, 0x00, 0x00, 0xff
];
}
macro_rules! reusable_ip_specific_tests {
@ -437,36 +401,29 @@ mod test {
#[test]
fn test_send_dispatch() {
let checksum_caps = &ChecksumCapabilities::default();
let mut socket = $socket(buffer(0), buffer(1));
assert!(socket.can_send());
assert_eq!(
socket.dispatch(&Context::DUMMY, |_| unreachable!()),
Err(Error::Exhausted)
);
assert_eq!(socket.dispatch(&checksum_caps, |_| unreachable!()),
Err(Error::Exhausted));
assert_eq!(socket.send_slice(&$packet[..]), Ok(()));
assert_eq!(socket.send_slice(b""), Err(Error::Exhausted));
assert!(!socket.can_send());
assert_eq!(
socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| {
assert_eq!(ip_repr, $hdr);
assert_eq!(ip_payload, &$payload);
Err(Error::Unaddressable)
}),
assert_eq!(socket.dispatch(&checksum_caps, |(ip_repr, ip_payload)| {
assert_eq!(ip_repr, $hdr);
assert_eq!(ip_payload, &$payload);
Err(Error::Unaddressable)
);
}), Err(Error::Unaddressable));
assert!(!socket.can_send());
assert_eq!(
socket.dispatch(&Context::DUMMY, |(ip_repr, ip_payload)| {
assert_eq!(ip_repr, $hdr);
assert_eq!(ip_payload, &$payload);
Ok(())
}),
assert_eq!(socket.dispatch(&checksum_caps, |(ip_repr, ip_payload)| {
assert_eq!(ip_repr, $hdr);
assert_eq!(ip_payload, &$payload);
Ok(())
);
}), Ok(()));
assert!(socket.can_send());
}
@ -475,7 +432,8 @@ mod test {
let mut socket = $socket(buffer(1), buffer(0));
assert!(socket.accepts(&$hdr));
assert_eq!(socket.process(&Context::DUMMY, &$hdr, &$payload), Ok(()));
assert_eq!(socket.process(&$hdr, &$payload,
&ChecksumCapabilities::default()), Ok(()));
let mut slice = [0; 4];
assert_eq!(socket.recv_slice(&mut slice[..]), Ok(4));
@ -490,36 +448,26 @@ mod test {
buffer[..$packet.len()].copy_from_slice(&$packet[..]);
assert!(socket.accepts(&$hdr));
assert_eq!(
socket.process(&Context::DUMMY, &$hdr, &buffer),
Err(Error::Truncated)
);
assert_eq!(socket.process(&$hdr, &buffer, &ChecksumCapabilities::default()),
Err(Error::Truncated));
}
}
};
}
}
#[cfg(feature = "proto-ipv4")]
reusable_ip_specific_tests!(
ipv4,
ipv4_locals::socket,
ipv4_locals::HEADER_REPR,
ipv4_locals::PACKET_BYTES,
ipv4_locals::PACKET_PAYLOAD
);
reusable_ip_specific_tests!(ipv4, ipv4_locals::socket, ipv4_locals::HEADER_REPR,
ipv4_locals::PACKET_BYTES, ipv4_locals::PACKET_PAYLOAD);
#[cfg(feature = "proto-ipv6")]
reusable_ip_specific_tests!(
ipv6,
ipv6_locals::socket,
ipv6_locals::HEADER_REPR,
ipv6_locals::PACKET_BYTES,
ipv6_locals::PACKET_PAYLOAD
);
reusable_ip_specific_tests!(ipv6, ipv6_locals::socket, ipv6_locals::HEADER_REPR,
ipv6_locals::PACKET_BYTES, ipv6_locals::PACKET_PAYLOAD);
#[test]
#[cfg(feature = "proto-ipv4")]
fn test_send_illegal() {
let checksum_caps = &ChecksumCapabilities::default();
#[cfg(feature = "proto-ipv4")]
{
let mut socket = ipv4_locals::socket(buffer(0), buffer(2));
@ -528,13 +476,15 @@ mod test {
Ipv4Packet::new_unchecked(&mut wrong_version).set_version(6);
assert_eq!(socket.send_slice(&wrong_version[..]), Ok(()));
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(()));
assert_eq!(socket.dispatch(&checksum_caps, |_| unreachable!()),
Ok(()));
let mut wrong_protocol = ipv4_locals::PACKET_BYTES;
Ipv4Packet::new_unchecked(&mut wrong_protocol).set_protocol(IpProtocol::Tcp);
assert_eq!(socket.send_slice(&wrong_protocol[..]), Ok(()));
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(()));
assert_eq!(socket.dispatch(&checksum_caps, |_| unreachable!()),
Ok(()));
}
#[cfg(feature = "proto-ipv6")]
{
@ -544,13 +494,15 @@ mod test {
Ipv6Packet::new_unchecked(&mut wrong_version[..]).set_version(4);
assert_eq!(socket.send_slice(&wrong_version[..]), Ok(()));
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(()));
assert_eq!(socket.dispatch(&checksum_caps, |_| unreachable!()),
Ok(()));
let mut wrong_protocol = ipv6_locals::PACKET_BYTES;
Ipv6Packet::new_unchecked(&mut wrong_protocol[..]).set_next_header(IpProtocol::Tcp);
assert_eq!(socket.send_slice(&wrong_protocol[..]), Ok(()));
assert_eq!(socket.dispatch(&Context::DUMMY, |_| unreachable!()), Ok(()));
assert_eq!(socket.dispatch(&checksum_caps, |_| unreachable!()),
Ok(()));
}
}
@ -566,25 +518,15 @@ mod test {
assert_eq!(socket.recv(), Err(Error::Exhausted));
assert!(socket.accepts(&ipv4_locals::HEADER_REPR));
assert_eq!(
socket.process(
&Context::DUMMY,
&ipv4_locals::HEADER_REPR,
&ipv4_locals::PACKET_PAYLOAD
),
Ok(())
);
assert_eq!(socket.process(&ipv4_locals::HEADER_REPR, &ipv4_locals::PACKET_PAYLOAD,
&ChecksumCapabilities::default()),
Ok(()));
assert!(socket.can_recv());
assert!(socket.accepts(&ipv4_locals::HEADER_REPR));
assert_eq!(
socket.process(
&Context::DUMMY,
&ipv4_locals::HEADER_REPR,
&ipv4_locals::PACKET_PAYLOAD
),
Err(Error::Exhausted)
);
assert_eq!(socket.process(&ipv4_locals::HEADER_REPR, &ipv4_locals::PACKET_PAYLOAD,
&ChecksumCapabilities::default()),
Err(Error::Exhausted));
assert_eq!(socket.recv(), Ok(&cksumd_packet[..]));
assert!(!socket.can_recv());
}
@ -595,25 +537,15 @@ mod test {
assert_eq!(socket.recv(), Err(Error::Exhausted));
assert!(socket.accepts(&ipv6_locals::HEADER_REPR));
assert_eq!(
socket.process(
&Context::DUMMY,
&ipv6_locals::HEADER_REPR,
&ipv6_locals::PACKET_PAYLOAD
),
Ok(())
);
assert_eq!(socket.process(&ipv6_locals::HEADER_REPR, &ipv6_locals::PACKET_PAYLOAD,
&ChecksumCapabilities::default()),
Ok(()));
assert!(socket.can_recv());
assert!(socket.accepts(&ipv6_locals::HEADER_REPR));
assert_eq!(
socket.process(
&Context::DUMMY,
&ipv6_locals::HEADER_REPR,
&ipv6_locals::PACKET_PAYLOAD
),
Err(Error::Exhausted)
);
assert_eq!(socket.process(&ipv6_locals::HEADER_REPR, &ipv6_locals::PACKET_PAYLOAD,
&ChecksumCapabilities::default()),
Err(Error::Exhausted));
assert_eq!(socket.recv(), Ok(&ipv6_locals::PACKET_BYTES[..]));
assert!(!socket.can_recv());
}
@ -623,24 +555,16 @@ mod test {
fn test_doesnt_accept_wrong_proto() {
#[cfg(feature = "proto-ipv4")]
{
let socket = RawSocket::new(
IpVersion::Ipv4,
IpProtocol::Unknown(ipv4_locals::IP_PROTO + 1),
buffer(1),
buffer(1),
);
let socket = RawSocket::new(IpVersion::Ipv4,
IpProtocol::Unknown(ipv4_locals::IP_PROTO+1), buffer(1), buffer(1));
assert!(!socket.accepts(&ipv4_locals::HEADER_REPR));
#[cfg(feature = "proto-ipv6")]
assert!(!socket.accepts(&ipv6_locals::HEADER_REPR));
}
#[cfg(feature = "proto-ipv6")]
{
let socket = RawSocket::new(
IpVersion::Ipv6,
IpProtocol::Unknown(ipv6_locals::IP_PROTO + 1),
buffer(1),
buffer(1),
);
let socket = RawSocket::new(IpVersion::Ipv6,
IpProtocol::Unknown(ipv6_locals::IP_PROTO+1), buffer(1), buffer(1));
assert!(!socket.accepts(&ipv6_locals::HEADER_REPR));
#[cfg(feature = "proto-ipv4")]
assert!(!socket.accepts(&ipv4_locals::HEADER_REPR));

89
src/socket/ref_.rs Normal file
View File

@ -0,0 +1,89 @@
use core::ops::{Deref, DerefMut};
#[cfg(feature = "socket-raw")]
use crate::socket::RawSocket;
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
use crate::socket::IcmpSocket;
#[cfg(feature = "socket-udp")]
use crate::socket::UdpSocket;
#[cfg(feature = "socket-tcp")]
use crate::socket::TcpSocket;
/// A trait for tracking a socket usage session.
///
/// Allows implementation of custom drop logic that runs only if the socket was changed
/// in specific ways. For example, drop logic for UDP would check if the local endpoint
/// has changed, and if yes, notify the socket set.
#[doc(hidden)]
pub trait Session {
fn finish(&mut self) {}
}
#[cfg(feature = "socket-raw")]
impl<'a> Session for RawSocket<'a> {}
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
impl<'a> Session for IcmpSocket<'a> {}
#[cfg(feature = "socket-udp")]
impl<'a> Session for UdpSocket<'a> {}
#[cfg(feature = "socket-tcp")]
impl<'a> Session for TcpSocket<'a> {}
/// A smart pointer to a socket.
///
/// Allows the network stack to efficiently determine if the socket state was changed in any way.
pub struct Ref<'a, T: Session + 'a> {
/// Reference to the socket.
///
/// This is almost always `Some` except when dropped in `into_inner` which removes the socket
/// reference. This properly tracks the initialization state without any additional bytes as
/// the `None` variant occupies the `0` pattern which is invalid for the reference.
socket: Option<&'a mut T>,
}
impl<'a, T: Session + 'a> Ref<'a, T> {
/// Wrap a pointer to a socket to make a smart pointer.
///
/// Calling this function is only necessary if your code is using [into_inner].
///
/// [into_inner]: #method.into_inner
pub fn new(socket: &'a mut T) -> Self {
Ref { socket: Some(socket) }
}
/// Unwrap a smart pointer to a socket.
///
/// The finalization code is not run. Prompt operation of the network stack depends
/// on wrapping the returned pointer back and dropping it.
///
/// Calling this function is only necessary to achieve composability if you *must*
/// map a `&mut SocketRef<'a, XSocket>` to a `&'a mut XSocket` (note the lifetimes);
/// be sure to call [new] afterwards.
///
/// [new]: #method.new
pub fn into_inner(mut ref_: Self) -> &'a mut T {
ref_.socket.take().unwrap()
}
}
impl<'a, T: Session> Deref for Ref<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
// Deref is only used while the socket is still in place (into inner has not been called).
self.socket.as_ref().unwrap()
}
}
impl<'a, T: Session> DerefMut for Ref<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.socket.as_mut().unwrap()
}
}
impl<'a, T: Session> Drop for Ref<'a, T> {
fn drop(&mut self) {
if let Some(socket) = self.socket.take() {
Session::finish(socket);
}
}
}

View File

@ -1,7 +1,9 @@
use core::{fmt, slice};
use managed::ManagedSlice;
use crate::socket::{AnySocket, Socket};
use crate::socket::{Socket, SocketRef, AnySocket};
#[cfg(feature = "socket-tcp")]
use crate::socket::TcpState;
/// An item of a socket set.
///
@ -10,11 +12,11 @@ use crate::socket::{AnySocket, Socket};
#[derive(Debug)]
pub struct Item<'a> {
socket: Socket<'a>,
refs: usize
}
/// A handle, identifying a socket in a set.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Handle(usize);
impl fmt::Display for Handle {
@ -28,32 +30,30 @@ impl fmt::Display for Handle {
/// The lifetime `'a` is used when storing a `Socket<'a>`.
#[derive(Debug)]
pub struct Set<'a> {
sockets: ManagedSlice<'a, Option<Item<'a>>>,
sockets: ManagedSlice<'a, Option<Item<'a>>>
}
impl<'a> Set<'a> {
/// Create a socket set using the provided storage.
pub fn new<SocketsT>(sockets: SocketsT) -> Set<'a>
where
SocketsT: Into<ManagedSlice<'a, Option<Item<'a>>>>,
{
where SocketsT: Into<ManagedSlice<'a, Option<Item<'a>>>> {
let sockets = sockets.into();
Set { sockets }
}
/// Add a socket to the set, and return its handle.
/// Add a socket to the set with the reference count 1, and return its handle.
///
/// # Panics
/// This function panics if the storage is fixed-size (not a `Vec`) and is full.
pub fn add<T>(&mut self, socket: T) -> Handle
where
T: Into<Socket<'a>>,
where T: Into<Socket<'a>>
{
fn put<'a>(index: usize, slot: &mut Option<Item<'a>>, mut socket: Socket<'a>) -> Handle {
fn put<'a>(index: usize, slot: &mut Option<Item<'a>>,
mut socket: Socket<'a>) -> Handle {
net_trace!("[{}]: adding", index);
let handle = Handle(index);
socket.meta_mut().handle = handle;
*slot = Some(Item { socket });
*slot = Some(Item { socket, refs: 1 });
handle
}
@ -61,12 +61,14 @@ impl<'a> Set<'a> {
for (index, slot) in self.sockets.iter_mut().enumerate() {
if slot.is_none() {
return put(index, slot, socket);
return put(index, slot, socket)
}
}
match self.sockets {
ManagedSlice::Borrowed(_) => panic!("adding a socket to a full SocketSet"),
ManagedSlice::Borrowed(_) => {
panic!("adding a socket to a full SocketSet")
}
#[cfg(any(feature = "std", feature = "alloc"))]
ManagedSlice::Owned(ref mut sockets) => {
sockets.push(None);
@ -81,12 +83,13 @@ impl<'a> Set<'a> {
/// # Panics
/// This function may panic if the handle does not belong to this socket set
/// or the socket has the wrong type.
pub fn get<T: AnySocket<'a>>(&mut self, handle: Handle) -> &mut T {
pub fn get<T: AnySocket<'a>>(&mut self, handle: Handle) -> SocketRef<T> {
match self.sockets[handle.0].as_mut() {
Some(item) => {
T::downcast(&mut item.socket).expect("handle refers to a socket of a wrong type")
T::downcast(SocketRef::new(&mut item.socket))
.expect("handle refers to a socket of a wrong type")
}
None => panic!("handle does not refer to a valid socket"),
None => panic!("handle does not refer to a valid socket")
}
}
@ -98,22 +101,77 @@ impl<'a> Set<'a> {
net_trace!("[{}]: removing", handle.0);
match self.sockets[handle.0].take() {
Some(item) => item.socket,
None => panic!("handle does not refer to a valid socket"),
None => panic!("handle does not refer to a valid socket")
}
}
/// Increase reference count by 1.
///
/// # Panics
/// This function may panic if the handle does not belong to this socket set.
pub fn retain(&mut self, handle: Handle) {
self.sockets[handle.0]
.as_mut()
.expect("handle does not refer to a valid socket")
.refs += 1
}
/// Decrease reference count by 1.
///
/// # Panics
/// This function may panic if the handle does not belong to this socket set,
/// or if the reference count is already zero.
pub fn release(&mut self, handle: Handle) {
let refs = &mut self.sockets[handle.0]
.as_mut()
.expect("handle does not refer to a valid socket")
.refs;
if *refs == 0 { panic!("decreasing reference count past zero") }
*refs -= 1
}
/// Prune the sockets in this set.
///
/// Pruning affects sockets with reference count 0. Open sockets are closed.
/// Closed sockets are removed and dropped.
pub fn prune(&mut self) {
for (index, item) in self.sockets.iter_mut().enumerate() {
let mut may_remove = false;
if let Some(Item { refs: 0, ref mut socket }) = *item {
match *socket {
#[cfg(feature = "socket-raw")]
Socket::Raw(_) =>
may_remove = true,
#[cfg(all(feature = "socket-icmp", any(feature = "proto-ipv4", feature = "proto-ipv6")))]
Socket::Icmp(_) =>
may_remove = true,
#[cfg(feature = "socket-udp")]
Socket::Udp(_) =>
may_remove = true,
#[cfg(feature = "socket-tcp")]
Socket::Tcp(ref mut socket) =>
if socket.state() == TcpState::Closed {
may_remove = true
} else {
socket.close()
},
}
}
if may_remove {
net_trace!("[{}]: pruning", index);
*item = None
}
}
}
/// Iterate every socket in this set.
pub fn iter<'d>(&'d self) -> Iter<'d, 'a> {
Iter {
lower: self.sockets.iter(),
}
Iter { lower: self.sockets.iter() }
}
/// Iterate every socket in this set.
/// Iterate every socket in this set, as SocketRef.
pub fn iter_mut<'d>(&'d mut self) -> IterMut<'d, 'a> {
IterMut {
lower: self.sockets.iter_mut(),
}
IterMut { lower: self.sockets.iter_mut() }
}
}
@ -122,16 +180,16 @@ impl<'a> Set<'a> {
/// This struct is created by the [iter](struct.SocketSet.html#method.iter)
/// on [socket sets](struct.SocketSet.html).
pub struct Iter<'a, 'b: 'a> {
lower: slice::Iter<'a, Option<Item<'b>>>,
lower: slice::Iter<'a, Option<Item<'b>>>
}
impl<'a, 'b: 'a> Iterator for Iter<'a, 'b> {
type Item = &'a Socket<'b>;
fn next(&mut self) -> Option<Self::Item> {
for item_opt in &mut self.lower {
while let Some(item_opt) = self.lower.next() {
if let Some(item) = item_opt.as_ref() {
return Some(&item.socket);
return Some(&item.socket)
}
}
None
@ -147,12 +205,12 @@ pub struct IterMut<'a, 'b: 'a> {
}
impl<'a, 'b: 'a> Iterator for IterMut<'a, 'b> {
type Item = &'a mut Socket<'b>;
type Item = SocketRef<'a, Socket<'b>>;
fn next(&mut self) -> Option<Self::Item> {
for item_opt in &mut self.lower {
while let Some(item_opt) = self.lower.next() {
if let Some(item) = item_opt.as_mut() {
return Some(&mut item.socket);
return Some(SocketRef::new(&mut item.socket))
}
}
None

File diff suppressed because it is too large Load Diff

View File

@ -2,12 +2,12 @@ use core::cmp::min;
#[cfg(feature = "async")]
use core::task::Waker;
use crate::{Error, Result};
use crate::socket::{Socket, SocketMeta, SocketHandle, PollAt};
use crate::storage::{PacketBuffer, PacketMetadata};
use crate::wire::{IpProtocol, IpRepr, IpEndpoint, UdpRepr};
#[cfg(feature = "async")]
use crate::socket::WakerRegistration;
use crate::socket::{Context, PollAt, Socket, SocketHandle, SocketMeta};
use crate::storage::{PacketBuffer, PacketMetadata};
use crate::wire::{IpEndpoint, IpProtocol, IpRepr, UdpRepr};
use crate::{Error, Result};
/// A UDP packet metadata.
pub type UdpPacketMetadata = PacketMetadata<IpEndpoint>;
@ -22,7 +22,7 @@ pub type UdpSocketBuffer<'a> = PacketBuffer<'a, IpEndpoint>;
#[derive(Debug)]
pub struct UdpSocket<'a> {
pub(crate) meta: SocketMeta,
endpoint: IpEndpoint,
endpoint: IpEndpoint,
rx_buffer: UdpSocketBuffer<'a>,
tx_buffer: UdpSocketBuffer<'a>,
/// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
@ -35,10 +35,11 @@ pub struct UdpSocket<'a> {
impl<'a> UdpSocket<'a> {
/// Create an UDP socket with the given buffers.
pub fn new(rx_buffer: UdpSocketBuffer<'a>, tx_buffer: UdpSocketBuffer<'a>) -> UdpSocket<'a> {
pub fn new(rx_buffer: UdpSocketBuffer<'a>,
tx_buffer: UdpSocketBuffer<'a>) -> UdpSocket<'a> {
UdpSocket {
meta: SocketMeta::default(),
endpoint: IpEndpoint::default(),
meta: SocketMeta::default(),
endpoint: IpEndpoint::default(),
rx_buffer: rx_buffer,
tx_buffer: tx_buffer,
hop_limit: None,
@ -53,12 +54,12 @@ impl<'a> UdpSocket<'a> {
///
/// The waker is woken on state changes that might affect the return value
/// of `recv` method calls, such as receiving data, or the socket closing.
///
///
/// Notes:
///
/// - Only one waker can be registered at a time. If another waker was previously registered,
/// it is overwritten and will no longer be woken.
/// - The Waker is woken only once. Once woken, you must register it again to receive more wakes.
/// - The Waker is woken only once. Once woken, you must register it again to receive more wakes.
/// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `recv` has
/// necessarily changed.
#[cfg(feature = "async")]
@ -71,12 +72,12 @@ impl<'a> UdpSocket<'a> {
/// The waker is woken on state changes that might affect the return value
/// of `send` method calls, such as space becoming available in the transmit
/// buffer, or the socket closing.
///
///
/// Notes:
///
/// - Only one waker can be registered at a time. If another waker was previously registered,
/// it is overwritten and will no longer be woken.
/// - The Waker is woken only once. Once woken, you must register it again to receive more wakes.
/// - The Waker is woken only once. Once woken, you must register it again to receive more wakes.
/// - "Spurious wakes" are allowed: a wake doesn't guarantee the result of `send` has
/// necessarily changed.
#[cfg(feature = "async")]
@ -130,13 +131,9 @@ impl<'a> UdpSocket<'a> {
/// if the port in the given endpoint is zero.
pub fn bind<T: Into<IpEndpoint>>(&mut self, endpoint: T) -> Result<()> {
let endpoint = endpoint.into();
if endpoint.port == 0 {
return Err(Error::Unaddressable);
}
if endpoint.port == 0 { return Err(Error::Unaddressable) }
if self.is_open() {
return Err(Error::Illegal);
}
if self.is_open() { return Err(Error::Illegal) }
self.endpoint = endpoint;
@ -149,22 +146,6 @@ impl<'a> UdpSocket<'a> {
Ok(())
}
/// Close the socket.
pub fn close(&mut self) {
// Clear the bound endpoint of the socket.
self.endpoint = IpEndpoint::default();
// Reset the RX and TX buffers of the socket.
self.tx_buffer.reset();
self.rx_buffer.reset();
#[cfg(feature = "async")]
{
self.rx_waker.wake();
self.tx_waker.wake();
}
}
/// Check whether the socket is open.
#[inline]
pub fn is_open(&self) -> bool {
@ -215,22 +196,13 @@ impl<'a> UdpSocket<'a> {
/// and `Err(Error::Truncated)` if there is not enough transmit buffer capacity
/// to ever send this packet.
pub fn send(&mut self, size: usize, endpoint: IpEndpoint) -> Result<&mut [u8]> {
if self.endpoint.port == 0 {
return Err(Error::Unaddressable);
}
if !endpoint.is_specified() {
return Err(Error::Unaddressable);
}
if self.endpoint.port == 0 { return Err(Error::Unaddressable) }
if !endpoint.is_specified() { return Err(Error::Unaddressable) }
let payload_buf = self.tx_buffer.enqueue(size, endpoint)?;
net_trace!(
"{}:{}:{}: buffer to send {} octets",
self.meta.handle,
self.endpoint,
endpoint,
size
);
net_trace!("{}:{}:{}: buffer to send {} octets",
self.meta.handle, self.endpoint, endpoint, size);
Ok(payload_buf)
}
@ -249,13 +221,9 @@ impl<'a> UdpSocket<'a> {
pub fn recv(&mut self) -> Result<(&[u8], IpEndpoint)> {
let (endpoint, payload_buf) = self.rx_buffer.dequeue()?;
net_trace!(
"{}:{}:{}: receive {} buffered octets",
self.meta.handle,
self.endpoint,
endpoint,
payload_buf.len()
);
net_trace!("{}:{}:{}: receive {} buffered octets",
self.meta.handle, self.endpoint,
endpoint, payload_buf.len());
Ok((payload_buf, endpoint))
}
@ -279,14 +247,10 @@ impl<'a> UdpSocket<'a> {
let handle = self.meta.handle;
let endpoint = self.endpoint;
self.rx_buffer.peek().map(|(remote_endpoint, payload_buf)| {
net_trace!(
"{}:{}:{}: peek {} buffered octets",
handle,
endpoint,
remote_endpoint,
payload_buf.len()
);
(payload_buf, remote_endpoint)
net_trace!("{}:{}:{}: peek {} buffered octets",
handle, endpoint,
remote_endpoint, payload_buf.len());
(payload_buf, remote_endpoint)
})
}
@ -304,46 +268,26 @@ impl<'a> UdpSocket<'a> {
}
pub(crate) fn accepts(&self, ip_repr: &IpRepr, repr: &UdpRepr) -> bool {
if self.endpoint.port != repr.dst_port {
return false;
}
if !self.endpoint.addr.is_unspecified()
&& self.endpoint.addr != ip_repr.dst_addr()
&& !ip_repr.dst_addr().is_broadcast()
&& !ip_repr.dst_addr().is_multicast()
{
return false;
}
if self.endpoint.port != repr.dst_port { return false }
if !self.endpoint.addr.is_unspecified() &&
self.endpoint.addr != ip_repr.dst_addr() &&
!ip_repr.dst_addr().is_broadcast() &&
!ip_repr.dst_addr().is_multicast() { return false }
true
}
pub(crate) fn process(
&mut self,
_cx: &Context,
ip_repr: &IpRepr,
repr: &UdpRepr,
payload: &[u8],
) -> Result<()> {
pub(crate) fn process(&mut self, ip_repr: &IpRepr, repr: &UdpRepr) -> Result<()> {
debug_assert!(self.accepts(ip_repr, repr));
let size = payload.len();
let size = repr.payload.len();
let endpoint = IpEndpoint {
addr: ip_repr.src_addr(),
port: repr.src_port,
};
self.rx_buffer
.enqueue(size, endpoint)?
.copy_from_slice(payload);
let endpoint = IpEndpoint { addr: ip_repr.src_addr(), port: repr.src_port };
self.rx_buffer.enqueue(size, endpoint)?.copy_from_slice(repr.payload);
net_trace!(
"{}:{}:{}: receiving {} octets",
self.meta.handle,
self.endpoint,
endpoint,
size
);
net_trace!("{}:{}:{}: receiving {} octets",
self.meta.handle, self.endpoint,
endpoint, size);
#[cfg(feature = "async")]
self.rx_waker.wake();
@ -351,37 +295,31 @@ impl<'a> UdpSocket<'a> {
Ok(())
}
pub(crate) fn dispatch<F>(&mut self, _cx: &Context, emit: F) -> Result<()>
where
F: FnOnce((IpRepr, UdpRepr, &[u8])) -> Result<()>,
{
let handle = self.handle();
let endpoint = self.endpoint;
pub(crate) fn dispatch<F>(&mut self, emit: F) -> Result<()>
where F: FnOnce((IpRepr, UdpRepr)) -> Result<()> {
let handle = self.handle();
let endpoint = self.endpoint;
let hop_limit = self.hop_limit.unwrap_or(64);
self.tx_buffer
.dequeue_with(|remote_endpoint, payload_buf| {
net_trace!(
"{}:{}:{}: sending {} octets",
handle,
endpoint,
endpoint,
payload_buf.len()
);
self.tx_buffer.dequeue_with(|remote_endpoint, payload_buf| {
net_trace!("{}:{}:{}: sending {} octets",
handle, endpoint,
endpoint, payload_buf.len());
let repr = UdpRepr {
src_port: endpoint.port,
dst_port: remote_endpoint.port,
};
let ip_repr = IpRepr::Unspecified {
src_addr: endpoint.addr,
dst_addr: remote_endpoint.addr,
protocol: IpProtocol::Udp,
payload_len: repr.header_len() + payload_buf.len(),
hop_limit: hop_limit,
};
emit((ip_repr, repr, payload_buf))
})?;
let repr = UdpRepr {
src_port: endpoint.port,
dst_port: remote_endpoint.port,
payload: payload_buf,
};
let ip_repr = IpRepr::Unspecified {
src_addr: endpoint.addr,
dst_addr: remote_endpoint.addr,
protocol: IpProtocol::Udp,
payload_len: repr.buffer_len(),
hop_limit: hop_limit,
};
emit((ip_repr, repr))
})?;
#[cfg(feature = "async")]
self.tx_waker.wake();
@ -389,7 +327,7 @@ impl<'a> UdpSocket<'a> {
Ok(())
}
pub(crate) fn poll_at(&self, _cx: &Context) -> PollAt {
pub(crate) fn poll_at(&self) -> PollAt {
if self.tx_buffer.is_empty() {
PollAt::Ingress
} else {
@ -398,47 +336,37 @@ impl<'a> UdpSocket<'a> {
}
}
impl<'a> From<UdpSocket<'a>> for Socket<'a> {
fn from(val: UdpSocket<'a>) -> Self {
Socket::Udp(val)
impl<'a> Into<Socket<'a>> for UdpSocket<'a> {
fn into(self) -> Socket<'a> {
Socket::Udp(self)
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3};
use crate::wire::{IpAddress, IpRepr, UdpRepr};
#[cfg(feature = "proto-ipv4")]
use crate::wire::Ipv4Repr;
#[cfg(feature = "proto-ipv6")]
use crate::wire::Ipv6Repr;
use crate::wire::{IpAddress, IpRepr, UdpRepr};
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2, MOCK_IP_ADDR_3};
use super::*;
fn buffer(packets: usize) -> UdpSocketBuffer<'static> {
UdpSocketBuffer::new(
vec![UdpPacketMetadata::EMPTY; packets],
vec![0; 16 * packets],
)
UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY; packets], vec![0; 16 * packets])
}
fn socket(
rx_buffer: UdpSocketBuffer<'static>,
tx_buffer: UdpSocketBuffer<'static>,
) -> UdpSocket<'static> {
fn socket(rx_buffer: UdpSocketBuffer<'static>,
tx_buffer: UdpSocketBuffer<'static>)
-> UdpSocket<'static> {
UdpSocket::new(rx_buffer, tx_buffer)
}
const LOCAL_PORT: u16 = 53;
const REMOTE_PORT: u16 = 49500;
const LOCAL_PORT: u16 = 53;
const REMOTE_PORT: u16 = 49500;
pub const LOCAL_END: IpEndpoint = IpEndpoint {
addr: MOCK_IP_ADDR_1,
port: LOCAL_PORT,
};
pub const REMOTE_END: IpEndpoint = IpEndpoint {
addr: MOCK_IP_ADDR_2,
port: REMOTE_PORT,
};
pub const LOCAL_END: IpEndpoint = IpEndpoint { addr: MOCK_IP_ADDR_1, port: LOCAL_PORT };
pub const REMOTE_END: IpEndpoint = IpEndpoint { addr: MOCK_IP_ADDR_2, port: REMOTE_PORT };
pub const LOCAL_IP_REPR: IpRepr = IpRepr::Unspecified {
src_addr: MOCK_IP_ADDR_1,
@ -451,15 +379,15 @@ mod test {
const LOCAL_UDP_REPR: UdpRepr = UdpRepr {
src_port: LOCAL_PORT,
dst_port: REMOTE_PORT,
payload: b"abcdef"
};
const REMOTE_UDP_REPR: UdpRepr = UdpRepr {
src_port: REMOTE_PORT,
dst_port: LOCAL_PORT,
payload: b"abcdef"
};
const PAYLOAD: &[u8] = b"abcdef";
fn remote_ip_repr() -> IpRepr {
match (MOCK_IP_ADDR_2, MOCK_IP_ADDR_1) {
#[cfg(feature = "proto-ipv4")]
@ -468,7 +396,7 @@ mod test {
dst_addr: dst,
protocol: IpProtocol::Udp,
payload_len: 8 + 6,
hop_limit: 64,
hop_limit: 64
}),
#[cfg(feature = "proto-ipv6")]
(IpAddress::Ipv6(src), IpAddress::Ipv6(dst)) => IpRepr::Ipv6(Ipv6Repr {
@ -476,9 +404,9 @@ mod test {
dst_addr: dst,
next_header: IpProtocol::Udp,
payload_len: 8 + 6,
hop_limit: 64,
hop_limit: 64
}),
_ => unreachable!(),
_ => unreachable!()
}
}
@ -505,31 +433,14 @@ mod test {
#[test]
fn test_send_unaddressable() {
let mut socket = socket(buffer(0), buffer(1));
assert_eq!(
socket.send_slice(b"abcdef", REMOTE_END),
Err(Error::Unaddressable)
);
assert_eq!(socket.send_slice(b"abcdef", REMOTE_END), Err(Error::Unaddressable));
assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
assert_eq!(
socket.send_slice(
b"abcdef",
IpEndpoint {
addr: IpAddress::Unspecified,
..REMOTE_END
}
),
Err(Error::Unaddressable)
);
assert_eq!(
socket.send_slice(
b"abcdef",
IpEndpoint {
port: 0,
..REMOTE_END
}
),
Err(Error::Unaddressable)
);
assert_eq!(socket.send_slice(b"abcdef",
IpEndpoint { addr: IpAddress::Unspecified, ..REMOTE_END }),
Err(Error::Unaddressable));
assert_eq!(socket.send_slice(b"abcdef",
IpEndpoint { port: 0, ..REMOTE_END }),
Err(Error::Unaddressable));
assert_eq!(socket.send_slice(b"abcdef", REMOTE_END), Ok(()));
}
@ -539,38 +450,25 @@ mod test {
assert_eq!(socket.bind(LOCAL_END), Ok(()));
assert!(socket.can_send());
assert_eq!(
socket.dispatch(&Context::DUMMY, |_| unreachable!()),
Err(Error::Exhausted)
);
assert_eq!(socket.dispatch(|_| unreachable!()),
Err(Error::Exhausted));
assert_eq!(socket.send_slice(b"abcdef", REMOTE_END), Ok(()));
assert_eq!(
socket.send_slice(b"123456", REMOTE_END),
Err(Error::Exhausted)
);
assert_eq!(socket.send_slice(b"123456", REMOTE_END), Err(Error::Exhausted));
assert!(!socket.can_send());
assert_eq!(
socket.dispatch(&Context::DUMMY, |(ip_repr, udp_repr, payload)| {
assert_eq!(ip_repr, LOCAL_IP_REPR);
assert_eq!(udp_repr, LOCAL_UDP_REPR);
assert_eq!(payload, PAYLOAD);
Err(Error::Unaddressable)
}),
assert_eq!(socket.dispatch(|(ip_repr, udp_repr)| {
assert_eq!(ip_repr, LOCAL_IP_REPR);
assert_eq!(udp_repr, LOCAL_UDP_REPR);
Err(Error::Unaddressable)
);
}), Err(Error::Unaddressable));
assert!(!socket.can_send());
assert_eq!(
socket.dispatch(&Context::DUMMY, |(ip_repr, udp_repr, payload)| {
assert_eq!(ip_repr, LOCAL_IP_REPR);
assert_eq!(udp_repr, LOCAL_UDP_REPR);
assert_eq!(payload, PAYLOAD);
Ok(())
}),
assert_eq!(socket.dispatch(|(ip_repr, udp_repr)| {
assert_eq!(ip_repr, LOCAL_IP_REPR);
assert_eq!(udp_repr, LOCAL_UDP_REPR);
Ok(())
);
}), Ok(()));
assert!(socket.can_send());
}
@ -583,27 +481,13 @@ mod test {
assert_eq!(socket.recv(), Err(Error::Exhausted));
assert!(socket.accepts(&remote_ip_repr(), &REMOTE_UDP_REPR));
assert_eq!(
socket.process(
&Context::DUMMY,
&remote_ip_repr(),
&REMOTE_UDP_REPR,
PAYLOAD
),
Ok(())
);
assert_eq!(socket.process(&remote_ip_repr(), &REMOTE_UDP_REPR),
Ok(()));
assert!(socket.can_recv());
assert!(socket.accepts(&remote_ip_repr(), &REMOTE_UDP_REPR));
assert_eq!(
socket.process(
&Context::DUMMY,
&remote_ip_repr(),
&REMOTE_UDP_REPR,
PAYLOAD
),
Err(Error::Exhausted)
);
assert_eq!(socket.process(&remote_ip_repr(), &REMOTE_UDP_REPR),
Err(Error::Exhausted));
assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END)));
assert!(!socket.can_recv());
}
@ -615,15 +499,8 @@ mod test {
assert_eq!(socket.peek(), Err(Error::Exhausted));
assert_eq!(
socket.process(
&Context::DUMMY,
&remote_ip_repr(),
&REMOTE_UDP_REPR,
PAYLOAD
),
Ok(())
);
assert_eq!(socket.process(&remote_ip_repr(), &REMOTE_UDP_REPR),
Ok(()));
assert_eq!(socket.peek(), Ok((&b"abcdef"[..], &REMOTE_END)));
assert_eq!(socket.recv(), Ok((&b"abcdef"[..], REMOTE_END)));
assert_eq!(socket.peek(), Err(Error::Exhausted));
@ -635,15 +512,8 @@ mod test {
assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
assert!(socket.accepts(&remote_ip_repr(), &REMOTE_UDP_REPR));
assert_eq!(
socket.process(
&Context::DUMMY,
&remote_ip_repr(),
&REMOTE_UDP_REPR,
PAYLOAD
),
Ok(())
);
assert_eq!(socket.process(&remote_ip_repr(), &REMOTE_UDP_REPR),
Ok(()));
let mut slice = [0; 4];
assert_eq!(socket.recv_slice(&mut slice[..]), Ok((4, REMOTE_END)));
@ -655,15 +525,8 @@ mod test {
let mut socket = socket(buffer(1), buffer(0));
assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
assert_eq!(
socket.process(
&Context::DUMMY,
&remote_ip_repr(),
&REMOTE_UDP_REPR,
PAYLOAD
),
Ok(())
);
assert_eq!(socket.process(&remote_ip_repr(), &REMOTE_UDP_REPR),
Ok(()));
let mut slice = [0; 4];
assert_eq!(socket.peek_slice(&mut slice[..]), Ok((4, &REMOTE_END)));
@ -680,22 +543,16 @@ mod test {
s.set_hop_limit(Some(0x2a));
assert_eq!(s.send_slice(b"abcdef", REMOTE_END), Ok(()));
assert_eq!(
s.dispatch(&Context::DUMMY, |(ip_repr, _, _)| {
assert_eq!(
ip_repr,
IpRepr::Unspecified {
src_addr: MOCK_IP_ADDR_1,
dst_addr: MOCK_IP_ADDR_2,
protocol: IpProtocol::Udp,
payload_len: 8 + 6,
hop_limit: 0x2a,
}
);
Ok(())
}),
assert_eq!(s.dispatch(|(ip_repr, _)| {
assert_eq!(ip_repr, IpRepr::Unspecified{
src_addr: MOCK_IP_ADDR_1,
dst_addr: MOCK_IP_ADDR_2,
protocol: IpProtocol::Udp,
payload_len: 8 + 6,
hop_limit: 0x2a,
});
Ok(())
);
}), Ok(()));
}
#[test]
@ -719,7 +576,7 @@ mod test {
dst_addr: dst,
protocol: IpProtocol::Udp,
payload_len: 8 + 6,
hop_limit: 64,
hop_limit: 64
}),
#[cfg(feature = "proto-ipv6")]
(IpAddress::Ipv6(src), IpAddress::Ipv6(dst)) => IpRepr::Ipv6(Ipv6Repr {
@ -727,9 +584,9 @@ mod test {
dst_addr: dst,
next_header: IpProtocol::Udp,
payload_len: 8 + 6,
hop_limit: 64,
hop_limit: 64
}),
_ => unreachable!(),
_ => unreachable!()
}
}
@ -749,11 +606,8 @@ mod test {
assert_eq!(socket.bind(LOCAL_END), Ok(()));
let too_large = b"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdefx";
assert_eq!(
socket.send_slice(too_large, REMOTE_END),
Err(Error::Truncated)
);
assert_eq!(socket.send_slice(&too_large[..16 * 4], REMOTE_END), Ok(()));
assert_eq!(socket.send_slice(too_large, REMOTE_END), Err(Error::Truncated));
assert_eq!(socket.send_slice(&too_large[..16*4], REMOTE_END), Ok(()));
}
#[test]
@ -765,22 +619,9 @@ mod test {
let repr = UdpRepr {
src_port: REMOTE_PORT,
dst_port: LOCAL_PORT,
payload: &[]
};
assert_eq!(
socket.process(&Context::DUMMY, &remote_ip_repr(), &repr, &[]),
Ok(())
);
assert_eq!(socket.process(&remote_ip_repr(), &repr), Ok(()));
assert_eq!(socket.recv(), Ok((&[][..], REMOTE_END)));
}
#[test]
fn test_closing() {
let recv_buffer = UdpSocketBuffer::new(vec![UdpPacketMetadata::EMPTY; 1], vec![]);
let mut socket = socket(recv_buffer, buffer(0));
assert_eq!(socket.bind(LOCAL_PORT), Ok(()));
assert!(socket.is_open());
socket.close();
assert!(!socket.is_open());
}
}

View File

@ -30,4 +30,4 @@ impl WakerRegistration {
pub fn wake(&mut self) {
self.waker.take().map(|w| w.wake());
}
}
}

View File

@ -5,47 +5,31 @@ pub struct TooManyHolesError;
/// A contiguous chunk of absent data, followed by a contiguous chunk of present data.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct Contig {
hole_size: usize,
data_size: usize,
data_size: usize
}
impl fmt::Display for Contig {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.has_hole() {
write!(f, "({})", self.hole_size)?;
}
if self.has_hole() && self.has_data() {
write!(f, " ")?;
}
if self.has_data() {
write!(f, "{}", self.data_size)?;
}
if self.has_hole() { write!(f, "({})", self.hole_size)?; }
if self.has_hole() && self.has_data() { write!(f, " ")?; }
if self.has_data() { write!(f, "{}", self.data_size)?; }
Ok(())
}
}
impl Contig {
fn empty() -> Contig {
Contig {
hole_size: 0,
data_size: 0,
}
Contig { hole_size: 0, data_size: 0 }
}
fn hole(size: usize) -> Contig {
Contig {
hole_size: size,
data_size: 0,
}
Contig { hole_size: size, data_size: 0 }
}
fn hole_and_data(hole_size: usize, data_size: usize) -> Contig {
Contig {
hole_size,
data_size,
}
Contig { hole_size, data_size }
}
fn has_hole(&self) -> bool {
@ -81,10 +65,10 @@ impl Contig {
}
}
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::boxed::Box;
#[cfg(feature = "std")]
use std::boxed::Box;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::boxed::Box;
#[cfg(any(feature = "std", feature = "alloc"))]
const CONTIG_COUNT: usize = 32;
@ -96,7 +80,6 @@ const CONTIG_COUNT: usize = 4;
/// Currently, up to a hardcoded limit of 4 or 32 holes can be tracked in the buffer.
#[derive(Debug)]
#[cfg_attr(test, derive(PartialEq, Eq, Clone))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Assembler {
#[cfg(not(any(feature = "std", feature = "alloc")))]
contigs: [Contig; CONTIG_COUNT],
@ -108,9 +91,7 @@ impl fmt::Display for Assembler {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[ ")?;
for contig in self.contigs.iter() {
if contig.is_empty() {
break;
}
if contig.is_empty() { break }
write!(f, "{} ", contig)?;
}
write!(f, "]")?;
@ -132,7 +113,10 @@ impl Assembler {
/// FIXME(whitequark): remove this once I'm certain enough that the assembler works well.
#[allow(dead_code)]
pub(crate) fn total_size(&self) -> usize {
self.contigs.iter().map(|contig| contig.total_size()).sum()
self.contigs
.iter()
.map(|contig| contig.total_size())
.sum()
}
fn front(&self) -> Contig {
@ -157,7 +141,7 @@ impl Assembler {
self.contigs[i] = self.contigs[i + 1];
if !self.contigs[i].has_data() {
self.contigs[i + 1] = Contig::empty();
return &mut self.contigs[i];
return &mut self.contigs[i]
}
}
@ -170,9 +154,7 @@ impl Assembler {
fn add_contig_at(&mut self, at: usize) -> Result<&mut Contig, TooManyHolesError> {
debug_assert!(!self.contigs[at].is_empty());
if !self.back().is_empty() {
return Err(TooManyHolesError);
}
if !self.back().is_empty() { return Err(TooManyHolesError) }
for i in (at + 1..self.contigs.len()).rev() {
self.contigs[i] = self.contigs[i - 1];
@ -217,11 +199,11 @@ impl Assembler {
// The range being added covers a part of the hole but not of the data
// in this contig, add a new contig containing the range.
{
let inserted = self.add_contig_at(index)?;
*inserted = Contig::hole_and_data(offset, size);
let inserted = self.add_contig_at(index)?;
*inserted = Contig::hole_and_data(offset, size);
}
// Previous contigs[index] got moved to contigs[index+1]
self.contigs[index + 1].shrink_hole_by(offset + size);
self.contigs[index+1].shrink_hole_by(offset + size);
index += 2;
} else {
unreachable!()
@ -231,7 +213,7 @@ impl Assembler {
if offset >= contig.total_size() {
offset = offset.saturating_sub(contig.total_size());
} else {
size = (offset + size).saturating_sub(contig.total_size());
size = (offset + size).saturating_sub(contig.total_size());
offset = 0;
}
}
@ -274,7 +256,7 @@ pub struct AssemblerIter<'a> {
offset: usize,
index: usize,
left: usize,
right: usize,
right: usize
}
impl<'a> AssemblerIter<'a> {
@ -284,7 +266,7 @@ impl<'a> AssemblerIter<'a> {
offset: offset,
index: 0,
left: 0,
right: 0,
right: 0
}
}
}
@ -313,8 +295,8 @@ impl<'a> Iterator for AssemblerIter<'a> {
#[cfg(test)]
mod test {
use super::*;
use std::vec::Vec;
use super::*;
impl From<Vec<(usize, usize)>> for Assembler {
fn from(vec: Vec<(usize, usize)>) -> Assembler {
@ -323,10 +305,7 @@ mod test {
#[cfg(any(feature = "std", feature = "alloc"))]
let mut contigs = Box::new([Contig::empty(); CONTIG_COUNT]);
for (i, &(hole_size, data_size)) in vec.iter().enumerate() {
contigs[i] = Contig {
hole_size,
data_size,
};
contigs[i] = Contig { hole_size, data_size };
}
Assembler { contigs }
}
@ -431,9 +410,9 @@ mod test {
#[test]
fn test_rejected_add_keeps_state() {
let mut assr = Assembler::new(CONTIG_COUNT * 20);
for c in 1..=CONTIG_COUNT - 1 {
assert_eq!(assr.add(c * 10, 3), Ok(()));
let mut assr = Assembler::new(CONTIG_COUNT*20);
for c in 1..=CONTIG_COUNT-1 {
assert_eq!(assr.add(c*10, 3), Ok(()));
}
// Maximum of allowed holes is reached
let assr_before = assr.clone();
@ -459,6 +438,7 @@ mod test {
let mut assr = contigs![(0, 4), (4, 4)];
assert_eq!(assr.remove_front(), Some(4));
assert_eq!(assr, contigs![(4, 4), (4, 0)]);
}
#[test]

View File

@ -6,12 +6,12 @@ or `alloc` crates being available, and heap-allocated memory.
*/
mod assembler;
mod packet_buffer;
mod ring_buffer;
mod packet_buffer;
pub use self::assembler::Assembler;
pub use self::packet_buffer::{PacketBuffer, PacketMetadata};
pub use self::ring_buffer::RingBuffer;
pub use self::packet_buffer::{PacketBuffer, PacketMetadata};
/// A trait for setting a value to a known state.
///

View File

@ -1,34 +1,30 @@
use managed::ManagedSlice;
use crate::storage::RingBuffer;
use crate::{Error, Result};
use crate::storage::RingBuffer;
/// Size and header of a packet.
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PacketMetadata<H> {
size: usize,
header: Option<H>,
size: usize,
header: Option<H>
}
impl<H> PacketMetadata<H> {
/// Empty packet description.
pub const EMPTY: PacketMetadata<H> = PacketMetadata {
size: 0,
header: None,
};
pub const EMPTY: PacketMetadata<H> = PacketMetadata { size: 0, header: None };
fn padding(size: usize) -> PacketMetadata<H> {
PacketMetadata {
size: size,
header: None,
size: size,
header: None
}
}
fn packet(size: usize, header: H) -> PacketMetadata<H> {
PacketMetadata {
size: size,
header: Some(header),
size: size,
header: Some(header)
}
}
@ -39,9 +35,9 @@ impl<H> PacketMetadata<H> {
/// An UDP packet ring buffer.
#[derive(Debug)]
pub struct PacketBuffer<'a, H: 'a> {
pub struct PacketBuffer<'a, H: 'a> {
metadata_ring: RingBuffer<'a, PacketMetadata<H>>,
payload_ring: RingBuffer<'a, u8>,
payload_ring: RingBuffer<'a, u8>,
}
impl<'a, H> PacketBuffer<'a, H> {
@ -50,13 +46,12 @@ impl<'a, H> PacketBuffer<'a, H> {
/// Metadata storage limits the maximum _number_ of packets in the buffer and payload
/// storage limits the maximum _total size_ of packets.
pub fn new<MS, PS>(metadata_storage: MS, payload_storage: PS) -> PacketBuffer<'a, H>
where
MS: Into<ManagedSlice<'a, PacketMetadata<H>>>,
PS: Into<ManagedSlice<'a, u8>>,
where MS: Into<ManagedSlice<'a, PacketMetadata<H>>>,
PS: Into<ManagedSlice<'a, u8>>,
{
PacketBuffer {
metadata_ring: RingBuffer::new(metadata_storage),
payload_ring: RingBuffer::new(payload_storage),
payload_ring: RingBuffer::new(payload_storage),
}
}
@ -79,32 +74,30 @@ impl<'a, H> PacketBuffer<'a, H> {
/// does not have enough spare payload space.
pub fn enqueue(&mut self, size: usize, header: H) -> Result<&mut [u8]> {
if self.payload_ring.capacity() < size {
return Err(Error::Truncated);
return Err(Error::Truncated)
}
if self.metadata_ring.is_full() {
return Err(Error::Exhausted);
return Err(Error::Exhausted)
}
let window = self.payload_ring.window();
let contig_window = self.payload_ring.contiguous_window();
if window < size {
return Err(Error::Exhausted);
return Err(Error::Exhausted)
} else if contig_window < size {
if window - contig_window < size {
// The buffer length is larger than the current contiguous window
// and is larger than the contiguous window will be after adding
// the padding necessary to circle around to the beginning of the
// ring buffer.
return Err(Error::Exhausted);
return Err(Error::Exhausted)
} else {
// Add padding to the end of the ring buffer so that the
// contiguous window is at the beginning of the ring buffer.
*self.metadata_ring.enqueue_one()? = PacketMetadata::padding(contig_window);
// note(discard): function does not write to the result
// enqueued padding buffer location
let _buf_enqueued = self.payload_ring.enqueue_many(contig_window);
self.payload_ring.enqueue_many(contig_window);
}
}
@ -116,15 +109,11 @@ impl<'a, H> PacketBuffer<'a, H> {
}
fn dequeue_padding(&mut self) {
let Self {
ref mut metadata_ring,
ref mut payload_ring,
} = *self;
let Self { ref mut metadata_ring, ref mut payload_ring } = *self;
let _ = metadata_ring.dequeue_one_with(|metadata| {
if metadata.is_padding() {
// note(discard): function does not use value of dequeued padding bytes
let _buf_dequeued = payload_ring.dequeue_many(metadata.size);
payload_ring.dequeue_many(metadata.size);
Ok(()) // dequeue metadata
} else {
Err(Error::Exhausted) // don't dequeue metadata
@ -135,32 +124,22 @@ impl<'a, H> PacketBuffer<'a, H> {
/// Call `f` with a single packet from the buffer, and dequeue the packet if `f`
/// returns successfully, or return `Err(Error::Exhausted)` if the buffer is empty.
pub fn dequeue_with<'c, R, F>(&'c mut self, f: F) -> Result<R>
where
F: FnOnce(&mut H, &'c mut [u8]) -> Result<R>,
{
where F: FnOnce(&mut H, &'c mut [u8]) -> Result<R> {
self.dequeue_padding();
let Self {
ref mut metadata_ring,
ref mut payload_ring,
} = *self;
let Self { ref mut metadata_ring, ref mut payload_ring } = *self;
metadata_ring.dequeue_one_with(move |metadata| {
let PacketMetadata {
ref mut header,
size,
} = *metadata;
let PacketMetadata { ref mut header, size } = *metadata;
payload_ring
.dequeue_many_with(|payload_buf| {
debug_assert!(payload_buf.len() >= size);
payload_ring.dequeue_many_with(|payload_buf| {
debug_assert!(payload_buf.len() >= size);
match f(header.as_mut().unwrap(), &mut payload_buf[..size]) {
Ok(val) => (size, Ok(val)),
Err(err) => (0, Err(err)),
}
})
.1
match f(header.as_mut().unwrap(), &mut payload_buf[..size]) {
Ok(val) => (size, Ok(val)),
Err(err) => (0, Err(err)),
}
}).1
})
}
@ -169,10 +148,7 @@ impl<'a, H> PacketBuffer<'a, H> {
pub fn dequeue(&mut self) -> Result<(H, &mut [u8])> {
self.dequeue_padding();
let PacketMetadata {
ref mut header,
size,
} = *self.metadata_ring.dequeue_one()?;
let PacketMetadata { ref mut header, size } = *self.metadata_ring.dequeue_one()?;
let payload_buf = self.payload_ring.dequeue_many(size);
debug_assert!(payload_buf.len() == size);
@ -187,10 +163,7 @@ impl<'a, H> PacketBuffer<'a, H> {
self.dequeue_padding();
if let Some(metadata) = self.metadata_ring.get_allocated(0, 1).first() {
Ok((
metadata.header.as_ref().unwrap(),
self.payload_ring.get_allocated(0, metadata.size),
))
Ok((metadata.header.as_ref().unwrap(), self.payload_ring.get_allocated(0, metadata.size)))
} else {
Err(Error::Exhausted)
}
@ -205,13 +178,6 @@ impl<'a, H> PacketBuffer<'a, H> {
pub fn payload_capacity(&self) -> usize {
self.payload_ring.capacity()
}
/// Reset the packet buffer and clear any staged.
#[allow(unused)]
pub(crate) fn reset(&mut self) {
self.payload_ring.clear();
self.metadata_ring.clear();
}
}
#[cfg(test)]
@ -219,7 +185,8 @@ mod test {
use super::*;
fn buffer() -> PacketBuffer<'static, ()> {
PacketBuffer::new(vec![PacketMetadata::EMPTY; 4], vec![0u8; 16])
PacketBuffer::new(vec![PacketMetadata::EMPTY; 4],
vec![0u8; 16])
}
#[test]
@ -262,10 +229,7 @@ mod test {
let mut buffer = buffer();
assert!(buffer.enqueue(12, ()).is_ok());
assert!(buffer.dequeue().is_ok());
buffer
.enqueue(12, ())
.unwrap()
.copy_from_slice(b"abcdefghijkl");
buffer.enqueue(12, ()).unwrap().copy_from_slice(b"abcdefghijkl");
}
#[test]
@ -278,34 +242,30 @@ mod test {
assert_eq!(buffer.metadata_ring.len(), 3);
assert!(buffer.dequeue().is_ok());
assert!(buffer
.dequeue_with(|_, _| Err(Error::Unaddressable) as Result<()>)
.is_err());
assert!(buffer.dequeue_with(|_, _| Err(Error::Unaddressable) as Result<()>).is_err());
assert_eq!(buffer.metadata_ring.len(), 1);
assert!(buffer
.dequeue_with(|&mut (), payload| {
assert_eq!(payload, &b"abcd"[..]);
Ok(())
})
.is_ok());
assert!(buffer.dequeue_with(|&mut (), payload| {
assert_eq!(payload, &b"abcd"[..]);
Ok(())
}).is_ok());
assert_eq!(buffer.metadata_ring.len(), 0);
}
#[test]
fn test_metadata_full_empty() {
let mut buffer = buffer();
assert!(buffer.is_empty());
assert!(!buffer.is_full());
assert_eq!(buffer.is_empty(), true);
assert_eq!(buffer.is_full(), false);
assert!(buffer.enqueue(1, ()).is_ok());
assert!(!buffer.is_empty());
assert_eq!(buffer.is_empty(), false);
assert!(buffer.enqueue(1, ()).is_ok());
assert!(buffer.enqueue(1, ()).is_ok());
assert!(!buffer.is_full());
assert!(!buffer.is_empty());
assert_eq!(buffer.is_full(), false);
assert_eq!(buffer.is_empty(), false);
assert!(buffer.enqueue(1, ()).is_ok());
assert!(buffer.is_full());
assert!(!buffer.is_empty());
assert_eq!(buffer.is_full(), true);
assert_eq!(buffer.is_empty(), false);
assert_eq!(buffer.metadata_ring.len(), 4);
assert_eq!(buffer.enqueue(1, ()), Err(Error::Exhausted));
}
@ -343,18 +303,4 @@ mod test {
assert!(buffer.dequeue().is_ok());
assert!(buffer.enqueue(5, ()).is_ok());
}
#[test]
fn clear() {
let mut buffer = buffer();
// Ensure enqueuing data in teh buffer fills it somewhat.
assert!(buffer.is_empty());
assert!(buffer.enqueue(6, ()).is_ok());
// Ensure that resetting the buffer causes it to be empty.
assert!(!buffer.is_empty());
buffer.reset();
assert!(buffer.is_empty());
}
}

View File

@ -1,12 +1,11 @@
// Some of the functions in ring buffer is marked as #[must_use]. It notes that
// these functions may have side effects, and it's implemented by [RFC 1940].
// Uncomment the #[must_use]s here once [RFC 1940] hits stable.
// [RFC 1940]: https://github.com/rust-lang/rust/issues/43302
use core::cmp;
use managed::ManagedSlice;
use crate::storage::Resettable;
use crate::{Error, Result};
use crate::storage::Resettable;
/// A ring buffer.
///
@ -26,7 +25,7 @@ use crate::{Error, Result};
pub struct RingBuffer<'a, T: 'a> {
storage: ManagedSlice<'a, T>,
read_at: usize,
length: usize,
length: usize,
}
impl<'a, T: 'a> RingBuffer<'a, T> {
@ -34,20 +33,19 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
///
/// During creation, every element in `storage` is reset.
pub fn new<S>(storage: S) -> RingBuffer<'a, T>
where
S: Into<ManagedSlice<'a, T>>,
where S: Into<ManagedSlice<'a, T>>,
{
RingBuffer {
storage: storage.into(),
read_at: 0,
length: 0,
length: 0,
}
}
/// Clear the ring buffer.
pub fn clear(&mut self) {
self.read_at = 0;
self.length = 0;
self.length = 0;
}
/// Return the maximum number of elements in the ring buffer.
@ -57,9 +55,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
/// Clear the ring buffer, and reset every element.
pub fn reset(&mut self)
where
T: Resettable,
{
where T: Resettable {
self.clear();
for elem in self.storage.iter_mut() {
elem.reset();
@ -116,12 +112,8 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
/// Call `f` with a single buffer element, and enqueue the element if `f`
/// returns successfully, or return `Err(Error::Exhausted)` if the buffer is full.
pub fn enqueue_one_with<'b, R, F>(&'b mut self, f: F) -> Result<R>
where
F: FnOnce(&'b mut T) -> Result<R>,
{
if self.is_full() {
return Err(Error::Exhausted);
}
where F: FnOnce(&'b mut T) -> Result<R> {
if self.is_full() { return Err(Error::Exhausted) }
let index = self.get_idx_unchecked(self.length);
match f(&mut self.storage[index]) {
@ -129,7 +121,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
self.length += 1;
Ok(result)
}
Err(error) => Err(error),
Err(error) => Err(error)
}
}
@ -144,12 +136,8 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
/// Call `f` with a single buffer element, and dequeue the element if `f`
/// returns successfully, or return `Err(Error::Exhausted)` if the buffer is empty.
pub fn dequeue_one_with<'b, R, F>(&'b mut self, f: F) -> Result<R>
where
F: FnOnce(&'b mut T) -> Result<R>,
{
if self.is_empty() {
return Err(Error::Exhausted);
}
where F: FnOnce(&'b mut T) -> Result<R> {
if self.is_empty() { return Err(Error::Exhausted) }
let next_at = self.get_idx_unchecked(1);
match f(&mut self.storage[self.read_at]) {
@ -158,7 +146,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
self.read_at = next_at;
Ok(result)
}
Err(error) => Err(error),
Err(error) => Err(error)
}
}
@ -181,9 +169,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
/// This function panics if the amount of elements returned by `f` is larger
/// than the size of the slice passed into it.
pub fn enqueue_many_with<'b, R, F>(&'b mut self, f: F) -> (usize, R)
where
F: FnOnce(&'b mut [T]) -> (usize, R),
{
where F: FnOnce(&'b mut [T]) -> (usize, R) {
if self.length == 0 {
// Ring is currently empty. Reset `read_at` to optimize
// for contiguous space.
@ -203,22 +189,19 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
///
/// This function may return a slice smaller than the given size
/// if the free space in the buffer is not contiguous.
#[must_use]
// #[must_use]
pub fn enqueue_many(&mut self, size: usize) -> &mut [T] {
self.enqueue_many_with(|buf| {
let size = cmp::min(size, buf.len());
(size, &mut buf[..size])
})
.1
}).1
}
/// Enqueue as many elements from the given slice into the buffer as possible,
/// and return the amount of elements that could fit.
#[must_use]
// #[must_use]
pub fn enqueue_slice(&mut self, data: &[T]) -> usize
where
T: Copy,
{
where T: Copy {
let (size_1, data) = self.enqueue_many_with(|buf| {
let size = cmp::min(buf.len(), data.len());
buf[..size].copy_from_slice(&data[..size]);
@ -239,9 +222,7 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
/// This function panics if the amount of elements returned by `f` is larger
/// than the size of the slice passed into it.
pub fn dequeue_many_with<'b, R, F>(&'b mut self, f: F) -> (usize, R)
where
F: FnOnce(&'b mut [T]) -> (usize, R),
{
where F: FnOnce(&'b mut [T]) -> (usize, R) {
let capacity = self.capacity();
let max_size = cmp::min(self.len(), capacity - self.read_at);
let (size, result) = f(&mut self.storage[self.read_at..self.read_at + max_size]);
@ -260,22 +241,19 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
///
/// This function may return a slice smaller than the given size
/// if the allocated space in the buffer is not contiguous.
#[must_use]
// #[must_use]
pub fn dequeue_many(&mut self, size: usize) -> &mut [T] {
self.dequeue_many_with(|buf| {
let size = cmp::min(size, buf.len());
(size, &mut buf[..size])
})
.1
}).1
}
/// Dequeue as many elements from the buffer into the given slice as possible,
/// and return the amount of elements that could fit.
#[must_use]
// #[must_use]
pub fn dequeue_slice(&mut self, data: &mut [T]) -> usize
where
T: Copy,
{
where T: Copy {
let (size_1, data) = self.dequeue_many_with(|buf| {
let size = cmp::min(buf.len(), data.len());
data[..size].copy_from_slice(&buf[..size]);
@ -295,23 +273,17 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
impl<'a, T: 'a> RingBuffer<'a, T> {
/// Return the largest contiguous slice of unallocated buffer elements starting
/// at the given offset past the last allocated element, and up to the given size.
#[must_use]
// #[must_use]
pub fn get_unallocated(&mut self, offset: usize, mut size: usize) -> &mut [T] {
let start_at = self.get_idx(self.length + offset);
// We can't access past the end of unallocated data.
if offset > self.window() {
return &mut [];
}
if offset > self.window() { return &mut [] }
// We can't enqueue more than there is free space.
let clamped_window = self.window() - offset;
if size > clamped_window {
size = clamped_window
}
if size > clamped_window { size = clamped_window }
// We can't contiguously enqueue past the end of the storage.
let until_end = self.capacity() - start_at;
if size > until_end {
size = until_end
}
if size > until_end { size = until_end }
&mut self.storage[start_at..start_at + size]
}
@ -319,11 +291,9 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
/// Write as many elements from the given slice into unallocated buffer elements
/// starting at the given offset past the last allocated element, and return
/// the amount written.
#[must_use]
// #[must_use]
pub fn write_unallocated(&mut self, offset: usize, data: &[T]) -> usize
where
T: Copy,
{
where T: Copy {
let (size_1, offset, data) = {
let slice = self.get_unallocated(offset, data.len());
let slice_len = slice.len();
@ -350,23 +320,17 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
/// Return the largest contiguous slice of allocated buffer elements starting
/// at the given offset past the first allocated element, and up to the given size.
#[must_use]
// #[must_use]
pub fn get_allocated(&self, offset: usize, mut size: usize) -> &[T] {
let start_at = self.get_idx(offset);
// We can't read past the end of the allocated data.
if offset > self.length {
return &mut [];
}
if offset > self.length { return &mut [] }
// We can't read more than we have allocated.
let clamped_length = self.length - offset;
if size > clamped_length {
size = clamped_length
}
if size > clamped_length { size = clamped_length }
// We can't contiguously dequeue past the end of the storage.
let until_end = self.capacity() - start_at;
if size > until_end {
size = until_end
}
if size > until_end { size = until_end }
&self.storage[start_at..start_at + size]
}
@ -374,11 +338,9 @@ impl<'a, T: 'a> RingBuffer<'a, T> {
/// Read as many elements from allocated buffer elements into the given slice
/// starting at the given offset past the first allocated element, and return
/// the amount read.
#[must_use]
// #[must_use]
pub fn read_allocated(&mut self, offset: usize, data: &mut [T]) -> usize
where
T: Copy,
{
where T: Copy {
let (size_1, offset, data) = {
let slice = self.get_allocated(offset, data.len());
data[..slice.len()].copy_from_slice(slice);
@ -440,10 +402,8 @@ mod test {
#[test]
fn test_buffer_enqueue_dequeue_one_with() {
let mut ring = RingBuffer::new(vec![0; 5]);
assert_eq!(
ring.dequeue_one_with(|_| unreachable!()) as Result<()>,
Err(Error::Exhausted)
);
assert_eq!(ring.dequeue_one_with(|_| unreachable!()) as Result<()>,
Err(Error::Exhausted));
ring.enqueue_one_with(Ok).unwrap();
assert!(!ring.is_empty());
@ -454,19 +414,15 @@ mod test {
assert!(!ring.is_empty());
}
assert!(ring.is_full());
assert_eq!(
ring.enqueue_one_with(|_| unreachable!()) as Result<()>,
Err(Error::Exhausted)
);
assert_eq!(ring.enqueue_one_with(|_| unreachable!()) as Result<()>,
Err(Error::Exhausted));
for i in 0..5 {
assert_eq!(ring.dequeue_one_with(|e| Ok(*e)).unwrap(), i);
assert!(!ring.is_full());
}
assert_eq!(
ring.dequeue_one_with(|_| unreachable!()) as Result<()>,
Err(Error::Exhausted)
);
assert_eq!(ring.dequeue_one_with(|_| unreachable!()) as Result<()>,
Err(Error::Exhausted));
assert!(ring.is_empty());
}
@ -498,14 +454,11 @@ mod test {
fn test_buffer_enqueue_many_with() {
let mut ring = RingBuffer::new(vec![b'.'; 12]);
assert_eq!(
ring.enqueue_many_with(|buf| {
assert_eq!(buf.len(), 12);
buf[0..2].copy_from_slice(b"ab");
(2, true)
}),
assert_eq!(ring.enqueue_many_with(|buf| {
assert_eq!(buf.len(), 12);
buf[0..2].copy_from_slice(b"ab");
(2, true)
);
}), (2, true));
assert_eq!(ring.len(), 2);
assert_eq!(&ring.storage[..], b"ab..........");
@ -592,15 +545,12 @@ mod test {
assert_eq!(ring.enqueue_slice(b"abcdefghijkl"), 12);
assert_eq!(
ring.dequeue_many_with(|buf| {
assert_eq!(buf.len(), 12);
assert_eq!(buf, b"abcdefghijkl");
buf[..4].copy_from_slice(b"....");
(4, true)
}),
assert_eq!(ring.dequeue_many_with(|buf| {
assert_eq!(buf.len(), 12);
assert_eq!(buf, b"abcdefghijkl");
buf[..4].copy_from_slice(b"....");
(4, true)
);
}), (4, true));
assert_eq!(ring.len(), 8);
assert_eq!(&ring.storage[..], b"....efghijkl");
@ -687,8 +637,7 @@ mod test {
}
assert_eq!(&ring.storage[..], b"abcd........");
let buf_enqueued = ring.enqueue_many(4);
assert_eq!(buf_enqueued.len(), 4);
ring.enqueue_many(4);
assert_eq!(ring.len(), 4);
{
@ -730,20 +679,17 @@ mod test {
let mut ring = RingBuffer::new(vec![b'.'; 12]);
assert_eq!(ring.get_allocated(16, 4), b"");
assert_eq!(ring.get_allocated(0, 4), b"");
assert_eq!(ring.get_allocated(0, 4), b"");
let len_enqueued = ring.enqueue_slice(b"abcd");
ring.enqueue_slice(b"abcd");
assert_eq!(ring.get_allocated(0, 8), b"abcd");
assert_eq!(len_enqueued, 4);
let len_enqueued = ring.enqueue_slice(b"efghijkl");
ring.enqueue_slice(b"efghijkl");
ring.dequeue_many(4).copy_from_slice(b"....");
assert_eq!(ring.get_allocated(4, 8), b"ijkl");
assert_eq!(len_enqueued, 8);
let len_enqueued = ring.enqueue_slice(b"abcd");
ring.enqueue_slice(b"abcd");
assert_eq!(ring.get_allocated(4, 8), b"ijkl");
assert_eq!(len_enqueued, 4);
}
#[test]
@ -765,6 +711,7 @@ mod test {
let mut data = [0; 6];
assert_eq!(ring.read_allocated(6, &mut data[..]), 3);
assert_eq!(&data[..], b"mno\x00\x00\x00");
}
#[test]
@ -787,11 +734,10 @@ mod test {
#[test]
fn test_buffer_write_wholly() {
let mut ring = RingBuffer::new(vec![b'.'; 8]);
ring.enqueue_many(2).copy_from_slice(b"ab");
ring.enqueue_many(2).copy_from_slice(b"cd");
ring.enqueue_many(2).copy_from_slice(b"xx");
ring.enqueue_many(2).copy_from_slice(b"xx");
assert_eq!(ring.len(), 4);
let buf_dequeued = ring.dequeue_many(4);
assert_eq!(buf_dequeued, b"abcd");
ring.dequeue_many(4);
assert_eq!(ring.len(), 0);
let large = ring.enqueue_many(8);

View File

@ -10,7 +10,7 @@ absolute and relative time.
[Duration]: struct.Duration.html
*/
use core::{fmt, ops};
use core::{ops, fmt};
/// A representation of an absolute time value.
///
@ -22,42 +22,19 @@ use core::{fmt, ops};
/// * A value less than `0` indicates a time before the starting
/// point.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Instant {
micros: i64,
pub millis: i64,
}
impl Instant {
/// Create a new `Instant` from a number of microseconds.
pub fn from_micros<T: Into<i64>>(micros: T) -> Instant {
Instant {
micros: micros.into(),
}
}
pub const fn from_micros_const(micros: i64) -> Instant {
Instant { micros }
}
/// Create a new `Instant` from a number of milliseconds.
pub fn from_millis<T: Into<i64>>(millis: T) -> Instant {
Instant {
micros: millis.into() * 1000,
}
}
/// Create a new `Instant` from a number of milliseconds.
pub const fn from_millis_const(millis: i64) -> Instant {
Instant {
micros: millis * 1000,
}
Instant { millis: millis.into() }
}
/// Create a new `Instant` from a number of seconds.
pub fn from_secs<T: Into<i64>>(secs: T) -> Instant {
Instant {
micros: secs.into() * 1000000,
}
Instant { millis: secs.into() * 1000 }
}
/// Create a new `Instant` from the current [std::time::SystemTime].
@ -74,30 +51,19 @@ impl Instant {
/// The fractional number of milliseconds that have passed
/// since the beginning of time.
pub const fn millis(&self) -> i64 {
self.micros % 1000000 / 1000
}
/// The fractional number of microseconds that have passed
/// since the beginning of time.
pub const fn micros(&self) -> i64 {
self.micros % 1000000
self.millis % 1000
}
/// The number of whole seconds that have passed since the
/// beginning of time.
pub const fn secs(&self) -> i64 {
self.micros / 1000000
self.millis / 1000
}
/// The total number of milliseconds that have passed since
/// the beginning of time.
/// the biginning of time.
pub const fn total_millis(&self) -> i64 {
self.micros / 1000
}
/// The total number of milliseconds that have passed since
/// the beginning of time.
pub const fn total_micros(&self) -> i64 {
self.micros
self.millis
}
}
@ -105,24 +71,23 @@ impl Instant {
impl From<::std::time::Instant> for Instant {
fn from(other: ::std::time::Instant) -> Instant {
let elapsed = other.elapsed();
Instant::from_micros((elapsed.as_secs() * 1_000000) as i64 + elapsed.subsec_micros() as i64)
Instant::from_millis((elapsed.as_secs() * 1_000) as i64 + elapsed.subsec_millis() as i64)
}
}
#[cfg(feature = "std")]
impl From<::std::time::SystemTime> for Instant {
fn from(other: ::std::time::SystemTime) -> Instant {
let n = other
.duration_since(::std::time::UNIX_EPOCH)
let n = other.duration_since(::std::time::UNIX_EPOCH)
.expect("start time must not be before the unix epoch");
Self::from_micros(n.as_secs() as i64 * 1000000 + n.subsec_micros() as i64)
Self::from_millis(n.as_secs() as i64 * 1000 + n.subsec_millis() as i64)
}
}
#[cfg(feature = "std")]
impl From<Instant> for ::std::time::SystemTime {
fn from(val: Instant) -> Self {
::std::time::UNIX_EPOCH + ::std::time::Duration::from_micros(val.micros as u64)
impl Into<::std::time::SystemTime> for Instant {
fn into(self) -> ::std::time::SystemTime {
::std::time::UNIX_EPOCH + ::std::time::Duration::from_millis(self.millis as u64)
}
}
@ -136,13 +101,13 @@ impl ops::Add<Duration> for Instant {
type Output = Instant;
fn add(self, rhs: Duration) -> Instant {
Instant::from_micros(self.micros + rhs.total_micros() as i64)
Instant::from_millis(self.millis + rhs.total_millis() as i64)
}
}
impl ops::AddAssign<Duration> for Instant {
fn add_assign(&mut self, rhs: Duration) {
self.micros += rhs.total_micros() as i64;
self.millis += rhs.total_millis() as i64;
}
}
@ -150,13 +115,13 @@ impl ops::Sub<Duration> for Instant {
type Output = Instant;
fn sub(self, rhs: Duration) -> Instant {
Instant::from_micros(self.micros - rhs.total_micros() as i64)
Instant::from_millis(self.millis - rhs.total_millis() as i64)
}
}
impl ops::SubAssign<Duration> for Instant {
fn sub_assign(&mut self, rhs: Duration) {
self.micros -= rhs.total_micros() as i64;
self.millis -= rhs.total_millis() as i64;
}
}
@ -164,61 +129,40 @@ impl ops::Sub<Instant> for Instant {
type Output = Duration;
fn sub(self, rhs: Instant) -> Duration {
Duration::from_micros((self.micros - rhs.micros).abs() as u64)
Duration::from_millis((self.millis - rhs.millis).abs() as u64)
}
}
/// A relative amount of time.
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Duration {
micros: u64,
pub millis: u64,
}
impl Duration {
pub const ZERO: Duration = Duration::from_micros(0);
/// Create a new `Duration` from a number of microeconds.
pub const fn from_micros(micros: u64) -> Duration {
Duration { micros }
}
/// Create a new `Duration` from a number of milliseconds.
pub const fn from_millis(millis: u64) -> Duration {
Duration {
micros: millis * 1000,
}
Duration { millis }
}
/// Create a new `Instant` from a number of seconds.
pub const fn from_secs(secs: u64) -> Duration {
Duration {
micros: secs * 1000000,
}
Duration { millis: secs * 1000 }
}
/// The fractional number of milliseconds in this `Duration`.
pub const fn millis(&self) -> u64 {
self.micros / 1000 % 1000
}
/// The fractional number of milliseconds in this `Duration`.
pub const fn micros(&self) -> u64 {
self.micros % 1000000
self.millis % 1000
}
/// The number of whole seconds in this `Duration`.
pub const fn secs(&self) -> u64 {
self.micros / 1000000
self.millis / 1000
}
/// The total number of milliseconds in this `Duration`.
pub const fn total_millis(&self) -> u64 {
self.micros / 1000
}
/// The total number of microseconds in this `Duration`.
pub const fn total_micros(&self) -> u64 {
self.micros
self.millis
}
}
@ -232,13 +176,13 @@ impl ops::Add<Duration> for Duration {
type Output = Duration;
fn add(self, rhs: Duration) -> Duration {
Duration::from_micros(self.micros + rhs.total_micros())
Duration::from_millis(self.millis + rhs.total_millis())
}
}
impl ops::AddAssign<Duration> for Duration {
fn add_assign(&mut self, rhs: Duration) {
self.micros += rhs.total_micros();
self.millis += rhs.total_millis();
}
}
@ -246,20 +190,15 @@ impl ops::Sub<Duration> for Duration {
type Output = Duration;
fn sub(self, rhs: Duration) -> Duration {
Duration::from_micros(
self.micros
.checked_sub(rhs.total_micros())
.expect("overflow when subtracting durations"),
)
Duration::from_millis(
self.millis.checked_sub(rhs.total_millis()).expect("overflow when subtracting durations"))
}
}
impl ops::SubAssign<Duration> for Duration {
fn sub_assign(&mut self, rhs: Duration) {
self.micros = self
.micros
.checked_sub(rhs.total_micros())
.expect("overflow when subtracting durations");
self.millis = self.millis.checked_sub(
rhs.total_millis()).expect("overflow when subtracting durations");
}
}
@ -267,13 +206,13 @@ impl ops::Mul<u32> for Duration {
type Output = Duration;
fn mul(self, rhs: u32) -> Duration {
Duration::from_micros(self.micros * rhs as u64)
Duration::from_millis(self.millis * rhs as u64)
}
}
impl ops::MulAssign<u32> for Duration {
fn mul_assign(&mut self, rhs: u32) {
self.micros *= rhs as u64;
self.millis *= rhs as u64;
}
}
@ -281,53 +220,29 @@ impl ops::Div<u32> for Duration {
type Output = Duration;
fn div(self, rhs: u32) -> Duration {
Duration::from_micros(self.micros / rhs as u64)
Duration::from_millis(self.millis / rhs as u64)
}
}
impl ops::DivAssign<u32> for Duration {
fn div_assign(&mut self, rhs: u32) {
self.micros /= rhs as u64;
}
}
impl ops::Shl<u32> for Duration {
type Output = Duration;
fn shl(self, rhs: u32) -> Duration {
Duration::from_micros(self.micros << rhs)
}
}
impl ops::ShlAssign<u32> for Duration {
fn shl_assign(&mut self, rhs: u32) {
self.micros <<= rhs;
}
}
impl ops::Shr<u32> for Duration {
type Output = Duration;
fn shr(self, rhs: u32) -> Duration {
Duration::from_micros(self.micros >> rhs)
}
}
impl ops::ShrAssign<u32> for Duration {
fn shr_assign(&mut self, rhs: u32) {
self.micros >>= rhs;
self.millis /= rhs as u64;
}
}
impl From<::core::time::Duration> for Duration {
fn from(other: ::core::time::Duration) -> Duration {
Duration::from_micros(other.as_secs() * 1000000 + other.subsec_micros() as u64)
Duration::from_millis(
other.as_secs() * 1000 + other.subsec_millis() as u64
)
}
}
impl From<Duration> for ::core::time::Duration {
fn from(val: Duration) -> Self {
::core::time::Duration::from_micros(val.total_micros())
impl Into<::core::time::Duration> for Duration {
fn into(self) -> ::core::time::Duration {
::core::time::Duration::from_millis(
self.total_millis()
)
}
}
@ -338,15 +253,9 @@ mod test {
#[test]
fn test_instant_ops() {
// std::ops::Add
assert_eq!(
Instant::from_millis(4) + Duration::from_millis(6),
Instant::from_millis(10)
);
assert_eq!(Instant::from_millis(4) + Duration::from_millis(6), Instant::from_millis(10));
// std::ops::Sub
assert_eq!(
Instant::from_millis(7) - Duration::from_millis(5),
Instant::from_millis(2)
);
assert_eq!(Instant::from_millis(7) - Duration::from_millis(5), Instant::from_millis(2));
}
#[test]
@ -367,34 +276,23 @@ mod test {
#[cfg(feature = "std")]
fn test_instant_conversions() {
let mut epoc: ::std::time::SystemTime = Instant::from_millis(0).into();
assert_eq!(
Instant::from(::std::time::UNIX_EPOCH),
Instant::from_millis(0)
);
assert_eq!(Instant::from(::std::time::UNIX_EPOCH),
Instant::from_millis(0));
assert_eq!(epoc, ::std::time::UNIX_EPOCH);
epoc = Instant::from_millis(2085955200i64 * 1000).into();
assert_eq!(
epoc,
::std::time::UNIX_EPOCH + ::std::time::Duration::from_secs(2085955200)
);
assert_eq!(epoc, ::std::time::UNIX_EPOCH + ::std::time::Duration::from_secs(2085955200));
}
#[test]
fn test_duration_ops() {
// std::ops::Add
assert_eq!(
Duration::from_millis(40) + Duration::from_millis(2),
Duration::from_millis(42)
);
assert_eq!(Duration::from_millis(40) + Duration::from_millis(2), Duration::from_millis(42));
// std::ops::Sub
assert_eq!(
Duration::from_millis(555) - Duration::from_millis(42),
Duration::from_millis(513)
);
assert_eq!(Duration::from_millis(555) - Duration::from_millis(42), Duration::from_millis(513));
// std::ops::Mul
assert_eq!(Duration::from_millis(13) * 22, Duration::from_millis(286));
// std::ops::Div
assert_eq!(Duration::from_millis(53) / 4, Duration::from_micros(13250));
assert_eq!(Duration::from_millis(53) / 4, Duration::from_millis(13));
}
#[test]
@ -407,7 +305,7 @@ mod test {
duration *= 4;
assert_eq!(duration, Duration::from_millis(20936));
duration /= 5;
assert_eq!(duration, Duration::from_micros(4187200));
assert_eq!(duration, Duration::from_millis(4187));
}
#[test]

View File

@ -1,5 +1,5 @@
use byteorder::{ByteOrder, NetworkEndian};
use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
use crate::{Error, Result};
@ -22,9 +22,8 @@ enum_with_unknown! {
/// A read/write wrapper around an Address Resolution Protocol packet buffer.
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> {
buffer: T,
buffer: T
}
mod field {
@ -34,9 +33,9 @@ mod field {
pub const HTYPE: Field = 0..2;
pub const PTYPE: Field = 2..4;
pub const HLEN: usize = 4;
pub const PLEN: usize = 5;
pub const OPER: Field = 6..8;
pub const HLEN: usize = 4;
pub const PLEN: usize = 5;
pub const OPER: Field = 6..8;
#[inline]
pub fn SHA(hardware_len: u8, _protocol_len: u8) -> Field {
@ -254,7 +253,6 @@ use crate::wire::{EthernetAddress, Ipv4Address};
/// A high-level representation of an Address Resolution Protocol packet.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Repr {
/// An Ethernet and IPv4 Address Resolution Protocol packet.
@ -263,7 +261,7 @@ pub enum Repr {
source_hardware_addr: EthernetAddress,
source_protocol_addr: Ipv4Address,
target_hardware_addr: EthernetAddress,
target_protocol_addr: Ipv4Address,
target_protocol_addr: Ipv4Address
},
}
@ -271,20 +269,22 @@ impl Repr {
/// Parse an Address Resolution Protocol packet and return a high-level representation,
/// or return `Err(Error::Unrecognized)` if the packet is not recognized.
pub fn parse<T: AsRef<[u8]>>(packet: &Packet<T>) -> Result<Repr> {
match (
packet.hardware_type(),
packet.protocol_type(),
packet.hardware_len(),
packet.protocol_len(),
) {
(Hardware::Ethernet, Protocol::Ipv4, 6, 4) => Ok(Repr::EthernetIpv4 {
operation: packet.operation(),
source_hardware_addr: EthernetAddress::from_bytes(packet.source_hardware_addr()),
source_protocol_addr: Ipv4Address::from_bytes(packet.source_protocol_addr()),
target_hardware_addr: EthernetAddress::from_bytes(packet.target_hardware_addr()),
target_protocol_addr: Ipv4Address::from_bytes(packet.target_protocol_addr()),
}),
_ => Err(Error::Unrecognized),
match (packet.hardware_type(), packet.protocol_type(),
packet.hardware_len(), packet.protocol_len()) {
(Hardware::Ethernet, Protocol::Ipv4, 6, 4) => {
Ok(Repr::EthernetIpv4 {
operation: packet.operation(),
source_hardware_addr:
EthernetAddress::from_bytes(packet.source_hardware_addr()),
source_protocol_addr:
Ipv4Address::from_bytes(packet.source_protocol_addr()),
target_hardware_addr:
EthernetAddress::from_bytes(packet.target_hardware_addr()),
target_protocol_addr:
Ipv4Address::from_bytes(packet.target_protocol_addr())
})
},
_ => Err(Error::Unrecognized)
}
}
@ -300,10 +300,8 @@ impl Repr {
match *self {
Repr::EthernetIpv4 {
operation,
source_hardware_addr,
source_protocol_addr,
target_hardware_addr,
target_protocol_addr,
source_hardware_addr, source_protocol_addr,
target_hardware_addr, target_protocol_addr
} => {
packet.set_hardware_type(Hardware::Ethernet);
packet.set_protocol_type(Protocol::Ipv4);
@ -314,7 +312,7 @@ impl Repr {
packet.set_source_protocol_addr(source_protocol_addr.as_bytes());
packet.set_target_hardware_addr(target_hardware_addr.as_bytes());
packet.set_target_protocol_addr(target_protocol_addr.as_bytes());
}
},
}
}
}
@ -325,23 +323,13 @@ impl<T: AsRef<[u8]>> fmt::Display for Packet<T> {
Ok(repr) => write!(f, "{}", repr),
_ => {
write!(f, "ARP (unrecognized)")?;
write!(
f,
" htype={:?} ptype={:?} hlen={:?} plen={:?} op={:?}",
self.hardware_type(),
self.protocol_type(),
self.hardware_len(),
self.protocol_len(),
self.operation()
)?;
write!(
f,
" sha={:?} spa={:?} tha={:?} tpa={:?}",
self.source_hardware_addr(),
self.source_protocol_addr(),
self.target_hardware_addr(),
self.target_protocol_addr()
)?;
write!(f, " htype={:?} ptype={:?} hlen={:?} plen={:?} op={:?}",
self.hardware_type(), self.protocol_type(),
self.hardware_len(), self.protocol_len(),
self.operation())?;
write!(f, " sha={:?} spa={:?} tha={:?} tpa={:?}",
self.source_hardware_addr(), self.source_protocol_addr(),
self.target_hardware_addr(), self.target_protocol_addr())?;
Ok(())
}
}
@ -353,36 +341,26 @@ impl fmt::Display for Repr {
match *self {
Repr::EthernetIpv4 {
operation,
source_hardware_addr,
source_protocol_addr,
target_hardware_addr,
target_protocol_addr,
source_hardware_addr, source_protocol_addr,
target_hardware_addr, target_protocol_addr
} => {
write!(
f,
"ARP type=Ethernet+IPv4 src={}/{} tgt={}/{} op={:?}",
source_hardware_addr,
source_protocol_addr,
target_hardware_addr,
target_protocol_addr,
operation
)
}
write!(f, "ARP type=Ethernet+IPv4 src={}/{} tgt={}/{} op={:?}",
source_hardware_addr, source_protocol_addr,
target_hardware_addr, target_protocol_addr,
operation)
},
}
}
}
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result {
match Packet::new_checked(buffer) {
Err(err) => write!(f, "{}({})", indent, err),
Ok(packet) => write!(f, "{}{}", indent, packet),
Ok(packet) => write!(f, "{}{}", indent, packet)
}
}
}
@ -391,10 +369,16 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
mod test {
use super::*;
static PACKET_BYTES: [u8; 28] = [
0x00, 0x01, 0x08, 0x00, 0x06, 0x04, 0x00, 0x01, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x21,
0x22, 0x23, 0x24, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x41, 0x42, 0x43, 0x44,
];
static PACKET_BYTES: [u8; 28] =
[0x00, 0x01,
0x08, 0x00,
0x06,
0x04,
0x00, 0x01,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
0x21, 0x22, 0x23, 0x24,
0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
0x41, 0x42, 0x43, 0x44];
#[test]
fn test_deconstruct() {
@ -404,15 +388,9 @@ mod test {
assert_eq!(packet.hardware_len(), 6);
assert_eq!(packet.protocol_len(), 4);
assert_eq!(packet.operation(), Operation::Request);
assert_eq!(
packet.source_hardware_addr(),
&[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]
);
assert_eq!(packet.source_hardware_addr(), &[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]);
assert_eq!(packet.source_protocol_addr(), &[0x21, 0x22, 0x23, 0x24]);
assert_eq!(
packet.target_hardware_addr(),
&[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]
);
assert_eq!(packet.target_hardware_addr(), &[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]);
assert_eq!(packet.target_protocol_addr(), &[0x41, 0x42, 0x43, 0x44]);
}
@ -435,14 +413,14 @@ mod test {
fn packet_repr() -> Repr {
Repr::EthernetIpv4 {
operation: Operation::Request,
source_hardware_addr: EthernetAddress::from_bytes(&[
0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
]),
source_protocol_addr: Ipv4Address::from_bytes(&[0x21, 0x22, 0x23, 0x24]),
target_hardware_addr: EthernetAddress::from_bytes(&[
0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
]),
target_protocol_addr: Ipv4Address::from_bytes(&[0x41, 0x42, 0x43, 0x44]),
source_hardware_addr:
EthernetAddress::from_bytes(&[0x11, 0x12, 0x13, 0x14, 0x15, 0x16]),
source_protocol_addr:
Ipv4Address::from_bytes(&[0x21, 0x22, 0x23, 0x24]),
target_hardware_addr:
EthernetAddress::from_bytes(&[0x31, 0x32, 0x33, 0x34, 0x35, 0x36]),
target_protocol_addr:
Ipv4Address::from_bytes(&[0x41, 0x42, 0x43, 0x44])
}
}

View File

@ -1,18 +1,15 @@
// See https://tools.ietf.org/html/rfc2131 for the DHCP specification.
use bitflags::bitflags;
use byteorder::{ByteOrder, NetworkEndian};
use crate::wire::arp::Hardware;
use crate::wire::{EthernetAddress, Ipv4Address};
use crate::{Error, Result};
pub const SERVER_PORT: u16 = 67;
pub const CLIENT_PORT: u16 = 68;
pub const MAX_DNS_SERVER_COUNT: usize = 3;
use crate::wire::{EthernetAddress, Ipv4Address};
use crate::wire::arp::Hardware;
const DHCP_MAGIC_NUMBER: u32 = 0x63825363;
pub const MAX_DNS_SERVERS: usize = 3;
enum_with_unknown! {
/// The possible opcodes of a DHCP packet.
pub enum OpCode(u8) {
@ -35,20 +32,11 @@ enum_with_unknown! {
}
}
bitflags! {
pub struct Flags: u16 {
const BROADCAST = 0b1000_0000_0000_0000;
}
}
impl MessageType {
fn opcode(&self) -> OpCode {
match *self {
MessageType::Discover
| MessageType::Inform
| MessageType::Request
| MessageType::Decline
| MessageType::Release => OpCode::Request,
MessageType::Discover | MessageType::Inform | MessageType::Request |
MessageType::Decline | MessageType::Release => OpCode::Request,
MessageType::Offer | MessageType::Ack | MessageType::Nak => OpCode::Reply,
MessageType::Unknown(_) => OpCode::Unknown(0),
}
@ -57,7 +45,6 @@ impl MessageType {
/// A representation of a single DHCP option.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum DhcpOption<'a> {
EndOfList,
Pad,
@ -69,7 +56,7 @@ pub enum DhcpOption<'a> {
Router(Ipv4Address),
SubnetMask(Ipv4Address),
MaximumDhcpMessageSize(u16),
Other { kind: u8, data: &'a [u8] },
Other { kind: u8, data: &'a [u8] }
}
impl<'a> DhcpOption<'a> {
@ -91,10 +78,12 @@ impl<'a> DhcpOption<'a> {
skip_len = length + 2;
let data = buffer.get(2..skip_len).ok_or(Error::Truncated)?;
match (kind, length) {
(field::OPT_END, _) | (field::OPT_PAD, _) => unreachable!(),
(field::OPT_END, _) |
(field::OPT_PAD, _) =>
unreachable!(),
(field::OPT_DHCP_MESSAGE_TYPE, 1) => {
option = DhcpOption::MessageType(MessageType::from(data[0]));
}
},
(field::OPT_REQUESTED_IP, 4) => {
option = DhcpOption::RequestedIp(Ipv4Address::from_bytes(data));
}
@ -103,8 +92,7 @@ impl<'a> DhcpOption<'a> {
if hardware_type != Hardware::Ethernet {
return Err(Error::Unrecognized);
}
option =
DhcpOption::ClientIdentifier(EthernetAddress::from_bytes(&data[1..]));
option = DhcpOption::ClientIdentifier(EthernetAddress::from_bytes(&data[1..]));
}
(field::OPT_SERVER_IDENTIFIER, 4) => {
option = DhcpOption::ServerIdentifier(Ipv4Address::from_bytes(data));
@ -116,20 +104,13 @@ impl<'a> DhcpOption<'a> {
option = DhcpOption::SubnetMask(Ipv4Address::from_bytes(data));
}
(field::OPT_MAX_DHCP_MESSAGE_SIZE, 2) => {
option = DhcpOption::MaximumDhcpMessageSize(u16::from_be_bytes([
data[0], data[1],
]));
option = DhcpOption::MaximumDhcpMessageSize(u16::from_be_bytes([data[0], data[1]]));
}
(field::OPT_IP_LEASE_TIME, 4) => {
option = DhcpOption::IpLeaseTime(u32::from_be_bytes([
data[0], data[1], data[2], data[3],
]))
option = DhcpOption::IpLeaseTime(u32::from_be_bytes([data[0], data[1], data[2], data[3]]))
}
(_, _) => {
option = DhcpOption::Other {
kind: kind,
data: data,
};
option = DhcpOption::Other { kind: kind, data: data };
}
}
}
@ -142,14 +123,20 @@ impl<'a> DhcpOption<'a> {
&DhcpOption::EndOfList => 1,
&DhcpOption::Pad => 1,
&DhcpOption::MessageType(_) => 3,
&DhcpOption::ClientIdentifier(eth_addr) => 3 + eth_addr.as_bytes().len(),
&DhcpOption::RequestedIp(ip)
| &DhcpOption::ServerIdentifier(ip)
| &DhcpOption::Router(ip)
| &DhcpOption::SubnetMask(ip) => 2 + ip.as_bytes().len(),
&DhcpOption::MaximumDhcpMessageSize(_) => 4,
&DhcpOption::ClientIdentifier(eth_addr) => {
3 + eth_addr.as_bytes().len()
}
&DhcpOption::RequestedIp(ip) |
&DhcpOption::ServerIdentifier(ip) |
&DhcpOption::Router(ip) |
&DhcpOption::SubnetMask(ip) => {
2 + ip.as_bytes().len()
},
&DhcpOption::MaximumDhcpMessageSize(_) => {
4
}
&DhcpOption::IpLeaseTime(_) => 6,
&DhcpOption::Other { data, .. } => 2 + data.len(),
&DhcpOption::Other { data, .. } => 2 + data.len()
}
}
@ -178,19 +165,19 @@ impl<'a> DhcpOption<'a> {
buffer[2] = u16::from(Hardware::Ethernet) as u8;
buffer[3..9].copy_from_slice(eth_addr.as_bytes());
}
DhcpOption::RequestedIp(ip) => {
DhcpOption::RequestedIp(ip) => {
buffer[0] = field::OPT_REQUESTED_IP;
buffer[2..6].copy_from_slice(ip.as_bytes());
}
DhcpOption::ServerIdentifier(ip) => {
DhcpOption::ServerIdentifier(ip) => {
buffer[0] = field::OPT_SERVER_IDENTIFIER;
buffer[2..6].copy_from_slice(ip.as_bytes());
}
DhcpOption::Router(ip) => {
DhcpOption::Router(ip) => {
buffer[0] = field::OPT_ROUTER;
buffer[2..6].copy_from_slice(ip.as_bytes());
}
DhcpOption::SubnetMask(mask) => {
DhcpOption::SubnetMask(mask) => {
buffer[0] = field::OPT_SUBNET_MASK;
buffer[2..6].copy_from_slice(mask.as_bytes());
}
@ -202,10 +189,7 @@ impl<'a> DhcpOption<'a> {
buffer[0] = field::OPT_IP_LEASE_TIME;
buffer[2..6].copy_from_slice(&lease_time.to_be_bytes()[..]);
}
DhcpOption::Other {
kind,
data: provided,
} => {
DhcpOption::Other { kind, data: provided } => {
buffer[0] = kind;
buffer[2..skip_length].copy_from_slice(provided);
}
@ -218,9 +202,8 @@ impl<'a> DhcpOption<'a> {
/// A read/write wrapper around a Dynamic Host Configuration Protocol packet buffer.
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> {
buffer: T,
buffer: T
}
pub(crate) mod field {
@ -459,9 +442,10 @@ impl<T: AsRef<[u8]>> Packet<T> {
Ipv4Address::from_bytes(field)
}
pub fn flags(&self) -> Flags {
/// Returns true if the broadcast flag is set.
pub fn broadcast_flag(&self) -> bool {
let field = &self.buffer.as_ref()[field::FLAGS];
Flags::from_bits_truncate(NetworkEndian::read_u16(field))
NetworkEndian::read_u16(field) & 0b1 == 0b1
}
}
@ -581,10 +565,10 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
field.copy_from_slice(value.as_bytes());
}
/// Sets the flags to the specified value.
pub fn set_flags(&mut self, val: Flags) {
/// Sets the broadcast flag to the specified value.
pub fn set_broadcast_flag(&mut self, value: bool) {
let field = &mut self.buffer.as_mut()[field::FLAGS];
NetworkEndian::write_u16(field, val.bits());
NetworkEndian::write_u16(field, if value { 1 } else { 0 });
}
}
@ -642,7 +626,6 @@ impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> Packet<&'a mut T> {
///
/// The `options` field has a variable length.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Repr<'a> {
/// This field is also known as `op` in the RFC. It indicates the type of DHCP message this
/// packet represents.
@ -699,11 +682,11 @@ pub struct Repr<'a> {
/// the client is interested in.
pub parameter_request_list: Option<&'a [u8]>,
/// DNS servers
pub dns_servers: Option<[Option<Ipv4Address>; MAX_DNS_SERVER_COUNT]>,
pub dns_servers: Option<[Option<Ipv4Address>; MAX_DNS_SERVERS]>,
/// The maximum size dhcp packet the interface can receive
pub max_size: Option<u16>,
/// The DHCP IP lease duration, specified in seconds.
pub lease_duration: Option<u32>,
pub lease_duration: Option<u32>
}
impl<'a> Repr<'a> {
@ -712,43 +695,26 @@ impl<'a> Repr<'a> {
let mut len = field::OPTIONS.start;
// message type and end-of-options options
len += 3 + 1;
if self.requested_ip.is_some() {
len += 6;
}
if self.client_identifier.is_some() {
len += 9;
}
if self.server_identifier.is_some() {
len += 6;
}
if self.max_size.is_some() {
len += 4;
}
if self.router.is_some() {
len += 6;
}
if self.subnet_mask.is_some() {
len += 6;
}
if self.lease_duration.is_some() {
len += 6;
}
if self.requested_ip.is_some() { len += 6; }
if self.client_identifier.is_some() { len += 9; }
if self.server_identifier.is_some() { len += 6; }
if self.max_size.is_some() { len += 4; }
if self.router.is_some() { len += 6; }
if self.subnet_mask.is_some() { len += 6; }
if self.lease_duration.is_some() { len += 6; }
if let Some(dns_servers) = self.dns_servers {
len += 2;
len += dns_servers.iter().flatten().count() * core::mem::size_of::<u32>();
}
if let Some(list) = self.parameter_request_list {
len += list.len() + 2;
}
if let Some(list) = self.parameter_request_list { len += list.len() + 2; }
len
}
/// Parse a DHCP packet and return a high-level representation.
pub fn parse<T>(packet: &Packet<&'a T>) -> Result<Self>
where
T: AsRef<[u8]> + ?Sized,
{
where T: AsRef<[u8]> + ?Sized {
let transaction_id = packet.transaction_id();
let client_hardware_address = packet.client_hardware_address();
let client_ip = packet.client_ip();
@ -786,12 +752,12 @@ impl<'a> Repr<'a> {
let (next_options, option) = DhcpOption::parse(options)?;
match option {
DhcpOption::EndOfList => break,
DhcpOption::Pad => {}
DhcpOption::Pad => {},
DhcpOption::MessageType(value) => {
if value.opcode() == packet.opcode() {
message_type = Ok(value);
}
}
},
DhcpOption::RequestedIp(ip) => {
requested_ip = Some(ip);
}
@ -806,56 +772,34 @@ impl<'a> Repr<'a> {
}
DhcpOption::SubnetMask(mask) => {
subnet_mask = Some(mask);
}
},
DhcpOption::MaximumDhcpMessageSize(size) => {
max_size = Some(size);
}
DhcpOption::IpLeaseTime(duration) => {
lease_duration = Some(duration);
}
DhcpOption::Other {
kind: field::OPT_PARAMETER_REQUEST_LIST,
data,
} => {
DhcpOption::Other {kind: field::OPT_PARAMETER_REQUEST_LIST, data} => {
parameter_request_list = Some(data);
}
DhcpOption::Other {
kind: field::OPT_DOMAIN_NAME_SERVER,
data,
} => {
let mut servers = [None; MAX_DNS_SERVER_COUNT];
let chunk_size = 4;
for (server, chunk) in servers.iter_mut().zip(data.chunks(chunk_size)) {
if chunk.len() != chunk_size {
return Err(Error::Malformed);
}
DhcpOption::Other {kind: field::OPT_DOMAIN_NAME_SERVER, data} => {
let mut servers = [None; MAX_DNS_SERVERS];
for (server, chunk) in servers.iter_mut().zip(data.chunks(4)) {
*server = Some(Ipv4Address::from_bytes(chunk));
}
dns_servers = Some(servers);
}
DhcpOption::Other { .. } => {}
DhcpOption::Other {..} => {}
}
options = next_options;
}
let broadcast = packet.flags().contains(Flags::BROADCAST);
let broadcast = packet.broadcast_flag();
Ok(Repr {
transaction_id,
client_hardware_address,
client_ip,
your_ip,
server_ip,
relay_agent_ip,
broadcast,
requested_ip,
server_identifier,
router,
subnet_mask,
client_identifier,
parameter_request_list,
dns_servers,
max_size,
transaction_id, client_hardware_address, client_ip, your_ip, server_ip, relay_agent_ip,
broadcast, requested_ip, server_identifier, router,
subnet_mask, client_identifier, parameter_request_list, dns_servers, max_size,
lease_duration,
message_type: message_type?,
})
@ -864,9 +808,7 @@ impl<'a> Repr<'a> {
/// Emit a high-level representation into a Dynamic Host
/// Configuration Protocol packet.
pub fn emit<T>(&self, packet: &mut Packet<&mut T>) -> Result<()>
where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
{
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
packet.set_sname_and_boot_file_to_zero();
packet.set_opcode(self.message_type.opcode());
packet.set_hardware_type(Hardware::Ethernet);
@ -880,62 +822,48 @@ impl<'a> Repr<'a> {
packet.set_your_ip(self.your_ip);
packet.set_server_ip(self.server_ip);
packet.set_relay_agent_ip(self.relay_agent_ip);
let mut flags = Flags::empty();
if self.broadcast {
flags |= Flags::BROADCAST;
}
packet.set_flags(flags);
packet.set_broadcast_flag(self.broadcast);
{
let mut options = packet.options_mut()?;
options = DhcpOption::MessageType(self.message_type).emit(options);
let tmp = options; options = DhcpOption::MessageType(self.message_type).emit(tmp);
if let Some(eth_addr) = self.client_identifier {
options = DhcpOption::ClientIdentifier(eth_addr).emit(options);
let tmp = options; options = DhcpOption::ClientIdentifier(eth_addr).emit(tmp);
}
if let Some(ip) = self.server_identifier {
options = DhcpOption::ServerIdentifier(ip).emit(options);
let tmp = options; options = DhcpOption::ServerIdentifier(ip).emit(tmp);
}
if let Some(ip) = self.router {
options = DhcpOption::Router(ip).emit(options);
let tmp = options; options = DhcpOption::Router(ip).emit(tmp);
}
if let Some(ip) = self.subnet_mask {
options = DhcpOption::SubnetMask(ip).emit(options);
let tmp = options; options = DhcpOption::SubnetMask(ip).emit(tmp);
}
if let Some(ip) = self.requested_ip {
options = DhcpOption::RequestedIp(ip).emit(options);
let tmp = options; options = DhcpOption::RequestedIp(ip).emit(tmp);
}
if let Some(size) = self.max_size {
options = DhcpOption::MaximumDhcpMessageSize(size).emit(options);
let tmp = options; options = DhcpOption::MaximumDhcpMessageSize(size).emit(tmp);
}
if let Some(duration) = self.lease_duration {
options = DhcpOption::IpLeaseTime(duration).emit(options);
let tmp = options; options = DhcpOption::IpLeaseTime(duration).emit(tmp);
}
if let Some(dns_servers) = self.dns_servers {
const IP_SIZE: usize = core::mem::size_of::<u32>();
let mut servers = [0; MAX_DNS_SERVER_COUNT * IP_SIZE];
let mut servers = [0; MAX_DNS_SERVERS * IP_SIZE];
let data_len = dns_servers
.iter()
.flatten()
let data_len = dns_servers.iter().flatten()
.enumerate()
.inspect(|(i, ip)| {
servers[(i * IP_SIZE)..((i + 1) * IP_SIZE)].copy_from_slice(ip.as_bytes());
})
.count()
* IP_SIZE;
let option = DhcpOption::Other {
kind: field::OPT_DOMAIN_NAME_SERVER,
data: &servers[..data_len],
};
options = option.emit(options);
servers[(i * IP_SIZE)..((i + 1) * IP_SIZE)]
.copy_from_slice(ip.as_bytes());
}).count() * IP_SIZE;
let option = DhcpOption::Other{ kind: field::OPT_DOMAIN_NAME_SERVER, data: &servers[..data_len] };
let tmp = options; options = option.emit(tmp);
}
if let Some(list) = self.parameter_request_list {
options = DhcpOption::Other {
kind: field::OPT_PARAMETER_REQUEST_LIST,
data: list,
}
.emit(options);
let option = DhcpOption::Other{ kind: field::OPT_PARAMETER_REQUEST_LIST, data: list };
let tmp = options; options = option.emit(tmp);
}
DhcpOption::EndOfList.emit(options);
}
@ -946,80 +874,75 @@ impl<'a> Repr<'a> {
#[cfg(test)]
mod test {
use super::*;
use crate::wire::Ipv4Address;
use super::*;
const MAGIC_COOKIE: u32 = 0x63825363;
static DISCOVER_BYTES: &[u8] = &[
0x01, 0x01, 0x06, 0x00, 0x00, 0x00, 0x3d, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b,
0x82, 0x01, 0xfc, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
0x35, 0x01, 0x01, 0x3d, 0x07, 0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42, 0x32, 0x04, 0x00,
0x00, 0x00, 0x00, 0x39, 0x2, 0x5, 0xdc, 0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x01, 0x06, 0x00, 0x00, 0x00, 0x3d, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x82, 0x01,
0xfc, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
0x35, 0x01, 0x01, 0x3d, 0x07, 0x01, 0x00, 0x0b, 0x82, 0x01, 0xfc, 0x42, 0x32, 0x04, 0x00, 0x00,
0x00, 0x00, 0x39, 0x2, 0x5, 0xdc, 0x37, 0x04, 0x01, 0x03, 0x06, 0x2a, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
static ACK_DNS_SERVER_BYTES: &[u8] = &[
0x02, 0x01, 0x06, 0x00, 0xcc, 0x34, 0x75, 0xab, 0x00, 0x00, 0x80, 0x00, 0x0a, 0xff, 0x06,
0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xff, 0x06, 0xfe, 0x34, 0x17,
0xeb, 0xc9, 0xaa, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
0x35, 0x01, 0x05, 0x36, 0x04, 0xa3, 0x01, 0x4a, 0x16, 0x01, 0x04, 0xff, 0xff, 0xff, 0x00,
0x2b, 0x05, 0xdc, 0x03, 0x4e, 0x41, 0x50, 0x0f, 0x15, 0x6e, 0x61, 0x74, 0x2e, 0x70, 0x68,
0x79, 0x73, 0x69, 0x63, 0x73, 0x2e, 0x6f, 0x78, 0x2e, 0x61, 0x63, 0x2e, 0x75, 0x6b, 0x00,
0x03, 0x04, 0x0a, 0xff, 0x06, 0xfe, 0x06, 0x10, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a,
0x07, 0xa3, 0x01, 0x4a, 0x03, 0xa3, 0x01, 0x4a, 0x04, 0x2c, 0x10, 0xa3, 0x01, 0x4a, 0x03,
0xa3, 0x01, 0x4a, 0x04, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0x2e, 0x01, 0x08,
0xff,
0x02, 0x01, 0x06, 0x00, 0xcc, 0x34, 0x75, 0xab, 0x00, 0x00, 0x80, 0x00, 0x0a, 0xff, 0x06, 0x91,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xff, 0x06, 0xfe, 0x34, 0x17, 0xeb, 0xc9,
0xaa, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
0x35, 0x01, 0x05, 0x36, 0x04, 0xa3, 0x01, 0x4a, 0x16, 0x01, 0x04, 0xff, 0xff, 0xff, 0x00, 0x2b,
0x05, 0xdc, 0x03, 0x4e, 0x41, 0x50, 0x0f, 0x15, 0x6e, 0x61, 0x74, 0x2e, 0x70, 0x68, 0x79, 0x73,
0x69, 0x63, 0x73, 0x2e, 0x6f, 0x78, 0x2e, 0x61, 0x63, 0x2e, 0x75, 0x6b, 0x00, 0x03, 0x04, 0x0a,
0xff, 0x06, 0xfe, 0x06, 0x10, 0xa3, 0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0xa3, 0x01, 0x4a,
0x03, 0xa3, 0x01, 0x4a, 0x04, 0x2c, 0x10, 0xa3, 0x01, 0x4a, 0x03, 0xa3, 0x01, 0x4a, 0x04, 0xa3,
0x01, 0x4a, 0x06, 0xa3, 0x01, 0x4a, 0x07, 0x2e, 0x01, 0x08, 0xff
];
static ACK_LEASE_TIME_BYTES: &[u8] = &[
0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x0a, 0x22, 0x10, 0x0b, 0x0a, 0x22, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x04, 0x91,
0x62, 0xd2, 0xa8, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
0x35, 0x01, 0x05, 0x36, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0x33, 0x04, 0x00, 0x00, 0x02, 0x56,
0x01, 0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0xff, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0a, 0x22, 0x10, 0x0b, 0x0a, 0x22, 0x10, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x04, 0x91, 0x62, 0xd2,
0xa8, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x82, 0x53, 0x63,
0x35, 0x01, 0x05, 0x36, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0x33, 0x04, 0x00, 0x00, 0x02, 0x56, 0x01,
0x04, 0xff, 0xff, 0xff, 0x00, 0x03, 0x04, 0x0a, 0x22, 0x10, 0x0a, 0xff, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
const IP_NULL: Ipv4Address = Ipv4Address([0, 0, 0, 0]);
@ -1061,13 +984,9 @@ mod test {
assert_eq!(options.len(), 6 + 1 + 7);
let (options, client_id) = DhcpOption::parse(options).unwrap();
assert_eq!(
client_id,
DhcpOption::Other {
kind: field::OPT_PARAMETER_REQUEST_LIST,
data: &[1, 3, 6, 42]
}
);
assert_eq!(client_id, DhcpOption::Other {
kind: field::OPT_PARAMETER_REQUEST_LIST, data: &[1, 3, 6, 42]
});
assert_eq!(options.len(), 1 + 7);
let (options, client_id) = DhcpOption::parse(options).unwrap();
@ -1087,7 +1006,7 @@ mod test {
packet.set_hops(0);
packet.set_transaction_id(0x3d1d);
packet.set_secs(0);
packet.set_flags(Flags::empty());
packet.set_broadcast_flag(false);
packet.set_client_ip(IP_NULL);
packet.set_your_ip(IP_NULL);
packet.set_server_ip(IP_NULL);
@ -1096,15 +1015,14 @@ mod test {
{
let mut options = packet.options_mut().unwrap();
options = DhcpOption::MessageType(MessageType::Discover).emit(options);
options = DhcpOption::ClientIdentifier(CLIENT_MAC).emit(options);
options = DhcpOption::RequestedIp(IP_NULL).emit(options);
options = DhcpOption::MaximumDhcpMessageSize(DHCP_SIZE).emit(options);
let tmp = options; options = DhcpOption::MessageType(MessageType::Discover).emit(tmp);
let tmp = options; options = DhcpOption::ClientIdentifier(CLIENT_MAC).emit(tmp);
let tmp = options; options = DhcpOption::RequestedIp(IP_NULL).emit(tmp);
let tmp = options; options = DhcpOption::MaximumDhcpMessageSize(DHCP_SIZE).emit(tmp);
let option = DhcpOption::Other {
kind: field::OPT_PARAMETER_REQUEST_LIST,
data: &[1, 3, 6, 42],
kind: field::OPT_PARAMETER_REQUEST_LIST, data: &[1, 3, 6, 42],
};
options = option.emit(options);
let tmp = options; options = option.emit(tmp);
DhcpOption::EndOfList.emit(options);
}
@ -1194,10 +1112,9 @@ mod test {
let repr = {
let mut repr = offer_repr();
repr.dns_servers = Some([
Some(Ipv4Address([163, 1, 74, 6])),
Some(Ipv4Address([163, 1, 74, 7])),
Some(Ipv4Address([163, 1, 74, 3])),
]);
Some(Ipv4Address([163, 1, 74, 6])),
Some(Ipv4Address([163, 1, 74, 7])),
Some(Ipv4Address([163, 1, 74, 3]))]);
repr
};
let mut bytes = vec![0xa5; repr.buffer_len()];
@ -1207,14 +1124,10 @@ mod test {
let packet = Packet::new_unchecked(&bytes);
let repr_parsed = Repr::parse(&packet).unwrap();
assert_eq!(
repr_parsed.dns_servers,
Some([
Some(Ipv4Address([163, 1, 74, 6])),
Some(Ipv4Address([163, 1, 74, 7])),
Some(Ipv4Address([163, 1, 74, 3]))
])
);
assert_eq!(repr_parsed.dns_servers, Some([
Some(Ipv4Address([163, 1, 74, 6])),
Some(Ipv4Address([163, 1, 74, 7])),
Some(Ipv4Address([163, 1, 74, 3]))]));
}
#[test]
@ -1229,10 +1142,7 @@ mod test {
let rest = dhcp_option.emit(&mut bytes);
assert_eq!(rest.len(), 0);
}
assert_eq!(
&bytes[0..2],
&[field::OPT_PARAMETER_REQUEST_LIST, DATA.len() as u8]
);
assert_eq!(&bytes[0..2], &[field::OPT_PARAMETER_REQUEST_LIST, DATA.len() as u8]);
assert_eq!(&bytes[2..], DATA);
}
@ -1244,14 +1154,10 @@ mod test {
// The packet described by ACK_BYTES advertises 4 DNS servers
// Here we ensure that we correctly parse the first 3 into our fixed
// length-3 array (see issue #305)
assert_eq!(
repr.dns_servers,
Some([
Some(Ipv4Address([163, 1, 74, 6])),
Some(Ipv4Address([163, 1, 74, 7])),
Some(Ipv4Address([163, 1, 74, 3]))
])
);
assert_eq!(repr.dns_servers, Some([
Some(Ipv4Address([163, 1, 74, 6])),
Some(Ipv4Address([163, 1, 74, 7])),
Some(Ipv4Address([163, 1, 74, 3]))]));
}
#[test]

View File

@ -1,5 +1,5 @@
use byteorder::{ByteOrder, NetworkEndian};
use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
use crate::{Error, Result};
@ -17,15 +17,14 @@ impl fmt::Display for EtherType {
match *self {
EtherType::Ipv4 => write!(f, "IPv4"),
EtherType::Ipv6 => write!(f, "IPv6"),
EtherType::Arp => write!(f, "ARP"),
EtherType::Unknown(id) => write!(f, "0x{:04x}", id),
EtherType::Arp => write!(f, "ARP"),
EtherType::Unknown(id) => write!(f, "0x{:04x}", id)
}
}
}
/// A six-octet Ethernet II address.
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Address(pub [u8; 6]);
impl Address {
@ -49,7 +48,8 @@ impl Address {
/// Query whether the address is an unicast address.
pub fn is_unicast(&self) -> bool {
!(self.is_broadcast() || self.is_multicast())
!(self.is_broadcast() ||
self.is_multicast())
}
/// Query whether this address is the broadcast address.
@ -71,28 +71,24 @@ impl Address {
impl fmt::Display for Address {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let bytes = self.0;
write!(
f,
"{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}",
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5]
)
write!(f, "{:02x}-{:02x}-{:02x}-{:02x}-{:02x}-{:02x}",
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5])
}
}
/// A read/write wrapper around an Ethernet II frame buffer.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Frame<T: AsRef<[u8]>> {
buffer: T,
buffer: T
}
mod field {
use crate::wire::field::*;
pub const DESTINATION: Field = 0..6;
pub const SOURCE: Field = 6..12;
pub const ETHERTYPE: Field = 12..14;
pub const PAYLOAD: Rest = 14..;
pub const DESTINATION: Field = 0..6;
pub const SOURCE: Field = 6..12;
pub const ETHERTYPE: Field = 12..14;
pub const PAYLOAD: Rest = 14..;
}
/// The Ethernet header length
@ -211,27 +207,19 @@ impl<T: AsRef<[u8]>> AsRef<[u8]> for Frame<T> {
impl<T: AsRef<[u8]>> fmt::Display for Frame<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"EthernetII src={} dst={} type={}",
self.src_addr(),
self.dst_addr(),
self.ethertype()
)
write!(f, "EthernetII src={} dst={} type={}",
self.src_addr(), self.dst_addr(), self.ethertype())
}
}
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
impl<T: AsRef<[u8]>> PrettyPrint for Frame<T> {
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result {
let frame = match Frame::new_checked(buffer) {
Err(err) => return write!(f, "{}({})", indent, err),
Ok(frame) => frame,
Err(err) => return write!(f, "{}({})", indent, err),
Ok(frame) => frame
};
write!(f, "{}{}", indent, frame)?;
@ -251,18 +239,17 @@ impl<T: AsRef<[u8]>> PrettyPrint for Frame<T> {
indent.increase(f)?;
super::Ipv6Packet::<&[u8]>::pretty_print(&frame.payload(), f, indent)
}
_ => Ok(()),
_ => Ok(())
}
}
}
/// A high-level representation of an Internet Protocol version 4 packet header.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Repr {
pub src_addr: Address,
pub dst_addr: Address,
pub ethertype: EtherType,
pub src_addr: Address,
pub dst_addr: Address,
pub ethertype: EtherType,
}
impl Repr {
@ -310,32 +297,32 @@ mod test_ipv4 {
// Tests that are valid only with "proto-ipv4"
use super::*;
static FRAME_BYTES: [u8; 64] = [
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x08, 0x00, 0xaa,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff,
];
static FRAME_BYTES: [u8; 64] =
[0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
0x08, 0x00,
0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff];
static PAYLOAD_BYTES: [u8; 50] = [
0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xff,
];
static PAYLOAD_BYTES: [u8; 50] =
[0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff];
#[test]
fn test_deconstruct() {
let frame = Frame::new_unchecked(&FRAME_BYTES[..]);
assert_eq!(
frame.dst_addr(),
Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06])
);
assert_eq!(
frame.src_addr(),
Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16])
);
assert_eq!(frame.dst_addr(), Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]));
assert_eq!(frame.src_addr(), Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16]));
assert_eq!(frame.ethertype(), EtherType::Ipv4);
assert_eq!(frame.payload(), &PAYLOAD_BYTES[..]);
}
@ -358,30 +345,28 @@ mod test_ipv6 {
// Tests that are valid only with "proto-ipv6"
use super::*;
static FRAME_BYTES: [u8; 54] = [
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x86, 0xdd, 0x60,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
];
static FRAME_BYTES: [u8; 54] =
[0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
0x86, 0xdd,
0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01];
static PAYLOAD_BYTES: [u8; 40] = [
0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
];
static PAYLOAD_BYTES: [u8; 40] =
[0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01];
#[test]
fn test_deconstruct() {
let frame = Frame::new_unchecked(&FRAME_BYTES[..]);
assert_eq!(
frame.dst_addr(),
Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06])
);
assert_eq!(
frame.src_addr(),
Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16])
);
assert_eq!(frame.dst_addr(), Address([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]));
assert_eq!(frame.src_addr(), Address([0x11, 0x12, 0x13, 0x14, 0x15, 0x16]));
assert_eq!(frame.ethertype(), EtherType::Ipv6);
assert_eq!(frame.payload(), &PAYLOAD_BYTES[..]);
}

View File

@ -4,7 +4,6 @@ use crate::wire::icmpv4;
use crate::wire::icmpv6;
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Repr<'a> {
#[cfg(feature = "proto-ipv4")]
Ipv4(icmpv4::Repr<'a>),

View File

@ -1,10 +1,10 @@
use byteorder::{ByteOrder, NetworkEndian};
use core::{cmp, fmt};
use byteorder::{ByteOrder, NetworkEndian};
use crate::{Error, Result};
use crate::phy::ChecksumCapabilities;
use crate::wire::ip::checksum;
use crate::wire::{Ipv4Packet, Ipv4Repr};
use crate::{Error, Result};
enum_with_unknown! {
/// Internet protocol control message type.
@ -35,17 +35,17 @@ enum_with_unknown! {
impl fmt::Display for Message {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Message::EchoReply => write!(f, "echo reply"),
Message::EchoReply => write!(f, "echo reply"),
Message::DstUnreachable => write!(f, "destination unreachable"),
Message::Redirect => write!(f, "message redirect"),
Message::EchoRequest => write!(f, "echo request"),
Message::RouterAdvert => write!(f, "router advertisement"),
Message::RouterSolicit => write!(f, "router solicitation"),
Message::TimeExceeded => write!(f, "time exceeded"),
Message::ParamProblem => write!(f, "parameter problem"),
Message::Timestamp => write!(f, "timestamp"),
Message::Redirect => write!(f, "message redirect"),
Message::EchoRequest => write!(f, "echo request"),
Message::RouterAdvert => write!(f, "router advertisement"),
Message::RouterSolicit => write!(f, "router solicitation"),
Message::TimeExceeded => write!(f, "time exceeded"),
Message::ParamProblem => write!(f, "parameter problem"),
Message::Timestamp => write!(f, "timestamp"),
Message::TimestampReply => write!(f, "timestamp reply"),
Message::Unknown(id) => write!(f, "{}", id),
Message::Unknown(id) => write!(f, "{}", id)
}
}
}
@ -91,25 +91,40 @@ enum_with_unknown! {
impl fmt::Display for DstUnreachable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
DstUnreachable::NetUnreachable => write!(f, "destination network unreachable"),
DstUnreachable::HostUnreachable => write!(f, "destination host unreachable"),
DstUnreachable::ProtoUnreachable => write!(f, "destination protocol unreachable"),
DstUnreachable::PortUnreachable => write!(f, "destination port unreachable"),
DstUnreachable::FragRequired => write!(f, "fragmentation required, and DF flag set"),
DstUnreachable::SrcRouteFailed => write!(f, "source route failed"),
DstUnreachable::DstNetUnknown => write!(f, "destination network unknown"),
DstUnreachable::DstHostUnknown => write!(f, "destination host unknown"),
DstUnreachable::SrcHostIsolated => write!(f, "source host isolated"),
DstUnreachable::NetProhibited => write!(f, "network administratively prohibited"),
DstUnreachable::HostProhibited => write!(f, "host administratively prohibited"),
DstUnreachable::NetUnreachToS => write!(f, "network unreachable for ToS"),
DstUnreachable::HostUnreachToS => write!(f, "host unreachable for ToS"),
DstUnreachable::CommProhibited => {
write!(f, "communication administratively prohibited")
}
DstUnreachable::HostPrecedViol => write!(f, "host precedence violation"),
DstUnreachable::PrecedCutoff => write!(f, "precedence cutoff in effect"),
DstUnreachable::Unknown(id) => write!(f, "{}", id),
DstUnreachable::NetUnreachable =>
write!(f, "destination network unreachable"),
DstUnreachable::HostUnreachable =>
write!(f, "destination host unreachable"),
DstUnreachable::ProtoUnreachable =>
write!(f, "destination protocol unreachable"),
DstUnreachable::PortUnreachable =>
write!(f, "destination port unreachable"),
DstUnreachable::FragRequired =>
write!(f, "fragmentation required, and DF flag set"),
DstUnreachable::SrcRouteFailed =>
write!(f, "source route failed"),
DstUnreachable::DstNetUnknown =>
write!(f, "destination network unknown"),
DstUnreachable::DstHostUnknown =>
write!(f, "destination host unknown"),
DstUnreachable::SrcHostIsolated =>
write!(f, "source host isolated"),
DstUnreachable::NetProhibited =>
write!(f, "network administratively prohibited"),
DstUnreachable::HostProhibited =>
write!(f, "host administratively prohibited"),
DstUnreachable::NetUnreachToS =>
write!(f, "network unreachable for ToS"),
DstUnreachable::HostUnreachToS =>
write!(f, "host unreachable for ToS"),
DstUnreachable::CommProhibited =>
write!(f, "communication administratively prohibited"),
DstUnreachable::HostPrecedViol =>
write!(f, "host precedence violation"),
DstUnreachable::PrecedCutoff =>
write!(f, "precedence cutoff in effect"),
DstUnreachable::Unknown(id) =>
write!(f, "{}", id)
}
}
}
@ -152,19 +167,18 @@ enum_with_unknown! {
/// A read/write wrapper around an Internet Control Message Protocol version 4 packet buffer.
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> {
buffer: T,
buffer: T
}
mod field {
use crate::wire::field::*;
pub const TYPE: usize = 0;
pub const CODE: usize = 1;
pub const CHECKSUM: Field = 2..4;
pub const TYPE: usize = 0;
pub const CODE: usize = 1;
pub const CHECKSUM: Field = 2..4;
pub const UNUSED: Field = 4..8;
pub const UNUSED: Field = 4..8;
pub const ECHO_IDENT: Field = 4..6;
pub const ECHO_SEQNO: Field = 6..8;
@ -253,10 +267,10 @@ impl<T: AsRef<[u8]>> Packet<T> {
/// The result depends on the value of the message type field.
pub fn header_len(&self) -> usize {
match self.msg_type() {
Message::EchoRequest => field::ECHO_SEQNO.end,
Message::EchoReply => field::ECHO_SEQNO.end,
Message::EchoRequest => field::ECHO_SEQNO.end,
Message::EchoReply => field::ECHO_SEQNO.end,
Message::DstUnreachable => field::UNUSED.end,
_ => field::UNUSED.end, // make a conservative assumption
_ => field::UNUSED.end // make a conservative assumption
}
}
@ -265,9 +279,7 @@ impl<T: AsRef<[u8]>> Packet<T> {
/// # Fuzzing
/// This function always returns `true` when fuzzing.
pub fn verify_checksum(&self) -> bool {
if cfg!(fuzzing) {
return true;
}
if cfg!(fuzzing) { return true }
let data = self.buffer.as_ref();
checksum::data(data) == !0
@ -354,53 +366,50 @@ impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
/// A high-level representation of an Internet Control Message Protocol version 4 packet header.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Repr<'a> {
EchoRequest {
ident: u16,
ident: u16,
seq_no: u16,
data: &'a [u8],
data: &'a [u8]
},
EchoReply {
ident: u16,
ident: u16,
seq_no: u16,
data: &'a [u8],
data: &'a [u8]
},
DstUnreachable {
reason: DstUnreachable,
header: Ipv4Repr,
data: &'a [u8],
data: &'a [u8]
},
}
impl<'a> Repr<'a> {
/// Parse an Internet Control Message Protocol version 4 packet and return
/// a high-level representation.
pub fn parse<T>(
packet: &Packet<&'a T>,
checksum_caps: &ChecksumCapabilities,
) -> Result<Repr<'a>>
where
T: AsRef<[u8]> + ?Sized,
{
pub fn parse<T>(packet: &Packet<&'a T>, checksum_caps: &ChecksumCapabilities)
-> Result<Repr<'a>>
where T: AsRef<[u8]> + ?Sized {
// Valid checksum is expected.
if checksum_caps.icmpv4.rx() && !packet.verify_checksum() {
return Err(Error::Checksum);
}
if checksum_caps.icmpv4.rx() && !packet.verify_checksum() { return Err(Error::Checksum) }
match (packet.msg_type(), packet.msg_code()) {
(Message::EchoRequest, 0) => Ok(Repr::EchoRequest {
ident: packet.echo_ident(),
seq_no: packet.echo_seq_no(),
data: packet.data(),
}),
(Message::EchoRequest, 0) => {
Ok(Repr::EchoRequest {
ident: packet.echo_ident(),
seq_no: packet.echo_seq_no(),
data: packet.data()
})
},
(Message::EchoReply, 0) => Ok(Repr::EchoReply {
ident: packet.echo_ident(),
seq_no: packet.echo_seq_no(),
data: packet.data(),
}),
(Message::EchoReply, 0) => {
Ok(Repr::EchoReply {
ident: packet.echo_ident(),
seq_no: packet.echo_seq_no(),
data: packet.data()
})
},
(Message::DstUnreachable, code) => {
let ip_packet = Ipv4Packet::new_checked(packet.data())?;
@ -408,9 +417,7 @@ impl<'a> Repr<'a> {
let payload = &packet.data()[ip_packet.header_len() as usize..];
// RFC 792 requires exactly eight bytes to be returned.
// We allow more, since there isn't a reason not to, but require at least eight.
if payload.len() < 8 {
return Err(Error::Truncated);
}
if payload.len() < 8 { return Err(Error::Truncated) }
Ok(Repr::DstUnreachable {
reason: DstUnreachable::from(code),
@ -419,21 +426,22 @@ impl<'a> Repr<'a> {
dst_addr: ip_packet.dst_addr(),
protocol: ip_packet.protocol(),
payload_len: payload.len(),
hop_limit: ip_packet.hop_limit(),
hop_limit: ip_packet.hop_limit()
},
data: payload,
data: payload
})
}
_ => Err(Error::Unrecognized),
_ => Err(Error::Unrecognized)
}
}
/// Return the length of a packet that will be emitted from this high-level representation.
pub fn buffer_len(&self) -> usize {
match self {
&Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => {
&Repr::EchoRequest { data, .. } |
&Repr::EchoReply { data, .. } => {
field::ECHO_SEQNO.end + data.len()
}
},
&Repr::DstUnreachable { header, data, .. } => {
field::UNUSED.end + header.buffer_len() + data.len()
}
@ -443,49 +451,35 @@ impl<'a> Repr<'a> {
/// Emit a high-level representation into an Internet Control Message Protocol version 4
/// packet.
pub fn emit<T>(&self, packet: &mut Packet<&mut T>, checksum_caps: &ChecksumCapabilities)
where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
{
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
packet.set_msg_code(0);
match *self {
Repr::EchoRequest {
ident,
seq_no,
data,
} => {
Repr::EchoRequest { ident, seq_no, data } => {
packet.set_msg_type(Message::EchoRequest);
packet.set_msg_code(0);
packet.set_echo_ident(ident);
packet.set_echo_seq_no(seq_no);
let data_len = cmp::min(packet.data_mut().len(), data.len());
packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
}
},
Repr::EchoReply {
ident,
seq_no,
data,
} => {
Repr::EchoReply { ident, seq_no, data } => {
packet.set_msg_type(Message::EchoReply);
packet.set_msg_code(0);
packet.set_echo_ident(ident);
packet.set_echo_seq_no(seq_no);
let data_len = cmp::min(packet.data_mut().len(), data.len());
packet.data_mut()[..data_len].copy_from_slice(&data[..data_len])
}
},
Repr::DstUnreachable {
reason,
header,
data,
} => {
Repr::DstUnreachable { reason, header, data } => {
packet.set_msg_type(Message::DstUnreachable);
packet.set_msg_code(reason.into());
let mut ip_packet = Ipv4Packet::new_unchecked(packet.data_mut());
header.emit(&mut ip_packet, checksum_caps);
let payload = &mut ip_packet.into_inner()[header.buffer_len()..];
payload.copy_from_slice(data)
payload.copy_from_slice(&data[..])
}
}
@ -507,10 +501,9 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
write!(f, "ICMPv4 ({})", err)?;
write!(f, " type={:?}", self.msg_type())?;
match self.msg_type() {
Message::DstUnreachable => {
write!(f, " code={:?}", DstUnreachable::from(self.msg_code()))
}
_ => write!(f, " code={}", self.msg_code()),
Message::DstUnreachable =>
write!(f, " code={:?}", DstUnreachable::from(self.msg_code())),
_ => write!(f, " code={}", self.msg_code())
}
}
}
@ -520,46 +513,27 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
impl<'a> fmt::Display for Repr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Repr::EchoRequest {
ident,
seq_no,
data,
} => write!(
f,
"ICMPv4 echo request id={} seq={} len={}",
ident,
seq_no,
data.len()
),
Repr::EchoReply {
ident,
seq_no,
data,
} => write!(
f,
"ICMPv4 echo reply id={} seq={} len={}",
ident,
seq_no,
data.len()
),
Repr::DstUnreachable { reason, .. } => {
write!(f, "ICMPv4 destination unreachable ({})", reason)
}
Repr::EchoRequest { ident, seq_no, data } =>
write!(f, "ICMPv4 echo request id={} seq={} len={}",
ident, seq_no, data.len()),
Repr::EchoReply { ident, seq_no, data } =>
write!(f, "ICMPv4 echo reply id={} seq={} len={}",
ident, seq_no, data.len()),
Repr::DstUnreachable { reason, .. } =>
write!(f, "ICMPv4 destination unreachable ({})",
reason),
}
}
}
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result {
let packet = match Packet::new_checked(buffer) {
Err(err) => return write!(f, "{}({})", indent, err),
Ok(packet) => packet,
Err(err) => return write!(f, "{}({})", indent, err),
Ok(packet) => packet
};
write!(f, "{}{}", indent, packet)?;
@ -568,7 +542,7 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
indent.increase(f)?;
super::Ipv4Packet::<&[u8]>::pretty_print(&packet.data(), f, indent)
}
_ => Ok(()),
_ => Ok(())
}
}
}
@ -577,11 +551,13 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
mod test {
use super::*;
static ECHO_PACKET_BYTES: [u8; 12] = [
0x08, 0x00, 0x8e, 0xfe, 0x12, 0x34, 0xab, 0xcd, 0xaa, 0x00, 0x00, 0xff,
];
static ECHO_PACKET_BYTES: [u8; 12] =
[0x08, 0x00, 0x8e, 0xfe,
0x12, 0x34, 0xab, 0xcd,
0xaa, 0x00, 0x00, 0xff];
static ECHO_DATA_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
static ECHO_DATA_BYTES: [u8; 4] =
[0xaa, 0x00, 0x00, 0xff];
#[test]
fn test_echo_deconstruct() {
@ -592,7 +568,7 @@ mod test {
assert_eq!(packet.echo_ident(), 0x1234);
assert_eq!(packet.echo_seq_no(), 0xabcd);
assert_eq!(packet.data(), &ECHO_DATA_BYTES[..]);
assert!(packet.verify_checksum());
assert_eq!(packet.verify_checksum(), true);
}
#[test]
@ -612,7 +588,7 @@ mod test {
Repr::EchoRequest {
ident: 0x1234,
seq_no: 0xabcd,
data: &ECHO_DATA_BYTES,
data: &ECHO_DATA_BYTES
}
}
@ -634,7 +610,8 @@ mod test {
#[test]
fn test_check_len() {
let bytes = [0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
let bytes = [0x0b, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00];
assert_eq!(Packet::new_checked(&[]), Err(Error::Truncated));
assert_eq!(Packet::new_checked(&bytes[..4]), Err(Error::Truncated));
assert!(Packet::new_checked(&bytes[..]).is_ok());

View File

@ -1,13 +1,13 @@
use byteorder::{ByteOrder, NetworkEndian};
use core::{cmp, fmt};
use byteorder::{ByteOrder, NetworkEndian};
use crate::{Error, Result};
use crate::phy::ChecksumCapabilities;
use crate::wire::ip::checksum;
use crate::wire::MldRepr;
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
use crate::wire::NdiscRepr;
use crate::wire::{IpAddress, IpProtocol, Ipv6Packet, Ipv6Repr};
use crate::{Error, Result};
use crate::wire::MldRepr;
#[cfg(feature = "ethernet")]
use crate::wire::NdiscRepr;
enum_with_unknown! {
/// Internet protocol control message type.
@ -57,11 +57,8 @@ impl Message {
/// [NDISC]: https://tools.ietf.org/html/rfc4861
pub fn is_ndisc(&self) -> bool {
match *self {
Message::RouterSolicit
| Message::RouterAdvert
| Message::NeighborSolicit
| Message::NeighborAdvert
| Message::Redirect => true,
Message::RouterSolicit | Message::RouterAdvert | Message::NeighborSolicit |
Message::NeighborAdvert | Message::Redirect => true,
_ => false,
}
}
@ -81,20 +78,20 @@ impl Message {
impl fmt::Display for Message {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Message::DstUnreachable => write!(f, "destination unreachable"),
Message::PktTooBig => write!(f, "packet too big"),
Message::TimeExceeded => write!(f, "time exceeded"),
Message::ParamProblem => write!(f, "parameter problem"),
Message::EchoReply => write!(f, "echo reply"),
Message::EchoRequest => write!(f, "echo request"),
Message::RouterSolicit => write!(f, "router solicitation"),
Message::RouterAdvert => write!(f, "router advertisement"),
Message::DstUnreachable => write!(f, "destination unreachable"),
Message::PktTooBig => write!(f, "packet too big"),
Message::TimeExceeded => write!(f, "time exceeded"),
Message::ParamProblem => write!(f, "parameter problem"),
Message::EchoReply => write!(f, "echo reply"),
Message::EchoRequest => write!(f, "echo request"),
Message::RouterSolicit => write!(f, "router solicitation"),
Message::RouterAdvert => write!(f, "router advertisement"),
Message::NeighborSolicit => write!(f, "neighbor solicitation"),
Message::NeighborAdvert => write!(f, "neighbor advert"),
Message::Redirect => write!(f, "redirect"),
Message::MldQuery => write!(f, "multicast listener query"),
Message::MldReport => write!(f, "multicast listener report"),
Message::Unknown(id) => write!(f, "{}", id),
Message::NeighborAdvert => write!(f, "neighbor advert"),
Message::Redirect => write!(f, "redirect"),
Message::MldQuery => write!(f, "multicast listener query"),
Message::MldReport => write!(f, "multicast listener report"),
Message::Unknown(id) => write!(f, "{}", id)
}
}
}
@ -122,19 +119,22 @@ enum_with_unknown! {
impl fmt::Display for DstUnreachable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
DstUnreachable::NoRoute => write!(f, "no route to destination"),
DstUnreachable::AdminProhibit => write!(
f,
"communication with destination administratively prohibited"
),
DstUnreachable::BeyondScope => write!(f, "beyond scope of source address"),
DstUnreachable::AddrUnreachable => write!(f, "address unreachable"),
DstUnreachable::PortUnreachable => write!(f, "port unreachable"),
DstUnreachable::FailedPolicy => {
write!(f, "source address failed ingress/egress policy")
}
DstUnreachable::RejectRoute => write!(f, "reject route to destination"),
DstUnreachable::Unknown(id) => write!(f, "{}", id),
DstUnreachable::NoRoute =>
write!(f, "no route to destination"),
DstUnreachable::AdminProhibit =>
write!(f, "communication with destination administratively prohibited"),
DstUnreachable::BeyondScope =>
write!(f, "beyond scope of source address"),
DstUnreachable::AddrUnreachable =>
write!(f, "address unreachable"),
DstUnreachable::PortUnreachable =>
write!(f, "port unreachable"),
DstUnreachable::FailedPolicy =>
write!(f, "source address failed ingress/egress policy"),
DstUnreachable::RejectRoute =>
write!(f, "reject route to destination"),
DstUnreachable::Unknown(id) =>
write!(f, "{}", id)
}
}
}
@ -154,10 +154,14 @@ enum_with_unknown! {
impl fmt::Display for ParamProblem {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ParamProblem::ErroneousHdrField => write!(f, "erroneous header field."),
ParamProblem::UnrecognizedNxtHdr => write!(f, "unrecognized next header type."),
ParamProblem::UnrecognizedOption => write!(f, "unrecognized IPv6 option."),
ParamProblem::Unknown(id) => write!(f, "{}", id),
ParamProblem::ErroneousHdrField =>
write!(f, "erroneous header field."),
ParamProblem::UnrecognizedNxtHdr =>
write!(f, "unrecognized next header type."),
ParamProblem::UnrecognizedOption =>
write!(f, "unrecognized IPv6 option."),
ParamProblem::Unknown(id) =>
write!(f, "{}", id)
}
}
}
@ -175,18 +179,20 @@ enum_with_unknown! {
impl fmt::Display for TimeExceeded {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
TimeExceeded::HopLimitExceeded => write!(f, "hop limit exceeded in transit"),
TimeExceeded::FragReassemExceeded => write!(f, "fragment reassembly time exceeded"),
TimeExceeded::Unknown(id) => write!(f, "{}", id),
TimeExceeded::HopLimitExceeded =>
write!(f, "hop limit exceeded in transit"),
TimeExceeded::FragReassemExceeded =>
write!(f, "fragment reassembly time exceeded"),
TimeExceeded::Unknown(id) =>
write!(f, "{}", id)
}
}
}
/// A read/write wrapper around an Internet Control Message Protocol version 6 packet buffer.
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> {
pub(super) buffer: T,
pub(super) buffer: T
}
// Ranges and constants describing key boundaries in the ICMPv6 header.
@ -194,54 +200,54 @@ pub(super) mod field {
use crate::wire::field::*;
// ICMPv6: See https://tools.ietf.org/html/rfc4443
pub const TYPE: usize = 0;
pub const CODE: usize = 1;
pub const CHECKSUM: Field = 2..4;
pub const TYPE: usize = 0;
pub const CODE: usize = 1;
pub const CHECKSUM: Field = 2..4;
pub const UNUSED: Field = 4..8;
pub const MTU: Field = 4..8;
pub const POINTER: Field = 4..8;
pub const ECHO_IDENT: Field = 4..6;
pub const ECHO_SEQNO: Field = 6..8;
pub const UNUSED: Field = 4..8;
pub const MTU: Field = 4..8;
pub const POINTER: Field = 4..8;
pub const ECHO_IDENT: Field = 4..6;
pub const ECHO_SEQNO: Field = 6..8;
pub const HEADER_END: usize = 8;
pub const HEADER_END: usize = 8;
// NDISC: See https://tools.ietf.org/html/rfc4861
// Router Advertisement message offsets
pub const CUR_HOP_LIMIT: usize = 4;
pub const ROUTER_FLAGS: usize = 5;
pub const ROUTER_LT: Field = 6..8;
pub const REACHABLE_TM: Field = 8..12;
pub const RETRANS_TM: Field = 12..16;
pub const CUR_HOP_LIMIT: usize = 4;
pub const ROUTER_FLAGS: usize = 5;
pub const ROUTER_LT: Field = 6..8;
pub const REACHABLE_TM: Field = 8..12;
pub const RETRANS_TM: Field = 12..16;
// Neighbor Solicitation message offsets
pub const TARGET_ADDR: Field = 8..24;
pub const TARGET_ADDR: Field = 8..24;
// Neighbor Advertisement message offsets
pub const NEIGH_FLAGS: usize = 4;
pub const NEIGH_FLAGS: usize = 4;
// Redirected Header message offsets
pub const DEST_ADDR: Field = 24..40;
pub const DEST_ADDR: Field = 24..40;
// MLD:
// - https://tools.ietf.org/html/rfc3810
// - https://tools.ietf.org/html/rfc3810
// Multicast Listener Query message
pub const MAX_RESP_CODE: Field = 4..6;
pub const QUERY_RESV: Field = 6..8;
pub const QUERY_MCAST_ADDR: Field = 8..24;
pub const SQRV: usize = 24;
pub const QQIC: usize = 25;
pub const QUERY_NUM_SRCS: Field = 26..28;
pub const MAX_RESP_CODE: Field = 4..6;
pub const QUERY_RESV: Field = 6..8;
pub const QUERY_MCAST_ADDR: Field = 8..24;
pub const SQRV: usize = 24;
pub const QQIC: usize = 25;
pub const QUERY_NUM_SRCS: Field = 26..28;
// Multicast Listener Report Message
pub const RECORD_RESV: Field = 4..6;
pub const NR_MCAST_RCRDS: Field = 6..8;
pub const RECORD_RESV: Field = 4..6;
pub const NR_MCAST_RCRDS: Field = 6..8;
// Multicast Address Record Offsets
pub const RECORD_TYPE: usize = 0;
pub const AUX_DATA_LEN: usize = 1;
pub const RECORD_NUM_SRCS: Field = 2..4;
pub const RECORD_TYPE: usize = 0;
pub const AUX_DATA_LEN: usize = 1;
pub const RECORD_NUM_SRCS: Field = 2..4;
pub const RECORD_MCAST_ADDR: Field = 4..20;
}
@ -326,28 +332,29 @@ impl<T: AsRef<[u8]>> Packet<T> {
NetworkEndian::read_u32(&data[field::POINTER])
}
/// Return the header length. The result depends on the value of
/// the message type field.
pub fn header_len(&self) -> usize {
match self.msg_type() {
Message::DstUnreachable => field::UNUSED.end,
Message::PktTooBig => field::MTU.end,
Message::TimeExceeded => field::UNUSED.end,
Message::ParamProblem => field::POINTER.end,
Message::EchoRequest => field::ECHO_SEQNO.end,
Message::EchoReply => field::ECHO_SEQNO.end,
Message::RouterSolicit => field::UNUSED.end,
Message::RouterAdvert => field::RETRANS_TM.end,
Message::DstUnreachable => field::UNUSED.end,
Message::PktTooBig => field::MTU.end,
Message::TimeExceeded => field::UNUSED.end,
Message::ParamProblem => field::POINTER.end,
Message::EchoRequest => field::ECHO_SEQNO.end,
Message::EchoReply => field::ECHO_SEQNO.end,
Message::RouterSolicit => field::UNUSED.end,
Message::RouterAdvert => field::RETRANS_TM.end,
Message::NeighborSolicit => field::TARGET_ADDR.end,
Message::NeighborAdvert => field::TARGET_ADDR.end,
Message::Redirect => field::DEST_ADDR.end,
Message::MldQuery => field::QUERY_NUM_SRCS.end,
Message::MldReport => field::NR_MCAST_RCRDS.end,
Message::NeighborAdvert => field::TARGET_ADDR.end,
Message::Redirect => field::DEST_ADDR.end,
Message::MldQuery => field::QUERY_NUM_SRCS.end,
Message::MldReport => field::NR_MCAST_RCRDS.end,
// For packets that are not included in RFC 4443, do not
// include the last 32 bits of the ICMPv6 header in
// `header_bytes`. This must be done so that these bytes
// can be accessed in the `payload`.
_ => field::CHECKSUM.end,
_ => field::CHECKSUM.end
}
}
@ -356,14 +363,13 @@ impl<T: AsRef<[u8]>> Packet<T> {
/// # Fuzzing
/// This function always returns `true` when fuzzing.
pub fn verify_checksum(&self, src_addr: &IpAddress, dst_addr: &IpAddress) -> bool {
if cfg!(fuzzing) {
return true;
}
if cfg!(fuzzing) { return true }
let data = self.buffer.as_ref();
checksum::combine(&[
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6, data.len() as u32),
checksum::data(data),
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6,
data.len() as u32),
checksum::data(data)
]) == !0
}
}
@ -402,18 +408,16 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
#[inline]
pub fn clear_reserved(&mut self) {
match self.msg_type() {
Message::RouterSolicit
| Message::NeighborSolicit
| Message::NeighborAdvert
| Message::Redirect => {
Message::RouterSolicit | Message::NeighborSolicit |
Message::NeighborAdvert | Message::Redirect => {
let data = self.buffer.as_mut();
NetworkEndian::write_u32(&mut data[field::UNUSED], 0);
}
},
Message::MldQuery => {
let data = self.buffer.as_mut();
NetworkEndian::write_u16(&mut data[field::QUERY_RESV], 0);
data[field::SQRV] &= 0xf;
}
},
Message::MldReport => {
let data = self.buffer.as_mut();
NetworkEndian::write_u16(&mut data[field::RECORD_RESV], 0);
@ -474,8 +478,9 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
let checksum = {
let data = self.buffer.as_ref();
!checksum::combine(&[
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6, data.len() as u32),
checksum::data(data),
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Icmpv6,
data.len() as u32),
checksum::data(data)
])
};
self.set_checksum(checksum)
@ -498,41 +503,40 @@ impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
/// A high-level representation of an Internet Control Message Protocol version 6 packet header.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Repr<'a> {
DstUnreachable {
reason: DstUnreachable,
header: Ipv6Repr,
data: &'a [u8],
data: &'a [u8]
},
PktTooBig {
mtu: u32,
header: Ipv6Repr,
data: &'a [u8],
data: &'a [u8]
},
TimeExceeded {
reason: TimeExceeded,
header: Ipv6Repr,
data: &'a [u8],
data: &'a [u8]
},
ParamProblem {
reason: ParamProblem,
reason: ParamProblem,
pointer: u32,
header: Ipv6Repr,
data: &'a [u8],
header: Ipv6Repr,
data: &'a [u8]
},
EchoRequest {
ident: u16,
ident: u16,
seq_no: u16,
data: &'a [u8],
data: &'a [u8]
},
EchoReply {
ident: u16,
ident: u16,
seq_no: u16,
data: &'a [u8],
data: &'a [u8]
},
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
#[cfg(feature = "ethernet")]
Ndisc(NdiscRepr<'a>),
Mld(MldRepr<'a>),
}
@ -540,37 +544,29 @@ pub enum Repr<'a> {
impl<'a> Repr<'a> {
/// Parse an Internet Control Message Protocol version 6 packet and return
/// a high-level representation.
pub fn parse<T>(
src_addr: &IpAddress,
dst_addr: &IpAddress,
packet: &Packet<&'a T>,
checksum_caps: &ChecksumCapabilities,
) -> Result<Repr<'a>>
where
T: AsRef<[u8]> + ?Sized,
{
fn create_packet_from_payload<'a, T>(packet: &Packet<&'a T>) -> Result<(&'a [u8], Ipv6Repr)>
where
T: AsRef<[u8]> + ?Sized,
{
pub fn parse<T>(src_addr: &IpAddress, dst_addr: &IpAddress,
packet: &Packet<&'a T>, checksum_caps: &ChecksumCapabilities)
-> Result<Repr<'a>>
where T: AsRef<[u8]> + ?Sized {
fn create_packet_from_payload<'a, T>(packet: &Packet<&'a T>)
-> Result<(&'a [u8], Ipv6Repr)>
where T: AsRef<[u8]> + ?Sized {
let ip_packet = Ipv6Packet::new_checked(packet.payload())?;
let payload = &packet.payload()[ip_packet.header_len() as usize..];
if payload.len() < 8 {
return Err(Error::Truncated);
}
if payload.len() < 8 { return Err(Error::Truncated) }
let repr = Ipv6Repr {
src_addr: ip_packet.src_addr(),
dst_addr: ip_packet.dst_addr(),
next_header: ip_packet.next_header(),
payload_len: payload.len(),
hop_limit: ip_packet.hop_limit(),
hop_limit: ip_packet.hop_limit()
};
Ok((payload, repr))
}
// Valid checksum is expected.
if checksum_caps.icmpv6.rx() && !packet.verify_checksum(src_addr, dst_addr) {
return Err(Error::Checksum);
return Err(Error::Checksum)
}
match (packet.msg_type(), packet.msg_code()) {
@ -579,161 +575,149 @@ impl<'a> Repr<'a> {
Ok(Repr::DstUnreachable {
reason: DstUnreachable::from(code),
header: repr,
data: payload,
data: payload
})
}
},
(Message::PktTooBig, 0) => {
let (payload, repr) = create_packet_from_payload(packet)?;
Ok(Repr::PktTooBig {
mtu: packet.pkt_too_big_mtu(),
header: repr,
data: payload,
data: payload
})
}
},
(Message::TimeExceeded, code) => {
let (payload, repr) = create_packet_from_payload(packet)?;
Ok(Repr::TimeExceeded {
reason: TimeExceeded::from(code),
header: repr,
data: payload,
data: payload
})
}
},
(Message::ParamProblem, code) => {
let (payload, repr) = create_packet_from_payload(packet)?;
Ok(Repr::ParamProblem {
reason: ParamProblem::from(code),
pointer: packet.param_problem_ptr(),
header: repr,
data: payload,
data: payload
})
}
(Message::EchoRequest, 0) => Ok(Repr::EchoRequest {
ident: packet.echo_ident(),
seq_no: packet.echo_seq_no(),
data: packet.payload(),
}),
(Message::EchoReply, 0) => Ok(Repr::EchoReply {
ident: packet.echo_ident(),
seq_no: packet.echo_seq_no(),
data: packet.payload(),
}),
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
(msg_type, 0) if msg_type.is_ndisc() => NdiscRepr::parse(packet).map(Repr::Ndisc),
(msg_type, 0) if msg_type.is_mld() => MldRepr::parse(packet).map(Repr::Mld),
_ => Err(Error::Unrecognized),
},
(Message::EchoRequest, 0) => {
Ok(Repr::EchoRequest {
ident: packet.echo_ident(),
seq_no: packet.echo_seq_no(),
data: packet.payload()
})
},
(Message::EchoReply, 0) => {
Ok(Repr::EchoReply {
ident: packet.echo_ident(),
seq_no: packet.echo_seq_no(),
data: packet.payload()
})
},
#[cfg(feature = "ethernet")]
(msg_type, 0) if msg_type.is_ndisc() => {
NdiscRepr::parse(packet).map(Repr::Ndisc)
},
(msg_type, 0) if msg_type.is_mld() => {
MldRepr::parse(packet).map(Repr::Mld)
},
_ => Err(Error::Unrecognized)
}
}
/// Return the length of a packet that will be emitted from this high-level representation.
pub fn buffer_len(&self) -> usize {
match self {
&Repr::DstUnreachable { header, data, .. }
| &Repr::PktTooBig { header, data, .. }
| &Repr::TimeExceeded { header, data, .. }
| &Repr::ParamProblem { header, data, .. } => {
&Repr::DstUnreachable { header, data, .. } | &Repr::PktTooBig { header, data, .. } |
&Repr::TimeExceeded { header, data, .. } | &Repr::ParamProblem { header, data, .. } => {
field::UNUSED.end + header.buffer_len() + data.len()
}
&Repr::EchoRequest { data, .. } | &Repr::EchoReply { data, .. } => {
&Repr::EchoRequest { data, .. } |
&Repr::EchoReply { data, .. } => {
field::ECHO_SEQNO.end + data.len()
}
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
&Repr::Ndisc(ndisc) => ndisc.buffer_len(),
&Repr::Mld(mld) => mld.buffer_len(),
},
#[cfg(feature = "ethernet")]
&Repr::Ndisc(ndisc) => {
ndisc.buffer_len()
},
&Repr::Mld(mld) => {
mld.buffer_len()
},
}
}
/// Emit a high-level representation into an Internet Control Message Protocol version 6
/// packet.
pub fn emit<T>(
&self,
src_addr: &IpAddress,
dst_addr: &IpAddress,
packet: &mut Packet<&mut T>,
checksum_caps: &ChecksumCapabilities,
) where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
{
pub fn emit<T>(&self, src_addr: &IpAddress, dst_addr: &IpAddress,
packet: &mut Packet<&mut T>, checksum_caps: &ChecksumCapabilities)
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
fn emit_contained_packet(buffer: &mut [u8], header: Ipv6Repr, data: &[u8]) {
let mut ip_packet = Ipv6Packet::new_unchecked(buffer);
header.emit(&mut ip_packet);
let payload = &mut ip_packet.into_inner()[header.buffer_len()..];
payload.copy_from_slice(data);
payload.copy_from_slice(&data[..]);
}
match *self {
Repr::DstUnreachable {
reason,
header,
data,
} => {
Repr::DstUnreachable { reason, header, data } => {
packet.set_msg_type(Message::DstUnreachable);
packet.set_msg_code(reason.into());
emit_contained_packet(packet.payload_mut(), header, data);
}
emit_contained_packet(packet.payload_mut(), header, &data);
},
Repr::PktTooBig { mtu, header, data } => {
packet.set_msg_type(Message::PktTooBig);
packet.set_msg_code(0);
packet.set_pkt_too_big_mtu(mtu);
emit_contained_packet(packet.payload_mut(), header, data);
}
emit_contained_packet(packet.payload_mut(), header, &data);
},
Repr::TimeExceeded {
reason,
header,
data,
} => {
Repr::TimeExceeded { reason, header, data } => {
packet.set_msg_type(Message::TimeExceeded);
packet.set_msg_code(reason.into());
emit_contained_packet(packet.payload_mut(), header, data);
}
emit_contained_packet(packet.payload_mut(), header, &data);
},
Repr::ParamProblem {
reason,
pointer,
header,
data,
} => {
Repr::ParamProblem { reason, pointer, header, data } => {
packet.set_msg_type(Message::ParamProblem);
packet.set_msg_code(reason.into());
packet.set_param_problem_ptr(pointer);
emit_contained_packet(packet.payload_mut(), header, data);
}
emit_contained_packet(packet.payload_mut(), header, &data);
},
Repr::EchoRequest {
ident,
seq_no,
data,
} => {
Repr::EchoRequest { ident, seq_no, data } => {
packet.set_msg_type(Message::EchoRequest);
packet.set_msg_code(0);
packet.set_echo_ident(ident);
packet.set_echo_seq_no(seq_no);
let data_len = cmp::min(packet.payload_mut().len(), data.len());
packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len])
}
},
Repr::EchoReply {
ident,
seq_no,
data,
} => {
Repr::EchoReply { ident, seq_no, data } => {
packet.set_msg_type(Message::EchoReply);
packet.set_msg_code(0);
packet.set_echo_ident(ident);
packet.set_echo_seq_no(seq_no);
let data_len = cmp::min(packet.payload_mut().len(), data.len());
packet.payload_mut()[..data_len].copy_from_slice(&data[..data_len])
}
},
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
Repr::Ndisc(ndisc) => ndisc.emit(packet),
#[cfg(feature = "ethernet")]
Repr::Ndisc(ndisc) => {
ndisc.emit(packet)
},
Repr::Mld(mld) => mld.emit(packet),
Repr::Mld(mld) => {
mld.emit(packet)
},
}
if checksum_caps.icmpv6.tx() {
@ -747,39 +731,60 @@ impl<'a> Repr<'a> {
#[cfg(test)]
mod test {
use super::*;
use crate::wire::{Ipv6Address, Ipv6Repr, IpProtocol};
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2};
use crate::wire::{IpProtocol, Ipv6Address, Ipv6Repr};
use super::*;
static ECHO_PACKET_BYTES: [u8; 12] = [
0x80, 0x00, 0x19, 0xb3, 0x12, 0x34, 0xab, 0xcd, 0xaa, 0x00, 0x00, 0xff,
];
static ECHO_PACKET_BYTES: [u8; 12] =
[0x80, 0x00, 0x19, 0xb3,
0x12, 0x34, 0xab, 0xcd,
0xaa, 0x00, 0x00, 0xff];
static ECHO_PACKET_PAYLOAD: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
static ECHO_PACKET_PAYLOAD: [u8; 4] =
[0xaa, 0x00, 0x00, 0xff];
static PKT_TOO_BIG_BYTES: [u8; 60] = [
0x02, 0x00, 0x0f, 0xc9, 0x00, 0x00, 0x05, 0xdc, 0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11,
0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02, 0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff,
];
static PKT_TOO_BIG_BYTES: [u8; 60] =
[0x02, 0x00, 0x0f, 0xc9,
0x00, 0x00, 0x05, 0xdc,
0x60, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x11, 0x40,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02,
0xbf, 0x00, 0x00, 0x35,
0x00, 0x0c, 0x12, 0x4d,
0xaa, 0x00, 0x00, 0xff];
static PKT_TOO_BIG_IP_PAYLOAD: [u8; 52] = [
0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xbf, 0x00, 0x00, 0x35, 0x00,
0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff,
];
static PKT_TOO_BIG_IP_PAYLOAD: [u8; 52] =
[0x60, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x11, 0x40,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02,
0xbf, 0x00, 0x00, 0x35,
0x00, 0x0c, 0x12, 0x4d,
0xaa, 0x00, 0x00, 0xff];
static PKT_TOO_BIG_UDP_PAYLOAD: [u8; 12] = [
0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff,
];
static PKT_TOO_BIG_UDP_PAYLOAD: [u8; 12] =
[0xbf, 0x00, 0x00, 0x35,
0x00, 0x0c, 0x12, 0x4d,
0xaa, 0x00, 0x00, 0xff];
fn echo_packet_repr() -> Repr<'static> {
Repr::EchoRequest {
ident: 0x1234,
seq_no: 0xabcd,
data: &ECHO_PACKET_PAYLOAD,
data: &ECHO_PACKET_PAYLOAD
}
}
@ -787,17 +792,17 @@ mod test {
Repr::PktTooBig {
mtu: 1500,
header: Ipv6Repr {
src_addr: Ipv6Address([
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01,
]),
dst_addr: Ipv6Address([
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x02,
]),
src_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01]),
dst_addr: Ipv6Address([0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02]),
next_header: IpProtocol::Udp,
payload_len: 12,
hop_limit: 0x40,
hop_limit: 0x40
},
data: &PKT_TOO_BIG_UDP_PAYLOAD,
}
@ -812,7 +817,7 @@ mod test {
assert_eq!(packet.echo_ident(), 0x1234);
assert_eq!(packet.echo_seq_no(), 0xabcd);
assert_eq!(packet.payload(), &ECHO_PACKET_PAYLOAD[..]);
assert!(packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2));
assert_eq!(packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2), true);
assert!(!packet.msg_type().is_error());
}
@ -824,9 +829,7 @@ mod test {
packet.set_msg_code(0);
packet.set_echo_ident(0x1234);
packet.set_echo_seq_no(0xabcd);
packet
.payload_mut()
.copy_from_slice(&ECHO_PACKET_PAYLOAD[..]);
packet.payload_mut().copy_from_slice(&ECHO_PACKET_PAYLOAD[..]);
packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2);
assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
}
@ -834,13 +837,8 @@ mod test {
#[test]
fn test_echo_repr_parse() {
let packet = Packet::new_unchecked(&ECHO_PACKET_BYTES[..]);
let repr = Repr::parse(
&MOCK_IP_ADDR_1,
&MOCK_IP_ADDR_2,
&packet,
&ChecksumCapabilities::default(),
)
.unwrap();
let repr = Repr::parse(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2,
&packet, &ChecksumCapabilities::default()).unwrap();
assert_eq!(repr, echo_packet_repr());
}
@ -849,12 +847,8 @@ mod test {
let repr = echo_packet_repr();
let mut bytes = vec![0xa5; repr.buffer_len()];
let mut packet = Packet::new_unchecked(&mut bytes);
repr.emit(
&MOCK_IP_ADDR_1,
&MOCK_IP_ADDR_2,
&mut packet,
&ChecksumCapabilities::default(),
);
repr.emit(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2,
&mut packet, &ChecksumCapabilities::default());
assert_eq!(&packet.into_inner()[..], &ECHO_PACKET_BYTES[..]);
}
@ -866,7 +860,7 @@ mod test {
assert_eq!(packet.checksum(), 0x0fc9);
assert_eq!(packet.pkt_too_big_mtu(), 1500);
assert_eq!(packet.payload(), &PKT_TOO_BIG_IP_PAYLOAD[..]);
assert!(packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2));
assert_eq!(packet.verify_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2), true);
assert!(packet.msg_type().is_error());
}
@ -877,9 +871,7 @@ mod test {
packet.set_msg_type(Message::PktTooBig);
packet.set_msg_code(0);
packet.set_pkt_too_big_mtu(1500);
packet
.payload_mut()
.copy_from_slice(&PKT_TOO_BIG_IP_PAYLOAD[..]);
packet.payload_mut().copy_from_slice(&PKT_TOO_BIG_IP_PAYLOAD[..]);
packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2);
assert_eq!(&packet.into_inner()[..], &PKT_TOO_BIG_BYTES[..]);
}
@ -887,13 +879,8 @@ mod test {
#[test]
fn test_too_big_repr_parse() {
let packet = Packet::new_unchecked(&PKT_TOO_BIG_BYTES[..]);
let repr = Repr::parse(
&MOCK_IP_ADDR_1,
&MOCK_IP_ADDR_2,
&packet,
&ChecksumCapabilities::default(),
)
.unwrap();
let repr = Repr::parse(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2,
&packet, &ChecksumCapabilities::default()).unwrap();
assert_eq!(repr, too_big_packet_repr());
}
@ -902,12 +889,8 @@ mod test {
let repr = too_big_packet_repr();
let mut bytes = vec![0xa5; repr.buffer_len()];
let mut packet = Packet::new_unchecked(&mut bytes);
repr.emit(
&MOCK_IP_ADDR_1,
&MOCK_IP_ADDR_2,
&mut packet,
&ChecksumCapabilities::default(),
);
repr.emit(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2,
&mut packet, &ChecksumCapabilities::default());
assert_eq!(&packet.into_inner()[..], &PKT_TOO_BIG_BYTES[..]);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,9 @@
use byteorder::{ByteOrder, NetworkEndian};
use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
use crate::time::Duration;
use crate::wire::ip::checksum;
use crate::{Error, Result};
use crate::wire::ip::checksum;
use crate::time::Duration;
use crate::wire::Ipv4Address;
@ -23,7 +23,6 @@ enum_with_unknown! {
/// A read/write wrapper around an Internet Group Management Protocol v1/v2 packet buffer.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> {
buffer: T,
}
@ -172,7 +171,6 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
/// A high-level representation of an Internet Group Management Protocol v1/v2 header.
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Repr {
MembershipQuery {
max_resp_time: Duration,
@ -190,7 +188,6 @@ pub enum Repr {
/// Type of IGMP membership report version
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum IgmpVersion {
/// IGMPv1
Version1,
@ -202,8 +199,7 @@ impl Repr {
/// Parse an Internet Group Management Protocol v1/v2 packet and return
/// a high-level representation.
pub fn parse<T>(packet: &Packet<&T>) -> Result<Repr>
where
T: AsRef<[u8]> + ?Sized,
where T: AsRef<[u8]> + ?Sized
{
// Check if the address is 0.0.0.0 or multicast
let addr = packet.group_addr();
@ -227,13 +223,13 @@ impl Repr {
version,
})
}
Message::MembershipReportV2 => Ok(Repr::MembershipReport {
group_addr: packet.group_addr(),
version: IgmpVersion::Version2,
}),
Message::LeaveGroup => Ok(Repr::LeaveGroup {
group_addr: packet.group_addr(),
}),
Message::MembershipReportV2 => {
Ok(Repr::MembershipReport {
group_addr: packet.group_addr(),
version: IgmpVersion::Version2,
})
}
Message::LeaveGroup => Ok(Repr::LeaveGroup { group_addr: packet.group_addr() }),
Message::MembershipReportV1 => {
// for backwards compatibility with IGMPv1
Ok(Repr::MembershipReport {
@ -253,21 +249,20 @@ impl Repr {
/// Emit a high-level representation into an Internet Group Management Protocol v2 packet.
pub fn emit<T>(&self, packet: &mut Packet<&mut T>)
where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized
{
match *self {
Repr::MembershipQuery {
max_resp_time,
group_addr,
version,
version
} => {
packet.set_msg_type(Message::MembershipQuery);
match version {
IgmpVersion::Version1 => packet.set_max_resp_code(0),
IgmpVersion::Version2 => {
packet.set_max_resp_code(duration_to_max_resp_code(max_resp_time))
}
IgmpVersion::Version1 =>
packet.set_max_resp_code(0),
IgmpVersion::Version2 =>
packet.set_max_resp_code(duration_to_max_resp_code(max_resp_time)),
}
packet.set_group_address(group_addr);
}
@ -337,19 +332,22 @@ impl<'a> fmt::Display for Repr {
max_resp_time,
group_addr,
version,
} => write!(
f,
"IGMP membership query max_resp_time={} group_addr={} version={:?}",
max_resp_time, group_addr, version
),
} => {
write!(f,
"IGMP membership query max_resp_time={} group_addr={} version={:?}",
max_resp_time,
group_addr,
version)
}
Repr::MembershipReport {
group_addr,
version,
} => write!(
f,
"IGMP membership report group_addr={} version={:?}",
group_addr, version
),
} => {
write!(f,
"IGMP membership report group_addr={} version={:?}",
group_addr,
version)
}
Repr::LeaveGroup { group_addr } => {
write!(f, "IGMP leave group group_addr={})", group_addr)
}
@ -360,11 +358,10 @@ impl<'a> fmt::Display for Repr {
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
fn pretty_print(buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent)
-> fmt::Result {
match Packet::new_checked(buffer) {
Err(err) => writeln!(f, "{}({})", indent, err),
Ok(packet) => writeln!(f, "{}{}", indent, packet),
@ -376,6 +373,7 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
mod test {
use super::*;
static LEAVE_PACKET_BYTES: [u8; 8] = [0x17, 0x00, 0x02, 0x69, 0xe0, 0x00, 0x06, 0x96];
static REPORT_PACKET_BYTES: [u8; 8] = [0x16, 0x00, 0x08, 0xda, 0xe1, 0x00, 0x00, 0x25];
@ -385,11 +383,9 @@ mod test {
assert_eq!(packet.msg_type(), Message::LeaveGroup);
assert_eq!(packet.max_resp_code(), 0);
assert_eq!(packet.checksum(), 0x269);
assert_eq!(
packet.group_addr(),
Ipv4Address::from_bytes(&[224, 0, 6, 150])
);
assert!(packet.verify_checksum());
assert_eq!(packet.group_addr(),
Ipv4Address::from_bytes(&[224, 0, 6, 150]));
assert_eq!(packet.verify_checksum(), true);
}
#[test]
@ -398,11 +394,9 @@ mod test {
assert_eq!(packet.msg_type(), Message::MembershipReportV2);
assert_eq!(packet.max_resp_code(), 0);
assert_eq!(packet.checksum(), 0x08da);
assert_eq!(
packet.group_addr(),
Ipv4Address::from_bytes(&[225, 0, 0, 37])
);
assert!(packet.verify_checksum());
assert_eq!(packet.group_addr(),
Ipv4Address::from_bytes(&[225, 0, 0, 37]));
assert_eq!(packet.verify_checksum(), true);
}
#[test]
@ -440,7 +434,9 @@ mod test {
#[test]
fn duration_to_max_resp_time_max() {
for duration in 31744..65536 {
let time = duration_to_max_resp_code(Duration::from_millis(duration * 100));
let time = duration_to_max_resp_code(
Duration::from_millis(duration * 100)
);
assert_eq!(time, 0xFF);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,9 @@
use byteorder::{ByteOrder, NetworkEndian};
use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
use crate::{Error, Result};
use crate::phy::ChecksumCapabilities;
use crate::wire::ip::{checksum, pretty_print_ip_payload};
use crate::{Error, Result};
pub use super::IpProtocol as Protocol;
@ -27,10 +27,10 @@ pub struct Address(pub [u8; 4]);
impl Address {
/// An unspecified address.
pub const UNSPECIFIED: Address = Address([0x00; 4]);
pub const UNSPECIFIED: Address = Address([0x00; 4]);
/// The broadcast address.
pub const BROADCAST: Address = Address([0xff; 4]);
pub const BROADCAST: Address = Address([0xff; 4]);
/// All multicast-capable nodes
pub const MULTICAST_ALL_SYSTEMS: Address = Address([224, 0, 0, 1]);
@ -60,7 +60,9 @@ impl Address {
/// Query whether the address is an unicast address.
pub fn is_unicast(&self) -> bool {
!(self.is_broadcast() || self.is_multicast() || self.is_unspecified())
!(self.is_broadcast() ||
self.is_multicast() ||
self.is_unspecified())
}
/// Query whether the address is the broadcast address.
@ -110,25 +112,11 @@ impl fmt::Display for Address {
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for Address {
fn format(&self, f: defmt::Formatter) {
defmt::write!(
f,
"{=u8}.{=u8}.{=u8}.{=u8}",
self.0[0],
self.0[1],
self.0[2],
self.0[3]
)
}
}
/// A specification of an IPv4 CIDR block, containing an address and a variable-length
/// subnet masking prefix length.
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
pub struct Cidr {
address: Address,
address: Address,
prefix_len: u8,
}
@ -142,20 +130,14 @@ impl Cidr {
// Replace with const panic (or assert) when stabilized
// see: https://github.com/rust-lang/rust/issues/51999
["Prefix length should be <= 32"][(prefix_len > 32) as usize];
Cidr {
address,
prefix_len,
}
Cidr { address, prefix_len }
}
/// Create an IPv4 CIDR block from the given address and network mask.
pub fn from_netmask(addr: Address, netmask: Address) -> Result<Cidr> {
let netmask = NetworkEndian::read_u32(&netmask.0[..]);
if netmask.leading_zeros() == 0 && netmask.trailing_zeros() == netmask.count_zeros() {
Ok(Cidr {
address: addr,
prefix_len: netmask.count_ones() as u8,
})
Ok(Cidr { address: addr, prefix_len: netmask.count_ones() as u8 })
} else {
Err(Error::Illegal)
}
@ -181,8 +163,8 @@ impl Cidr {
let data = [
((number >> 24) & 0xff) as u8,
((number >> 16) & 0xff) as u8,
((number >> 8) & 0xff) as u8,
((number >> 0) & 0xff) as u8,
((number >> 8) & 0xff) as u8,
((number >> 0) & 0xff) as u8,
];
Address(data)
@ -201,8 +183,8 @@ impl Cidr {
let data = [
((number >> 24) & 0xff) as u8,
((number >> 16) & 0xff) as u8,
((number >> 8) & 0xff) as u8,
((number >> 0) & 0xff) as u8,
((number >> 8) & 0xff) as u8,
((number >> 0) & 0xff) as u8,
];
Some(Address(data))
@ -217,19 +199,14 @@ impl Cidr {
self.address.0[2] & mask[2],
self.address.0[3] & mask[3],
];
Cidr {
address: Address(network),
prefix_len: self.prefix_len,
}
Cidr { address: Address(network), prefix_len: self.prefix_len }
}
/// Query whether the subnetwork described by this IPv4 CIDR block contains
/// the given address.
pub fn contains_addr(&self, addr: &Address) -> bool {
// right shift by 32 is not legal
if self.prefix_len == 0 {
return true;
}
if self.prefix_len == 0 { return true }
let shift = 32 - self.prefix_len;
let self_prefix = NetworkEndian::read_u32(self.address.as_bytes()) >> shift;
@ -250,37 +227,27 @@ impl fmt::Display for Cidr {
}
}
#[cfg(feature = "defmt")]
impl defmt::Format for Cidr {
fn format(&self, f: defmt::Formatter) {
defmt::write!(f, "{:?}/{=u8}", self.address, self.prefix_len);
}
}
/// A read/write wrapper around an Internet Protocol version 4 packet buffer.
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> {
buffer: T,
buffer: T
}
mod field {
use crate::wire::field::*;
pub const VER_IHL: usize = 0;
pub const VER_IHL: usize = 0;
pub const DSCP_ECN: usize = 1;
pub const LENGTH: Field = 2..4;
pub const IDENT: Field = 4..6;
pub const FLG_OFF: Field = 6..8;
pub const TTL: usize = 8;
pub const LENGTH: Field = 2..4;
pub const IDENT: Field = 4..6;
pub const FLG_OFF: Field = 6..8;
pub const TTL: usize = 8;
pub const PROTOCOL: usize = 9;
pub const CHECKSUM: Field = 10..12;
pub const SRC_ADDR: Field = 12..16;
pub const DST_ADDR: Field = 16..20;
}
pub const HEADER_LEN: usize = field::DST_ADDR.end;
impl<T: AsRef<[u8]>> Packet<T> {
/// Imbue a raw octet buffer with IPv4 packet structure.
pub fn new_unchecked(buffer: T) -> Packet<T> {
@ -429,9 +396,7 @@ impl<T: AsRef<[u8]>> Packet<T> {
/// # Fuzzing
/// This function always returns `true` when fuzzing.
pub fn verify_checksum(&self) -> bool {
if cfg!(fuzzing) {
return true;
}
if cfg!(fuzzing) { return true }
let data = self.buffer.as_ref();
checksum::data(&data[..self.header_len() as usize]) == !0
@ -587,48 +552,37 @@ impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
/// A high-level representation of an Internet Protocol version 4 packet header.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Repr {
pub src_addr: Address,
pub dst_addr: Address,
pub protocol: Protocol,
pub src_addr: Address,
pub dst_addr: Address,
pub protocol: Protocol,
pub payload_len: usize,
pub hop_limit: u8,
pub hop_limit: u8
}
impl Repr {
/// Parse an Internet Protocol version 4 packet and return a high-level representation.
pub fn parse<T: AsRef<[u8]> + ?Sized>(
packet: &Packet<&T>,
checksum_caps: &ChecksumCapabilities,
) -> Result<Repr> {
pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&T>,
checksum_caps: &ChecksumCapabilities) -> Result<Repr> {
// Version 4 is expected.
if packet.version() != 4 {
return Err(Error::Malformed);
}
if packet.version() != 4 { return Err(Error::Malformed) }
// Valid checksum is expected.
if checksum_caps.ipv4.rx() && !packet.verify_checksum() {
return Err(Error::Checksum);
}
if checksum_caps.ipv4.rx() && !packet.verify_checksum() { return Err(Error::Checksum) }
// We do not support fragmentation.
if packet.more_frags() || packet.frag_offset() != 0 {
return Err(Error::Fragmented);
}
if packet.more_frags() || packet.frag_offset() != 0 { return Err(Error::Fragmented) }
// Since the packet is not fragmented, it must include the entire payload.
let payload_len = packet.total_len() as usize - packet.header_len() as usize;
if packet.payload().len() < payload_len {
return Err(Error::Truncated);
}
if packet.payload().len() < payload_len { return Err(Error::Truncated) }
// All DSCP values are acceptable, since they are of no concern to receiving endpoint.
// All ECN values are acceptable, since ECN requires opt-in from both endpoints.
// All TTL values are acceptable, since we do not perform routing.
Ok(Repr {
src_addr: packet.src_addr(),
dst_addr: packet.dst_addr(),
protocol: packet.protocol(),
src_addr: packet.src_addr(),
dst_addr: packet.dst_addr(),
protocol: packet.protocol(),
payload_len: payload_len,
hop_limit: packet.hop_limit(),
hop_limit: packet.hop_limit()
})
}
@ -639,11 +593,7 @@ impl Repr {
}
/// Emit a high-level representation into an Internet Protocol version 4 packet.
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(
&self,
packet: &mut Packet<T>,
checksum_caps: &ChecksumCapabilities,
) {
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]>>(&self, packet: &mut Packet<T>, checksum_caps: &ChecksumCapabilities) {
packet.set_version(4);
packet.set_header_len(field::DST_ADDR.end as u8);
packet.set_dscp(0);
@ -676,14 +626,8 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
Ok(repr) => write!(f, "{}", repr),
Err(err) => {
write!(f, "IPv4 ({})", err)?;
write!(
f,
" src={} dst={} proto={} hop_limit={}",
self.src_addr(),
self.dst_addr(),
self.protocol(),
self.hop_limit()
)?;
write!(f, " src={} dst={} proto={} hop_limit={}",
self.src_addr(), self.dst_addr(), self.protocol(), self.hop_limit())?;
if self.version() != 4 {
write!(f, " ver={}", self.version())?;
}
@ -717,36 +661,32 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
impl fmt::Display for Repr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"IPv4 src={} dst={} proto={}",
self.src_addr, self.dst_addr, self.protocol
)
write!(f, "IPv4 src={} dst={} proto={}",
self.src_addr, self.dst_addr, self.protocol)
}
}
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result {
use crate::wire::ip::checksum::format_checksum;
let checksum_caps = ChecksumCapabilities::ignored();
let (ip_repr, payload) = match Packet::new_checked(buffer) {
Err(err) => return write!(f, "{}({})", indent, err),
Ok(ip_packet) => match Repr::parse(&ip_packet, &checksum_caps) {
Err(_) => return Ok(()),
Ok(ip_repr) => {
write!(f, "{}{}", indent, ip_repr)?;
format_checksum(f, ip_packet.verify_checksum())?;
(ip_repr, ip_packet.payload())
Ok(ip_packet) => {
match Repr::parse(&ip_packet, &checksum_caps) {
Err(_) => return Ok(()),
Ok(ip_repr) => {
write!(f, "{}{}", indent, ip_repr)?;
format_checksum(f, ip_packet.verify_checksum())?;
(ip_repr, ip_packet.payload())
}
}
},
}
};
pretty_print_ip_payload(f, indent, ip_repr, payload)
@ -757,12 +697,20 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
mod test {
use super::*;
static PACKET_BYTES: [u8; 30] = [
0x45, 0x00, 0x00, 0x1e, 0x01, 0x02, 0x62, 0x03, 0x1a, 0x01, 0xd5, 0x6e, 0x11, 0x12, 0x13,
0x14, 0x21, 0x22, 0x23, 0x24, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
];
static PACKET_BYTES: [u8; 30] =
[0x45, 0x00, 0x00, 0x1e,
0x01, 0x02, 0x62, 0x03,
0x1a, 0x01, 0xd5, 0x6e,
0x11, 0x12, 0x13, 0x14,
0x21, 0x22, 0x23, 0x24,
0xaa, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0xff];
static PAYLOAD_BYTES: [u8; 10] = [0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff];
static PAYLOAD_BYTES: [u8; 10] =
[0xaa, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0xff];
#[test]
fn test_deconstruct() {
@ -773,15 +721,15 @@ mod test {
assert_eq!(packet.ecn(), 0);
assert_eq!(packet.total_len(), 30);
assert_eq!(packet.ident(), 0x102);
assert!(packet.more_frags());
assert!(packet.dont_frag());
assert_eq!(packet.more_frags(), true);
assert_eq!(packet.dont_frag(), true);
assert_eq!(packet.frag_offset(), 0x203 * 8);
assert_eq!(packet.hop_limit(), 0x1a);
assert_eq!(packet.protocol(), Protocol::Icmp);
assert_eq!(packet.checksum(), 0xd56e);
assert_eq!(packet.src_addr(), Address([0x11, 0x12, 0x13, 0x14]));
assert_eq!(packet.dst_addr(), Address([0x21, 0x22, 0x23, 0x24]));
assert!(packet.verify_checksum());
assert_eq!(packet.verify_checksum(), true);
assert_eq!(packet.payload(), &PAYLOAD_BYTES[..]);
}
@ -814,14 +762,10 @@ mod test {
bytes.extend(&PACKET_BYTES[..]);
bytes.push(0);
assert_eq!(
Packet::new_unchecked(&bytes).payload().len(),
PAYLOAD_BYTES.len()
);
assert_eq!(
Packet::new_unchecked(&mut bytes).payload_mut().len(),
PAYLOAD_BYTES.len()
);
assert_eq!(Packet::new_unchecked(&bytes).payload().len(),
PAYLOAD_BYTES.len());
assert_eq!(Packet::new_unchecked(&mut bytes).payload_mut().len(),
PAYLOAD_BYTES.len());
}
#[test]
@ -830,23 +774,28 @@ mod test {
bytes.extend(&PACKET_BYTES[..]);
Packet::new_unchecked(&mut bytes).set_total_len(128);
assert_eq!(Packet::new_checked(&bytes).unwrap_err(), Error::Truncated);
assert_eq!(Packet::new_checked(&bytes).unwrap_err(),
Error::Truncated);
}
static REPR_PACKET_BYTES: [u8; 24] = [
0x45, 0x00, 0x00, 0x18, 0x00, 0x00, 0x40, 0x00, 0x40, 0x01, 0xd2, 0x79, 0x11, 0x12, 0x13,
0x14, 0x21, 0x22, 0x23, 0x24, 0xaa, 0x00, 0x00, 0xff,
];
static REPR_PACKET_BYTES: [u8; 24] =
[0x45, 0x00, 0x00, 0x18,
0x00, 0x00, 0x40, 0x00,
0x40, 0x01, 0xd2, 0x79,
0x11, 0x12, 0x13, 0x14,
0x21, 0x22, 0x23, 0x24,
0xaa, 0x00, 0x00, 0xff];
static REPR_PAYLOAD_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
static REPR_PAYLOAD_BYTES: [u8; 4] =
[0xaa, 0x00, 0x00, 0xff];
fn packet_repr() -> Repr {
Repr {
src_addr: Address([0x11, 0x12, 0x13, 0x14]),
dst_addr: Address([0x21, 0x22, 0x23, 0x24]),
protocol: Protocol::Icmp,
src_addr: Address([0x11, 0x12, 0x13, 0x14]),
dst_addr: Address([0x21, 0x22, 0x23, 0x24]),
protocol: Protocol::Icmp,
payload_len: 4,
hop_limit: 64,
hop_limit: 64
}
}
@ -865,10 +814,7 @@ mod test {
packet.set_version(6);
packet.fill_checksum();
let packet = Packet::new_unchecked(&*packet.into_inner());
assert_eq!(
Repr::parse(&packet, &ChecksumCapabilities::default()),
Err(Error::Malformed)
);
assert_eq!(Repr::parse(&packet, &ChecksumCapabilities::default()), Err(Error::Malformed));
}
#[test]
@ -911,34 +857,28 @@ mod test {
let cidr = Cidr::new(Address::new(192, 168, 1, 10), 24);
let inside_subnet = [
[192, 168, 1, 0],
[192, 168, 1, 1],
[192, 168, 1, 2],
[192, 168, 1, 10],
[192, 168, 1, 127],
[192, 168, 1, 255],
[192, 168, 1, 0], [192, 168, 1, 1],
[192, 168, 1, 2], [192, 168, 1, 10],
[192, 168, 1, 127], [192, 168, 1, 255],
];
let outside_subnet = [
[192, 168, 0, 0],
[127, 0, 0, 1],
[192, 168, 2, 0],
[192, 168, 0, 255],
[0, 0, 0, 0],
[255, 255, 255, 255],
[192, 168, 0, 0], [127, 0, 0, 1],
[192, 168, 2, 0], [192, 168, 0, 255],
[ 0, 0, 0, 0], [255, 255, 255, 255],
];
let subnets = [
([192, 168, 1, 0], 32),
([192, 168, 1, 255], 24),
([192, 168, 1, 10], 30),
([192, 168, 1, 0], 32),
([192, 168, 1, 255], 24),
([192, 168, 1, 10], 30),
];
let not_subnets = [
([192, 168, 1, 10], 23),
([127, 0, 0, 1], 8),
([192, 168, 1, 0], 0),
([192, 168, 0, 255], 32),
([192, 168, 1, 10], 23),
([127, 0, 0, 1], 8),
([192, 168, 1, 0], 0),
([192, 168, 0, 255], 32),
];
for addr in inside_subnet.iter().map(|a| Address::from_bytes(a)) {
@ -949,17 +889,13 @@ mod test {
assert!(!cidr.contains_addr(&addr));
}
for subnet in subnets
.iter()
.map(|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p))
{
for subnet in subnets.iter().map(
|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p)) {
assert!(cidr.contains_subnet(&subnet));
}
for subnet in not_subnets
.iter()
.map(|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p))
{
for subnet in not_subnets.iter().map(
|&(a, p)| Cidr::new(Address::new(a[0], a[1], a[2], a[3]), p)) {
assert!(!cidr.contains_subnet(&subnet));
}
@ -969,169 +905,94 @@ mod test {
#[test]
fn test_cidr_from_netmask() {
assert!(Cidr::from_netmask(Address([0, 0, 0, 0]), Address([1, 0, 2, 0])).is_err());
assert!(Cidr::from_netmask(Address([0, 0, 0, 0]), Address([0, 0, 0, 0])).is_err());
assert_eq!(
Cidr::from_netmask(Address([0, 0, 0, 1]), Address([255, 255, 255, 0])).unwrap(),
Cidr::new(Address([0, 0, 0, 1]), 24)
);
assert_eq!(
Cidr::from_netmask(Address([192, 168, 0, 1]), Address([255, 255, 0, 0])).unwrap(),
Cidr::new(Address([192, 168, 0, 1]), 16)
);
assert_eq!(
Cidr::from_netmask(Address([172, 16, 0, 1]), Address([255, 240, 0, 0])).unwrap(),
Cidr::new(Address([172, 16, 0, 1]), 12)
);
assert_eq!(
Cidr::from_netmask(Address([255, 255, 255, 1]), Address([255, 255, 255, 0])).unwrap(),
Cidr::new(Address([255, 255, 255, 1]), 24)
);
assert_eq!(
Cidr::from_netmask(Address([255, 255, 255, 255]), Address([255, 255, 255, 255]))
.unwrap(),
Cidr::new(Address([255, 255, 255, 255]), 32)
);
assert_eq!(Cidr::from_netmask(Address([0, 0, 0, 0]), Address([1, 0, 2, 0])).is_err(),
true);
assert_eq!(Cidr::from_netmask(Address([0, 0, 0, 0]), Address([0, 0, 0, 0])).is_err(),
true);
assert_eq!(Cidr::from_netmask(Address([0, 0, 0, 1]), Address([255, 255, 255, 0])).unwrap(),
Cidr::new(Address([0, 0, 0, 1]), 24));
assert_eq!(Cidr::from_netmask(Address([192, 168, 0, 1]), Address([255, 255, 0, 0])).unwrap(),
Cidr::new(Address([192, 168, 0, 1]), 16));
assert_eq!(Cidr::from_netmask(Address([172, 16, 0, 1]), Address([255, 240, 0, 0])).unwrap(),
Cidr::new(Address([172, 16, 0, 1]), 12));
assert_eq!(Cidr::from_netmask(Address([255, 255, 255, 1]), Address([255, 255, 255, 0])).unwrap(),
Cidr::new(Address([255, 255, 255, 1]), 24));
assert_eq!(Cidr::from_netmask(Address([255, 255, 255, 255]), Address([255, 255, 255, 255])).unwrap(),
Cidr::new(Address([255, 255, 255, 255]), 32));
}
#[test]
fn test_cidr_netmask() {
assert_eq!(
Cidr::new(Address([0, 0, 0, 0]), 0).netmask(),
Address([0, 0, 0, 0])
);
assert_eq!(
Cidr::new(Address([0, 0, 0, 1]), 24).netmask(),
Address([255, 255, 255, 0])
);
assert_eq!(
Cidr::new(Address([0, 0, 0, 0]), 32).netmask(),
Address([255, 255, 255, 255])
);
assert_eq!(
Cidr::new(Address([127, 0, 0, 0]), 8).netmask(),
Address([255, 0, 0, 0])
);
assert_eq!(
Cidr::new(Address([192, 168, 0, 0]), 16).netmask(),
Address([255, 255, 0, 0])
);
assert_eq!(
Cidr::new(Address([192, 168, 1, 1]), 16).netmask(),
Address([255, 255, 0, 0])
);
assert_eq!(
Cidr::new(Address([192, 168, 1, 1]), 17).netmask(),
Address([255, 255, 128, 0])
);
assert_eq!(
Cidr::new(Address([172, 16, 0, 0]), 12).netmask(),
Address([255, 240, 0, 0])
);
assert_eq!(
Cidr::new(Address([255, 255, 255, 1]), 24).netmask(),
Address([255, 255, 255, 0])
);
assert_eq!(
Cidr::new(Address([255, 255, 255, 255]), 32).netmask(),
Address([255, 255, 255, 255])
);
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 0).netmask(),
Address([0, 0, 0, 0]));
assert_eq!(Cidr::new(Address([0, 0, 0, 1]), 24).netmask(),
Address([255, 255, 255, 0]));
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).netmask(),
Address([255, 255, 255, 255]));
assert_eq!(Cidr::new(Address([127, 0, 0, 0]), 8).netmask(),
Address([255, 0, 0, 0]));
assert_eq!(Cidr::new(Address([192, 168, 0, 0]), 16).netmask(),
Address([255, 255, 0, 0]));
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 16).netmask(),
Address([255, 255, 0, 0]));
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 17).netmask(),
Address([255, 255, 128, 0]));
assert_eq!(Cidr::new(Address([172, 16, 0, 0]), 12).netmask(),
Address([255, 240, 0, 0]));
assert_eq!(Cidr::new(Address([255, 255, 255, 1]), 24).netmask(),
Address([255, 255, 255, 0]));
assert_eq!(Cidr::new(Address([255, 255, 255, 255]), 32).netmask(),
Address([255, 255, 255, 255]));
}
#[test]
fn test_cidr_broadcast() {
assert_eq!(
Cidr::new(Address([0, 0, 0, 0]), 0).broadcast().unwrap(),
Address([255, 255, 255, 255])
);
assert_eq!(
Cidr::new(Address([0, 0, 0, 1]), 24).broadcast().unwrap(),
Address([0, 0, 0, 255])
);
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).broadcast(), None);
assert_eq!(
Cidr::new(Address([127, 0, 0, 0]), 8).broadcast().unwrap(),
Address([127, 255, 255, 255])
);
assert_eq!(
Cidr::new(Address([192, 168, 0, 0]), 16)
.broadcast()
.unwrap(),
Address([192, 168, 255, 255])
);
assert_eq!(
Cidr::new(Address([192, 168, 1, 1]), 16)
.broadcast()
.unwrap(),
Address([192, 168, 255, 255])
);
assert_eq!(
Cidr::new(Address([192, 168, 1, 1]), 17)
.broadcast()
.unwrap(),
Address([192, 168, 127, 255])
);
assert_eq!(
Cidr::new(Address([172, 16, 0, 1]), 12).broadcast().unwrap(),
Address([172, 31, 255, 255])
);
assert_eq!(
Cidr::new(Address([255, 255, 255, 1]), 24)
.broadcast()
.unwrap(),
Address([255, 255, 255, 255])
);
assert_eq!(
Cidr::new(Address([255, 255, 255, 254]), 31).broadcast(),
None
);
assert_eq!(
Cidr::new(Address([255, 255, 255, 255]), 32).broadcast(),
None
);
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 0).broadcast().unwrap(),
Address([255, 255, 255, 255]));
assert_eq!(Cidr::new(Address([0, 0, 0, 1]), 24).broadcast().unwrap(),
Address([0, 0, 0, 255]));
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).broadcast(),
None);
assert_eq!(Cidr::new(Address([127, 0, 0, 0]), 8).broadcast().unwrap(),
Address([127, 255, 255, 255]));
assert_eq!(Cidr::new(Address([192, 168, 0, 0]), 16).broadcast().unwrap(),
Address([192, 168, 255, 255]));
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 16).broadcast().unwrap(),
Address([192, 168, 255, 255]));
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 17).broadcast().unwrap(),
Address([192, 168, 127, 255]));
assert_eq!(Cidr::new(Address([172, 16, 0, 1]), 12).broadcast().unwrap(),
Address([172, 31, 255, 255]));
assert_eq!(Cidr::new(Address([255, 255, 255, 1]), 24).broadcast().unwrap(),
Address([255, 255, 255, 255]));
assert_eq!(Cidr::new(Address([255, 255, 255, 254]), 31).broadcast(),
None);
assert_eq!(Cidr::new(Address([255, 255, 255, 255]), 32).broadcast(),
None);
}
#[test]
fn test_cidr_network() {
assert_eq!(
Cidr::new(Address([0, 0, 0, 0]), 0).network(),
Cidr::new(Address([0, 0, 0, 0]), 0)
);
assert_eq!(
Cidr::new(Address([0, 0, 0, 1]), 24).network(),
Cidr::new(Address([0, 0, 0, 0]), 24)
);
assert_eq!(
Cidr::new(Address([0, 0, 0, 0]), 32).network(),
Cidr::new(Address([0, 0, 0, 0]), 32)
);
assert_eq!(
Cidr::new(Address([127, 0, 0, 0]), 8).network(),
Cidr::new(Address([127, 0, 0, 0]), 8)
);
assert_eq!(
Cidr::new(Address([192, 168, 0, 0]), 16).network(),
Cidr::new(Address([192, 168, 0, 0]), 16)
);
assert_eq!(
Cidr::new(Address([192, 168, 1, 1]), 16).network(),
Cidr::new(Address([192, 168, 0, 0]), 16)
);
assert_eq!(
Cidr::new(Address([192, 168, 1, 1]), 17).network(),
Cidr::new(Address([192, 168, 0, 0]), 17)
);
assert_eq!(
Cidr::new(Address([172, 16, 0, 1]), 12).network(),
Cidr::new(Address([172, 16, 0, 0]), 12)
);
assert_eq!(
Cidr::new(Address([255, 255, 255, 1]), 24).network(),
Cidr::new(Address([255, 255, 255, 0]), 24)
);
assert_eq!(
Cidr::new(Address([255, 255, 255, 255]), 32).network(),
Cidr::new(Address([255, 255, 255, 255]), 32)
);
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 0).network(),
Cidr::new(Address([0, 0, 0, 0]), 0));
assert_eq!(Cidr::new(Address([0, 0, 0, 1]), 24).network(),
Cidr::new(Address([0, 0, 0, 0]), 24));
assert_eq!(Cidr::new(Address([0, 0, 0, 0]), 32).network(),
Cidr::new(Address([0, 0, 0, 0]), 32));
assert_eq!(Cidr::new(Address([127, 0, 0, 0]), 8).network(),
Cidr::new(Address([127, 0, 0, 0]), 8));
assert_eq!(Cidr::new(Address([192, 168, 0, 0]), 16).network(),
Cidr::new(Address([192, 168, 0, 0]), 16));
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 16).network(),
Cidr::new(Address([192, 168, 0, 0]), 16));
assert_eq!(Cidr::new(Address([192, 168, 1, 1]), 17).network(),
Cidr::new(Address([192, 168, 0, 0]), 17));
assert_eq!(Cidr::new(Address([172, 16, 0, 1]), 12).network(),
Cidr::new(Address([172, 16, 0, 0]), 12));
assert_eq!(Cidr::new(Address([255, 255, 255, 1]), 24).network(),
Cidr::new(Address([255, 255, 255, 0]), 24));
assert_eq!(Cidr::new(Address([255, 255, 255, 255]), 32).network(),
Cidr::new(Address([255, 255, 255, 255]), 32));
}
}

View File

@ -1,12 +1,12 @@
#![deny(missing_docs)]
use byteorder::{ByteOrder, NetworkEndian};
use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
use crate::{Error, Result};
use crate::wire::ip::pretty_print_ip_payload;
#[cfg(feature = "proto-ipv4")]
use crate::wire::ipv4;
use crate::{Error, Result};
pub use super::IpProtocol as Protocol;
@ -17,7 +17,6 @@ pub const MIN_MTU: usize = 1280;
/// A sixteen-octet IPv6 address.
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Address(pub [u8; 16]);
impl Address {
@ -29,30 +28,28 @@ impl Address {
/// The link-local [all routers multicast address].
///
/// [all routers multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1
pub const LINK_LOCAL_ALL_NODES: Address = Address([
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01,
]);
pub const LINK_LOCAL_ALL_NODES: Address =
Address([0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]);
/// The link-local [all nodes multicast address].
///
/// [all nodes multicast address]: https://tools.ietf.org/html/rfc4291#section-2.7.1
pub const LINK_LOCAL_ALL_ROUTERS: Address = Address([
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02,
]);
pub const LINK_LOCAL_ALL_ROUTERS: Address =
Address([0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]);
/// The [loopback address].
///
/// [loopback address]: https://tools.ietf.org/html/rfc4291#section-2.5.3
pub const LOOPBACK: Address = Address([
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01,
]);
pub const LOOPBACK: Address =
Address([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]);
/// Construct an IPv6 address from parts.
#[allow(clippy::too_many_arguments)]
pub fn new(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16) -> Address {
pub fn new(a0: u16, a1: u16, a2: u16, a3: u16,
a4: u16, a5: u16, a6: u16, a7: u16) -> Address {
let mut addr = [0u8; 16];
NetworkEndian::write_u16(&mut addr[0..2], a0);
NetworkEndian::write_u16(&mut addr[2..4], a1);
@ -129,7 +126,8 @@ impl Address {
///
/// [link-local]: https://tools.ietf.org/html/rfc4291#section-2.5.6
pub fn is_link_local(&self) -> bool {
self.0[0..8] == [0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
self.0[0..8] == [0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00]
}
/// Query whether the IPv6 address is the [loopback address].
@ -150,9 +148,7 @@ impl Address {
/// Convert an IPv4 mapped IPv6 address to an IPv4 address.
pub fn as_ipv4(&self) -> Option<ipv4::Address> {
if self.is_ipv4_mapped() {
Some(ipv4::Address::new(
self.0[12], self.0[13], self.0[14], self.0[15],
))
Some(ipv4::Address::new(self.0[12], self.0[13], self.0[14], self.0[15]))
} else {
None
}
@ -168,7 +164,7 @@ impl Address {
let idx = (mask as usize) / 8;
let modulus = (mask as usize) % 8;
let (first, second) = self.0.split_at(idx);
bytes[0..idx].copy_from_slice(first);
bytes[0..idx].copy_from_slice(&first);
if idx < 16 {
let part = second[0];
bytes[idx] = part & (!(0xff >> modulus) as u8);
@ -183,10 +179,10 @@ impl Address {
/// unicast.
pub fn solicited_node(&self) -> Address {
assert!(self.is_unicast());
Address([
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xFF,
self.0[13], self.0[14], self.0[15],
])
let mut bytes = [0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
bytes[14..].copy_from_slice(&self.0[14..]);
Address(bytes)
}
}
@ -207,11 +203,7 @@ impl From<Address> for ::std::net::Ipv6Addr {
impl fmt::Display for Address {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.is_ipv4_mapped() {
return write!(
f,
"::ffff:{}.{}.{}.{}",
self.0[12], self.0[13], self.0[14], self.0[15]
);
return write!(f, "::ffff:{}.{}.{}.{}", self.0[12], self.0[13], self.0[14], self.0[15])
}
// The string representation of an IPv6 address should
@ -224,7 +216,7 @@ impl fmt::Display for Address {
Head,
HeadBody,
Tail,
TailBody,
TailBody
}
let mut words = [0u16; 8];
self.write_parts(&mut words);
@ -236,7 +228,7 @@ impl fmt::Display for Address {
(0, &State::Head) | (0, &State::HeadBody) => {
write!(f, "::")?;
State::Tail
}
},
// Continue iterating without writing any characters until
// we hit anothing non-zero value.
(0, &State::Tail) => State::Tail,
@ -245,11 +237,11 @@ impl fmt::Display for Address {
(_, &State::Head) => {
write!(f, "{:x}", word)?;
State::HeadBody
}
},
(_, &State::Tail) => {
write!(f, "{:x}", word)?;
State::TailBody
}
},
// Write the u16 with a leading colon when parsing a value
// that isn't the first in a section
(_, &State::HeadBody) | (_, &State::TailBody) => {
@ -267,18 +259,16 @@ impl fmt::Display for Address {
impl From<ipv4::Address> for Address {
fn from(address: ipv4::Address) -> Self {
let octets = address.0;
Address([
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, octets[0], octets[1], octets[2], octets[3],
])
Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff,
octets[0], octets[1], octets[2], octets[3]])
}
}
/// A specification of an IPv6 CIDR block, containing an address and a variable-length
/// subnet masking prefix length.
#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Cidr {
address: Address,
address: Address,
prefix_len: u8,
}
@ -286,13 +276,12 @@ impl Cidr {
/// The [solicited node prefix].
///
/// [solicited node prefix]: https://tools.ietf.org/html/rfc4291#section-2.7.1
pub const SOLICITED_NODE_PREFIX: Cidr = Cidr {
address: Address([
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x00,
0x00, 0x00,
]),
prefix_len: 104,
};
pub const SOLICITED_NODE_PREFIX: Cidr =
Cidr {
address: Address([0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00]),
prefix_len: 104
};
/// Create an IPv6 CIDR block from the given address and prefix length.
///
@ -300,10 +289,7 @@ impl Cidr {
/// This function panics if the prefix length is larger than 128.
pub fn new(address: Address, prefix_len: u8) -> Cidr {
assert!(prefix_len <= 128);
Cidr {
address,
prefix_len,
}
Cidr { address, prefix_len }
}
/// Return the address of this IPv6 CIDR block.
@ -320,9 +306,7 @@ impl Cidr {
/// the given address.
pub fn contains_addr(&self, addr: &Address) -> bool {
// right shift by 128 is not legal
if self.prefix_len == 0 {
return true;
}
if self.prefix_len == 0 { return true }
let shift = 128 - self.prefix_len;
self.address.mask(shift) == addr.mask(shift)
@ -344,9 +328,8 @@ impl fmt::Display for Cidr {
/// A read/write wrapper around an Internet Protocol version 6 packet buffer.
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> {
buffer: T,
buffer: T
}
// Ranges and constants describing the IPv6 header
@ -381,22 +364,19 @@ mod field {
pub const VER_TC_FLOW: Field = 0..4;
// 16-bit value representing the length of the payload.
// Note: Options are included in this length.
pub const LENGTH: Field = 4..6;
pub const LENGTH: Field = 4..6;
// 8-bit value identifying the type of header following this
// one. Note: The same numbers are used in IPv4.
pub const NXT_HDR: usize = 6;
pub const NXT_HDR: usize = 6;
// 8-bit value decremented by each node that forwards this
// packet. The packet is discarded when the value is 0.
pub const HOP_LIMIT: usize = 7;
pub const HOP_LIMIT: usize = 7;
// IPv6 address of the source node.
pub const SRC_ADDR: Field = 8..24;
pub const SRC_ADDR: Field = 8..24;
// IPv6 address of the destination node.
pub const DST_ADDR: Field = 24..40;
pub const DST_ADDR: Field = 24..40;
}
/// Length of an IPv6 header.
pub const HEADER_LEN: usize = field::DST_ADDR.end;
impl<T: AsRef<[u8]>> Packet<T> {
/// Create a raw octet buffer with an IPv6 packet structure.
#[inline]
@ -613,18 +593,17 @@ impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
/// A high-level representation of an Internet Protocol version 6 packet header.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Repr {
/// IPv6 address of the source node.
pub src_addr: Address,
pub src_addr: Address,
/// IPv6 address of the destination node.
pub dst_addr: Address,
pub dst_addr: Address,
/// Protocol contained in the next header.
pub next_header: Protocol,
/// Length of the payload including the extension headers.
pub payload_len: usize,
/// The 8-bit hop limit field.
pub hop_limit: u8,
pub hop_limit: u8
}
impl Repr {
@ -632,15 +611,13 @@ impl Repr {
pub fn parse<T: AsRef<[u8]> + ?Sized>(packet: &Packet<&T>) -> Result<Repr> {
// Ensure basic accessors will work
packet.check_len()?;
if packet.version() != 6 {
return Err(Error::Malformed);
}
if packet.version() != 6 { return Err(Error::Malformed); }
Ok(Repr {
src_addr: packet.src_addr(),
dst_addr: packet.dst_addr(),
src_addr: packet.src_addr(),
dst_addr: packet.dst_addr(),
next_header: packet.next_header(),
payload_len: packet.payload_len() as usize,
hop_limit: packet.hop_limit(),
hop_limit: packet.hop_limit()
})
}
@ -667,33 +644,29 @@ impl Repr {
impl fmt::Display for Repr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"IPv6 src={} dst={} nxt_hdr={} hop_limit={}",
self.src_addr, self.dst_addr, self.next_header, self.hop_limit
)
write!(f, "IPv6 src={} dst={} nxt_hdr={} hop_limit={}",
self.src_addr, self.dst_addr, self.next_header, self.hop_limit)
}
}
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
// TODO: This is very similar to the implementation for IPv4. Make
// a way to have less copy and pasted code here.
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result {
let (ip_repr, payload) = match Packet::new_checked(buffer) {
Err(err) => return write!(f, "{}({})", indent, err),
Ok(ip_packet) => match Repr::parse(&ip_packet) {
Err(_) => return Ok(()),
Ok(ip_repr) => {
write!(f, "{}{}", indent, ip_repr)?;
(ip_repr, ip_packet.payload())
Ok(ip_packet) => {
match Repr::parse(&ip_packet) {
Err(_) => return Ok(()),
Ok(ip_repr) => {
write!(f, "{}{}", indent, ip_repr)?;
(ip_repr, ip_packet.payload())
}
}
},
}
};
pretty_print_ip_payload(f, indent, ip_repr, payload)
@ -702,18 +675,18 @@ impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
#[cfg(test)]
mod test {
use crate::Error;
use super::{Address, Cidr};
use super::{Packet, Protocol, Repr};
use crate::wire::pretty_print::PrettyPrinter;
use crate::Error;
use crate::wire::pretty_print::{PrettyPrinter};
#[cfg(feature = "proto-ipv4")]
use crate::wire::ipv4::Address as Ipv4Address;
static LINK_LOCAL_ADDR: Address = Address([
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01,
]);
static LINK_LOCAL_ADDR: Address = Address([0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01]);
#[test]
fn test_basic_multicast() {
assert!(!Address::LINK_LOCAL_ALL_ROUTERS.is_unspecified());
@ -744,62 +717,48 @@ mod test {
#[test]
fn test_address_format() {
assert_eq!("ff02::1", format!("{}", Address::LINK_LOCAL_ALL_NODES));
assert_eq!("fe80::1", format!("{}", LINK_LOCAL_ADDR));
assert_eq!(
"fe80::7f00:0:1",
format!(
"{}",
Address::new(0xfe80, 0, 0, 0, 0, 0x7f00, 0x0000, 0x0001)
)
);
assert_eq!("::", format!("{}", Address::UNSPECIFIED));
assert_eq!("::1", format!("{}", Address::LOOPBACK));
assert_eq!("ff02::1",
format!("{}", Address::LINK_LOCAL_ALL_NODES));
assert_eq!("fe80::1",
format!("{}", LINK_LOCAL_ADDR));
assert_eq!("fe80::7f00:0:1",
format!("{}", Address::new(0xfe80, 0, 0, 0, 0, 0x7f00, 0x0000, 0x0001)));
assert_eq!("::",
format!("{}", Address::UNSPECIFIED));
assert_eq!("::1",
format!("{}", Address::LOOPBACK));
#[cfg(feature = "proto-ipv4")]
assert_eq!(
"::ffff:192.168.1.1",
format!("{}", Address::from(Ipv4Address::new(192, 168, 1, 1)))
);
assert_eq!("::ffff:192.168.1.1",
format!("{}", Address::from(Ipv4Address::new(192, 168, 1, 1))));
}
#[test]
fn test_new() {
assert_eq!(
Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1),
Address::LINK_LOCAL_ALL_NODES
);
assert_eq!(
Address::new(0xff02, 0, 0, 0, 0, 0, 0, 2),
Address::LINK_LOCAL_ALL_ROUTERS
);
assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 1), Address::LOOPBACK);
assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 0), Address::UNSPECIFIED);
assert_eq!(Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1), LINK_LOCAL_ADDR);
assert_eq!(Address::new(0xff02, 0, 0, 0, 0, 0, 0, 1),
Address::LINK_LOCAL_ALL_NODES);
assert_eq!(Address::new(0xff02, 0, 0, 0, 0, 0, 0, 2),
Address::LINK_LOCAL_ALL_ROUTERS);
assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 1),
Address::LOOPBACK);
assert_eq!(Address::new(0, 0, 0, 0, 0, 0, 0, 0),
Address::UNSPECIFIED);
assert_eq!(Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
LINK_LOCAL_ADDR);
}
#[test]
fn test_from_parts() {
assert_eq!(
Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 1]),
Address::LINK_LOCAL_ALL_NODES
);
assert_eq!(
Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 2]),
Address::LINK_LOCAL_ALL_ROUTERS
);
assert_eq!(
Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 1]),
Address::LOOPBACK
);
assert_eq!(
Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 0]),
Address::UNSPECIFIED
);
assert_eq!(
Address::from_parts(&[0xfe80, 0, 0, 0, 0, 0, 0, 1]),
LINK_LOCAL_ADDR
);
assert_eq!(Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 1]),
Address::LINK_LOCAL_ALL_NODES);
assert_eq!(Address::from_parts(&[0xff02, 0, 0, 0, 0, 0, 0, 2]),
Address::LINK_LOCAL_ALL_ROUTERS);
assert_eq!(Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 1]),
Address::LOOPBACK);
assert_eq!(Address::from_parts(&[0, 0, 0, 0, 0, 0, 0, 0]),
Address::UNSPECIFIED);
assert_eq!(Address::from_parts(&[0xfe80, 0, 0, 0, 0, 0, 0, 1]),
LINK_LOCAL_ADDR);
}
#[test]
@ -822,33 +781,18 @@ mod test {
#[test]
fn test_mask() {
let addr = Address::new(0x0123, 0x4567, 0x89ab, 0, 0, 0, 0, 1);
assert_eq!(
addr.mask(11),
[0x01, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
addr.mask(15),
[0x01, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
addr.mask(26),
[0x01, 0x23, 0x45, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(
addr.mask(128),
[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
);
assert_eq!(
addr.mask(127),
[0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
);
assert_eq!(addr.mask(11), [0x01, 0x20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(addr.mask(15), [0x01, 0x22, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(addr.mask(26), [0x01, 0x23, 0x45, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(addr.mask(128), [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
assert_eq!(addr.mask(127), [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
}
#[cfg(feature = "proto-ipv4")]
#[test]
fn test_is_ipv4_mapped() {
assert!(!Address::UNSPECIFIED.is_ipv4_mapped());
assert!(Address::from(Ipv4Address::new(192, 168, 1, 1)).is_ipv4_mapped());
assert_eq!(false, Address::UNSPECIFIED.is_ipv4_mapped());
assert_eq!(true, Address::from(Ipv4Address::new(192, 168, 1, 1)).is_ipv4_mapped());
}
#[cfg(feature = "proto-ipv4")]
@ -863,14 +807,10 @@ mod test {
#[cfg(feature = "proto-ipv4")]
#[test]
fn test_from_ipv4_address() {
assert_eq!(
Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1]),
Address::from(Ipv4Address::new(192, 168, 1, 1))
);
assert_eq!(
Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 222, 1, 41, 90]),
Address::from(Ipv4Address::new(222, 1, 41, 90))
);
assert_eq!(Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 192, 168, 1, 1]),
Address::from(Ipv4Address::new(192, 168, 1, 1)));
assert_eq!(Address([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff, 222, 1, 41, 90]),
Address::from(Ipv4Address::new(222, 1, 41, 90)));
}
#[test]
@ -878,96 +818,52 @@ mod test {
let cidr = Cidr::new(LINK_LOCAL_ADDR, 64);
let inside_subnet = [
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02,
],
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
0x77, 0x88,
],
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00,
],
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0xff,
],
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02],
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88],
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff]
];
let outside_subnet = [
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
],
[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
],
[
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
],
[
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x02,
],
[0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
[0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
[0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02]
];
let subnets = [
(
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff,
],
65,
),
(
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01,
],
128,
),
(
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,
0x34, 0x56, 0x78,
],
96,
),
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
65),
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
128),
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78],
96)
];
let not_subnets = [
(
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff,
],
63,
),
(
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff,
],
64,
),
(
[
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff,
],
65,
),
(
[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01,
],
128,
),
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
63),
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
64),
([0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff],
65),
([0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01],
128)
];
for addr in inside_subnet.iter().map(|a| Address::from_bytes(a)) {
@ -978,11 +874,13 @@ mod test {
assert!(!cidr.contains_addr(&addr));
}
for subnet in subnets.iter().map(|&(a, p)| Cidr::new(Address(a), p)) {
for subnet in subnets.iter().map(
|&(a, p)| Cidr::new(Address(a), p)) {
assert!(cidr.contains_subnet(&subnet));
}
for subnet in not_subnets.iter().map(|&(a, p)| Cidr::new(Address(a), p)) {
for subnet in not_subnets.iter().map(
|&(a, p)| Cidr::new(Address(a), p)) {
assert!(!cidr.contains_subnet(&subnet));
}
@ -1002,26 +900,33 @@ mod test {
let _ = Address::from_parts(&[0u16; 7]);
}
static REPR_PACKET_BYTES: [u8; 52] = [
0x60, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x11, 0x40, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00,
0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff,
];
static REPR_PAYLOAD_BYTES: [u8; 12] = [
0x00, 0x01, 0x00, 0x02, 0x00, 0x0c, 0x02, 0x4e, 0xff, 0xff, 0xff, 0xff,
];
static REPR_PACKET_BYTES: [u8; 52] = [0x60, 0x00, 0x00, 0x00,
0x00, 0x0c, 0x11, 0x40,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0x00, 0x01, 0x00, 0x02,
0x00, 0x0c, 0x02, 0x4e,
0xff, 0xff, 0xff, 0xff];
static REPR_PAYLOAD_BYTES: [u8; 12] = [0x00, 0x01, 0x00, 0x02,
0x00, 0x0c, 0x02, 0x4e,
0xff, 0xff, 0xff, 0xff];
fn packet_repr() -> Repr {
Repr {
src_addr: Address([
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
]),
dst_addr: Address::LINK_LOCAL_ALL_NODES,
src_addr: Address([0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01]),
dst_addr: Address::LINK_LOCAL_ALL_NODES,
next_header: Protocol::Udp,
payload_len: 12,
hop_limit: 64,
hop_limit: 64
}
}
@ -1036,13 +941,10 @@ mod test {
assert_eq!(packet.payload_len() as usize, REPR_PAYLOAD_BYTES.len());
assert_eq!(packet.next_header(), Protocol::Udp);
assert_eq!(packet.hop_limit(), 0x40);
assert_eq!(
packet.src_addr(),
Address([
0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01
])
);
assert_eq!(packet.src_addr(), Address([0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01]));
assert_eq!(packet.dst_addr(), Address::LINK_LOCAL_ALL_NODES);
assert_eq!(packet.payload(), &REPR_PAYLOAD_BYTES[..]);
}
@ -1067,14 +969,15 @@ mod test {
packet.set_hop_limit(0xfe);
packet.set_src_addr(Address::LINK_LOCAL_ALL_ROUTERS);
packet.set_dst_addr(Address::LINK_LOCAL_ALL_NODES);
packet
.payload_mut()
.copy_from_slice(&REPR_PAYLOAD_BYTES[..]);
packet.payload_mut().copy_from_slice(&REPR_PAYLOAD_BYTES[..]);
let mut expected_bytes = [
0x69, 0x95, 0x43, 0x21, 0x00, 0x0c, 0x11, 0xfe, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x69, 0x95, 0x43, 0x21, 0x00, 0x0c, 0x11, 0xfe,
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
];
let start = expected_bytes.len() - REPR_PAYLOAD_BYTES.len();
expected_bytes[start..].copy_from_slice(&REPR_PAYLOAD_BYTES[..]);
@ -1088,14 +991,10 @@ mod test {
bytes.extend(&REPR_PACKET_BYTES[..]);
bytes.push(0);
assert_eq!(
Packet::new_unchecked(&bytes).payload().len(),
REPR_PAYLOAD_BYTES.len()
);
assert_eq!(
Packet::new_unchecked(&mut bytes).payload_mut().len(),
REPR_PAYLOAD_BYTES.len()
);
assert_eq!(Packet::new_unchecked(&bytes).payload().len(),
REPR_PAYLOAD_BYTES.len());
assert_eq!(Packet::new_unchecked(&mut bytes).payload_mut().len(),
REPR_PAYLOAD_BYTES.len());
}
#[test]
@ -1104,7 +1003,8 @@ mod test {
bytes.extend(&REPR_PACKET_BYTES[..]);
Packet::new_unchecked(&mut bytes).set_payload_len(0x80);
assert_eq!(Packet::new_checked(&bytes).unwrap_err(), Error::Truncated);
assert_eq!(Packet::new_checked(&bytes).unwrap_err(),
Error::Truncated);
}
#[test]
@ -1156,12 +1056,7 @@ mod test {
#[test]
fn test_pretty_print() {
assert_eq!(
format!(
"{}",
PrettyPrinter::<Packet<&'static [u8]>>::new("\n", &&REPR_PACKET_BYTES[..])
),
"\nIPv6 src=fe80::1 dst=ff02::1 nxt_hdr=UDP hop_limit=64\n \\ UDP src=1 dst=2 len=4"
);
assert_eq!(format!("{}", PrettyPrinter::<Packet<&'static [u8]>>::new("\n", &&REPR_PACKET_BYTES[..])),
"\nIPv6 src=fe80::1 dst=ff02::1 nxt_hdr=UDP hop_limit=64\n \\ UDP src=1 dst=2 len=4");
}
}

View File

@ -1,5 +1,5 @@
use crate::{Error, Result};
use core::fmt;
use crate::{Error, Result};
use byteorder::{ByteOrder, NetworkEndian};
@ -7,9 +7,8 @@ pub use super::IpProtocol as Protocol;
/// A read/write wrapper around an IPv6 Fragment Header.
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Header<T: AsRef<[u8]>> {
buffer: T,
buffer: T
}
// Format of the Fragment Header
@ -25,13 +24,13 @@ mod field {
use crate::wire::field::*;
// 8-bit identifier of the header immediately following this header.
pub const NXT_HDR: usize = 0;
pub const NXT_HDR: usize = 0;
// 8-bit reserved field.
pub const RESERVED: usize = 1;
pub const RESERVED: usize = 1;
// 16-bit field containing the fragment offset, reserved and more fragments values.
pub const FR_OF_M: Field = 2..4;
pub const FR_OF_M: Field = 2..4;
// 32-bit field identifying the fragmented packet
pub const IDENT: Field = 4..8;
pub const IDENT: Field = 4..8;
}
impl<T: AsRef<[u8]>> Header<T> {
@ -159,7 +158,6 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> {
/// A high-level representation of an IPv6 Fragment header.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Repr {
/// The type of header immediately following the Fragment header.
pub next_header: Protocol,
@ -170,19 +168,17 @@ pub struct Repr {
pub more_frags: bool,
/// The identification for every packet that is fragmented.
pub ident: u32,
}
impl Repr {
/// Parse an IPv6 Fragment Header and return a high-level representation.
pub fn parse<T>(header: &Header<&T>) -> Result<Repr>
where
T: AsRef<[u8]> + ?Sized,
{
pub fn parse<T>(header: &Header<&T>) -> Result<Repr> where T: AsRef<[u8]> + ?Sized {
Ok(Repr {
next_header: header.next_header(),
frag_offset: header.frag_offset(),
more_frags: header.more_frags(),
ident: header.ident(),
ident: header.ident()
})
}
@ -204,11 +200,8 @@ impl Repr {
impl<'a> fmt::Display for Repr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"IPv6 Fragment next_hdr={} offset={} more={} ident={}",
self.next_header, self.frag_offset, self.more_frags, self.ident
)
write!(f, "IPv6 Fragment next_hdr={} offset={} more={} ident={}",
self.next_header, self.frag_offset, self.more_frags, self.ident)
}
}
@ -217,23 +210,21 @@ mod test {
use super::*;
// A Fragment Header with more fragments remaining
static BYTES_HEADER_MORE_FRAG: [u8; 8] = [0x6, 0x0, 0x0, 0x1, 0x0, 0x0, 0x30, 0x39];
static BYTES_HEADER_MORE_FRAG: [u8; 8] = [0x6, 0x0, 0x0, 0x1,
0x0, 0x0, 0x30, 0x39];
// A Fragment Header with no more fragments remaining
static BYTES_HEADER_LAST_FRAG: [u8; 8] = [0x6, 0x0, 0xa, 0x0, 0x0, 0x1, 0x9, 0x32];
static BYTES_HEADER_LAST_FRAG: [u8; 8] = [0x6, 0x0, 0xa, 0x0,
0x0, 0x1, 0x9, 0x32];
#[test]
fn test_check_len() {
// less than 8 bytes
assert_eq!(
Err(Error::Truncated),
Header::new_unchecked(&BYTES_HEADER_MORE_FRAG[..7]).check_len()
);
assert_eq!(Err(Error::Truncated),
Header::new_unchecked(&BYTES_HEADER_MORE_FRAG[..7]).check_len());
// valid
assert_eq!(
Ok(()),
Header::new_unchecked(&BYTES_HEADER_MORE_FRAG).check_len()
);
assert_eq!(Ok(()),
Header::new_unchecked(&BYTES_HEADER_MORE_FRAG).check_len());
}
#[test]
@ -241,13 +232,13 @@ mod test {
let header = Header::new_unchecked(&BYTES_HEADER_MORE_FRAG);
assert_eq!(header.next_header(), Protocol::Tcp);
assert_eq!(header.frag_offset(), 0);
assert!(header.more_frags());
assert_eq!(header.more_frags(), true);
assert_eq!(header.ident(), 12345);
let header = Header::new_unchecked(&BYTES_HEADER_LAST_FRAG);
assert_eq!(header.next_header(), Protocol::Tcp);
assert_eq!(header.frag_offset(), 320);
assert!(!header.more_frags());
assert_eq!(header.more_frags(), false);
assert_eq!(header.ident(), 67890);
}
@ -255,48 +246,24 @@ mod test {
fn test_repr_parse_valid() {
let header = Header::new_unchecked(&BYTES_HEADER_MORE_FRAG);
let repr = Repr::parse(&header).unwrap();
assert_eq!(
repr,
Repr {
next_header: Protocol::Tcp,
frag_offset: 0,
more_frags: true,
ident: 12345
}
);
assert_eq!(repr,
Repr{ next_header: Protocol::Tcp, frag_offset: 0, more_frags: true, ident: 12345 });
let header = Header::new_unchecked(&BYTES_HEADER_LAST_FRAG);
let repr = Repr::parse(&header).unwrap();
assert_eq!(
repr,
Repr {
next_header: Protocol::Tcp,
frag_offset: 320,
more_frags: false,
ident: 67890
}
);
assert_eq!(repr,
Repr{ next_header: Protocol::Tcp, frag_offset: 320, more_frags: false, ident: 67890 });
}
#[test]
fn test_repr_emit() {
let repr = Repr {
next_header: Protocol::Tcp,
frag_offset: 0,
more_frags: true,
ident: 12345,
};
let repr = Repr{ next_header: Protocol::Tcp, frag_offset: 0, more_frags: true, ident: 12345 };
let mut bytes = [0u8; 8];
let mut header = Header::new_unchecked(&mut bytes);
repr.emit(&mut header);
assert_eq!(header.into_inner(), &BYTES_HEADER_MORE_FRAG[0..8]);
let repr = Repr {
next_header: Protocol::Tcp,
frag_offset: 320,
more_frags: false,
ident: 67890,
};
let repr = Repr{ next_header: Protocol::Tcp, frag_offset: 320, more_frags: false, ident: 67890 };
let mut bytes = [0u8; 8];
let mut header = Header::new_unchecked(&mut bytes);
repr.emit(&mut header);

View File

@ -1,14 +1,13 @@
use crate::{Error, Result};
use core::fmt;
use crate::{Error, Result};
pub use super::IpProtocol as Protocol;
use crate::wire::ipv6option::Ipv6OptionsIterator;
pub use super::IpProtocol as Protocol;
/// A read/write wrapper around an IPv6 Hop-by-Hop Options Header.
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Header<T: AsRef<[u8]>> {
buffer: T,
buffer: T
}
// Format of the Hop-by-Hop Options Header
@ -31,13 +30,13 @@ mod field {
use crate::wire::field::*;
// Minimum size of the header.
pub const MIN_HEADER_SIZE: usize = 8;
pub const MIN_HEADER_SIZE: usize = 8;
// 8-bit identifier of the header immediately following this header.
pub const NXT_HDR: usize = 0;
pub const NXT_HDR: usize = 0;
// 8-bit unsigned integer. Length of the OPTIONS field in 8-octet units,
// not including the first 8 octets.
pub const LENGTH: usize = 1;
pub const LENGTH: usize = 1;
// Variable-length field. Option-Type-specific data.
//
// Length of the header is in 8-octet units, not including the first 8 octets. The first two
@ -111,7 +110,7 @@ impl<T: AsRef<[u8]>> Header<T> {
impl<'a, T: AsRef<[u8]> + ?Sized> Header<&'a T> {
/// Return the option data.
#[inline]
pub fn options(&self) -> &'a [u8] {
pub fn options(&self) -> &'a[u8] {
let data = self.buffer.as_ref();
&data[field::OPTIONS(data[field::LENGTH])]
}
@ -158,26 +157,22 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> {
/// A high-level representation of an IPv6 Hop-by-Hop Options header.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Repr<'a> {
/// The type of header immediately following the Hop-by-Hop Options header.
pub next_header: Protocol,
/// Length of the Hop-by-Hop Options header in 8-octet units, not including the first 8 octets.
pub length: u8,
pub length: u8,
/// The options contained in the Hop-by-Hop Options header.
pub options: &'a [u8],
pub options: &'a [u8]
}
impl<'a> Repr<'a> {
/// Parse an IPv6 Hop-by-Hop Options Header and return a high-level representation.
pub fn parse<T>(header: &Header<&'a T>) -> Result<Repr<'a>>
where
T: AsRef<[u8]> + ?Sized,
{
pub fn parse<T>(header: &Header<&'a T>) -> Result<Repr<'a>> where T: AsRef<[u8]> + ?Sized {
Ok(Repr {
next_header: header.next_header(),
length: header.header_len(),
options: header.options(),
options: header.options()
})
}
@ -202,11 +197,7 @@ impl<'a> Repr<'a> {
impl<'a> fmt::Display for Repr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"IPv6 Hop-by-Hop Options next_hdr={} length={} ",
self.next_header, self.length
)
write!(f, "IPv6 Hop-by-Hop Options next_hdr={} length={} ", self.next_header, self.length)
}
}
@ -215,43 +206,36 @@ mod test {
use super::*;
// A Hop-by-Hop Option header with a PadN option of option data length 4.
static REPR_PACKET_PAD4: [u8; 8] = [0x6, 0x0, 0x1, 0x4, 0x0, 0x0, 0x0, 0x0];
static REPR_PACKET_PAD4: [u8; 8] = [0x6, 0x0, 0x1, 0x4,
0x0, 0x0, 0x0, 0x0];
// A Hop-by-Hop Option header with a PadN option of option data length 12.
static REPR_PACKET_PAD12: [u8; 16] = [
0x06, 0x1, 0x1, 0x12, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
];
static REPR_PACKET_PAD12: [u8; 16] = [0x06, 0x1, 0x1, 0x12,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0];
#[test]
fn test_check_len() {
// zero byte buffer
assert_eq!(
Err(Error::Truncated),
Header::new_unchecked(&REPR_PACKET_PAD4[..0]).check_len()
);
assert_eq!(Err(Error::Truncated),
Header::new_unchecked(&REPR_PACKET_PAD4[..0]).check_len());
// no length field
assert_eq!(
Err(Error::Truncated),
Header::new_unchecked(&REPR_PACKET_PAD4[..1]).check_len()
);
assert_eq!(Err(Error::Truncated),
Header::new_unchecked(&REPR_PACKET_PAD4[..1]).check_len());
// less than 8 bytes
assert_eq!(
Err(Error::Truncated),
Header::new_unchecked(&REPR_PACKET_PAD4[..7]).check_len()
);
assert_eq!(Err(Error::Truncated),
Header::new_unchecked(&REPR_PACKET_PAD4[..7]).check_len());
// valid
assert_eq!(Ok(()), Header::new_unchecked(&REPR_PACKET_PAD4).check_len());
assert_eq!(Ok(()),
Header::new_unchecked(&REPR_PACKET_PAD4).check_len());
// valid
assert_eq!(
Ok(()),
Header::new_unchecked(&REPR_PACKET_PAD12).check_len()
);
assert_eq!(Ok(()),
Header::new_unchecked(&REPR_PACKET_PAD12).check_len());
// length field value greater than number of bytes
let header: [u8; 8] = [0x06, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0];
assert_eq!(
Err(Error::Truncated),
Header::new_unchecked(&header).check_len()
);
assert_eq!(Err(Error::Truncated),
Header::new_unchecked(&header).check_len());
}
#[test]
@ -273,27 +257,19 @@ mod test {
bytes.extend(&REPR_PACKET_PAD4[..]);
bytes.push(0);
assert_eq!(
Header::new_unchecked(&bytes).options().len(),
REPR_PACKET_PAD4[2..].len()
);
assert_eq!(
Header::new_unchecked(&mut bytes).options_mut().len(),
REPR_PACKET_PAD4[2..].len()
);
assert_eq!(Header::new_unchecked(&bytes).options().len(),
REPR_PACKET_PAD4[2..].len());
assert_eq!(Header::new_unchecked(&mut bytes).options_mut().len(),
REPR_PACKET_PAD4[2..].len());
let mut bytes = vec![];
bytes.extend(&REPR_PACKET_PAD12[..]);
bytes.push(0);
assert_eq!(
Header::new_unchecked(&bytes).options().len(),
REPR_PACKET_PAD12[2..].len()
);
assert_eq!(
Header::new_unchecked(&mut bytes).options_mut().len(),
REPR_PACKET_PAD12[2..].len()
);
assert_eq!(Header::new_unchecked(&bytes).options().len(),
REPR_PACKET_PAD12[2..].len());
assert_eq!(Header::new_unchecked(&mut bytes).options_mut().len(),
REPR_PACKET_PAD12[2..].len());
}
#[test]
@ -317,44 +293,26 @@ mod test {
fn test_repr_parse_valid() {
let header = Header::new_unchecked(&REPR_PACKET_PAD4);
let repr = Repr::parse(&header).unwrap();
assert_eq!(
repr,
Repr {
next_header: Protocol::Tcp,
length: 0,
options: &REPR_PACKET_PAD4[2..]
}
);
assert_eq!(repr, Repr {
next_header: Protocol::Tcp, length: 0, options: &REPR_PACKET_PAD4[2..]
});
let header = Header::new_unchecked(&REPR_PACKET_PAD12);
let repr = Repr::parse(&header).unwrap();
assert_eq!(
repr,
Repr {
next_header: Protocol::Tcp,
length: 1,
options: &REPR_PACKET_PAD12[2..]
}
);
assert_eq!(repr, Repr {
next_header: Protocol::Tcp, length: 1, options: &REPR_PACKET_PAD12[2..]
});
}
#[test]
fn test_repr_emit() {
let repr = Repr {
next_header: Protocol::Tcp,
length: 0,
options: &REPR_PACKET_PAD4[2..],
};
let repr = Repr{ next_header: Protocol::Tcp, length: 0, options: &REPR_PACKET_PAD4[2..] };
let mut bytes = [0u8; 8];
let mut header = Header::new_unchecked(&mut bytes);
repr.emit(&mut header);
assert_eq!(header.into_inner(), &REPR_PACKET_PAD4[..]);
let repr = Repr {
next_header: Protocol::Tcp,
length: 1,
options: &REPR_PACKET_PAD12[2..],
};
let repr = Repr{ next_header: Protocol::Tcp, length: 1, options: &REPR_PACKET_PAD12[2..] };
let mut bytes = [0u8; 16];
let mut header = Header::new_unchecked(&mut bytes);
repr.emit(&mut header);

View File

@ -1,5 +1,5 @@
use crate::{Error, Result};
use core::fmt;
use crate::{Error, Result};
enum_with_unknown! {
/// IPv6 Extension Header Option Type
@ -14,9 +14,9 @@ enum_with_unknown! {
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Type::Pad1 => write!(f, "Pad1"),
Type::PadN => write!(f, "PadN"),
Type::Unknown(id) => write!(f, "{}", id),
Type::Pad1 => write!(f, "Pad1"),
Type::PadN => write!(f, "PadN"),
Type::Unknown(id) => write!(f, "{}", id)
}
}
}
@ -40,11 +40,11 @@ enum_with_unknown! {
impl fmt::Display for FailureType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
FailureType::Skip => write!(f, "skip"),
FailureType::Discard => write!(f, "discard"),
FailureType::DiscardSendAll => write!(f, "discard and send error"),
FailureType::Skip => write!(f, "skip"),
FailureType::Discard => write!(f, "discard"),
FailureType::DiscardSendAll => write!(f, "discard and send error"),
FailureType::DiscardSendUnicast => write!(f, "discard and send error if unicast"),
FailureType::Unknown(id) => write!(f, "Unknown({})", id),
FailureType::Unknown(id) => write!(f, "Unknown({})", id),
}
}
}
@ -58,9 +58,8 @@ impl From<Type> for FailureType {
/// A read/write wrapper around an IPv6 Extension Header Option.
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Ipv6Option<T: AsRef<[u8]>> {
buffer: T,
buffer: T
}
// Format of Option
@ -77,9 +76,9 @@ mod field {
use crate::wire::field::*;
// 8-bit identifier of the type of option.
pub const TYPE: usize = 0;
pub const TYPE: usize = 0;
// 8-bit unsigned integer. Length of the DATA field of this option, in octets.
pub const LENGTH: usize = 1;
pub const LENGTH: usize = 1;
// Variable-length field. Option-Type-specific data.
pub fn DATA(length: u8) -> Field {
2..length as usize + 2
@ -215,32 +214,32 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Ipv6Option<&'a T> {
/// A high-level representation of an IPv6 Extension Header Option.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Repr<'a> {
Pad1,
PadN(u8),
Unknown {
type_: Type,
type_: Type,
length: u8,
data: &'a [u8],
data: &'a [u8]
},
}
impl<'a> Repr<'a> {
/// Parse an IPv6 Extension Header Option and return a high-level representation.
pub fn parse<T>(opt: &Ipv6Option<&'a T>) -> Result<Repr<'a>>
where
T: AsRef<[u8]> + ?Sized,
{
pub fn parse<T>(opt: &Ipv6Option<&'a T>) -> Result<Repr<'a>> where T: AsRef<[u8]> + ?Sized {
match opt.option_type() {
Type::Pad1 => Ok(Repr::Pad1),
Type::PadN => Ok(Repr::PadN(opt.data_len())),
unknown_type @ Type::Unknown(_) => Ok(Repr::Unknown {
type_: unknown_type,
length: opt.data_len(),
data: opt.data(),
}),
Type::Pad1 =>
Ok(Repr::Pad1),
Type::PadN =>
Ok(Repr::PadN(opt.data_len())),
unknown_type @ Type::Unknown(_) => {
Ok(Repr::Unknown {
type_: unknown_type,
length: opt.data_len(),
data: opt.data(),
})
}
}
}
@ -248,15 +247,18 @@ impl<'a> Repr<'a> {
pub fn buffer_len(&self) -> usize {
match *self {
Repr::Pad1 => 1,
Repr::PadN(length) => field::DATA(length).end,
Repr::Unknown { length, .. } => field::DATA(length).end,
Repr::PadN(length) =>
field::DATA(length).end,
Repr::Unknown{ length, .. } =>
field::DATA(length).end,
}
}
/// Emit a high-level representation into an IPv6 Extension Header Option.
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self, opt: &mut Ipv6Option<&'a mut T>) {
match *self {
Repr::Pad1 => opt.set_option_type(Type::Pad1),
Repr::Pad1 =>
opt.set_option_type(Type::Pad1),
Repr::PadN(len) => {
opt.set_option_type(Type::PadN);
opt.set_data_len(len);
@ -265,11 +267,7 @@ impl<'a> Repr<'a> {
*x = 0
}
}
Repr::Unknown {
type_,
length,
data,
} => {
Repr::Unknown{ type_, length, data } => {
opt.set_option_type(type_);
opt.set_data_len(length);
opt.data_mut().copy_from_slice(&data[..length as usize]);
@ -280,12 +278,11 @@ impl<'a> Repr<'a> {
/// A iterator for IPv6 options.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Ipv6OptionsIterator<'a> {
pos: usize,
length: usize,
data: &'a [u8],
hit_error: bool,
hit_error: bool
}
impl<'a> Ipv6OptionsIterator<'a> {
@ -301,8 +298,7 @@ impl<'a> Ipv6OptionsIterator<'a> {
Ipv6OptionsIterator {
pos: 0,
hit_error: false,
length,
data,
length, data
}
}
}
@ -315,16 +311,18 @@ impl<'a> Iterator for Ipv6OptionsIterator<'a> {
// If we still have data to parse and we have not previously
// hit an error, attempt to parse the next option.
match Ipv6Option::new_checked(&self.data[self.pos..]) {
Ok(hdr) => match Repr::parse(&hdr) {
Ok(repr) => {
self.pos += repr.buffer_len();
Some(Ok(repr))
Ok(hdr) => {
match Repr::parse(&hdr) {
Ok(repr) => {
self.pos += repr.buffer_len();
Some(Ok(repr))
}
Err(e) => {
self.hit_error = true;
Some(Err(e))
}
}
Err(e) => {
self.hit_error = true;
Some(Err(e))
}
},
}
Err(e) => {
self.hit_error = true;
Some(Err(e))
@ -342,9 +340,12 @@ impl<'a> fmt::Display for Repr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "IPv6 Option ")?;
match *self {
Repr::Pad1 => write!(f, "{} ", Type::Pad1),
Repr::PadN(len) => write!(f, "{} length={} ", Type::PadN, len),
Repr::Unknown { type_, length, .. } => write!(f, "{} length={} ", type_, length),
Repr::Pad1 =>
write!(f, "{} ", Type::Pad1),
Repr::PadN(len) =>
write!(f, "{} length={} ", Type::PadN, len),
Repr::Unknown{ type_, length, .. } =>
write!(f, "{} length={} ", type_, length),
}
}
}
@ -353,49 +354,35 @@ impl<'a> fmt::Display for Repr<'a> {
mod test {
use super::*;
static IPV6OPTION_BYTES_PAD1: [u8; 1] = [0x0];
static IPV6OPTION_BYTES_PADN: [u8; 3] = [0x1, 0x1, 0x0];
static IPV6OPTION_BYTES_PAD1: [u8; 1] = [0x0];
static IPV6OPTION_BYTES_PADN: [u8; 3] = [0x1, 0x1, 0x0];
static IPV6OPTION_BYTES_UNKNOWN: [u8; 5] = [0xff, 0x3, 0x0, 0x0, 0x0];
#[test]
fn test_check_len() {
let bytes = [0u8];
// zero byte buffer
assert_eq!(
Err(Error::Truncated),
Ipv6Option::new_unchecked(&bytes[..0]).check_len()
);
assert_eq!(Err(Error::Truncated),
Ipv6Option::new_unchecked(&bytes[..0]).check_len());
// pad1
assert_eq!(
Ok(()),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1).check_len()
);
assert_eq!(Ok(()),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PAD1).check_len());
// padn with truncated data
assert_eq!(
Err(Error::Truncated),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN[..2]).check_len()
);
assert_eq!(Err(Error::Truncated),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN[..2]).check_len());
// padn
assert_eq!(
Ok(()),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN).check_len()
);
assert_eq!(Ok(()),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_PADN).check_len());
// unknown option type with truncated data
assert_eq!(
Err(Error::Truncated),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..4]).check_len()
);
assert_eq!(
Err(Error::Truncated),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..1]).check_len()
);
assert_eq!(Err(Error::Truncated),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..4]).check_len());
assert_eq!(Err(Error::Truncated),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN[..1]).check_len());
// unknown type
assert_eq!(
Ok(()),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN).check_len()
);
assert_eq!(Ok(()),
Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN).check_len());
}
#[test]
@ -412,7 +399,7 @@ mod test {
assert_eq!(opt.option_type(), Type::Pad1);
// two octets of padding
let bytes: [u8; 2] = [0x1, 0x0];
let bytes: [u8; 2] = [0x1, 0x0];
let opt = Ipv6Option::new_unchecked(&bytes);
assert_eq!(opt.option_type(), Type::PadN);
assert_eq!(opt.data_len(), 0);
@ -424,14 +411,14 @@ mod test {
assert_eq!(opt.data(), &[0]);
// extra bytes in buffer
let bytes: [u8; 10] = [0x1, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff];
let bytes: [u8; 10] = [0x1, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xff];
let opt = Ipv6Option::new_unchecked(&bytes);
assert_eq!(opt.option_type(), Type::PadN);
assert_eq!(opt.data_len(), 7);
assert_eq!(opt.data(), &[0, 0, 0, 0, 0, 0, 0]);
// unrecognized option
let bytes: [u8; 1] = [0xff];
let bytes: [u8; 1] = [0xff];
let opt = Ipv6Option::new_unchecked(&bytes);
assert_eq!(opt.option_type(), Type::Unknown(255));
@ -457,14 +444,7 @@ mod test {
let data = [0u8; 3];
let opt = Ipv6Option::new_unchecked(&IPV6OPTION_BYTES_UNKNOWN);
let unknown = Repr::parse(&opt).unwrap();
assert_eq!(
unknown,
Repr::Unknown {
type_: Type::Unknown(255),
length: 3,
data: &data
}
);
assert_eq!(unknown, Repr::Unknown { type_: Type::Unknown(255), length: 3, data: &data });
}
#[test]
@ -482,11 +462,7 @@ mod test {
assert_eq!(opt.into_inner(), &IPV6OPTION_BYTES_PADN);
let data = [0u8; 3];
let repr = Repr::Unknown {
type_: Type::Unknown(255),
length: 3,
data: &data,
};
let repr = Repr::Unknown { type_: Type::Unknown(255), length: 3, data: &data };
let mut bytes = [254u8; 5]; // don't assume bytes are initialized to zero
let mut opt = Ipv6Option::new_unchecked(&mut bytes);
repr.emit(&mut opt);
@ -509,10 +485,10 @@ mod test {
#[test]
fn test_options_iter() {
let options = [
0x00, 0x01, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x11, 0x00, 0x01,
0x08, 0x00,
];
let options = [0x00, 0x01, 0x01, 0x00,
0x01, 0x02, 0x00, 0x00,
0x01, 0x00, 0x00, 0x11,
0x00, 0x01, 0x08, 0x00];
let mut iterator = Ipv6OptionsIterator::new(&options, 0);
assert_eq!(iterator.next(), None);
@ -525,14 +501,8 @@ mod test {
(2, Ok(Repr::PadN(2))) => continue,
(3, Ok(Repr::PadN(0))) => continue,
(4, Ok(Repr::Pad1)) => continue,
(
5,
Ok(Repr::Unknown {
type_: Type::Unknown(0x11),
length: 0,
..
}),
) => continue,
(5, Ok(Repr::Unknown { type_: Type::Unknown(0x11), length: 0, .. })) =>
continue,
(6, Err(Error::Truncated)) => continue,
(i, res) => panic!("Unexpected option `{:?}` at index {}", res, i),
}

View File

@ -1,5 +1,5 @@
use crate::{Error, Result};
use core::fmt;
use crate::{Error, Result};
use crate::wire::IpProtocol as Protocol;
use crate::wire::Ipv6Address as Address;
@ -37,23 +37,22 @@ enum_with_unknown! {
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Type::Type0 => write!(f, "Type0"),
Type::Nimrod => write!(f, "Nimrod"),
Type::Type2 => write!(f, "Type2"),
Type::Rpl => write!(f, "Rpl"),
Type::Experiment1 => write!(f, "Experiment1"),
Type::Experiment2 => write!(f, "Experiment2"),
Type::Reserved => write!(f, "Reserved"),
Type::Unknown(id) => write!(f, "{}", id),
Type::Type0 => write!(f, "Type0"),
Type::Nimrod => write!(f, "Nimrod"),
Type::Type2 => write!(f, "Type2"),
Type::Rpl => write!(f, "Rpl"),
Type::Experiment1 => write!(f, "Experiment1"),
Type::Experiment2 => write!(f, "Experiment2"),
Type::Reserved => write!(f, "Reserved"),
Type::Unknown(id) => write!(f, "{}", id)
}
}
}
/// A read/write wrapper around an IPv6 Routing Header buffer.
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Header<T: AsRef<[u8]>> {
buffer: T,
buffer: T
}
// Format of the Routing Header
@ -76,15 +75,15 @@ mod field {
use crate::wire::field::*;
// Minimum size of the header.
pub const MIN_HEADER_SIZE: usize = 4;
pub const MIN_HEADER_SIZE: usize = 4;
// 8-bit identifier of the header immediately following this header.
pub const NXT_HDR: usize = 0;
pub const NXT_HDR: usize = 0;
// 8-bit unsigned integer. Length of the DATA field in 8-octet units,
// not including the first 8 octets.
pub const LENGTH: usize = 1;
pub const LENGTH: usize = 1;
// 8-bit identifier of a particular Routing header variant.
pub const TYPE: usize = 2;
pub const TYPE: usize = 2;
// 8-bit unsigned integer. The number of route segments remaining.
pub const SEG_LEFT: usize = 3;
// Variable-length field. Routing-Type-specific data.
@ -130,9 +129,9 @@ mod field {
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 8-bit field containing the CmprI and CmprE values.
pub const CMPR: usize = 4;
pub const CMPR: usize = 4;
// 8-bit field containing the Pad value.
pub const PAD: usize = 5;
pub const PAD: usize = 5;
// Variable length field containing addresses
pub fn ADDRESSES(length_field: u8) -> Field {
let data = DATA(length_field);
@ -234,6 +233,7 @@ impl<T: AsRef<[u8]>> Header<T> {
data[field::CMPR] >> 4
}
/// Return the number of prefix octects elided from the last address (`addresses[n]`).
///
/// # Panics
@ -315,7 +315,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Header<T> {
data[7] = 0;
}
_ => panic!("Unrecognized routing type when clearing reserved fields."),
_ => panic!("Unrecognized routing type when clearing reserved fields.")
}
}
}
@ -389,62 +389,63 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Header<&'a T> {
/// A high-level representation of an IPv6 Routing Header.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub enum Repr<'a> {
Type2 {
/// The type of header immediately following the Routing header.
next_header: Protocol,
next_header: Protocol,
/// Length of the Routing header in 8-octet units, not including the first 8 octets.
length: u8,
length: u8,
/// Number of route segments remaining.
segments_left: u8,
segments_left: u8,
/// The home address of the destination mobile node.
home_address: Address,
},
Rpl {
/// The type of header immediately following the Routing header.
next_header: Protocol,
next_header: Protocol,
/// Length of the Routing header in 8-octet units, not including the first 8 octets.
length: u8,
length: u8,
/// Number of route segments remaining.
segments_left: u8,
segments_left: u8,
/// Number of prefix octets from each segment, except the last segment, that are elided.
cmpr_i: u8,
cmpr_i: u8,
/// Number of prefix octets from the last segment that are elided.
cmpr_e: u8,
cmpr_e: u8,
/// Number of octets that are used for padding after `address[n]` at the end of the
/// RPL Source Route Header.
pad: u8,
pad: u8,
/// Vector of addresses, numbered 1 to `n`.
addresses: &'a [u8],
addresses: &'a[u8],
},
}
impl<'a> Repr<'a> {
/// Parse an IPv6 Routing Header and return a high-level representation.
pub fn parse<T>(header: &'a Header<&'a T>) -> Result<Repr<'a>>
where
T: AsRef<[u8]> + ?Sized,
{
pub fn parse<T>(header: &'a Header<&'a T>) -> Result<Repr<'a>> where T: AsRef<[u8]> + ?Sized {
match header.routing_type() {
Type::Type2 => Ok(Repr::Type2 {
next_header: header.next_header(),
length: header.header_len(),
segments_left: header.segments_left(),
home_address: header.home_address(),
}),
Type::Rpl => Ok(Repr::Rpl {
next_header: header.next_header(),
length: header.header_len(),
segments_left: header.segments_left(),
cmpr_i: header.cmpr_i(),
cmpr_e: header.cmpr_e(),
pad: header.pad(),
addresses: header.addresses(),
}),
Type::Type2 => {
Ok(Repr::Type2 {
next_header: header.next_header(),
length: header.header_len(),
segments_left: header.segments_left(),
home_address: header.home_address(),
})
}
Type::Rpl => {
Ok(Repr::Rpl {
next_header: header.next_header(),
length: header.header_len(),
segments_left: header.segments_left(),
cmpr_i: header.cmpr_i(),
cmpr_e: header.cmpr_e(),
pad: header.pad(),
addresses: header.addresses(),
})
}
_ => Err(Error::Unrecognized),
_ => Err(Error::Unrecognized)
}
}
@ -452,19 +453,16 @@ impl<'a> Repr<'a> {
/// representation.
pub fn buffer_len(&self) -> usize {
match self {
&Repr::Rpl { length, .. } | &Repr::Type2 { length, .. } => field::DATA(length).end,
&Repr::Rpl { length, .. } | &Repr::Type2 { length, .. } => {
field::DATA(length).end
}
}
}
/// Emit a high-level representation into an IPv6 Routing Header.
pub fn emit<T: AsRef<[u8]> + AsMut<[u8]> + ?Sized>(&self, header: &mut Header<&mut T>) {
match *self {
Repr::Type2 {
next_header,
length,
segments_left,
home_address,
} => {
Repr::Type2 { next_header, length, segments_left, home_address } => {
header.set_next_header(next_header);
header.set_header_len(length);
header.set_routing_type(Type::Type2);
@ -472,15 +470,7 @@ impl<'a> Repr<'a> {
header.clear_reserved();
header.set_home_address(home_address);
}
Repr::Rpl {
next_header,
length,
segments_left,
cmpr_i,
cmpr_e,
pad,
addresses,
} => {
Repr::Rpl { next_header, length, segments_left, cmpr_i, cmpr_e, pad, addresses } => {
header.set_next_header(next_header);
header.set_header_len(length);
header.set_routing_type(Type::Rpl);
@ -498,31 +488,11 @@ impl<'a> Repr<'a> {
impl<'a> fmt::Display for Repr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Repr::Type2 {
next_header,
length,
segments_left,
home_address,
} => {
write!(
f,
"IPv6 Routing next_hdr={} length={} type={} seg_left={} home_address={}",
next_header,
length,
Type::Type2,
segments_left,
home_address
)
Repr::Type2 { next_header, length, segments_left, home_address } => {
write!(f, "IPv6 Routing next_hdr={} length={} type={} seg_left={} home_address={}",
next_header, length, Type::Type2, segments_left, home_address)
}
Repr::Rpl {
next_header,
length,
segments_left,
cmpr_i,
cmpr_e,
pad,
..
} => {
Repr::Rpl { next_header, length, segments_left, cmpr_i, cmpr_e, pad, .. } => {
write!(f, "IPv6 Routing next_hdr={} length={} type={} seg_left={} cmpr_i={} cmpr_e={} pad={}",
next_header, length, Type::Rpl, segments_left, cmpr_i, cmpr_e, pad)
}
@ -535,10 +505,12 @@ mod test {
use super::*;
// A Type 2 Routing Header
static BYTES_TYPE2: [u8; 24] = [
0x6, 0x2, 0x2, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x1,
];
static BYTES_TYPE2: [u8; 24] = [0x6, 0x2, 0x2, 0x1,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x1];
// A representation of a Type 2 Routing header
static REPR_TYPE2: Repr = Repr::Type2 {
@ -549,11 +521,16 @@ mod test {
};
// A Source Routing Header with full IPv6 addresses in bytes
static BYTES_SRH_FULL: [u8; 40] = [
0x6, 0x4, 0x3, 0x2, 0x0, 0x0, 0x0, 0x0, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x3, 0x1,
];
static BYTES_SRH_FULL: [u8; 40] = [0x6, 0x4, 0x3, 0x2,
0x0, 0x0, 0x0, 0x0,
0xfd, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2,
0xfd, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x3, 0x1];
// A representation of a Source Routing Header with full IPv6 addresses
static REPR_SRH_FULL: Repr = Repr::Rpl {
@ -563,16 +540,21 @@ mod test {
cmpr_i: 0,
cmpr_e: 0,
pad: 0,
addresses: &[
0xfd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x2, 0xfd,
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x1,
],
addresses: &[0xfd, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x2,
0xfd, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x0, 0x0,
0x0, 0x0, 0x3, 0x1]
};
// A Source Routing Header with elided IPv6 addresses in bytes
static BYTES_SRH_ELIDED: [u8; 16] = [
0x6, 0x1, 0x3, 0x2, 0xfe, 0x50, 0x0, 0x0, 0x2, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0,
];
static BYTES_SRH_ELIDED: [u8; 16] = [0x6, 0x1, 0x3, 0x2,
0xfe, 0x50, 0x0, 0x0,
0x2, 0x3, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0];
// A representation of a Source Routing Header with elided IPv6 addresses
static REPR_SRH_ELIDED: Repr = Repr::Rpl {
@ -582,37 +564,20 @@ mod test {
cmpr_i: 15,
cmpr_e: 14,
pad: 5,
addresses: &[0x2, 0x3, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0],
addresses: &[0x2, 0x3, 0x1, 0x0,
0x0, 0x0, 0x0, 0x0]
};
#[test]
fn test_check_len() {
// less than min header size
assert_eq!(
Err(Error::Truncated),
Header::new(&BYTES_TYPE2[..3]).check_len()
);
assert_eq!(
Err(Error::Truncated),
Header::new(&BYTES_SRH_FULL[..3]).check_len()
);
assert_eq!(
Err(Error::Truncated),
Header::new(&BYTES_SRH_ELIDED[..3]).check_len()
);
assert_eq!(Err(Error::Truncated), Header::new(&BYTES_TYPE2[..3]).check_len());
assert_eq!(Err(Error::Truncated), Header::new(&BYTES_SRH_FULL[..3]).check_len());
assert_eq!(Err(Error::Truncated), Header::new(&BYTES_SRH_ELIDED[..3]).check_len());
// less than specfied length field
assert_eq!(
Err(Error::Truncated),
Header::new(&BYTES_TYPE2[..23]).check_len()
);
assert_eq!(
Err(Error::Truncated),
Header::new(&BYTES_SRH_FULL[..39]).check_len()
);
assert_eq!(
Err(Error::Truncated),
Header::new(&BYTES_SRH_ELIDED[..11]).check_len()
);
assert_eq!(Err(Error::Truncated), Header::new(&BYTES_TYPE2[..23]).check_len());
assert_eq!(Err(Error::Truncated), Header::new(&BYTES_SRH_FULL[..39]).check_len());
assert_eq!(Err(Error::Truncated), Header::new(&BYTES_SRH_ELIDED[..11]).check_len());
// valid
assert_eq!(Ok(()), Header::new(&BYTES_TYPE2[..]).check_len());
assert_eq!(Ok(()), Header::new(&BYTES_SRH_FULL[..]).check_len());

View File

@ -6,9 +6,9 @@
use byteorder::{ByteOrder, NetworkEndian};
use crate::{Error, Result};
use crate::wire::icmpv6::{field, Message, Packet};
use crate::wire::Ipv6Address;
use crate::{Error, Result};
enum_with_unknown! {
/// MLDv2 Multicast Listener Report Record Type. See [RFC 3810 § 5.2.12] for
@ -166,9 +166,8 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
/// A read/write wrapper around an MLDv2 Listener Report Message Address Record.
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct AddressRecord<T: AsRef<[u8]>> {
buffer: T,
buffer: T
}
impl<T: AsRef<[u8]>> AddressRecord<T> {
@ -296,7 +295,6 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> AddressRecord<T> {
/// A high-level representation of an MLDv2 packet header.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Repr<'a> {
Query {
max_resp_code: u16,
@ -305,61 +303,58 @@ pub enum Repr<'a> {
qrv: u8,
qqic: u8,
num_srcs: u16,
data: &'a [u8],
data: &'a [u8]
},
Report {
nr_mcast_addr_rcrds: u16,
data: &'a [u8],
},
data: &'a [u8]
}
}
impl<'a> Repr<'a> {
/// Parse an MLDv2 packet and return a high-level representation.
pub fn parse<T>(packet: &Packet<&'a T>) -> Result<Repr<'a>>
where
T: AsRef<[u8]> + ?Sized,
{
where T: AsRef<[u8]> + ?Sized {
match packet.msg_type() {
Message::MldQuery => Ok(Repr::Query {
max_resp_code: packet.max_resp_code(),
mcast_addr: packet.mcast_addr(),
s_flag: packet.s_flag(),
qrv: packet.qrv(),
qqic: packet.qqic(),
num_srcs: packet.num_srcs(),
data: packet.payload(),
}),
Message::MldReport => Ok(Repr::Report {
nr_mcast_addr_rcrds: packet.nr_mcast_addr_rcrds(),
data: packet.payload(),
}),
_ => Err(Error::Unrecognized),
Message::MldQuery => {
Ok(Repr::Query {
max_resp_code: packet.max_resp_code(),
mcast_addr: packet.mcast_addr(),
s_flag: packet.s_flag(),
qrv: packet.qrv(),
qqic: packet.qqic(),
num_srcs: packet.num_srcs(),
data: packet.payload()
})
},
Message::MldReport => {
Ok(Repr::Report {
nr_mcast_addr_rcrds: packet.nr_mcast_addr_rcrds(),
data: packet.payload()
})
},
_ => Err(Error::Unrecognized)
}
}
/// Return the length of a packet that will be emitted from this high-level representation.
pub fn buffer_len(&self) -> usize {
match self {
Repr::Query { .. } => field::QUERY_NUM_SRCS.end,
Repr::Report { .. } => field::NR_MCAST_RCRDS.end,
Repr::Query { .. } => {
field::QUERY_NUM_SRCS.end
}
Repr::Report { .. } => {
field::NR_MCAST_RCRDS.end
}
}
}
/// Emit a high-level representation into an MLDv2 packet.
pub fn emit<T>(&self, packet: &mut Packet<&mut T>)
where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
{
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
match self {
Repr::Query {
max_resp_code,
mcast_addr,
s_flag,
qrv,
qqic,
num_srcs,
data,
} => {
Repr::Query { max_resp_code, mcast_addr, s_flag,
qrv, qqic, num_srcs, data } => {
packet.set_msg_type(Message::MldQuery);
packet.set_msg_code(0);
packet.clear_reserved();
@ -374,11 +369,8 @@ impl<'a> Repr<'a> {
packet.set_qqic(*qqic);
packet.set_num_srcs(*num_srcs);
packet.payload_mut().copy_from_slice(&data[..]);
}
Repr::Report {
nr_mcast_addr_rcrds,
data,
} => {
},
Repr::Report { nr_mcast_addr_rcrds, data } => {
packet.set_msg_type(Message::MldReport);
packet.set_msg_code(0);
packet.clear_reserved();
@ -391,49 +383,74 @@ impl<'a> Repr<'a> {
#[cfg(test)]
mod test {
use super::*;
use crate::phy::ChecksumCapabilities;
use crate::wire::icmpv6::Message;
use crate::wire::Icmpv6Repr;
use crate::wire::icmpv6::Message;
use super::*;
static QUERY_PACKET_BYTES: [u8; 44] = [
0x82, 0x00, 0x73, 0x74, 0x04, 0x00, 0x00, 0x00, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0a, 0x12, 0x00, 0x01, 0xff, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
];
static QUERY_PACKET_BYTES: [u8; 44] =
[0x82, 0x00, 0x73, 0x74,
0x04, 0x00, 0x00, 0x00,
0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0x0a, 0x12, 0x00, 0x01,
0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02];
static QUERY_PACKET_PAYLOAD: [u8; 16] = [
0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x02,
];
static QUERY_PACKET_PAYLOAD: [u8; 16] =
[0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02];
static REPORT_PACKET_BYTES: [u8; 44] = [
0x8f, 0x00, 0x73, 0x85, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
];
static REPORT_PACKET_BYTES: [u8; 44] =
[0x8f, 0x00, 0x73, 0x85,
0x00, 0x00, 0x00, 0x01,
0x01, 0x00, 0x00, 0x01,
0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02];
static REPORT_PACKET_PAYLOAD: [u8; 36] =
[0x01, 0x00, 0x00, 0x01,
0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0xff, 0x02, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x02];
static REPORT_PACKET_PAYLOAD: [u8; 36] = [
0x01, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
];
fn create_repr<'a>(ty: Message) -> Icmpv6Repr<'a> {
match ty {
Message::MldQuery => Icmpv6Repr::Mld(Repr::Query {
max_resp_code: 0x400,
mcast_addr: Ipv6Address::LINK_LOCAL_ALL_NODES,
s_flag: true,
qrv: 0x02,
qqic: 0x12,
num_srcs: 0x01,
data: &QUERY_PACKET_PAYLOAD,
}),
Message::MldReport => Icmpv6Repr::Mld(Repr::Report {
nr_mcast_addr_rcrds: 1,
data: &REPORT_PACKET_PAYLOAD,
}),
Message::MldQuery => {
Icmpv6Repr::Mld(Repr::Query {
max_resp_code: 0x400,
mcast_addr: Ipv6Address::LINK_LOCAL_ALL_NODES,
s_flag: true,
qrv: 0x02,
qqic: 0x12,
num_srcs: 0x01,
data: &QUERY_PACKET_PAYLOAD
})
},
Message::MldReport => {
Icmpv6Repr::Mld(Repr::Report {
nr_mcast_addr_rcrds: 1,
data: &REPORT_PACKET_PAYLOAD
})
},
_ => {
panic!("Message type must be a MLDv2 message type");
}
@ -448,14 +465,12 @@ mod test {
assert_eq!(packet.checksum(), 0x7374);
assert_eq!(packet.max_resp_code(), 0x0400);
assert_eq!(packet.mcast_addr(), Ipv6Address::LINK_LOCAL_ALL_NODES);
assert!(packet.s_flag());
assert_eq!(packet.s_flag(), true);
assert_eq!(packet.qrv(), 0x02);
assert_eq!(packet.qqic(), 0x12);
assert_eq!(packet.num_srcs(), 0x01);
assert_eq!(
Ipv6Address::from_bytes(packet.payload()),
Ipv6Address::LINK_LOCAL_ALL_ROUTERS
);
assert_eq!(Ipv6Address::from_bytes(packet.payload()),
Ipv6Address::LINK_LOCAL_ALL_ROUTERS);
}
#[test]
@ -470,14 +485,10 @@ mod test {
packet.set_qrv(0x02);
packet.set_qqic(0x12);
packet.set_num_srcs(0x01);
packet
.payload_mut()
.copy_from_slice(Ipv6Address::LINK_LOCAL_ALL_ROUTERS.as_bytes());
packet.payload_mut().copy_from_slice(Ipv6Address::LINK_LOCAL_ALL_ROUTERS.as_bytes());
packet.clear_reserved();
packet.fill_checksum(
&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
);
packet.fill_checksum(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into());
assert_eq!(&packet.into_inner()[..], &QUERY_PACKET_BYTES[..]);
}
@ -493,10 +504,8 @@ mod test {
assert_eq!(addr_rcrd.aux_data_len(), 0x00);
assert_eq!(addr_rcrd.num_srcs(), 0x01);
assert_eq!(addr_rcrd.mcast_addr(), Ipv6Address::LINK_LOCAL_ALL_NODES);
assert_eq!(
Ipv6Address::from_bytes(addr_rcrd.payload()),
Ipv6Address::LINK_LOCAL_ALL_ROUTERS
);
assert_eq!(Ipv6Address::from_bytes(addr_rcrd.payload()),
Ipv6Address::LINK_LOCAL_ALL_ROUTERS);
}
#[test]
@ -513,38 +522,31 @@ mod test {
addr_rcrd.set_aux_data_len(0);
addr_rcrd.set_num_srcs(1);
addr_rcrd.set_mcast_addr(Ipv6Address::LINK_LOCAL_ALL_NODES);
addr_rcrd
.payload_mut()
addr_rcrd.payload_mut()
.copy_from_slice(Ipv6Address::LINK_LOCAL_ALL_ROUTERS.as_bytes());
}
packet.fill_checksum(
&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
);
packet.fill_checksum(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into());
assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]);
}
#[test]
fn test_query_repr_parse() {
let packet = Packet::new_unchecked(&QUERY_PACKET_BYTES[..]);
let repr = Icmpv6Repr::parse(
&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&packet,
&ChecksumCapabilities::default(),
);
let repr = Icmpv6Repr::parse(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&packet,
&ChecksumCapabilities::default());
assert_eq!(repr, Ok(create_repr(Message::MldQuery)));
}
#[test]
fn test_report_repr_parse() {
let packet = Packet::new_unchecked(&REPORT_PACKET_BYTES[..]);
let repr = Icmpv6Repr::parse(
&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&packet,
&ChecksumCapabilities::default(),
);
let repr = Icmpv6Repr::parse(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&packet,
&ChecksumCapabilities::default());
assert_eq!(repr, Ok(create_repr(Message::MldReport)));
}
@ -553,12 +555,10 @@ mod test {
let mut bytes = [0x2a; 44];
let mut packet = Packet::new_unchecked(&mut bytes[..]);
let repr = create_repr(Message::MldQuery);
repr.emit(
&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&mut packet,
&ChecksumCapabilities::default(),
);
repr.emit(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&mut packet,
&ChecksumCapabilities::default());
assert_eq!(&packet.into_inner()[..], &QUERY_PACKET_BYTES[..]);
}
@ -567,12 +567,10 @@ mod test {
let mut bytes = [0x2a; 44];
let mut packet = Packet::new_unchecked(&mut bytes[..]);
let repr = create_repr(Message::MldReport);
repr.emit(
&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&mut packet,
&ChecksumCapabilities::default(),
);
repr.emit(&Ipv6Address::LINK_LOCAL_ALL_NODES.into(),
&Ipv6Address::LINK_LOCAL_ALL_ROUTERS.into(),
&mut packet,
&ChecksumCapabilities::default());
assert_eq!(&packet.into_inner()[..], &REPORT_PACKET_BYTES[..]);
}
}

View File

@ -72,341 +72,154 @@ let mut buffer = vec![0; repr.buffer_len() + repr.payload_len];
mod field {
pub type Field = ::core::ops::Range<usize>;
pub type Rest = ::core::ops::RangeFrom<usize>;
pub type Rest = ::core::ops::RangeFrom<usize>;
}
pub mod pretty_print;
#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
mod arp;
#[cfg(feature = "proto-dhcpv4")]
pub(crate) mod dhcpv4;
#[cfg(feature = "medium-ethernet")]
#[cfg(feature = "ethernet")]
mod ethernet;
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
mod icmp;
#[cfg(feature = "proto-ipv4")]
mod icmpv4;
#[cfg(feature = "proto-ipv6")]
mod icmpv6;
#[cfg(feature = "medium-ieee802154")]
pub mod ieee802154;
#[cfg(feature = "proto-igmp")]
mod igmp;
#[cfg(all(feature = "proto-ipv4", feature = "ethernet"))]
mod arp;
pub(crate) mod ip;
#[cfg(feature = "proto-ipv4")]
mod ipv4;
#[cfg(feature = "proto-ipv6")]
mod ipv6;
#[cfg(feature = "proto-ipv6")]
mod ipv6fragment;
mod ipv6option;
#[cfg(feature = "proto-ipv6")]
mod ipv6hopbyhop;
#[cfg(feature = "proto-ipv6")]
mod ipv6option;
mod ipv6fragment;
#[cfg(feature = "proto-ipv6")]
mod ipv6routing;
#[cfg(feature = "proto-ipv4")]
mod icmpv4;
#[cfg(feature = "proto-ipv6")]
mod icmpv6;
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
mod icmp;
#[cfg(feature = "proto-igmp")]
mod igmp;
#[cfg(all(feature = "proto-ipv6", feature = "ethernet"))]
mod ndisc;
#[cfg(all(feature = "proto-ipv6", feature = "ethernet"))]
mod ndiscoption;
#[cfg(feature = "proto-ipv6")]
mod mld;
#[cfg(all(
feature = "proto-ipv6",
any(feature = "medium-ethernet", feature = "medium-ieee802154")
))]
mod ndisc;
#[cfg(all(
feature = "proto-ipv6",
any(feature = "medium-ethernet", feature = "medium-ieee802154")
))]
mod ndiscoption;
#[cfg(all(feature = "proto-sixlowpan", feature = "medium-ieee802154"))]
mod sixlowpan;
mod tcp;
mod udp;
use crate::{phy::Medium, Error};
mod tcp;
#[cfg(feature = "proto-dhcpv4")]
pub(crate) mod dhcpv4;
pub use self::pretty_print::PrettyPrinter;
#[cfg(feature = "medium-ethernet")]
pub use self::ethernet::{
Address as EthernetAddress, EtherType as EthernetProtocol, Frame as EthernetFrame,
Repr as EthernetRepr, HEADER_LEN as ETHERNET_HEADER_LEN,
};
#[cfg(feature = "ethernet")]
pub use self::ethernet::{EtherType as EthernetProtocol,
Address as EthernetAddress,
Frame as EthernetFrame,
HEADER_LEN as ETHERNET_HEADER_LEN,
Repr as EthernetRepr};
#[cfg(all(feature = "proto-ipv4", feature = "medium-ethernet"))]
pub use self::arp::{
Hardware as ArpHardware, Operation as ArpOperation, Packet as ArpPacket, Repr as ArpRepr,
};
#[cfg(all(feature = "proto-ipv4", feature = "ethernet"))]
pub use self::arp::{Hardware as ArpHardware,
Operation as ArpOperation,
Packet as ArpPacket,
Repr as ArpRepr};
#[cfg(all(feature = "proto-sixlowpan", feature = "medium-ieee802154"))]
pub use self::sixlowpan::{
iphc::{Packet as SixlowpanIphcPacket, Repr as SixlowpanIphcRepr},
nhc::{
ExtensionHeaderPacket as SixlowpanExtHeaderPacket,
ExtensionHeaderRepr as SixlowpanExtHeaderRepr, Packet as SixlowpanNhcPacket,
UdpNhcRepr as SixlowpanUdpRepr, UdpPacket as SixlowpanUdpPacket,
},
NextHeader as SixlowpanNextHeader,
};
#[cfg(feature = "medium-ieee802154")]
pub use self::ieee802154::{
Address as Ieee802154Address, AddressingMode as Ieee802154AddressingMode,
Frame as Ieee802154Frame, FrameType as Ieee802154FrameType,
FrameVersion as Ieee802154FrameVersion, Pan as Ieee802154Pan, Repr as Ieee802154Repr,
};
pub use self::ip::{
Address as IpAddress, Cidr as IpCidr, Endpoint as IpEndpoint, Protocol as IpProtocol,
Repr as IpRepr, Version as IpVersion,
};
pub use self::ip::{Version as IpVersion,
Protocol as IpProtocol,
Address as IpAddress,
Endpoint as IpEndpoint,
Repr as IpRepr,
Cidr as IpCidr};
#[cfg(feature = "proto-ipv4")]
pub use self::ipv4::{
Address as Ipv4Address, Cidr as Ipv4Cidr, Packet as Ipv4Packet, Repr as Ipv4Repr,
HEADER_LEN as IPV4_HEADER_LEN, MIN_MTU as IPV4_MIN_MTU,
};
pub use self::ipv4::{Address as Ipv4Address,
Packet as Ipv4Packet,
Repr as Ipv4Repr,
Cidr as Ipv4Cidr,
MIN_MTU as IPV4_MIN_MTU};
#[cfg(feature = "proto-ipv6")]
pub use self::ipv6::{
Address as Ipv6Address, Cidr as Ipv6Cidr, Packet as Ipv6Packet, Repr as Ipv6Repr,
HEADER_LEN as IPV6_HEADER_LEN, MIN_MTU as IPV6_MIN_MTU,
};
pub use self::ipv6::{Address as Ipv6Address,
Packet as Ipv6Packet,
Repr as Ipv6Repr,
Cidr as Ipv6Cidr,
MIN_MTU as IPV6_MIN_MTU};
#[cfg(feature = "proto-ipv6")]
pub use self::ipv6option::{
FailureType as Ipv6OptionFailureType, Ipv6Option, Repr as Ipv6OptionRepr,
Type as Ipv6OptionType,
};
pub use self::ipv6option::{Ipv6Option,
Repr as Ipv6OptionRepr,
Type as Ipv6OptionType,
FailureType as Ipv6OptionFailureType};
#[cfg(feature = "proto-ipv6")]
pub use self::ipv6hopbyhop::{Header as Ipv6HopByHopHeader, Repr as Ipv6HopByHopRepr};
pub use self::ipv6hopbyhop::{Header as Ipv6HopByHopHeader,
Repr as Ipv6HopByHopRepr};
#[cfg(feature = "proto-ipv6")]
pub use self::ipv6fragment::{Header as Ipv6FragmentHeader, Repr as Ipv6FragmentRepr};
pub use self::ipv6fragment::{Header as Ipv6FragmentHeader,
Repr as Ipv6FragmentRepr};
#[cfg(feature = "proto-ipv6")]
pub use self::ipv6routing::{Header as Ipv6RoutingHeader, Repr as Ipv6RoutingRepr};
pub use self::ipv6routing::{Header as Ipv6RoutingHeader,
Repr as Ipv6RoutingRepr};
#[cfg(feature = "proto-ipv4")]
pub use self::icmpv4::{
DstUnreachable as Icmpv4DstUnreachable, Message as Icmpv4Message, Packet as Icmpv4Packet,
ParamProblem as Icmpv4ParamProblem, Redirect as Icmpv4Redirect, Repr as Icmpv4Repr,
TimeExceeded as Icmpv4TimeExceeded,
};
pub use self::icmpv4::{Message as Icmpv4Message,
DstUnreachable as Icmpv4DstUnreachable,
Redirect as Icmpv4Redirect,
TimeExceeded as Icmpv4TimeExceeded,
ParamProblem as Icmpv4ParamProblem,
Packet as Icmpv4Packet,
Repr as Icmpv4Repr};
#[cfg(feature = "proto-igmp")]
pub use self::igmp::{IgmpVersion, Packet as IgmpPacket, Repr as IgmpRepr};
pub use self::igmp::{Packet as IgmpPacket,
Repr as IgmpRepr,
IgmpVersion};
#[cfg(feature = "proto-ipv6")]
pub use self::icmpv6::{
DstUnreachable as Icmpv6DstUnreachable, Message as Icmpv6Message, Packet as Icmpv6Packet,
ParamProblem as Icmpv6ParamProblem, Repr as Icmpv6Repr, TimeExceeded as Icmpv6TimeExceeded,
};
pub use self::icmpv6::{Message as Icmpv6Message,
DstUnreachable as Icmpv6DstUnreachable,
TimeExceeded as Icmpv6TimeExceeded,
ParamProblem as Icmpv6ParamProblem,
Packet as Icmpv6Packet,
Repr as Icmpv6Repr};
#[cfg(any(feature = "proto-ipv4", feature = "proto-ipv6"))]
pub use self::icmp::Repr as IcmpRepr;
#[cfg(all(
feature = "proto-ipv6",
any(feature = "medium-ethernet", feature = "medium-ieee802154")
))]
pub use self::ndisc::{
NeighborFlags as NdiscNeighborFlags, Repr as NdiscRepr, RouterFlags as NdiscRouterFlags,
};
#[cfg(all(
feature = "proto-ipv6",
any(feature = "medium-ethernet", feature = "medium-ieee802154")
))]
pub use self::ndiscoption::{
NdiscOption, PrefixInfoFlags as NdiscPrefixInfoFlags,
PrefixInformation as NdiscPrefixInformation, RedirectedHeader as NdiscRedirectedHeader,
Repr as NdiscOptionRepr, Type as NdiscOptionType,
};
#[cfg(all(feature = "proto-ipv6", feature = "ethernet"))]
pub use self::ndisc::{Repr as NdiscRepr,
RouterFlags as NdiscRouterFlags,
NeighborFlags as NdiscNeighborFlags};
#[cfg(all(feature = "proto-ipv6", feature = "ethernet"))]
pub use self::ndiscoption::{NdiscOption,
Repr as NdiscOptionRepr,
Type as NdiscOptionType,
PrefixInformation as NdiscPrefixInformation,
RedirectedHeader as NdiscRedirectedHeader,
PrefixInfoFlags as NdiscPrefixInfoFlags};
#[cfg(feature = "proto-ipv6")]
pub use self::mld::{AddressRecord as MldAddressRecord, Repr as MldRepr};
pub use self::mld::{AddressRecord as MldAddressRecord,
Repr as MldRepr};
pub use self::udp::{Packet as UdpPacket, Repr as UdpRepr, HEADER_LEN as UDP_HEADER_LEN};
pub use self::udp::{Packet as UdpPacket,
Repr as UdpRepr};
pub use self::tcp::{
Control as TcpControl, Packet as TcpPacket, Repr as TcpRepr, SeqNumber as TcpSeqNumber,
TcpOption, HEADER_LEN as TCP_HEADER_LEN,
};
pub use self::tcp::{SeqNumber as TcpSeqNumber,
Packet as TcpPacket,
TcpOption,
Repr as TcpRepr,
Control as TcpControl};
#[cfg(feature = "proto-dhcpv4")]
pub use self::dhcpv4::{
MessageType as DhcpMessageType, Packet as DhcpPacket, Repr as DhcpRepr,
CLIENT_PORT as DHCP_CLIENT_PORT, MAX_DNS_SERVER_COUNT as DHCP_MAX_DNS_SERVER_COUNT,
SERVER_PORT as DHCP_SERVER_PORT,
};
/// Representation of an hardware address, such as an Ethernet address or an IEEE802.15.4 address.
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum HardwareAddress {
#[cfg(feature = "medium-ethernet")]
Ethernet(EthernetAddress),
#[cfg(feature = "medium-ieee802154")]
Ieee802154(Ieee802154Address),
}
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
impl HardwareAddress {
pub fn as_bytes(&self) -> &[u8] {
match self {
#[cfg(feature = "medium-ethernet")]
HardwareAddress::Ethernet(addr) => addr.as_bytes(),
#[cfg(feature = "medium-ieee802154")]
HardwareAddress::Ieee802154(addr) => addr.as_bytes(),
}
}
/// Query wether the address is an unicast address.
pub fn is_unicast(&self) -> bool {
match self {
#[cfg(feature = "medium-ethernet")]
HardwareAddress::Ethernet(addr) => addr.is_unicast(),
#[cfg(feature = "medium-ieee802154")]
HardwareAddress::Ieee802154(addr) => addr.is_unicast(),
}
}
/// Query wether the address is a broadcast address.
pub fn is_broadcast(&self) -> bool {
match self {
#[cfg(feature = "medium-ethernet")]
HardwareAddress::Ethernet(addr) => addr.is_broadcast(),
#[cfg(feature = "medium-ieee802154")]
HardwareAddress::Ieee802154(addr) => addr.is_broadcast(),
}
}
}
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
impl core::fmt::Display for HardwareAddress {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
#[cfg(feature = "medium-ethernet")]
HardwareAddress::Ethernet(addr) => write!(f, "{}", addr),
#[cfg(feature = "medium-ieee802154")]
HardwareAddress::Ieee802154(addr) => write!(f, "{}", addr),
}
}
}
#[cfg(feature = "medium-ethernet")]
impl From<EthernetAddress> for HardwareAddress {
fn from(addr: EthernetAddress) -> Self {
HardwareAddress::Ethernet(addr)
}
}
#[cfg(feature = "medium-ieee802154")]
impl From<Ieee802154Address> for HardwareAddress {
fn from(addr: Ieee802154Address) -> Self {
HardwareAddress::Ieee802154(addr)
}
}
#[cfg(not(feature = "medium-ieee802154"))]
pub const MAX_HARDWARE_ADDRESS_LEN: usize = 6;
#[cfg(feature = "medium-ieee802154")]
pub const MAX_HARDWARE_ADDRESS_LEN: usize = 8;
/// Unparsed hardware address.
///
/// Used to make NDISC parsing agnostic of the hardware medium in use.
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RawHardwareAddress {
len: u8,
data: [u8; MAX_HARDWARE_ADDRESS_LEN],
}
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
impl RawHardwareAddress {
pub fn from_bytes(addr: &[u8]) -> Self {
let mut data = [0u8; MAX_HARDWARE_ADDRESS_LEN];
data[..addr.len()].copy_from_slice(addr);
Self {
len: addr.len() as u8,
data,
}
}
pub fn as_bytes(&self) -> &[u8] {
&self.data[..self.len as usize]
}
pub fn len(&self) -> usize {
self.len as usize
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn parse(&self, medium: Medium) -> Result<HardwareAddress, Error> {
match medium {
#[cfg(feature = "medium-ethernet")]
Medium::Ethernet => {
if self.len() < 6 {
return Err(Error::Malformed);
}
Ok(HardwareAddress::Ethernet(EthernetAddress::from_bytes(
self.as_bytes(),
)))
}
#[cfg(feature = "medium-ieee802154")]
Medium::Ieee802154 => {
if self.len() < 8 {
return Err(Error::Malformed);
}
Ok(HardwareAddress::Ieee802154(Ieee802154Address::from_bytes(
self.as_bytes(),
)))
}
#[cfg(feature = "medium-ip")]
Medium::Ip => unreachable!(),
}
}
}
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
impl core::fmt::Display for RawHardwareAddress {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
for (i, &b) in self.as_bytes().iter().enumerate() {
if i != 0 {
write!(f, ":")?;
}
write!(f, "{:02x}", b)?;
}
Ok(())
}
}
#[cfg(feature = "medium-ethernet")]
impl From<EthernetAddress> for RawHardwareAddress {
fn from(addr: EthernetAddress) -> Self {
Self::from_bytes(addr.as_bytes())
}
}
#[cfg(feature = "medium-ieee802154")]
impl From<Ieee802154Address> for RawHardwareAddress {
fn from(addr: Ieee802154Address) -> Self {
Self::from_bytes(addr.as_bytes())
}
}
#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))]
impl From<HardwareAddress> for RawHardwareAddress {
fn from(addr: HardwareAddress) -> Self {
Self::from_bytes(addr.as_bytes())
}
}
pub use self::dhcpv4::{Packet as DhcpPacket,
Repr as DhcpRepr,
MessageType as DhcpMessageType};

View File

@ -1,17 +1,15 @@
use bitflags::bitflags;
use byteorder::{ByteOrder, NetworkEndian};
use bitflags::bitflags;
use crate::time::Duration;
use crate::{Error, Result};
use crate::wire::icmpv6::{field, Message, Packet};
use crate::wire::Ipv6Address;
use crate::wire::RawHardwareAddress;
use crate::wire::{Ipv6Packet, Ipv6Repr};
use crate::wire::{EthernetAddress, Ipv6Repr, Ipv6Packet};
use crate::wire::{NdiscOption, NdiscOptionRepr, NdiscOptionType};
use crate::wire::{NdiscPrefixInformation, NdiscRedirectedHeader};
use crate::{Error, Result};
use crate::time::Duration;
use crate::wire::Ipv6Address;
bitflags! {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RouterFlags: u8 {
const MANAGED = 0b10000000;
const OTHER = 0b01000000;
@ -19,7 +17,6 @@ bitflags! {
}
bitflags! {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct NeighborFlags: u8 {
const ROUTER = 0b10000000;
const SOLICITED = 0b01000000;
@ -83,6 +80,7 @@ impl<T: AsRef<[u8]>> Packet<T> {
}
}
/// Getters for the Neighbor Solicitation message header.
/// See [RFC 4861 § 4.3].
///
@ -191,10 +189,9 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
/// A high-level representation of an Neighbor Discovery packet header.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Repr<'a> {
RouterSolicit {
lladdr: Option<RawHardwareAddress>,
lladdr: Option<EthernetAddress>
},
RouterAdvert {
hop_limit: u8,
@ -202,49 +199,46 @@ pub enum Repr<'a> {
router_lifetime: Duration,
reachable_time: Duration,
retrans_time: Duration,
lladdr: Option<RawHardwareAddress>,
lladdr: Option<EthernetAddress>,
mtu: Option<u32>,
prefix_info: Option<NdiscPrefixInformation>,
prefix_info: Option<NdiscPrefixInformation>
},
NeighborSolicit {
target_addr: Ipv6Address,
lladdr: Option<RawHardwareAddress>,
lladdr: Option<EthernetAddress>
},
NeighborAdvert {
flags: NeighborFlags,
target_addr: Ipv6Address,
lladdr: Option<RawHardwareAddress>,
lladdr: Option<EthernetAddress>
},
Redirect {
target_addr: Ipv6Address,
dest_addr: Ipv6Address,
lladdr: Option<RawHardwareAddress>,
redirected_hdr: Option<NdiscRedirectedHeader<'a>>,
},
lladdr: Option<EthernetAddress>,
redirected_hdr: Option<NdiscRedirectedHeader<'a>>
}
}
impl<'a> Repr<'a> {
/// Parse an NDISC packet and return a high-level representation of the
/// packet.
pub fn parse<T>(packet: &Packet<&'a T>) -> Result<Repr<'a>>
where
T: AsRef<[u8]> + ?Sized,
{
pub fn parse<T>(packet: &Packet<&'a T>)
-> Result<Repr<'a>>
where T: AsRef<[u8]> + ?Sized {
match packet.msg_type() {
Message::RouterSolicit => {
let lladdr = if !packet.payload().is_empty() {
let opt = NdiscOption::new_checked(packet.payload())?;
match opt.option_type() {
NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()),
_ => {
return Err(Error::Unrecognized);
}
_ => { return Err(Error::Unrecognized); }
}
} else {
None
};
Ok(Repr::RouterSolicit { lladdr })
}
},
Message::RouterAdvert => {
let mut offset = 0;
let (mut lladdr, mut mtu, mut prefix_info) = (None, None, None);
@ -255,9 +249,7 @@ impl<'a> Repr<'a> {
NdiscOptionRepr::SourceLinkLayerAddr(addr) => lladdr = Some(addr),
NdiscOptionRepr::Mtu(val) => mtu = Some(val),
NdiscOptionRepr::PrefixInformation(info) => prefix_info = Some(info),
_ => {
return Err(Error::Unrecognized);
}
_ => { return Err(Error::Unrecognized); }
}
offset += opt.buffer_len();
}
@ -267,36 +259,29 @@ impl<'a> Repr<'a> {
router_lifetime: packet.router_lifetime(),
reachable_time: packet.reachable_time(),
retrans_time: packet.retrans_time(),
lladdr,
mtu,
prefix_info,
lladdr, mtu, prefix_info
})
}
},
Message::NeighborSolicit => {
let lladdr = if !packet.payload().is_empty() {
let opt = NdiscOption::new_checked(packet.payload())?;
match opt.option_type() {
NdiscOptionType::SourceLinkLayerAddr => Some(opt.link_layer_addr()),
_ => {
return Err(Error::Unrecognized);
}
_ => { return Err(Error::Unrecognized); }
}
} else {
None
};
Ok(Repr::NeighborSolicit {
target_addr: packet.target_addr(),
lladdr,
target_addr: packet.target_addr(), lladdr
})
}
},
Message::NeighborAdvert => {
let lladdr = if !packet.payload().is_empty() {
let opt = NdiscOption::new_checked(packet.payload())?;
match opt.option_type() {
NdiscOptionType::TargetLinkLayerAddr => Some(opt.link_layer_addr()),
_ => {
return Err(Error::Unrecognized);
}
_ => { return Err(Error::Unrecognized); }
}
} else {
None
@ -304,9 +289,9 @@ impl<'a> Repr<'a> {
Ok(Repr::NeighborAdvert {
flags: packet.neighbor_flags(),
target_addr: packet.target_addr(),
lladdr,
lladdr
})
}
},
Message::Redirect => {
let mut offset = 0;
let (mut lladdr, mut redirected_hdr) = (None, None);
@ -316,50 +301,43 @@ impl<'a> Repr<'a> {
NdiscOptionType::SourceLinkLayerAddr => {
lladdr = Some(opt.link_layer_addr());
offset += 8;
}
},
NdiscOptionType::RedirectedHeader => {
if opt.data_len() < 6 {
return Err(Error::Truncated);
return Err(Error::Truncated)
} else {
let ip_packet =
Ipv6Packet::new_unchecked(&opt.data()[offset + 8..]);
let ip_repr = Ipv6Repr::parse(&ip_packet)?;
let data = &opt.data()[offset + 8 + ip_repr.buffer_len()..];
redirected_hdr = Some(NdiscRedirectedHeader {
header: ip_repr,
data,
header: ip_repr, data
});
offset += 8 + ip_repr.buffer_len() + data.len();
}
}
_ => {
return Err(Error::Unrecognized);
}
_ => { return Err(Error::Unrecognized); }
}
}
Ok(Repr::Redirect {
target_addr: packet.target_addr(),
dest_addr: packet.dest_addr(),
lladdr,
redirected_hdr,
lladdr, redirected_hdr
})
}
_ => Err(Error::Unrecognized),
},
_ => Err(Error::Unrecognized)
}
}
pub fn buffer_len(&self) -> usize {
match self {
&Repr::RouterSolicit { lladdr } => match lladdr {
Some(_) => field::UNUSED.end + 8,
None => field::UNUSED.end,
&Repr::RouterSolicit { lladdr } => {
match lladdr {
Some(_) => field::UNUSED.end + 8,
None => field::UNUSED.end,
}
},
&Repr::RouterAdvert {
lladdr,
mtu,
prefix_info,
..
} => {
&Repr::RouterAdvert { lladdr, mtu, prefix_info, .. } => {
let mut offset = 0;
if lladdr.is_some() {
offset += 8;
@ -371,19 +349,14 @@ impl<'a> Repr<'a> {
offset += 32;
}
field::RETRANS_TM.end + offset
}
},
&Repr::NeighborSolicit { lladdr, .. } | &Repr::NeighborAdvert { lladdr, .. } => {
let mut offset = field::TARGET_ADDR.end;
if let Some(lladdr) = lladdr {
offset += NdiscOptionRepr::SourceLinkLayerAddr(lladdr).buffer_len();
match lladdr {
Some(_) => field::TARGET_ADDR.end + 8,
None => field::TARGET_ADDR.end,
}
offset
}
&Repr::Redirect {
lladdr,
redirected_hdr,
..
} => {
},
&Repr::Redirect { lladdr, redirected_hdr, .. } => {
let mut offset = 0;
if lladdr.is_some() {
offset += 8;
@ -397,9 +370,7 @@ impl<'a> Repr<'a> {
}
pub fn emit<T>(&self, packet: &mut Packet<&mut T>)
where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
{
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
match *self {
Repr::RouterSolicit { lladdr } => {
packet.set_msg_type(Message::RouterSolicit);
@ -409,18 +380,10 @@ impl<'a> Repr<'a> {
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt);
}
}
},
Repr::RouterAdvert {
hop_limit,
flags,
router_lifetime,
reachable_time,
retrans_time,
lladdr,
mtu,
prefix_info,
} => {
Repr::RouterAdvert { hop_limit, flags, router_lifetime, reachable_time,
retrans_time, lladdr, mtu, prefix_info } => {
packet.set_msg_type(Message::RouterAdvert);
packet.set_msg_code(0);
packet.set_current_hop_limit(hop_limit);
@ -430,10 +393,10 @@ impl<'a> Repr<'a> {
packet.set_retrans_time(retrans_time);
let mut offset = 0;
if let Some(lladdr) = lladdr {
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
let opt = NdiscOptionRepr::SourceLinkLayerAddr(lladdr);
opt.emit(&mut opt_pkt);
offset += opt.buffer_len();
let mut opt_pkt =
NdiscOption::new_unchecked(packet.payload_mut());
NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt);
offset += 8;
}
if let Some(mtu) = mtu {
let mut opt_pkt =
@ -446,44 +409,34 @@ impl<'a> Repr<'a> {
NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]);
NdiscOptionRepr::PrefixInformation(prefix_info).emit(&mut opt_pkt)
}
}
},
Repr::NeighborSolicit {
target_addr,
lladdr,
} => {
Repr::NeighborSolicit { target_addr, lladdr } => {
packet.set_msg_type(Message::NeighborSolicit);
packet.set_msg_code(0);
packet.clear_reserved();
packet.set_target_addr(target_addr);
if let Some(lladdr) = lladdr {
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
let mut opt_pkt =
NdiscOption::new_unchecked(packet.payload_mut());
NdiscOptionRepr::SourceLinkLayerAddr(lladdr).emit(&mut opt_pkt);
}
}
},
Repr::NeighborAdvert {
flags,
target_addr,
lladdr,
} => {
Repr::NeighborAdvert { flags, target_addr, lladdr } => {
packet.set_msg_type(Message::NeighborAdvert);
packet.set_msg_code(0);
packet.clear_reserved();
packet.set_neighbor_flags(flags);
packet.set_target_addr(target_addr);
if let Some(lladdr) = lladdr {
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
let mut opt_pkt =
NdiscOption::new_unchecked(packet.payload_mut());
NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt);
}
}
},
Repr::Redirect {
target_addr,
dest_addr,
lladdr,
redirected_hdr,
} => {
Repr::Redirect { target_addr, dest_addr, lladdr, redirected_hdr } => {
packet.set_msg_type(Message::Redirect);
packet.set_msg_code(0);
packet.clear_reserved();
@ -491,10 +444,11 @@ impl<'a> Repr<'a> {
packet.set_dest_addr(dest_addr);
let offset = match lladdr {
Some(lladdr) => {
let mut opt_pkt = NdiscOption::new_unchecked(packet.payload_mut());
let mut opt_pkt =
NdiscOption::new_unchecked(packet.payload_mut());
NdiscOptionRepr::TargetLinkLayerAddr(lladdr).emit(&mut opt_pkt);
8
}
},
None => 0,
};
if let Some(redirected_hdr) = redirected_hdr {
@ -502,24 +456,28 @@ impl<'a> Repr<'a> {
NdiscOption::new_unchecked(&mut packet.payload_mut()[offset..]);
NdiscOptionRepr::RedirectedHeader(redirected_hdr).emit(&mut opt_pkt);
}
}
},
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::phy::ChecksumCapabilities;
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2};
use crate::wire::EthernetAddress;
use super::*;
use crate::wire::Icmpv6Repr;
use crate::wire::ip::test::{MOCK_IP_ADDR_1, MOCK_IP_ADDR_2};
static ROUTER_ADVERT_BYTES: [u8; 24] = [
0x86, 0x00, 0xa9, 0xde, 0x40, 0x80, 0x03, 0x84, 0x00, 0x00, 0x03, 0x84, 0x00, 0x00, 0x03,
0x84, 0x01, 0x01, 0x52, 0x54, 0x00, 0x12, 0x34, 0x56,
];
static SOURCE_LINK_LAYER_OPT: [u8; 8] = [0x01, 0x01, 0x52, 0x54, 0x00, 0x12, 0x34, 0x56];
static ROUTER_ADVERT_BYTES: [u8; 24] =
[0x86, 0x00, 0xa9, 0xde,
0x40, 0x80, 0x03, 0x84,
0x00, 0x00, 0x03, 0x84,
0x00, 0x00, 0x03, 0x84,
0x01, 0x01, 0x52, 0x54,
0x00, 0x12, 0x34, 0x56];
static SOURCE_LINK_LAYER_OPT: [u8; 8] =
[0x01, 0x01, 0x52, 0x54,
0x00, 0x12, 0x34, 0x56];
fn create_repr<'a>() -> Icmpv6Repr<'a> {
Icmpv6Repr::Ndisc(Repr::RouterAdvert {
@ -528,9 +486,9 @@ mod test {
router_lifetime: Duration::from_secs(900),
reachable_time: Duration::from_millis(900),
retrans_time: Duration::from_millis(900),
lladdr: Some(EthernetAddress([0x52, 0x54, 0x00, 0x12, 0x34, 0x56]).into()),
lladdr: Some(EthernetAddress([0x52, 0x54, 0x00, 0x12, 0x34, 0x56])),
mtu: None,
prefix_info: None,
prefix_info: None
})
}
@ -558,9 +516,7 @@ mod test {
packet.set_router_lifetime(Duration::from_secs(900));
packet.set_reachable_time(Duration::from_millis(900));
packet.set_retrans_time(Duration::from_millis(900));
packet
.payload_mut()
.copy_from_slice(&SOURCE_LINK_LAYER_OPT[..]);
packet.payload_mut().copy_from_slice(&SOURCE_LINK_LAYER_OPT[..]);
packet.fill_checksum(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2);
assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]);
}
@ -568,28 +524,17 @@ mod test {
#[test]
fn test_router_advert_repr_parse() {
let packet = Packet::new_unchecked(&ROUTER_ADVERT_BYTES[..]);
assert_eq!(
Icmpv6Repr::parse(
&MOCK_IP_ADDR_1,
&MOCK_IP_ADDR_2,
&packet,
&ChecksumCapabilities::default()
)
.unwrap(),
create_repr()
);
assert_eq!(Icmpv6Repr::parse(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2,
&packet, &ChecksumCapabilities::default()).unwrap(),
create_repr());
}
#[test]
fn test_router_advert_repr_emit() {
let mut bytes = vec![0x2a; 24];
let mut packet = Packet::new_unchecked(&mut bytes[..]);
create_repr().emit(
&MOCK_IP_ADDR_1,
&MOCK_IP_ADDR_2,
&mut packet,
&ChecksumCapabilities::default(),
);
create_repr().emit(&MOCK_IP_ADDR_1, &MOCK_IP_ADDR_2,
&mut packet, &ChecksumCapabilities::default());
assert_eq!(&packet.into_inner()[..], &ROUTER_ADVERT_BYTES[..]);
}
}

View File

@ -1,12 +1,10 @@
use bitflags::bitflags;
use byteorder::{ByteOrder, NetworkEndian};
use core::fmt;
use byteorder::{NetworkEndian, ByteOrder};
use bitflags::bitflags;
use crate::time::Duration;
use crate::wire::{Ipv6Address, Ipv6Packet, Ipv6Repr, MAX_HARDWARE_ADDRESS_LEN};
use crate::{Error, Result};
use crate::wire::RawHardwareAddress;
use crate::time::Duration;
use crate::wire::{EthernetAddress, Ipv6Address, Ipv6Packet, Ipv6Repr};
enum_with_unknown! {
/// NDISC Option Type
@ -29,16 +27,15 @@ impl fmt::Display for Type {
match self {
Type::SourceLinkLayerAddr => write!(f, "source link-layer address"),
Type::TargetLinkLayerAddr => write!(f, "target link-layer address"),
Type::PrefixInformation => write!(f, "prefix information"),
Type::RedirectedHeader => write!(f, "redirected header"),
Type::Mtu => write!(f, "mtu"),
Type::Unknown(id) => write!(f, "{}", id),
Type::PrefixInformation => write!(f, "prefix information"),
Type::RedirectedHeader => write!(f, "redirected header"),
Type::Mtu => write!(f, "mtu"),
Type::Unknown(id) => write!(f, "{}", id)
}
}
}
bitflags! {
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PrefixInfoFlags: u8 {
const ON_LINK = 0b10000000;
const ADDRCONF = 0b01000000;
@ -49,9 +46,8 @@ bitflags! {
///
/// [NDISC Option]: https://tools.ietf.org/html/rfc4861#section-4.6
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct NdiscOption<T: AsRef<[u8]>> {
buffer: T,
buffer: T
}
// Format of an NDISC Option
@ -69,11 +65,11 @@ mod field {
use crate::wire::field::*;
// 8-bit identifier of the type of option.
pub const TYPE: usize = 0;
pub const TYPE: usize = 0;
// 8-bit unsigned integer. Length of the option, in units of 8 octests.
pub const LENGTH: usize = 1;
pub const LENGTH: usize = 1;
// Minimum length of an option.
pub const MIN_OPT_LEN: usize = 8;
pub const MIN_OPT_LEN: usize = 8;
// Variable-length field. Option-Type-specific data.
pub fn DATA(length: u8) -> Field {
2..length as usize * 8
@ -84,6 +80,9 @@ mod field {
// | Type | Length | Link-Layer Address ...
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// Link-Layer Address
pub const LL_ADDR: Field = 2..8;
// Prefix Information Option fields.
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | Type | Length | Prefix Length |L|A| Reserved1 |
@ -104,17 +103,17 @@ mod field {
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// Prefix length.
pub const PREFIX_LEN: usize = 2;
pub const PREFIX_LEN: usize = 2;
// Flags field of prefix header.
pub const FLAGS: usize = 3;
pub const FLAGS: usize = 3;
// Valid lifetime.
pub const VALID_LT: Field = 4..8;
pub const VALID_LT: Field = 4..8;
// Preferred lifetime.
pub const PREF_LT: Field = 8..12;
pub const PREF_LT: Field = 8..12;
// Reserved bits
pub const PREF_RESERVED: Field = 12..16;
// Prefix
pub const PREFIX: Field = 16..32;
pub const PREFIX: Field = 16..32;
// Redirected Header Option fields.
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@ -128,10 +127,10 @@ mod field {
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// Reserved bits.
pub const IP_RESERVED: Field = 4..8;
pub const IP_RESERVED: Field = 4..8;
// Redirected header IP header + data.
pub const IP_DATA: usize = 8;
pub const REDIR_MIN_SZ: usize = 48;
pub const IP_DATA: usize = 8;
pub const REDIR_MIN_SZ: usize = 48;
// MTU Option fields
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@ -141,7 +140,7 @@ mod field {
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// MTU
pub const MTU: Field = 4..8;
pub const MTU: Field = 4..8;
}
/// Core getter methods relevant to any type of NDISC option.
@ -179,11 +178,16 @@ impl<T: AsRef<[u8]>> NdiscOption<T> {
Err(Error::Truncated)
} else {
match self.option_type() {
Type::SourceLinkLayerAddr | Type::TargetLinkLayerAddr | Type::Mtu => Ok(()),
Type::PrefixInformation if data_range.end >= field::PREFIX.end => Ok(()),
Type::RedirectedHeader if data_range.end >= field::REDIR_MIN_SZ => Ok(()),
Type::Unknown(_) => Ok(()),
_ => Err(Error::Truncated),
Type::SourceLinkLayerAddr | Type::TargetLinkLayerAddr | Type::Mtu =>
Ok(()),
Type::PrefixInformation if data_range.end >= field::PREFIX.end =>
Ok(()),
Type::RedirectedHeader if data_range.end >= field::REDIR_MIN_SZ =>
Ok(()),
Type::Unknown(_) =>
Ok(()),
_ =>
Err(Error::Truncated),
}
}
}
@ -213,10 +217,9 @@ impl<T: AsRef<[u8]>> NdiscOption<T> {
impl<T: AsRef<[u8]>> NdiscOption<T> {
/// Return the Source/Target Link-layer Address.
#[inline]
pub fn link_layer_addr(&self) -> RawHardwareAddress {
let len = MAX_HARDWARE_ADDRESS_LEN.min(self.data_len() as usize * 8 - 2);
pub fn link_layer_addr(&self) -> EthernetAddress {
let data = self.buffer.as_ref();
RawHardwareAddress::from_bytes(&data[2..len + 2])
EthernetAddress::from_bytes(&data[field::LL_ADDR])
}
}
@ -297,9 +300,9 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> NdiscOption<T> {
impl<T: AsRef<[u8]> + AsMut<[u8]>> NdiscOption<T> {
/// Set the Source/Target Link-layer Address.
#[inline]
pub fn set_link_layer_addr(&mut self, addr: RawHardwareAddress) {
pub fn set_link_layer_addr(&mut self, addr: EthernetAddress) {
let data = self.buffer.as_mut();
data[2..2 + addr.len()].copy_from_slice(addr.as_bytes())
data[field::LL_ADDR].copy_from_slice(addr.as_bytes())
}
}
@ -366,6 +369,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> NdiscOption<T> {
}
}
impl<'a, T: AsRef<[u8]> + AsMut<[u8]> + ?Sized> NdiscOption<&'a mut T> {
/// Return a mutable pointer to the option data.
#[inline]
@ -389,44 +393,39 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for NdiscOption<&'a T> {
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PrefixInformation {
pub prefix_len: u8,
pub flags: PrefixInfoFlags,
pub valid_lifetime: Duration,
pub preferred_lifetime: Duration,
pub prefix: Ipv6Address,
pub prefix: Ipv6Address
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RedirectedHeader<'a> {
pub header: Ipv6Repr,
pub data: &'a [u8],
pub data: &'a [u8]
}
/// A high-level representation of an NDISC Option.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Repr<'a> {
SourceLinkLayerAddr(RawHardwareAddress),
TargetLinkLayerAddr(RawHardwareAddress),
SourceLinkLayerAddr(EthernetAddress),
TargetLinkLayerAddr(EthernetAddress),
PrefixInformation(PrefixInformation),
RedirectedHeader(RedirectedHeader<'a>),
Mtu(u32),
Unknown {
type_: u8,
type_: u8,
length: u8,
data: &'a [u8],
data: &'a [u8]
},
}
impl<'a> Repr<'a> {
/// Parse an NDISC Option and return a high-level representation.
pub fn parse<T>(opt: &'a NdiscOption<&'a T>) -> Result<Repr<'a>>
where
T: AsRef<[u8]> + ?Sized,
{
where T: AsRef<[u8]> + ?Sized {
match opt.option_type() {
Type::SourceLinkLayerAddr => {
if opt.data_len() == 1 {
@ -434,14 +433,14 @@ impl<'a> Repr<'a> {
} else {
Err(Error::Malformed)
}
}
},
Type::TargetLinkLayerAddr => {
if opt.data_len() == 1 {
Ok(Repr::TargetLinkLayerAddr(opt.link_layer_addr()))
} else {
Err(Error::Malformed)
}
}
},
Type::PrefixInformation => {
if opt.data_len() == 4 {
Ok(Repr::PrefixInformation(PrefixInformation {
@ -449,12 +448,12 @@ impl<'a> Repr<'a> {
flags: opt.prefix_flags(),
valid_lifetime: opt.valid_lifetime(),
preferred_lifetime: opt.preferred_lifetime(),
prefix: opt.prefix(),
prefix: opt.prefix()
}))
} else {
Err(Error::Malformed)
}
}
},
Type::RedirectedHeader => {
// If the options data length is less than 6, the option
// does not have enough data to fill out the IP header
@ -466,66 +465,60 @@ impl<'a> Repr<'a> {
let ip_repr = Ipv6Repr::parse(&ip_packet)?;
Ok(Repr::RedirectedHeader(RedirectedHeader {
header: ip_repr,
data: &opt.data()[field::IP_DATA + ip_repr.buffer_len()..],
data: &opt.data()[field::IP_DATA + ip_repr.buffer_len()..]
}))
}
}
},
Type::Mtu => {
if opt.data_len() == 1 {
Ok(Repr::Mtu(opt.mtu()))
} else {
Err(Error::Malformed)
}
},
Type::Unknown(id) => {
Ok(Repr::Unknown {
type_: id,
length: opt.data_len(),
data: opt.data()
})
}
Type::Unknown(id) => Ok(Repr::Unknown {
type_: id,
length: opt.data_len(),
data: opt.data(),
}),
}
}
/// Return the length of a header that will be emitted from this high-level representation.
pub fn buffer_len(&self) -> usize {
match self {
&Repr::SourceLinkLayerAddr(addr) | &Repr::TargetLinkLayerAddr(addr) => {
let len = 2 + addr.len();
// Round up to next multiple of 8
(len + 7) / 8 * 8
}
&Repr::PrefixInformation(_) => field::PREFIX.end,
&Repr::RedirectedHeader(RedirectedHeader { header, data }) => {
field::IP_DATA + header.buffer_len() + data.len()
}
&Repr::Mtu(_) => field::MTU.end,
&Repr::Unknown { length, .. } => field::DATA(length).end,
&Repr::SourceLinkLayerAddr(_) | &Repr::TargetLinkLayerAddr(_) =>
field::LL_ADDR.end,
&Repr::PrefixInformation(_) =>
field::PREFIX.end,
&Repr::RedirectedHeader(RedirectedHeader { header, data }) =>
field::IP_DATA + header.buffer_len() + data.len(),
&Repr::Mtu(_) =>
field::MTU.end,
&Repr::Unknown { length, .. } =>
field::DATA(length).end
}
}
/// Emit a high-level representation into an NDISC Option.
pub fn emit<T>(&self, opt: &mut NdiscOption<&'a mut T>)
where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
{
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
match *self {
Repr::SourceLinkLayerAddr(addr) => {
opt.set_option_type(Type::SourceLinkLayerAddr);
let opt_len = addr.len() + 2;
opt.set_data_len(((opt_len + 7) / 8) as u8); // round to next multiple of 8.
opt.set_data_len(1);
opt.set_link_layer_addr(addr);
}
},
Repr::TargetLinkLayerAddr(addr) => {
opt.set_option_type(Type::TargetLinkLayerAddr);
let opt_len = addr.len() + 2;
opt.set_data_len(((opt_len + 7) / 8) as u8); // round to next multiple of 8.
opt.set_data_len(1);
opt.set_link_layer_addr(addr);
}
},
Repr::PrefixInformation(PrefixInformation {
prefix_len,
flags,
valid_lifetime,
preferred_lifetime,
prefix,
prefix_len, flags, valid_lifetime,
preferred_lifetime, prefix
}) => {
opt.clear_prefix_reserved();
opt.set_option_type(Type::PrefixInformation);
@ -535,8 +528,10 @@ impl<'a> Repr<'a> {
opt.set_valid_lifetime(valid_lifetime);
opt.set_preferred_lifetime(preferred_lifetime);
opt.set_prefix(prefix);
}
Repr::RedirectedHeader(RedirectedHeader { header, data }) => {
},
Repr::RedirectedHeader(RedirectedHeader {
header, data
}) => {
let data_len = data.len() / 8;
opt.clear_redirected_reserved();
opt.set_option_type(Type::RedirectedHeader);
@ -552,11 +547,7 @@ impl<'a> Repr<'a> {
opt.set_data_len(1);
opt.set_mtu(mtu);
}
Repr::Unknown {
type_: id,
length,
data,
} => {
Repr::Unknown { type_: id, length, data } => {
opt.set_option_type(Type::Unknown(id));
opt.set_data_len(length);
opt.data_mut().copy_from_slice(data);
@ -571,61 +562,67 @@ impl<'a> fmt::Display for Repr<'a> {
match *self {
Repr::SourceLinkLayerAddr(addr) => {
write!(f, "SourceLinkLayer addr={}", addr)
}
},
Repr::TargetLinkLayerAddr(addr) => {
write!(f, "TargetLinkLayer addr={}", addr)
}
},
Repr::PrefixInformation(PrefixInformation {
prefix, prefix_len, ..
prefix, prefix_len,
..
}) => {
write!(f, "PrefixInformation prefix={}/{}", prefix, prefix_len)
}
Repr::RedirectedHeader(RedirectedHeader { header, .. }) => {
},
Repr::RedirectedHeader(RedirectedHeader {
header,
..
}) => {
write!(f, "RedirectedHeader header={}", header)
}
},
Repr::Mtu(mtu) => {
write!(f, "MTU mtu={}", mtu)
}
Repr::Unknown {
type_: id, length, ..
} => {
},
Repr::Unknown { type_: id, length, .. } => {
write!(f, "Unknown({}) length={}", id, length)
}
}
}
}
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
impl<T: AsRef<[u8]>> PrettyPrint for NdiscOption<T> {
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result {
match NdiscOption::new_checked(buffer) {
Err(err) => return write!(f, "{}({})", indent, err),
Ok(ndisc) => match Repr::parse(&ndisc) {
Err(_) => Ok(()),
Ok(repr) => {
write!(f, "{}{}", indent, repr)
Ok(ndisc) => {
match Repr::parse(&ndisc) {
Err(_) => Ok(()),
Ok(repr) => {
write!(f, "{}{}", indent, repr)
}
}
},
}
}
}
}
#[cfg(test)]
mod test {
use super::{NdiscOption, PrefixInfoFlags, PrefixInformation, Repr, Type};
use crate::Error;
use crate::time::Duration;
use crate::wire::{EthernetAddress, Ipv6Address};
use crate::Error;
use super::{NdiscOption, Type, PrefixInfoFlags, PrefixInformation, Repr};
static PREFIX_OPT_BYTES: [u8; 32] = [
0x03, 0x04, 0x40, 0xc0, 0x00, 0x00, 0x03, 0x84, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00,
0x00, 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
0x03, 0x04, 0x40, 0xc0,
0x00, 0x00, 0x03, 0x84,
0x00, 0x00, 0x03, 0xe8,
0x00, 0x00, 0x00, 0x00,
0xfe, 0x80, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01
];
#[test]
@ -634,10 +631,7 @@ mod test {
assert_eq!(opt.option_type(), Type::PrefixInformation);
assert_eq!(opt.data_len(), 4);
assert_eq!(opt.prefix_len(), 64);
assert_eq!(
opt.prefix_flags(),
PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF
);
assert_eq!(opt.prefix_flags(), PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF);
assert_eq!(opt.valid_lifetime(), Duration::from_secs(900));
assert_eq!(opt.preferred_lifetime(), Duration::from_secs(1000));
assert_eq!(opt.prefix(), Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1));
@ -659,11 +653,11 @@ mod test {
#[test]
fn test_short_packet() {
assert_eq!(
NdiscOption::new_checked(&[0x00, 0x00]),
Err(Error::Truncated)
);
let bytes = [0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
assert_eq!(NdiscOption::new_checked(&[0x00, 0x00]), Err(Error::Truncated));
let bytes = [
0x03, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00
];
assert_eq!(NdiscOption::new_checked(&bytes), Err(Error::Truncated));
}
@ -672,17 +666,13 @@ mod test {
let mut bytes = [0x01, 0x01, 0x54, 0x52, 0x00, 0x12, 0x23, 0x34];
let addr = EthernetAddress([0x54, 0x52, 0x00, 0x12, 0x23, 0x34]);
{
assert_eq!(
Repr::parse(&NdiscOption::new_unchecked(&bytes)),
Ok(Repr::SourceLinkLayerAddr(addr.into()))
);
assert_eq!(Repr::parse(&NdiscOption::new_unchecked(&bytes)),
Ok(Repr::SourceLinkLayerAddr(addr)));
}
bytes[0] = 0x02;
{
assert_eq!(
Repr::parse(&NdiscOption::new_unchecked(&bytes)),
Ok(Repr::TargetLinkLayerAddr(addr.into()))
);
assert_eq!(Repr::parse(&NdiscOption::new_unchecked(&bytes)),
Ok(Repr::TargetLinkLayerAddr(addr)));
}
}
@ -693,12 +683,9 @@ mod test {
flags: PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF,
valid_lifetime: Duration::from_secs(900),
preferred_lifetime: Duration::from_secs(1000),
prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)
});
assert_eq!(
Repr::parse(&NdiscOption::new_unchecked(&PREFIX_OPT_BYTES)),
Ok(repr)
);
assert_eq!(Repr::parse(&NdiscOption::new_unchecked(&PREFIX_OPT_BYTES)), Ok(repr));
}
#[test]
@ -709,7 +696,7 @@ mod test {
flags: PrefixInfoFlags::ON_LINK | PrefixInfoFlags::ADDRCONF,
valid_lifetime: Duration::from_secs(900),
preferred_lifetime: Duration::from_secs(1000),
prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1),
prefix: Ipv6Address::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)
});
let mut opt = NdiscOption::new_unchecked(&mut bytes);
repr.emit(&mut opt);
@ -719,9 +706,6 @@ mod test {
#[test]
fn test_repr_parse_mtu() {
let bytes = [0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x05, 0xdc];
assert_eq!(
Repr::parse(&NdiscOption::new_unchecked(&bytes)),
Ok(Repr::Mtu(1500))
);
assert_eq!(Repr::parse(&NdiscOption::new_unchecked(&bytes)), Ok(Repr::Mtu(1500)));
}
}

View File

@ -34,10 +34,9 @@ use core::marker::PhantomData;
/// Indentation state.
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PrettyIndent {
prefix: &'static str,
level: usize,
level: usize
}
impl PrettyIndent {
@ -72,27 +71,24 @@ pub trait PrettyPrint {
///
/// `pretty_print` accepts a buffer and not a packet wrapper because the packet might
/// be truncated, and so it might not be possible to create the packet wrapper.
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
fmt: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result;
fn pretty_print(buffer: &dyn AsRef<[u8]>, fmt: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result;
}
/// Wrapper for using a `PrettyPrint` where a `Display` is expected.
pub struct PrettyPrinter<'a, T: PrettyPrint> {
prefix: &'static str,
buffer: &'a dyn AsRef<[u8]>,
phantom: PhantomData<T>,
prefix: &'static str,
buffer: &'a dyn AsRef<[u8]>,
phantom: PhantomData<T>
}
impl<'a, T: PrettyPrint> PrettyPrinter<'a, T> {
/// Format the listing with the recorded parameters when Display::fmt is called.
pub fn new(prefix: &'static str, buffer: &'a dyn AsRef<[u8]>) -> PrettyPrinter<'a, T> {
PrettyPrinter {
prefix: prefix,
buffer: buffer,
phantom: PhantomData,
prefix: prefix,
buffer: buffer,
phantom: PhantomData
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,16 @@
use core::{i32, ops, cmp, fmt};
use byteorder::{ByteOrder, NetworkEndian};
use core::{cmp, fmt, i32, ops};
use crate::phy::ChecksumCapabilities;
use crate::wire::ip::checksum;
use crate::wire::{IpAddress, IpProtocol};
use crate::{Error, Result};
use crate::phy::ChecksumCapabilities;
use crate::wire::{IpProtocol, IpAddress};
use crate::wire::ip::checksum;
/// A TCP sequence number.
///
/// A sequence number is a monotonically advancing integer modulo 2<sup>32</sup>.
/// Sequence numbers do not have a discontiguity when compared pairwise across a signed overflow.
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct SeqNumber(pub i32);
impl fmt::Display for SeqNumber {
@ -68,9 +67,8 @@ impl cmp::PartialOrd for SeqNumber {
/// A read/write wrapper around a Transmission Control Protocol packet buffer.
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> {
buffer: T,
buffer: T
}
mod field {
@ -80,12 +78,12 @@ mod field {
pub const SRC_PORT: Field = 0..2;
pub const DST_PORT: Field = 2..4;
pub const SEQ_NUM: Field = 4..8;
pub const ACK_NUM: Field = 8..12;
pub const FLAGS: Field = 12..14;
pub const SEQ_NUM: Field = 4..8;
pub const ACK_NUM: Field = 8..12;
pub const FLAGS: Field = 12..14;
pub const WIN_SIZE: Field = 14..16;
pub const CHECKSUM: Field = 16..18;
pub const URGENT: Field = 18..20;
pub const URGENT: Field = 18..20;
pub fn OPTIONS(length: u8) -> Field {
URGENT.end..(length as usize)
@ -99,18 +97,16 @@ mod field {
pub const FLG_URG: u16 = 0x020;
pub const FLG_ECE: u16 = 0x040;
pub const FLG_CWR: u16 = 0x080;
pub const FLG_NS: u16 = 0x100;
pub const FLG_NS: u16 = 0x100;
pub const OPT_END: u8 = 0x00;
pub const OPT_NOP: u8 = 0x01;
pub const OPT_MSS: u8 = 0x02;
pub const OPT_WS: u8 = 0x03;
pub const OPT_WS: u8 = 0x03;
pub const OPT_SACKPERM: u8 = 0x04;
pub const OPT_SACKRNG: u8 = 0x05;
pub const OPT_SACKRNG: u8 = 0x05;
}
pub const HEADER_LEN: usize = field::URGENT.end;
impl<T: AsRef<[u8]>> Packet<T> {
/// Imbue a raw octet buffer with TCP packet structure.
pub fn new_unchecked(buffer: T) -> Packet<T> {
@ -289,12 +285,8 @@ impl<T: AsRef<[u8]>> Packet<T> {
pub fn segment_len(&self) -> usize {
let data = self.buffer.as_ref();
let mut length = data.len() - self.header_len() as usize;
if self.syn() {
length += 1
}
if self.fin() {
length += 1
}
if self.syn() { length += 1 }
if self.fin() { length += 1 }
length
}
@ -337,14 +329,13 @@ impl<T: AsRef<[u8]>> Packet<T> {
/// # Fuzzing
/// This function always returns `true` when fuzzing.
pub fn verify_checksum(&self, src_addr: &IpAddress, dst_addr: &IpAddress) -> bool {
if cfg!(fuzzing) {
return true;
}
if cfg!(fuzzing) { return true }
let data = self.buffer.as_ref();
checksum::combine(&[
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Tcp, data.len() as u32),
checksum::data(data),
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Tcp,
data.len() as u32),
checksum::data(data)
]) == !0
}
}
@ -410,11 +401,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
pub fn set_fin(&mut self, value: bool) {
let data = self.buffer.as_mut();
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
let raw = if value {
raw | field::FLG_FIN
} else {
raw & !field::FLG_FIN
};
let raw = if value { raw | field::FLG_FIN } else { raw & !field::FLG_FIN };
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
}
@ -423,11 +410,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
pub fn set_syn(&mut self, value: bool) {
let data = self.buffer.as_mut();
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
let raw = if value {
raw | field::FLG_SYN
} else {
raw & !field::FLG_SYN
};
let raw = if value { raw | field::FLG_SYN } else { raw & !field::FLG_SYN };
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
}
@ -436,11 +419,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
pub fn set_rst(&mut self, value: bool) {
let data = self.buffer.as_mut();
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
let raw = if value {
raw | field::FLG_RST
} else {
raw & !field::FLG_RST
};
let raw = if value { raw | field::FLG_RST } else { raw & !field::FLG_RST };
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
}
@ -449,11 +428,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
pub fn set_psh(&mut self, value: bool) {
let data = self.buffer.as_mut();
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
let raw = if value {
raw | field::FLG_PSH
} else {
raw & !field::FLG_PSH
};
let raw = if value { raw | field::FLG_PSH } else { raw & !field::FLG_PSH };
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
}
@ -462,11 +437,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
pub fn set_ack(&mut self, value: bool) {
let data = self.buffer.as_mut();
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
let raw = if value {
raw | field::FLG_ACK
} else {
raw & !field::FLG_ACK
};
let raw = if value { raw | field::FLG_ACK } else { raw & !field::FLG_ACK };
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
}
@ -475,11 +446,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
pub fn set_urg(&mut self, value: bool) {
let data = self.buffer.as_mut();
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
let raw = if value {
raw | field::FLG_URG
} else {
raw & !field::FLG_URG
};
let raw = if value { raw | field::FLG_URG } else { raw & !field::FLG_URG };
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
}
@ -488,11 +455,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
pub fn set_ece(&mut self, value: bool) {
let data = self.buffer.as_mut();
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
let raw = if value {
raw | field::FLG_ECE
} else {
raw & !field::FLG_ECE
};
let raw = if value { raw | field::FLG_ECE } else { raw & !field::FLG_ECE };
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
}
@ -501,11 +464,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
pub fn set_cwr(&mut self, value: bool) {
let data = self.buffer.as_mut();
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
let raw = if value {
raw | field::FLG_CWR
} else {
raw & !field::FLG_CWR
};
let raw = if value { raw | field::FLG_CWR } else { raw & !field::FLG_CWR };
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
}
@ -514,11 +473,7 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
pub fn set_ns(&mut self, value: bool) {
let data = self.buffer.as_mut();
let raw = NetworkEndian::read_u16(&data[field::FLAGS]);
let raw = if value {
raw | field::FLG_NS
} else {
raw & !field::FLG_NS
};
let raw = if value { raw | field::FLG_NS } else { raw & !field::FLG_NS };
NetworkEndian::write_u16(&mut data[field::FLAGS], raw)
}
@ -562,8 +517,9 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
let checksum = {
let data = self.buffer.as_ref();
!checksum::combine(&[
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Tcp, data.len() as u32),
checksum::data(data),
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Tcp,
data.len() as u32),
checksum::data(data)
])
};
self.set_checksum(checksum)
@ -594,7 +550,6 @@ impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
/// A representation of a single TCP option.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TcpOption<'a> {
EndOfList,
NoOperation,
@ -602,7 +557,7 @@ pub enum TcpOption<'a> {
WindowScale(u8),
SackPermitted,
SackRange([Option<(u32, u32)>; 3]),
Unknown { kind: u8, data: &'a [u8] },
Unknown { kind: u8, data: &'a [u8] }
}
impl<'a> TcpOption<'a> {
@ -621,18 +576,24 @@ impl<'a> TcpOption<'a> {
length = *buffer.get(1).ok_or(Error::Truncated)? as usize;
let data = buffer.get(2..length).ok_or(Error::Truncated)?;
match (kind, length) {
(field::OPT_END, _) | (field::OPT_NOP, _) => unreachable!(),
(field::OPT_MSS, 4) => {
option = TcpOption::MaxSegmentSize(NetworkEndian::read_u16(data))
}
(field::OPT_MSS, _) => return Err(Error::Malformed),
(field::OPT_WS, 3) => option = TcpOption::WindowScale(data[0]),
(field::OPT_WS, _) => return Err(Error::Malformed),
(field::OPT_SACKPERM, 2) => option = TcpOption::SackPermitted,
(field::OPT_SACKPERM, _) => return Err(Error::Malformed),
(field::OPT_END, _) |
(field::OPT_NOP, _) =>
unreachable!(),
(field::OPT_MSS, 4) =>
option = TcpOption::MaxSegmentSize(NetworkEndian::read_u16(data)),
(field::OPT_MSS, _) =>
return Err(Error::Malformed),
(field::OPT_WS, 3) =>
option = TcpOption::WindowScale(data[0]),
(field::OPT_WS, _) =>
return Err(Error::Malformed),
(field::OPT_SACKPERM, 2) =>
option = TcpOption::SackPermitted,
(field::OPT_SACKPERM, _) =>
return Err(Error::Malformed),
(field::OPT_SACKRNG, n) => {
if n < 10 || (n - 2) % 8 != 0 {
return Err(Error::Malformed);
if n < 10 || (n-2) % 8 != 0 {
return Err(Error::Malformed)
}
if n > 26 {
// It's possible for a remote to send 4 SACK blocks, but extremely rare.
@ -656,16 +617,19 @@ impl<'a> TcpOption<'a> {
*nmut = if left < data.len() {
let mid = left + 4;
let right = mid + 4;
let range_left = NetworkEndian::read_u32(&data[left..mid]);
let range_right = NetworkEndian::read_u32(&data[mid..right]);
let range_left = NetworkEndian::read_u32(
&data[left..mid]);
let range_right = NetworkEndian::read_u32(
&data[mid..right]);
Some((range_left, range_right))
} else {
None
};
});
option = TcpOption::SackRange(sack_ranges);
}
(_, _) => option = TcpOption::Unknown { kind, data },
},
(_, _) =>
option = TcpOption::Unknown { kind, data }
}
}
}
@ -680,7 +644,7 @@ impl<'a> TcpOption<'a> {
TcpOption::WindowScale(_) => 3,
TcpOption::SackPermitted => 2,
TcpOption::SackRange(s) => s.iter().filter(|s| s.is_some()).count() * 8 + 2,
TcpOption::Unknown { data, .. } => 2 + data.len(),
TcpOption::Unknown { data, .. } => 2 + data.len()
}
}
@ -688,21 +652,23 @@ impl<'a> TcpOption<'a> {
let length;
match *self {
TcpOption::EndOfList => {
length = 1;
length = 1;
// There may be padding space which also should be initialized.
for p in buffer.iter_mut() {
*p = field::OPT_END;
}
}
TcpOption::NoOperation => {
length = 1;
length = 1;
buffer[0] = field::OPT_NOP;
}
_ => {
length = self.buffer_len();
length = self.buffer_len();
buffer[1] = length as u8;
match self {
&TcpOption::EndOfList | &TcpOption::NoOperation => unreachable!(),
&TcpOption::EndOfList |
&TcpOption::NoOperation =>
unreachable!(),
&TcpOption::MaxSegmentSize(value) => {
buffer[0] = field::OPT_MSS;
NetworkEndian::write_u16(&mut buffer[2..], value)
@ -716,21 +682,14 @@ impl<'a> TcpOption<'a> {
}
&TcpOption::SackRange(slice) => {
buffer[0] = field::OPT_SACKRNG;
slice
.iter()
.filter(|s| s.is_some())
.enumerate()
.for_each(|(i, s)| {
let (first, second) = *s.as_ref().unwrap();
let pos = i * 8 + 2;
NetworkEndian::write_u32(&mut buffer[pos..], first);
NetworkEndian::write_u32(&mut buffer[pos + 4..], second);
});
slice.iter().filter(|s| s.is_some()).enumerate().for_each(|(i, s)| {
let (first, second) = *s.as_ref().unwrap();
let pos = i * 8 + 2;
NetworkEndian::write_u32(&mut buffer[pos..], first);
NetworkEndian::write_u32(&mut buffer[pos+4..], second);
});
}
&TcpOption::Unknown {
kind,
data: provided,
} => {
&TcpOption::Unknown { kind, data: provided } => {
buffer[0] = kind;
buffer[2..].copy_from_slice(provided)
}
@ -743,13 +702,12 @@ impl<'a> TcpOption<'a> {
/// The possible control flags of a Transmission Control Protocol packet.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Control {
None,
Psh,
Syn,
Fin,
Rst,
Rst
}
#[allow(clippy::len_without_is_empty)]
@ -757,8 +715,8 @@ impl Control {
/// Return the length of a control flag, in terms of sequence space.
pub fn len(self) -> usize {
match self {
Control::Syn | Control::Fin => 1,
_ => 0,
Control::Syn | Control::Fin => 1,
_ => 0
}
}
@ -766,63 +724,54 @@ impl Control {
pub fn quash_psh(self) -> Control {
match self {
Control::Psh => Control::None,
_ => self,
_ => self
}
}
}
/// A high-level representation of a Transmission Control Protocol packet.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Repr<'a> {
pub src_port: u16,
pub dst_port: u16,
pub control: Control,
pub seq_number: SeqNumber,
pub ack_number: Option<SeqNumber>,
pub window_len: u16,
pub src_port: u16,
pub dst_port: u16,
pub control: Control,
pub seq_number: SeqNumber,
pub ack_number: Option<SeqNumber>,
pub window_len: u16,
pub window_scale: Option<u8>,
pub max_seg_size: Option<u16>,
pub sack_permitted: bool,
pub sack_ranges: [Option<(u32, u32)>; 3],
pub payload: &'a [u8],
pub sack_ranges: [Option<(u32, u32)>; 3],
pub payload: &'a [u8]
}
impl<'a> Repr<'a> {
/// Parse a Transmission Control Protocol packet and return a high-level representation.
pub fn parse<T>(
packet: &Packet<&'a T>,
src_addr: &IpAddress,
dst_addr: &IpAddress,
checksum_caps: &ChecksumCapabilities,
) -> Result<Repr<'a>>
where
T: AsRef<[u8]> + ?Sized,
{
pub fn parse<T>(packet: &Packet<&'a T>, src_addr: &IpAddress, dst_addr: &IpAddress,
checksum_caps: &ChecksumCapabilities) -> Result<Repr<'a>>
where T: AsRef<[u8]> + ?Sized {
// Source and destination ports must be present.
if packet.src_port() == 0 {
return Err(Error::Malformed);
}
if packet.dst_port() == 0 {
return Err(Error::Malformed);
}
if packet.src_port() == 0 { return Err(Error::Malformed) }
if packet.dst_port() == 0 { return Err(Error::Malformed) }
// Valid checksum is expected.
if checksum_caps.tcp.rx() && !packet.verify_checksum(src_addr, dst_addr) {
return Err(Error::Checksum);
return Err(Error::Checksum)
}
let control = match (packet.syn(), packet.fin(), packet.rst(), packet.psh()) {
(false, false, false, false) => Control::None,
(false, false, false, true) => Control::Psh,
(true, false, false, _) => Control::Syn,
(false, true, false, _) => Control::Fin,
(false, false, true, _) => Control::Rst,
_ => return Err(Error::Malformed),
};
let ack_number = match packet.ack() {
true => Some(packet.ack_number()),
false => None,
};
let control =
match (packet.syn(), packet.fin(), packet.rst(), packet.psh()) {
(false, false, false, false) => Control::None,
(false, false, false, true) => Control::Psh,
(true, false, false, _) => Control::Syn,
(false, true, false, _) => Control::Fin,
(false, false, true , _) => Control::Rst,
_ => return Err(Error::Malformed)
};
let ack_number =
match packet.ack() {
true => Some(packet.ack_number()),
false => None
};
// The PSH flag is ignored.
// The URG flag and the urgent field is ignored. This behavior is standards-compliant,
// however, most deployed systems (e.g. Linux) are *not* standards-compliant, and would
@ -838,44 +787,41 @@ impl<'a> Repr<'a> {
match option {
TcpOption::EndOfList => break,
TcpOption::NoOperation => (),
TcpOption::MaxSegmentSize(value) => max_seg_size = Some(value),
TcpOption::MaxSegmentSize(value) =>
max_seg_size = Some(value),
TcpOption::WindowScale(value) => {
// RFC 1323: Thus, the shift count must be limited to 14 (which allows windows
// of 2**30 = 1 Gbyte). If a Window Scale option is received with a shift.cnt
// value exceeding 14, the TCP should log the error but use 14 instead of the
// specified value.
window_scale = if value > 14 {
net_debug!(
"{}:{}:{}:{}: parsed window scaling factor >14, setting to 14",
src_addr,
packet.src_port(),
dst_addr,
packet.dst_port()
);
net_debug!("{}:{}:{}:{}: parsed window scaling factor >14, setting to 14", src_addr, packet.src_port(), dst_addr, packet.dst_port());
Some(14)
} else {
Some(value)
};
}
TcpOption::SackPermitted => sack_permitted = true,
TcpOption::SackRange(slice) => sack_ranges = slice,
},
TcpOption::SackPermitted =>
sack_permitted = true,
TcpOption::SackRange(slice) =>
sack_ranges = slice,
_ => (),
}
options = next_options;
}
Ok(Repr {
src_port: packet.src_port(),
dst_port: packet.dst_port(),
control: control,
seq_number: packet.seq_number(),
ack_number: ack_number,
window_len: packet.window_len(),
src_port: packet.src_port(),
dst_port: packet.dst_port(),
control: control,
seq_number: packet.seq_number(),
ack_number: ack_number,
window_len: packet.window_len(),
window_scale: window_scale,
max_seg_size: max_seg_size,
sack_permitted: sack_permitted,
sack_ranges: sack_ranges,
payload: packet.payload(),
sack_ranges: sack_ranges,
payload: packet.payload()
})
}
@ -894,11 +840,9 @@ impl<'a> Repr<'a> {
if self.sack_permitted {
length += 2;
}
let sack_range_len: usize = self
.sack_ranges
.iter()
.map(|o| o.map(|_| 8).unwrap_or(0))
.sum();
let sack_range_len: usize = self.sack_ranges.iter().map(
|o| o.map(|_| 8).unwrap_or(0)
).sum();
if sack_range_len > 0 {
length += sack_range_len + 2;
}
@ -908,21 +852,23 @@ impl<'a> Repr<'a> {
length
}
/// Return the length of the header for the TCP protocol.
///
/// Per RFC 6691, this should be used for MSS calculations. It may be smaller than the buffer
/// space required to accomodate this packet's data.
pub fn mss_header_len(&self) -> usize {
field::URGENT.end
}
/// Return the length of a packet that will be emitted from this high-level representation.
pub fn buffer_len(&self) -> usize {
self.header_len() + self.payload.len()
}
/// Emit a high-level representation into a Transmission Control Protocol packet.
pub fn emit<T>(
&self,
packet: &mut Packet<&mut T>,
src_addr: &IpAddress,
dst_addr: &IpAddress,
checksum_caps: &ChecksumCapabilities,
) where
T: AsRef<[u8]> + AsMut<[u8]> + ?Sized,
{
pub fn emit<T>(&self, packet: &mut Packet<&mut T>, src_addr: &IpAddress, dst_addr: &IpAddress,
checksum_caps: &ChecksumCapabilities)
where T: AsRef<[u8]> + AsMut<[u8]> + ?Sized {
packet.set_src_port(self.src_port);
packet.set_dst_port(self.dst_port);
packet.set_seq_number(self.seq_number);
@ -932,28 +878,24 @@ impl<'a> Repr<'a> {
packet.clear_flags();
match self.control {
Control::None => (),
Control::Psh => packet.set_psh(true),
Control::Syn => packet.set_syn(true),
Control::Fin => packet.set_fin(true),
Control::Rst => packet.set_rst(true),
Control::Psh => packet.set_psh(true),
Control::Syn => packet.set_syn(true),
Control::Fin => packet.set_fin(true),
Control::Rst => packet.set_rst(true)
}
packet.set_ack(self.ack_number.is_some());
{
let mut options = packet.options_mut();
if let Some(value) = self.max_seg_size {
let tmp = options;
options = TcpOption::MaxSegmentSize(value).emit(tmp);
let tmp = options; options = TcpOption::MaxSegmentSize(value).emit(tmp);
}
if let Some(value) = self.window_scale {
let tmp = options;
options = TcpOption::WindowScale(value).emit(tmp);
let tmp = options; options = TcpOption::WindowScale(value).emit(tmp);
}
if self.sack_permitted {
let tmp = options;
options = TcpOption::SackPermitted.emit(tmp);
let tmp = options; options = TcpOption::SackPermitted.emit(tmp);
} else if self.ack_number.is_some() && self.sack_ranges.iter().any(|s| s.is_some()) {
let tmp = options;
options = TcpOption::SackRange(self.sack_ranges).emit(tmp);
let tmp = options; options = TcpOption::SackRange(self.sack_ranges).emit(tmp);
}
if !options.is_empty() {
@ -981,8 +923,8 @@ impl<'a> Repr<'a> {
pub fn is_empty(&self) -> bool {
match self.control {
_ if !self.payload.is_empty() => false,
Control::Syn | Control::Fin | Control::Rst => false,
Control::None | Control::Psh => true,
Control::Syn | Control::Fin | Control::Rst => false,
Control::None | Control::Psh => true
}
}
}
@ -990,28 +932,15 @@ impl<'a> Repr<'a> {
impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Cannot use Repr::parse because we don't have the IP addresses.
write!(f, "TCP src={} dst={}", self.src_port(), self.dst_port())?;
if self.syn() {
write!(f, " syn")?
}
if self.fin() {
write!(f, " fin")?
}
if self.rst() {
write!(f, " rst")?
}
if self.psh() {
write!(f, " psh")?
}
if self.ece() {
write!(f, " ece")?
}
if self.cwr() {
write!(f, " cwr")?
}
if self.ns() {
write!(f, " ns")?
}
write!(f, "TCP src={} dst={}",
self.src_port(), self.dst_port())?;
if self.syn() { write!(f, " syn")? }
if self.fin() { write!(f, " fin")? }
if self.rst() { write!(f, " rst")? }
if self.psh() { write!(f, " psh")? }
if self.ece() { write!(f, " ece")? }
if self.cwr() { write!(f, " cwr")? }
if self.ns() { write!(f, " ns" )? }
write!(f, " seq={}", self.seq_number())?;
if self.ack() {
write!(f, " ack={}", self.ack_number())?;
@ -1024,18 +953,24 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
let mut options = self.options();
while !options.is_empty() {
let (next_options, option) = match TcpOption::parse(options) {
Ok(res) => res,
Err(err) => return write!(f, " ({})", err),
};
let (next_options, option) =
match TcpOption::parse(options) {
Ok(res) => res,
Err(err) => return write!(f, " ({})", err)
};
match option {
TcpOption::EndOfList => break,
TcpOption::NoOperation => (),
TcpOption::MaxSegmentSize(value) => write!(f, " mss={}", value)?,
TcpOption::WindowScale(value) => write!(f, " ws={}", value)?,
TcpOption::SackPermitted => write!(f, " sACK")?,
TcpOption::SackRange(slice) => write!(f, " sACKr{:?}", slice)?, // debug print conveniently includes the []s
TcpOption::Unknown { kind, .. } => write!(f, " opt({})", kind)?,
TcpOption::MaxSegmentSize(value) =>
write!(f, " mss={}", value)?,
TcpOption::WindowScale(value) =>
write!(f, " ws={}", value)?,
TcpOption::SackPermitted =>
write!(f, " sACK")?,
TcpOption::SackRange(slice) =>
write!(f, " sACKr{:?}", slice)?, // debug print conveniently includes the []s
TcpOption::Unknown { kind, .. } =>
write!(f, " opt({})", kind)?,
}
options = next_options;
}
@ -1045,13 +980,14 @@ impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
impl<'a> fmt::Display for Repr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "TCP src={} dst={}", self.src_port, self.dst_port)?;
write!(f, "TCP src={} dst={}",
self.src_port, self.dst_port)?;
match self.control {
Control::Syn => write!(f, " syn")?,
Control::Fin => write!(f, " fin")?,
Control::Rst => write!(f, " rst")?,
Control::Psh => write!(f, " psh")?,
Control::None => (),
Control::None => ()
}
write!(f, " seq={}", self.seq_number)?;
if let Some(ack_number) = self.ack_number {
@ -1066,26 +1002,23 @@ impl<'a> fmt::Display for Repr<'a> {
}
}
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result {
match Packet::new_checked(buffer) {
Err(err) => write!(f, "{}({})", indent, err),
Ok(packet) => write!(f, "{}{}", indent, packet),
Err(err) => write!(f, "{}({})", indent, err),
Ok(packet) => write!(f, "{}{}", indent, packet)
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[cfg(feature = "proto-ipv4")]
use crate::wire::Ipv4Address;
use super::*;
#[cfg(feature = "proto-ipv4")]
const SRC_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 1]);
@ -1093,16 +1026,22 @@ mod test {
const DST_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 2]);
#[cfg(feature = "proto-ipv4")]
static PACKET_BYTES: [u8; 28] = [
0xbf, 0x00, 0x00, 0x50, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x60, 0x35, 0x01,
0x23, 0x01, 0xb6, 0x02, 0x01, 0x03, 0x03, 0x0c, 0x01, 0xaa, 0x00, 0x00, 0xff,
];
static PACKET_BYTES: [u8; 28] =
[0xbf, 0x00, 0x00, 0x50,
0x01, 0x23, 0x45, 0x67,
0x89, 0xab, 0xcd, 0xef,
0x60, 0x35, 0x01, 0x23,
0x01, 0xb6, 0x02, 0x01,
0x03, 0x03, 0x0c, 0x01,
0xaa, 0x00, 0x00, 0xff];
#[cfg(feature = "proto-ipv4")]
static OPTION_BYTES: [u8; 4] = [0x03, 0x03, 0x0c, 0x01];
static OPTION_BYTES: [u8; 4] =
[0x03, 0x03, 0x0c, 0x01];
#[cfg(feature = "proto-ipv4")]
static PAYLOAD_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
static PAYLOAD_BYTES: [u8; 4] =
[0xaa, 0x00, 0x00, 0xff];
#[test]
#[cfg(feature = "proto-ipv4")]
@ -1113,18 +1052,18 @@ mod test {
assert_eq!(packet.seq_number(), SeqNumber(0x01234567));
assert_eq!(packet.ack_number(), SeqNumber(0x89abcdefu32 as i32));
assert_eq!(packet.header_len(), 24);
assert!(packet.fin());
assert!(!packet.syn());
assert!(packet.rst());
assert!(!packet.psh());
assert!(packet.ack());
assert!(packet.urg());
assert_eq!(packet.fin(), true);
assert_eq!(packet.syn(), false);
assert_eq!(packet.rst(), true);
assert_eq!(packet.psh(), false);
assert_eq!(packet.ack(), true);
assert_eq!(packet.urg(), true);
assert_eq!(packet.window_len(), 0x0123);
assert_eq!(packet.urgent_at(), 0x0201);
assert_eq!(packet.checksum(), 0x01b6);
assert_eq!(packet.options(), &OPTION_BYTES[..]);
assert_eq!(packet.payload(), &PAYLOAD_BYTES[..]);
assert!(packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()));
assert_eq!(packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()), true);
}
#[test]
@ -1169,25 +1108,28 @@ mod test {
}
#[cfg(feature = "proto-ipv4")]
static SYN_PACKET_BYTES: [u8; 24] = [
0xbf, 0x00, 0x00, 0x50, 0x01, 0x23, 0x45, 0x67, 0x00, 0x00, 0x00, 0x00, 0x50, 0x02, 0x01,
0x23, 0x7a, 0x8d, 0x00, 0x00, 0xaa, 0x00, 0x00, 0xff,
];
static SYN_PACKET_BYTES: [u8; 24] =
[0xbf, 0x00, 0x00, 0x50,
0x01, 0x23, 0x45, 0x67,
0x00, 0x00, 0x00, 0x00,
0x50, 0x02, 0x01, 0x23,
0x7a, 0x8d, 0x00, 0x00,
0xaa, 0x00, 0x00, 0xff];
#[cfg(feature = "proto-ipv4")]
fn packet_repr() -> Repr<'static> {
Repr {
src_port: 48896,
dst_port: 80,
seq_number: SeqNumber(0x01234567),
ack_number: None,
window_len: 0x0123,
src_port: 48896,
dst_port: 80,
seq_number: SeqNumber(0x01234567),
ack_number: None,
window_len: 0x0123,
window_scale: None,
control: Control::Syn,
control: Control::Syn,
max_seg_size: None,
sack_permitted: false,
sack_ranges: [None, None, None],
payload: &PAYLOAD_BYTES,
sack_ranges: [None, None, None],
payload: &PAYLOAD_BYTES
}
}
@ -1195,13 +1137,7 @@ mod test {
#[cfg(feature = "proto-ipv4")]
fn test_parse() {
let packet = Packet::new_unchecked(&SYN_PACKET_BYTES[..]);
let repr = Repr::parse(
&packet,
&SRC_ADDR.into(),
&DST_ADDR.into(),
&ChecksumCapabilities::default(),
)
.unwrap();
let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into(), &ChecksumCapabilities::default()).unwrap();
assert_eq!(repr, packet_repr());
}
@ -1211,12 +1147,7 @@ mod test {
let repr = packet_repr();
let mut bytes = vec![0xa5; repr.buffer_len()];
let mut packet = Packet::new_unchecked(&mut bytes);
repr.emit(
&mut packet,
&SRC_ADDR.into(),
&DST_ADDR.into(),
&ChecksumCapabilities::default(),
);
repr.emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into(), &ChecksumCapabilities::default());
assert_eq!(&packet.into_inner()[..], &SYN_PACKET_BYTES[..]);
}
@ -1229,62 +1160,57 @@ mod test {
}
macro_rules! assert_option_parses {
($opt:expr, $data:expr) => {{
($opt:expr, $data:expr) => ({
assert_eq!(TcpOption::parse($data), Ok((&[][..], $opt)));
let buffer = &mut [0; 40][..$opt.buffer_len()];
assert_eq!($opt.emit(buffer), &mut []);
assert_eq!(&*buffer, $data);
}};
})
}
#[test]
fn test_tcp_options() {
assert_option_parses!(TcpOption::EndOfList, &[0x00]);
assert_option_parses!(TcpOption::NoOperation, &[0x01]);
assert_option_parses!(TcpOption::MaxSegmentSize(1500), &[0x02, 0x04, 0x05, 0xdc]);
assert_option_parses!(TcpOption::WindowScale(12), &[0x03, 0x03, 0x0c]);
assert_option_parses!(TcpOption::SackPermitted, &[0x4, 0x02]);
assert_option_parses!(
TcpOption::SackRange([Some((500, 1500)), None, None]),
&[0x05, 0x0a, 0x00, 0x00, 0x01, 0xf4, 0x00, 0x00, 0x05, 0xdc]
);
assert_option_parses!(
TcpOption::SackRange([Some((875, 1225)), Some((1500, 2500)), None]),
&[
0x05, 0x12, 0x00, 0x00, 0x03, 0x6b, 0x00, 0x00, 0x04, 0xc9, 0x00, 0x00, 0x05, 0xdc,
0x00, 0x00, 0x09, 0xc4
]
);
assert_option_parses!(
TcpOption::SackRange([
Some((875000, 1225000)),
Some((1500000, 2500000)),
Some((876543210, 876654320))
]),
&[
0x05, 0x1a, 0x00, 0x0d, 0x59, 0xf8, 0x00, 0x12, 0xb1, 0x28, 0x00, 0x16, 0xe3, 0x60,
0x00, 0x26, 0x25, 0xa0, 0x34, 0x3e, 0xfc, 0xea, 0x34, 0x40, 0xae, 0xf0
]
);
assert_option_parses!(
TcpOption::Unknown {
kind: 12,
data: &[1, 2, 3][..]
},
&[0x0c, 0x05, 0x01, 0x02, 0x03]
)
assert_option_parses!(TcpOption::EndOfList,
&[0x00]);
assert_option_parses!(TcpOption::NoOperation,
&[0x01]);
assert_option_parses!(TcpOption::MaxSegmentSize(1500),
&[0x02, 0x04, 0x05, 0xdc]);
assert_option_parses!(TcpOption::WindowScale(12),
&[0x03, 0x03, 0x0c]);
assert_option_parses!(TcpOption::SackPermitted,
&[0x4, 0x02]);
assert_option_parses!(TcpOption::SackRange([Some((500, 1500)), None, None]),
&[0x05, 0x0a,
0x00, 0x00, 0x01, 0xf4, 0x00, 0x00, 0x05, 0xdc]);
assert_option_parses!(TcpOption::SackRange([Some((875, 1225)), Some((1500, 2500)), None]),
&[0x05, 0x12,
0x00, 0x00, 0x03, 0x6b, 0x00, 0x00, 0x04, 0xc9,
0x00, 0x00, 0x05, 0xdc, 0x00, 0x00, 0x09, 0xc4]);
assert_option_parses!(TcpOption::SackRange([Some((875000, 1225000)),
Some((1500000, 2500000)),
Some((876543210, 876654320))]),
&[0x05, 0x1a,
0x00, 0x0d, 0x59, 0xf8, 0x00, 0x12, 0xb1, 0x28,
0x00, 0x16, 0xe3, 0x60, 0x00, 0x26, 0x25, 0xa0,
0x34, 0x3e, 0xfc, 0xea, 0x34, 0x40, 0xae, 0xf0]);
assert_option_parses!(TcpOption::Unknown { kind: 12, data: &[1, 2, 3][..] },
&[0x0c, 0x05, 0x01, 0x02, 0x03])
}
#[test]
fn test_malformed_tcp_options() {
assert_eq!(TcpOption::parse(&[]), Err(Error::Truncated));
assert_eq!(TcpOption::parse(&[0xc]), Err(Error::Truncated));
assert_eq!(
TcpOption::parse(&[0xc, 0x05, 0x01, 0x02]),
Err(Error::Truncated)
);
assert_eq!(TcpOption::parse(&[0xc, 0x01]), Err(Error::Truncated));
assert_eq!(TcpOption::parse(&[0x2, 0x02]), Err(Error::Malformed));
assert_eq!(TcpOption::parse(&[0x3, 0x02]), Err(Error::Malformed));
assert_eq!(TcpOption::parse(&[]),
Err(Error::Truncated));
assert_eq!(TcpOption::parse(&[0xc]),
Err(Error::Truncated));
assert_eq!(TcpOption::parse(&[0xc, 0x05, 0x01, 0x02]),
Err(Error::Truncated));
assert_eq!(TcpOption::parse(&[0xc, 0x01]),
Err(Error::Truncated));
assert_eq!(TcpOption::parse(&[0x2, 0x02]),
Err(Error::Malformed));
assert_eq!(TcpOption::parse(&[0x3, 0x02]),
Err(Error::Malformed));
}
}

View File

@ -1,16 +1,15 @@
use byteorder::{ByteOrder, NetworkEndian};
use core::fmt;
use byteorder::{ByteOrder, NetworkEndian};
use crate::phy::ChecksumCapabilities;
use crate::wire::ip::checksum;
use crate::wire::{IpAddress, IpProtocol};
use crate::{Error, Result};
use crate::phy::ChecksumCapabilities;
use crate::wire::{IpProtocol, IpAddress};
use crate::wire::ip::checksum;
/// A read/write wrapper around an User Datagram Protocol packet buffer.
#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Packet<T: AsRef<[u8]>> {
buffer: T,
buffer: T
}
mod field {
@ -20,7 +19,7 @@ mod field {
pub const SRC_PORT: Field = 0..2;
pub const DST_PORT: Field = 2..4;
pub const LENGTH: Field = 4..6;
pub const LENGTH: Field = 4..6;
pub const CHECKSUM: Field = 6..8;
pub fn PAYLOAD(length: u16) -> Field {
@ -28,8 +27,6 @@ mod field {
}
}
pub const HEADER_LEN: usize = field::CHECKSUM.end;
#[allow(clippy::len_without_is_empty)]
impl<T: AsRef<[u8]>> Packet<T> {
/// Imbue a raw octet buffer with UDP packet structure.
@ -57,13 +54,13 @@ impl<T: AsRef<[u8]>> Packet<T> {
/// [set_len]: #method.set_len
pub fn check_len(&self) -> Result<()> {
let buffer_len = self.buffer.as_ref().len();
if buffer_len < HEADER_LEN {
if buffer_len < field::CHECKSUM.end {
Err(Error::Truncated)
} else {
let field_len = self.len() as usize;
if buffer_len < field_len {
Err(Error::Truncated)
} else if field_len < HEADER_LEN {
} else if field_len < field::CHECKSUM.end {
Err(Error::Malformed)
} else {
Ok(())
@ -113,14 +110,13 @@ impl<T: AsRef<[u8]>> Packet<T> {
/// # Fuzzing
/// This function always returns `true` when fuzzing.
pub fn verify_checksum(&self, src_addr: &IpAddress, dst_addr: &IpAddress) -> bool {
if cfg!(fuzzing) {
return true;
}
if cfg!(fuzzing) { return true }
let data = self.buffer.as_ref();
checksum::combine(&[
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Udp, self.len() as u32),
checksum::data(&data[..self.len() as usize]),
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Udp,
self.len() as u32),
checksum::data(&data[..self.len() as usize])
]) == !0
}
}
@ -174,8 +170,9 @@ impl<T: AsRef<[u8]> + AsMut<[u8]>> Packet<T> {
let checksum = {
let data = self.buffer.as_ref();
!checksum::combine(&[
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Udp, self.len() as u32),
checksum::data(&data[..self.len() as usize]),
checksum::pseudo_header(src_addr, dst_addr, IpProtocol::Udp,
self.len() as u32),
checksum::data(&data[..self.len() as usize])
])
};
// UDP checksum value of 0 means no checksum; if the checksum really is zero,
@ -202,64 +199,54 @@ impl<T: AsRef<[u8]>> AsRef<[u8]> for Packet<T> {
/// A high-level representation of an User Datagram Protocol packet.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Repr {
pub struct Repr<'a> {
pub src_port: u16,
pub dst_port: u16,
pub payload: &'a [u8]
}
impl Repr {
impl<'a> Repr<'a> {
/// Parse an User Datagram Protocol packet and return a high-level representation.
pub fn parse<T>(
packet: &Packet<&T>,
src_addr: &IpAddress,
dst_addr: &IpAddress,
checksum_caps: &ChecksumCapabilities,
) -> Result<Repr>
where
T: AsRef<[u8]> + ?Sized,
{
pub fn parse<T>(packet: &Packet<&'a T>, src_addr: &IpAddress, dst_addr: &IpAddress,
checksum_caps: &ChecksumCapabilities) -> Result<Repr<'a>>
where T: AsRef<[u8]> + ?Sized {
// Destination port cannot be omitted (but source port can be).
if packet.dst_port() == 0 {
return Err(Error::Malformed);
}
if packet.dst_port() == 0 { return Err(Error::Malformed) }
// Valid checksum is expected...
if checksum_caps.udp.rx() && !packet.verify_checksum(src_addr, dst_addr) {
match (src_addr, dst_addr) {
// ... except on UDP-over-IPv4, where it can be omitted.
#[cfg(feature = "proto-ipv4")]
(&IpAddress::Ipv4(_), &IpAddress::Ipv4(_)) if packet.checksum() == 0 => (),
_ => return Err(Error::Checksum),
(&IpAddress::Ipv4(_), &IpAddress::Ipv4(_))
if packet.checksum() == 0 => (),
_ => {
return Err(Error::Checksum)
}
}
}
Ok(Repr {
src_port: packet.src_port(),
dst_port: packet.dst_port(),
payload: packet.payload()
})
}
/// Return the length of the packet header that will be emitted from this high-level representation.
pub fn header_len(&self) -> usize {
HEADER_LEN
/// Return the length of a packet that will be emitted from this high-level representation.
pub fn buffer_len(&self) -> usize {
field::CHECKSUM.end + self.payload.len()
}
/// Emit a high-level representation into an User Datagram Protocol packet.
pub fn emit<T: ?Sized>(
&self,
packet: &mut Packet<&mut T>,
src_addr: &IpAddress,
dst_addr: &IpAddress,
payload_len: usize,
emit_payload: impl FnOnce(&mut [u8]),
checksum_caps: &ChecksumCapabilities,
) where
T: AsRef<[u8]> + AsMut<[u8]>,
{
pub fn emit<T: ?Sized>(&self, packet: &mut Packet<&mut T>,
src_addr: &IpAddress,
dst_addr: &IpAddress,
checksum_caps: &ChecksumCapabilities)
where T: AsRef<[u8]> + AsMut<[u8]> {
packet.set_src_port(self.src_port);
packet.set_dst_port(self.dst_port);
packet.set_len((HEADER_LEN + payload_len) as u16);
emit_payload(packet.payload_mut());
packet.set_len((field::CHECKSUM.end + self.payload.len()) as u16);
packet.payload_mut().copy_from_slice(self.payload);
if checksum_caps.udp.tx() {
packet.fill_checksum(src_addr, dst_addr)
@ -274,42 +261,35 @@ impl Repr {
impl<'a, T: AsRef<[u8]> + ?Sized> fmt::Display for Packet<&'a T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// Cannot use Repr::parse because we don't have the IP addresses.
write!(
f,
"UDP src={} dst={} len={}",
self.src_port(),
self.dst_port(),
self.payload().len()
)
write!(f, "UDP src={} dst={} len={}",
self.src_port(), self.dst_port(), self.payload().len())
}
}
impl fmt::Display for Repr {
impl<'a> fmt::Display for Repr<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "UDP src={} dst={}", self.src_port, self.dst_port)
write!(f, "UDP src={} dst={} len={}",
self.src_port, self.dst_port, self.payload.len())
}
}
use crate::wire::pretty_print::{PrettyIndent, PrettyPrint};
use crate::wire::pretty_print::{PrettyPrint, PrettyIndent};
impl<T: AsRef<[u8]>> PrettyPrint for Packet<T> {
fn pretty_print(
buffer: &dyn AsRef<[u8]>,
f: &mut fmt::Formatter,
indent: &mut PrettyIndent,
) -> fmt::Result {
fn pretty_print(buffer: &dyn AsRef<[u8]>, f: &mut fmt::Formatter,
indent: &mut PrettyIndent) -> fmt::Result {
match Packet::new_checked(buffer) {
Err(err) => write!(f, "{}({})", indent, err),
Ok(packet) => write!(f, "{}{}", indent, packet),
Err(err) => write!(f, "{}({})", indent, err),
Ok(packet) => write!(f, "{}{}", indent, packet)
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[cfg(feature = "proto-ipv4")]
use crate::wire::Ipv4Address;
use super::*;
#[cfg(feature = "proto-ipv4")]
const SRC_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 1]);
@ -317,17 +297,20 @@ mod test {
const DST_ADDR: Ipv4Address = Ipv4Address([192, 168, 1, 2]);
#[cfg(feature = "proto-ipv4")]
static PACKET_BYTES: [u8; 12] = [
0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x12, 0x4d, 0xaa, 0x00, 0x00, 0xff,
];
static PACKET_BYTES: [u8; 12] =
[0xbf, 0x00, 0x00, 0x35,
0x00, 0x0c, 0x12, 0x4d,
0xaa, 0x00, 0x00, 0xff];
#[cfg(feature = "proto-ipv4")]
static NO_CHECKSUM_PACKET: [u8; 12] = [
0xbf, 0x00, 0x00, 0x35, 0x00, 0x0c, 0x00, 0x00, 0xaa, 0x00, 0x00, 0xff,
];
static NO_CHECKSUM_PACKET: [u8; 12] =
[0xbf, 0x00, 0x00, 0x35,
0x00, 0x0c, 0x00, 0x00,
0xaa, 0x00, 0x00, 0xff];
#[cfg(feature = "proto-ipv4")]
static PAYLOAD_BYTES: [u8; 4] = [0xaa, 0x00, 0x00, 0xff];
static PAYLOAD_BYTES: [u8; 4] =
[0xaa, 0x00, 0x00, 0xff];
#[test]
#[cfg(feature = "proto-ipv4")]
@ -338,7 +321,7 @@ mod test {
assert_eq!(packet.len(), 12);
assert_eq!(packet.checksum(), 0x124d);
assert_eq!(packet.payload(), &PAYLOAD_BYTES[..]);
assert!(packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()));
assert_eq!(packet.verify_checksum(&SRC_ADDR.into(), &DST_ADDR.into()), true);
}
#[test]
@ -376,10 +359,11 @@ mod test {
}
#[cfg(feature = "proto-ipv4")]
fn packet_repr() -> Repr {
fn packet_repr() -> Repr<'static> {
Repr {
src_port: 48896,
dst_port: 53,
payload: &PAYLOAD_BYTES
}
}
@ -387,13 +371,8 @@ mod test {
#[cfg(feature = "proto-ipv4")]
fn test_parse() {
let packet = Packet::new_unchecked(&PACKET_BYTES[..]);
let repr = Repr::parse(
&packet,
&SRC_ADDR.into(),
&DST_ADDR.into(),
&ChecksumCapabilities::default(),
)
.unwrap();
let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into(),
&ChecksumCapabilities::default()).unwrap();
assert_eq!(repr, packet_repr());
}
@ -401,16 +380,10 @@ mod test {
#[cfg(feature = "proto-ipv4")]
fn test_emit() {
let repr = packet_repr();
let mut bytes = vec![0xa5; repr.header_len() + PAYLOAD_BYTES.len()];
let mut bytes = vec![0xa5; repr.buffer_len()];
let mut packet = Packet::new_unchecked(&mut bytes);
repr.emit(
&mut packet,
&SRC_ADDR.into(),
&DST_ADDR.into(),
PAYLOAD_BYTES.len(),
|payload| payload.copy_from_slice(&PAYLOAD_BYTES),
&ChecksumCapabilities::default(),
);
repr.emit(&mut packet, &SRC_ADDR.into(), &DST_ADDR.into(),
&ChecksumCapabilities::default());
assert_eq!(&packet.into_inner()[..], &PACKET_BYTES[..]);
}
@ -418,13 +391,8 @@ mod test {
#[cfg(feature = "proto-ipv4")]
fn test_checksum_omitted() {
let packet = Packet::new_unchecked(&NO_CHECKSUM_PACKET[..]);
let repr = Repr::parse(
&packet,
&SRC_ADDR.into(),
&DST_ADDR.into(),
&ChecksumCapabilities::default(),
)
.unwrap();
let repr = Repr::parse(&packet, &SRC_ADDR.into(), &DST_ADDR.into(),
&ChecksumCapabilities::default()).unwrap();
assert_eq!(repr, packet_repr());
}
}

View File

@ -1,24 +1,25 @@
use getopts::Options;
use std::cell::RefCell;
use std::io::{self, Read, Write};
use std::path::Path;
use std::fs::File;
use std::env;
use std::process::exit;
use smoltcp::phy::{PcapLinkType, PcapSink};
use smoltcp::time::Instant;
use std::env;
use std::fs::File;
use std::io::{self, Read};
use std::path::Path;
use std::process::exit;
use getopts::Options;
fn convert(
packet_filename: &Path,
pcap_filename: &Path,
link_type: PcapLinkType,
) -> io::Result<()> {
fn convert(packet_filename: &Path, pcap_filename: &Path, link_type: PcapLinkType)
-> io::Result<()> {
let mut packet_file = File::open(packet_filename)?;
let mut packet = Vec::new();
packet_file.read_to_end(&mut packet)?;
let pcap = RefCell::new(Vec::new());
PcapSink::global_header(&pcap, link_type);
PcapSink::packet(&pcap, Instant::from_millis(0), &packet[..]);
let mut pcap_file = File::create(pcap_filename)?;
PcapSink::global_header(&mut pcap_file, link_type);
PcapSink::packet(&mut pcap_file, Instant::from_millis(0), &packet[..]);
pcap_file.write_all(&pcap.borrow()[..])?;
Ok(())
}
@ -34,37 +35,31 @@ fn main() {
let mut opts = Options::new();
opts.optflag("h", "help", "print this help menu");
opts.optopt(
"t",
"link-type",
"set link type (one of: ethernet ip)",
"TYPE",
);
opts.optopt("t", "link-type", "set link type (one of: ethernet ip)", "TYPE");
let matches = match opts.parse(&args[1..]) {
Ok(m) => m,
Err(e) => {
eprintln!("{}", e);
return;
return
}
};
let link_type = match matches.opt_str("t").as_ref().map(|s| &s[..]) {
Some("ethernet") => Some(PcapLinkType::Ethernet),
Some("ip") => Some(PcapLinkType::Ip),
_ => None,
};
let link_type =
match matches.opt_str("t").as_ref().map(|s| &s[..]) {
Some("ethernet") => Some(PcapLinkType::Ethernet),
Some("ip") => Some(PcapLinkType::Ip),
_ => None
};
if matches.opt_present("h") || matches.free.len() != 2 || link_type.is_none() {
print_usage(&program, opts);
return;
return
}
match convert(
Path::new(&matches.free[0]),
Path::new(&matches.free[1]),
link_type.unwrap(),
) {
match convert(Path::new(&matches.free[0]),
Path::new(&matches.free[1]),
link_type.unwrap()) {
Ok(()) => (),
Err(e) => {
eprintln!("Cannot convert packet to pcap: {}", e);