1# smoltcp 2 3[![docs.rs](https://docs.rs/smoltcp/badge.svg)](https://docs.rs/smoltcp) 4[![crates.io](https://img.shields.io/crates/v/smoltcp.svg)](https://crates.io/crates/smoltcp) 5[![crates.io](https://img.shields.io/crates/d/smoltcp.svg)](https://crates.io/crates/smoltcp) 6[![crates.io](https://img.shields.io/matrix/smoltcp:matrix.org)](https://matrix.to/#/#smoltcp:matrix.org) 7 8_smoltcp_ is a standalone, event-driven TCP/IP stack that is designed for bare-metal, 9real-time systems. Its design goals are simplicity and robustness. Its design anti-goals 10include complicated compile-time computations, such as macro or type tricks, even 11at cost of performance degradation. 12 13_smoltcp_ does not need heap allocation *at all*, is [extensively documented][docs], 14and compiles on stable Rust 1.65 and later. 15 16_smoltcp_ achieves [~Gbps of throughput](#examplesbenchmarkrs) when tested against 17the Linux TCP stack in loopback mode. 18 19[docs]: https://docs.rs/smoltcp/ 20 21## Features 22 23_smoltcp_ is missing many widely deployed features, usually because no one implemented them yet. 24To set expectations right, both implemented and omitted features are listed. 25 26### Media layer 27 28There are 3 supported mediums. 29 30* Ethernet 31 * Regular Ethernet II frames are supported. 32 * Unicast, broadcast and multicast packets are supported. 33 * ARP packets (including gratuitous requests and replies) are supported. 34 * ARP requests are sent at a rate not exceeding one per second. 35 * Cached ARP entries expire after one minute. 36 * 802.3 frames and 802.1Q are **not** supported. 37 * Jumbo frames are **not** supported. 38* IP 39 * Unicast, broadcast and multicast packets are supported. 40* IEEE 802.15.4 + 6LoWPAN (experimental) 41 * Unicast, broadcast and multicast packets are supported. 42 * ONLY UDP packets are supported. 43 44### IP layer 45 46#### IPv4 47 48 * IPv4 header checksum is generated and validated. 49 * IPv4 time-to-live value is configurable per socket, set to 64 by default. 50 * IPv4 default gateway is supported. 51 * Routing outgoing IPv4 packets is supported, through a default gateway or a CIDR route table. 52 * IPv4 fragmentation and reassembly is supported. 53 * IPv4 options are **not** supported and are silently ignored. 54 55#### IPv6 56 57 * IPv6 hop-limit value is configurable per socket, set to 64 by default. 58 * Routing outgoing IPv6 packets is supported, through a default gateway or a CIDR route table. 59 * IPv6 hop-by-hop header is supported. 60 * ICMPv6 parameter problem message is generated in response to an unrecognized IPv6 next header. 61 * ICMPv6 parameter problem message is **not** generated in response to an unknown IPv6 62 hop-by-hop option. 63 64### IP multicast 65 66#### IGMP 67 68The IGMPv1 and IGMPv2 protocols are supported, and IPv4 multicast is available. 69 70 * Membership reports are sent in response to membership queries at 71 equal intervals equal to the maximum response time divided by the 72 number of groups to be reported. 73 74### ICMP layer 75 76#### ICMPv4 77 78The ICMPv4 protocol is supported, and ICMP sockets are available. 79 80 * ICMPv4 header checksum is supported. 81 * ICMPv4 echo replies are generated in response to echo requests. 82 * ICMP sockets can listen to ICMPv4 Port Unreachable messages, or any ICMPv4 messages with 83 a given IPv4 identifier field. 84 * ICMPv4 protocol unreachable messages are **not** passed to higher layers when received. 85 * ICMPv4 parameter problem messages are **not** generated. 86 87#### ICMPv6 88 89The ICMPv6 protocol is supported, and ICMP sockets are available. 90 91 * ICMPv6 header checksum is supported. 92 * ICMPv6 echo replies are generated in response to echo requests. 93 * ICMPv6 protocol unreachable messages are **not** passed to higher layers when received. 94 95#### NDISC 96 97 * Neighbor Advertisement messages are generated in response to Neighbor Solicitations. 98 * Router Advertisement messages are **not** generated or read. 99 * Router Solicitation messages are **not** generated or read. 100 * Redirected Header messages are **not** generated or read. 101 102### UDP layer 103 104The UDP protocol is supported over IPv4 and IPv6, and UDP sockets are available. 105 106 * Header checksum is always generated and validated. 107 * In response to a packet arriving at a port without a listening socket, 108 an ICMP destination unreachable message is generated. 109 110### TCP layer 111 112The TCP protocol is supported over IPv4 and IPv6, and server and client TCP sockets are available. 113 114 * Header checksum is generated and validated. 115 * Maximum segment size is negotiated. 116 * Window scaling is negotiated. 117 * Multiple packets are transmitted without waiting for an acknowledgement. 118 * Reassembly of out-of-order segments is supported, with no more than 4 or 32 gaps in sequence space. 119 * Keep-alive packets may be sent at a configurable interval. 120 * Retransmission timeout starts at at an estimate of RTT, and doubles every time. 121 * Time-wait timeout has a fixed interval of 10 s. 122 * User timeout has a configurable interval. 123 * Delayed acknowledgements are supported, with configurable delay. 124 * Nagle's algorithm is implemented. 125 * Selective acknowledgements are **not** implemented. 126 * Silly window syndrome avoidance is **not** implemented. 127 * Congestion control is **not** implemented. 128 * Timestamping is **not** supported. 129 * Urgent pointer is **ignored**. 130 * Probing Zero Windows is **not** implemented. 131 * Packetization Layer Path MTU Discovery [PLPMTU](https://tools.ietf.org/rfc/rfc4821.txt) is **not** implemented. 132 133## Installation 134 135To use the _smoltcp_ library in your project, add the following to `Cargo.toml`: 136 137```toml 138[dependencies] 139smoltcp = "0.8.0" 140``` 141 142The default configuration assumes a hosted environment, for ease of evaluation. 143You probably want to disable default features and configure them one by one: 144 145```toml 146[dependencies] 147smoltcp = { version = "0.8.0", default-features = false, features = ["log"] } 148``` 149 150## Feature flags 151 152### Feature `std` 153 154The `std` feature enables use of objects and slices owned by the networking stack through a 155dependency on `std::boxed::Box` and `std::vec::Vec`. 156 157This feature is enabled by default. 158 159### Feature `alloc` 160 161The `alloc` feature enables use of objects owned by the networking stack through a dependency 162on collections from the `alloc` crate. This only works on nightly rustc. 163 164This feature is disabled by default. 165 166### Feature `log` 167 168The `log` feature enables logging of events within the networking stack through 169the [log crate][log]. Normal events (e.g. buffer level or TCP state changes) are emitted with 170the TRACE log level. Exceptional events (e.g. malformed packets) are emitted with 171the DEBUG log level. 172 173[log]: https://crates.io/crates/log 174 175This feature is enabled by default. 176 177### Feature `defmt` 178 179The `defmt` feature enables logging of events with the [defmt crate][defmt]. 180 181[defmt]: https://crates.io/crates/defmt 182 183This feature is disabled by default, and cannot be used at the same time as `log`. 184 185### Feature `verbose` 186 187The `verbose` feature enables logging of events where the logging itself may incur very high 188overhead. For example, emitting a log line every time an application reads or writes as little 189as 1 octet from a socket is likely to overwhelm the application logic unless a `BufReader` 190or `BufWriter` is used, which are of course not available on heap-less systems. 191 192This feature is disabled by default. 193 194### Features `phy-raw_socket` and `phy-tuntap_interface` 195 196Enable `smoltcp::phy::RawSocket` and `smoltcp::phy::TunTapInterface`, respectively. 197 198These features are enabled by default. 199 200### Features `socket-raw`, `socket-udp`, `socket-tcp`, `socket-icmp`, `socket-dhcpv4`, `socket-dns` 201 202Enable the corresponding socket type. 203 204These features are enabled by default. 205 206### Features `proto-ipv4` and `proto-ipv6` 207 208Enable [IPv4] and [IPv6] respectively. 209 210[IPv4]: https://tools.ietf.org/rfc/rfc791.txt 211[IPv6]: https://tools.ietf.org/rfc/rfc8200.txt 212 213## Configuration 214 215_smoltcp_ has some configuration settings that are set at compile time, affecting sizes 216and counts of buffers. 217 218They can be set in two ways: 219 220- Via Cargo features: enable a feature like `<name>-<value>`. `name` must be in lowercase and 221use dashes instead of underscores. For example. `iface-max-addr-count-3`. Only a selection of values 222is available, check `Cargo.toml` for the list. 223- Via environment variables at build time: set the variable named `SMOLTCP_<value>`. For example 224`SMOLTCP_IFACE_MAX_ADDR_COUNT=3 cargo build`. You can also set them in the `[env]` section of `.cargo/config.toml`. 225Any value can be set, unlike with Cargo features. 226 227Environment variables take precedence over Cargo features. If two Cargo features are enabled for the same setting 228with different values, compilation fails. 229 230### `IFACE_MAX_ADDR_COUNT` 231 232Max amount of IP addresses that can be assigned to one interface (counting both IPv4 and IPv6 addresses). Default: 2. 233 234### `IFACE_MAX_MULTICAST_GROUP_COUNT` 235 236Max amount of multicast groups that can be joined by one interface. Default: 4. 237 238### `IFACE_MAX_SIXLOWPAN_ADDRESS_CONTEXT_COUNT` 239 240Max amount of 6LoWPAN address contexts that can be assigned to one interface. Default: 4. 241 242### `IFACE_NEIGHBOR_CACHE_COUNT` 243 244Amount of "IP address -> hardware address" entries the neighbor cache (also known as the "ARP cache" or the "ARP table") holds. Default: 4. 245 246### `IFACE_MAX_ROUTE_COUNT` 247 248Max amount of routes that can be added to one interface. Includes the default route. Includes both IPv4 and IPv6. Default: 2. 249 250### `FRAGMENTATION_BUFFER_SIZE` 251 252Size of the buffer used for fragmenting outgoing packets larger than the MTU. Packets larger than this setting will be dropped instead of fragmented. Default: 1500. 253 254### `ASSEMBLER_MAX_SEGMENT_COUNT` 255 256Maximum number of non-contiguous segments the assembler can hold. Used for both packet reassembly and TCP stream reassembly. Default: 4. 257 258### `REASSEMBLY_BUFFER_SIZE` 259 260Size of the buffer used for reassembling (de-fragmenting) incoming packets. If the reassembled packet is larger than this setting, it will be dropped instead of reassembled. Default: 1500. 261 262### `REASSEMBLY_BUFFER_COUNT` 263 264Number of reassembly buffers, i.e how many different incoming packets can be reassembled at the same time. Default: 1. 265 266### `DNS_MAX_RESULT_COUNT` 267 268Maximum amount of address results for a given DNS query that will be kept. For example, if this is set to 2 and the queried name has 4 `A` records, only the first 2 will be returned. Default: 1. 269 270### `DNS_MAX_SERVER_COUNT` 271 272Maximum amount of DNS servers that can be configured in one DNS socket. Default: 1. 273 274### `DNS_MAX_NAME_SIZE` 275 276Maximum length of DNS names that can be queried. Default: 255. 277 278 279 280## Hosted usage examples 281 282_smoltcp_, being a freestanding networking stack, needs to be able to transmit and receive 283raw frames. For testing purposes, we will use a regular OS, and run _smoltcp_ in 284a userspace process. Only Linux is supported (right now). 285 286On \*nix OSes, transmitting and receiving raw frames normally requires superuser privileges, but 287on Linux it is possible to create a _persistent tap interface_ that can be manipulated by 288a specific user: 289 290```sh 291sudo ip tuntap add name tap0 mode tap user $USER 292sudo ip link set tap0 up 293sudo ip addr add 192.168.69.100/24 dev tap0 294sudo ip -6 addr add fe80::100/64 dev tap0 295sudo ip -6 addr add fdaa::100/64 dev tap0 296sudo ip -6 route add fe80::/64 dev tap0 297sudo ip -6 route add fdaa::/64 dev tap0 298``` 299 300It's possible to let _smoltcp_ access Internet by enabling routing for the tap interface: 301 302```sh 303sudo iptables -t nat -A POSTROUTING -s 192.168.69.0/24 -j MASQUERADE 304sudo sysctl net.ipv4.ip_forward=1 305sudo ip6tables -t nat -A POSTROUTING -s fdaa::/64 -j MASQUERADE 306sudo sysctl -w net.ipv6.conf.all.forwarding=1 307 308# Some distros have a default policy of DROP. This allows the traffic. 309sudo iptables -A FORWARD -i tap0 -s 192.168.69.0/24 -j ACCEPT 310sudo iptables -A FORWARD -o tap0 -d 192.168.69.0/24 -j ACCEPT 311``` 312 313### Bridged connection 314 315Instead of the routed connection above, you may also set up a bridged (switched) 316connection. This will make smoltcp speak directly to your LAN, with real ARP, etc. 317It is needed to run the DHCP example. 318 319NOTE: In this case, the examples' IP configuration must match your LAN's! 320 321NOTE: this ONLY works with actual wired Ethernet connections. It 322will NOT work on a WiFi connection. 323 324```sh 325# Replace with your wired Ethernet interface name 326ETH=enp0s20f0u1u1 327 328sudo modprobe bridge 329sudo modprobe br_netfilter 330 331sudo sysctl -w net.bridge.bridge-nf-call-arptables=0 332sudo sysctl -w net.bridge.bridge-nf-call-ip6tables=0 333sudo sysctl -w net.bridge.bridge-nf-call-iptables=0 334 335sudo ip tuntap add name tap0 mode tap user $USER 336sudo brctl addbr br0 337sudo brctl addif br0 tap0 338sudo brctl addif br0 $ETH 339sudo ip link set tap0 up 340sudo ip link set $ETH up 341sudo ip link set br0 up 342 343# This connects your host system to the internet, so you can use it 344# at the same time you run the examples. 345sudo dhcpcd br0 346``` 347 348To tear down: 349 350``` 351sudo killall dhcpcd 352sudo ip link set br0 down 353sudo brctl delbr br0 354``` 355 356### Fault injection 357 358In order to demonstrate the response of _smoltcp_ to adverse network conditions, all examples 359implement fault injection, available through command-line options: 360 361 * The `--drop-chance` option randomly drops packets, with given probability in percents. 362 * The `--corrupt-chance` option randomly mutates one octet in a packet, with given 363 probability in percents. 364 * The `--size-limit` option drops packets larger than specified size. 365 * The `--tx-rate-limit` and `--rx-rate-limit` options set the amount of tokens for 366 a token bucket rate limiter, in packets per bucket. 367 * The `--shaping-interval` option sets the refill interval of a token bucket rate limiter, 368 in milliseconds. 369 370A good starting value for `--drop-chance` and `--corrupt-chance` is 15%. A good starting 371value for `--?x-rate-limit` is 4 and `--shaping-interval` is 50 ms. 372 373Note that packets dropped by the fault injector still get traced; 374the `rx: randomly dropping a packet` message indicates that the packet *above* it got dropped, 375and the `tx: randomly dropping a packet` message indicates that the packet *below* it was. 376 377### Packet dumps 378 379All examples provide a `--pcap` option that writes a [libpcap] file containing a view of every 380packet as it is seen by _smoltcp_. 381 382[libpcap]: https://wiki.wireshark.org/Development/LibpcapFileFormat 383 384### examples/tcpdump.rs 385 386_examples/tcpdump.rs_ is a tiny clone of the _tcpdump_ utility. 387 388Unlike the rest of the examples, it uses raw sockets, and so it can be used on regular interfaces, 389e.g. `eth0` or `wlan0`, as well as the `tap0` interface we've created above. 390 391Read its [source code](/examples/tcpdump.rs), then run it as: 392 393```sh 394cargo build --example tcpdump 395sudo ./target/debug/examples/tcpdump eth0 396``` 397 398### examples/httpclient.rs 399 400_examples/httpclient.rs_ emulates a network host that can initiate HTTP requests. 401 402The host is assigned the hardware address `02-00-00-00-00-02`, IPv4 address `192.168.69.1`, and IPv6 address `fdaa::1`. 403 404Read its [source code](/examples/httpclient.rs), then run it as: 405 406```sh 407cargo run --example httpclient -- --tap tap0 ADDRESS URL 408``` 409 410For example: 411 412```sh 413cargo run --example httpclient -- --tap tap0 93.184.216.34 http://example.org/ 414``` 415 416or: 417 418```sh 419cargo run --example httpclient -- --tap tap0 2606:2800:220:1:248:1893:25c8:1946 http://example.org/ 420``` 421 422It connects to the given address (not a hostname) and URL, and prints any returned response data. 423The TCP socket buffers are limited to 1024 bytes to make packet traces more interesting. 424 425### examples/ping.rs 426 427_examples/ping.rs_ implements a minimal version of the `ping` utility using raw sockets. 428 429The host is assigned the hardware address `02-00-00-00-00-02` and IPv4 address `192.168.69.1`. 430 431Read its [source code](/examples/ping.rs), then run it as: 432 433```sh 434cargo run --example ping -- --tap tap0 ADDRESS 435``` 436 437It sends a series of 4 ICMP ECHO\_REQUEST packets to the given address at one second intervals and 438prints out a status line on each valid ECHO\_RESPONSE received. 439 440The first ECHO\_REQUEST packet is expected to be lost since arp\_cache is empty after startup; 441the ECHO\_REQUEST packet is dropped and an ARP request is sent instead. 442 443Currently, netmasks are not implemented, and so the only address this example can reach 444is the other endpoint of the tap interface, `192.168.69.100`. It cannot reach itself because 445packets entering a tap interface do not loop back. 446 447### examples/server.rs 448 449_examples/server.rs_ emulates a network host that can respond to basic requests. 450 451The host is assigned the hardware address `02-00-00-00-00-01` and IPv4 address `192.168.69.1`. 452 453Read its [source code](/examples/server.rs), then run it as: 454 455```sh 456cargo run --example server -- --tap tap0 457``` 458 459It responds to: 460 461 * pings (`ping 192.168.69.1`); 462 * UDP packets on port 6969 (`socat stdio udp4-connect:192.168.69.1:6969 <<<"abcdefg"`), 463 where it will respond with reversed chunks of the input indefinitely; 464 * TCP connections on port 6969 (`socat stdio tcp4-connect:192.168.69.1:6969`), 465 where it will respond "hello" to any incoming connection and immediately close it; 466 * TCP connections on port 6970 (`socat stdio tcp4-connect:192.168.69.1:6970 <<<"abcdefg"`), 467 where it will respond with reversed chunks of the input indefinitely. 468 * TCP connections on port 6971 (`socat stdio tcp4-connect:192.168.69.1:6971 </dev/urandom`), 469 which will sink data. Also, keep-alive packets (every 1 s) and a user timeout (at 2 s) 470 are enabled on this port; try to trigger them using fault injection. 471 * TCP connections on port 6972 (`socat stdio tcp4-connect:192.168.69.1:6972 >/dev/null`), 472 which will source data. 473 474Except for the socket on port 6971. the buffers are only 64 bytes long, for convenience 475of testing resource exhaustion conditions. 476 477### examples/client.rs 478 479_examples/client.rs_ emulates a network host that can initiate basic requests. 480 481The host is assigned the hardware address `02-00-00-00-00-02` and IPv4 address `192.168.69.2`. 482 483Read its [source code](/examples/client.rs), then run it as: 484 485```sh 486cargo run --example client -- --tap tap0 ADDRESS PORT 487``` 488 489It connects to the given address (not a hostname) and port (e.g. `socat stdio tcp4-listen:1234`), 490and will respond with reversed chunks of the input indefinitely. 491 492### examples/benchmark.rs 493 494_examples/benchmark.rs_ implements a simple throughput benchmark. 495 496Read its [source code](/examples/benchmark.rs), then run it as: 497 498```sh 499cargo run --release --example benchmark -- --tap tap0 [reader|writer] 500``` 501 502It establishes a connection to itself from a different thread and reads or writes a large amount 503of data in one direction. 504 505A typical result (achieved on a Intel Core i7-7500U CPU and a Linux 4.9.65 x86_64 kernel running 506on a Dell XPS 13 9360 laptop) is as follows: 507 508``` 509$ cargo run -q --release --example benchmark -- --tap tap0 reader 510throughput: 2.556 Gbps 511$ cargo run -q --release --example benchmark -- --tap tap0 writer 512throughput: 5.301 Gbps 513``` 514 515## Bare-metal usage examples 516 517Examples that use no services from the host OS are necessarily less illustrative than examples 518that do. Because of this, only one such example is provided. 519 520### examples/loopback.rs 521 522_examples/loopback.rs_ sets up _smoltcp_ to talk with itself via a loopback interface. 523Although it does not require `std`, this example still requires the `alloc` feature to run, as well as `log`, `proto-ipv4` and `socket-tcp`. 524 525Read its [source code](/examples/loopback.rs), then run it without `std`: 526 527```sh 528cargo run --example loopback --no-default-features --features="log proto-ipv4 socket-tcp alloc" 529``` 530 531... or with `std` (in this case the features don't have to be explicitly listed): 532 533```sh 534cargo run --example loopback -- --pcap loopback.pcap 535``` 536 537It opens a server and a client TCP socket, and transfers a chunk of data. You can examine 538the packet exchange by opening `loopback.pcap` in [Wireshark]. 539 540If the `std` feature is enabled, it will print logs and packet dumps, and fault injection 541is possible; otherwise, nothing at all will be displayed and no options are accepted. 542 543[wireshark]: https://wireshark.org 544 545## License 546 547_smoltcp_ is distributed under the terms of 0-clause BSD license. 548 549See [LICENSE-0BSD](LICENSE-0BSD.txt) for details. 550