17 个版本

使用旧版 Rust 2015

0.2.5 2018 年 8 月 13 日
0.2.4 2018 年 8 月 8 日
0.2.3 2018 年 7 月 26 日
0.2.2 2018 年 6 月 6 日
0.1.2 2018 年 2 月 27 日

#2020网络编程

Download history 1/week @ 2024-02-14 16/week @ 2024-02-21 5/week @ 2024-02-28 1/week @ 2024-03-06 10/week @ 2024-03-13 26/week @ 2024-03-27 32/week @ 2024-04-03

58 每月下载次数

MIT 或 BSD-3-ClauseGPL-2.0 许可

365KB
8K SLoC

netsim - Rust 网络模拟和测试库(目前仅支持 Linux)。

netsim 是一个用于模拟网络以测试面向网络的 Rust 代码的 crate。您可以使用它运行在隔离的容器中的 Rust 函数,并为这些函数组装虚拟网络以进行通信。

依赖项

POSIX 功能库

$ apt install libcap-dev
$ dnf install libcap-devel # Fedora

在隔离的网络命名空间中启动线程

网络命名空间是 Linux 的一个功能,可以为线程或进程提供其自己的系统网络接口和路由表视图。此 crate 的 spawn 模块提供了用于在它们自己的网络命名空间中启动线程的 new_namespace 函数。在这个演示中,我们使用 get_if_addrs crate 列出可见的网络接口。

extern crate netsim;
extern crate get_if_addrs;
extern crate tokio_core;
use netsim::spawn;
use tokio_core::reactor::Core;
use get_if_addrs::get_if_addrs;

// First, check that there is more than one network interface. This will generally be true
// since there will at least be the loopback interface.
let interfaces = get_if_addrs().unwrap();
assert!(interfaces.len() > 0);

// Now check how many network interfaces we can see inside a fresh network namespace. There
// should be zero.
let spawn_complete = spawn::new_namespace(|| {
    get_if_addrs().unwrap()
});
let mut core = Core::new().unwrap();
let interfaces = core.run(spawn_complete).unwrap();
assert!(interfaces.is_empty());

这演示了如何将线程启动到沙盒环境中 - 例如运行自动化测试 - 但是没有网络接口的环境相当无用了...

创建虚拟接口

我们可以使用 iface 模块中的类型创建虚拟 IP 和以太网接口。例如,IpIface 允许您创建新的 IP(TUN)接口,并实现了 futures::{Stream, Sink},这样您就可以读取/写入原始数据包。

extern crate netsim;
extern crate tokio_core;
extern crate futures;

use std::net::Ipv4Addr;
use tokio_core::reactor::Core;
use futures::{Future, Stream};
use netsim::iface::IpIfaceBuilder;
use netsim::spawn;

let mut core = Core::new().unwrap();
let handle = core.handle();

// Create a network interface named "netsim"
// Note: This will likely fail with "permission denied" unless we run it in a fresh network
// environment
let iface = {
    IpIfaceBuilder::new()
    .name("netsim")
    .ipv4_addr(Ipv4Addr::new(192, 168, 0, 24), 24)
    .build(&handle)
    .unwrap()
};

// Read the first `Ipv4Packet` sent from the interface.
let packet = core.run({
    iface
    .into_future()
    .map_err(|(e, _)| e)
    .map(|(packet_opt, _)| packet_opt.unwrap())
}).unwrap();

然而,对于简单测试网络代码,您不需要像这样手动创建接口。

隔离网络代码

而不是单独执行上述两个步骤,您可以使用 spawn::ipv4_tree 函数以及 node 模块为您设置一个具有 IPv4 接口的命名空间。

extern crate netsim;
extern crate tokio_core;
extern crate futures;

use std::net::UdpSocket;
use tokio_core::reactor::Core;
use futures::{Future, Stream};
use netsim::{spawn, node, Network, Ipv4Range};
use netsim::wire::Ipv4Payload;

// Create an event loop and a network to bind devices to.
let mut core = Core::new().unwrap();
let network = Network::new(&core.handle());
let handle = network.handle();

// Spawn a network with a single node - a machine with an IPv4 interface in the 10.0.0.0/8
// range, running the given callback.
let (spawn_complete, ipv4_plug) = spawn::ipv4_tree(
    &handle,
    Ipv4Range::local_subnet_10(),
    node::ipv4::machine(|ipv4_addr| {
        // Send a packet out the interface
        let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
        socket.send_to(b"hello world", "10.1.2.3:4567").unwrap();
    }),
);

let (packet_tx, packet_rx) = ipv4_plug.split();

// Inspect the packet sent out the interface.
core.run({
    packet_rx
    .into_future()
    .map(|(packet_opt, _)| {
        let packet = packet_opt.unwrap();
        match packet.payload() {
            Ipv4Payload::Udp(udp) => {
                assert_eq!(&udp.payload()[..], &b"hello world"[..]);
            },
            _ => panic!("unexpected payload"),
        }
    })
}).unwrap()

模拟通信节点的网络

使用 spawnnode 模块,您可以设置一些通过虚拟网络连接的节点。

extern crate tokio_core;
extern crate future_utils;
extern crate netsim;

use std::net::UdpSocket;
use tokio_core::reactor::Core;
use netsim::{spawn, node, Network, Ipv4Range};

// Create an event loop and a network to bind devices to.
let mut core = Core::new().unwrap();
let network = Network::new(&core.handle());
let handle = network.handle();

let (tx, rx) = std::sync::mpsc::channel();

// Create a machine which will receive a UDP packet and return its contents
let receiver_node = node::ipv4::machine(move |ipv4_addr| {
    let socket = UdpSocket::bind(("0.0.0.0", 1234)).unwrap();
    /// Tell the sending node our IP address
    tx.send(ipv4_addr).unwrap();
    let mut buffer = [0; 1024];
    let (n, _sender_addr) = socket.recv_from(&mut buffer).unwrap();
    buffer[..n].to_owned()
});

// Create the machine which will send the UDP packet
let sender_node = node::ipv4::machine(move |_ipv4_addr| {
    let receiver_ip = rx.recv().unwrap();
    let socket = UdpSocket::bind("0.0.0.0:0").unwrap();
    socket.send_to(b"hello world", (receiver_ip, 1234)).unwrap();
});

// Connect the sending and receiving nodes via a router
let router_node = node::ipv4::router((receiver_node, sender_node));

// Run the network with the router as the top-most node. `_plug` could be used send/receive
// packets from/to outside the network
let (spawn_complete, _plug) = spawn::ipv4_tree(&handle, Ipv4Range::global(), router_node);

// Drive the network on the event loop and get the data returned by the receiving node.
let (received, ()) = core.run(spawn_complete).unwrap();
assert_eq!(&received[..], b"hello world");

其余部分

可以通过直接使用device模块中的原语来设置更复杂的(非层次结构)网络拓扑、以太网网络、具有多个接口的命名空间等。请探索API,如果任何内容需要解释或可以改进,请通过错误跟踪器告诉我们:)

许可证

此库根据您的选择在修改后的BSD许可证(LICENSE-BSD https://opensource.org/licenses/BSD-3-Clause)或MIT许可证(LICENSE-MIT https://opensource.org/licenses/MIT)下双授权。

依赖项

~14MB
~255K SLoC