netsim/
lib.rs

1/*!
2`netsim` is a library for testing networking code. It allows you to run tests in network-isolated
3threads, on virtual network interfaces, in parallel, without tests interfering with each other or
4with your actual network. You can also connect these isolated environments to each other
5to create simulated networks on which you can capture and inject packets.
6
7### Example 1: Isolating tests.
8
9Suppose you have multiple tests that need to bind to the same port for some reason. By using
10`#[netsim::isolate]` you can run your test suite without having to use `--test-threads=1` and
11without having to stop any daemons running on your dev machine.
12
13```no_run
14#[test]
15#[netsim::isolate]
16fn a_test() {
17    let _listener = std::net::TcpListener::bind("0.0.0.0:80").unwrap();
18}
19
20#[test]
21#[netsim::isolate]
22fn another_test_that_runs_in_parallel() {
23    let _listener = std::net::TcpListener::bind("0.0.0.0:80").unwrap();
24}
25```
26
27### Example 2: Capturing a packet.
28
29The `#[netsim::isolate]` attribute showcased above is just a convenient way to setup a `Machine`
30and spawn a task onto it. To capture a packet you'll need to do these steps yourself. You'll also
31need to give the machine a network interface to send the packet on.
32
33In this example we create a UDP socket and use it to send the message "hello" towards an arbitary
34address. The packet then arrives on our `IpIface`, hoping to be routed somewhere, and we can check
35that it still contains the correct message.
36
37```rust
38# use {
39#     std::net::SocketAddrV4,
40#     netsim::{
41#         Machine,
42#         packet::{IpPacketVersion, Ipv4PacketProtocol},
43#     },
44#     tokio::net::UdpSocket,
45#     futures::prelude::stream::StreamExt,
46# };
47# #[tokio::main]
48# async fn main() {
49let local_addr: SocketAddrV4 = "10.1.2.3:5555".parse().unwrap();
50let remote_addr: SocketAddrV4 = "1.1.1.1:53".parse().unwrap();
51let machine = Machine::new().unwrap();
52let mut iface = {
53    machine
54    .add_ip_iface()
55    .ipv4_addr(*local_addr.ip())
56    .ipv4_default_route()
57    .build()
58    .unwrap()
59};
60machine.spawn(async move {
61    let socket = UdpSocket::bind(local_addr).await.unwrap();
62    socket.send_to(b"hello", remote_addr).await.unwrap();
63}).await.unwrap();
64
65let packet = loop {
66    let packet = iface.next().await.unwrap().unwrap();
67    let IpPacketVersion::V4(packet) = packet.version_box() else { continue };
68    let Ipv4PacketProtocol::Udp(packet) = packet.protocol_box() else { continue };
69    break packet;
70};
71assert_eq!(packet.data(), b"hello");
72# }
73```
74
75### More, longer examples.
76
77Check out the [examples](https://github.com/canndrew/netsim-ng/tree/master/examples) directory in
78the repo.
79
80*/
81
82#![allow(clippy::let_unit_value)]
83
84extern crate self as netsim;
85
86mod priv_prelude;
87mod namespace;
88mod machine;
89mod iface;
90mod ioctl;
91mod network;
92mod connect;
93mod stream_ext;
94pub mod adapter;
95pub mod device;
96pub mod packet;
97mod sys;
98
99pub use {
100    machine::{Machine, JoinHandle},
101    iface::{
102        create::IpIfaceBuilder,
103        stream::{IpIface, IpSinkStream},
104    },
105    connect::connect,
106    network::{Ipv4Network, Ipv6Network, NetworkParseError, Ipv4NetworkIter, Ipv6NetworkIter},
107    netsim_macros::{ipv4_network, ipv6_network, isolate},
108    stream_ext::SinkStreamExt,
109    tokio,
110};
111
112#[cfg(test)]
113mod tests;
114