Initial commit
This commit is contained in:
commit
32f496d3d0
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
49
2
Normal file
49
2
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
use std::net::Ipv4Addr;
|
||||||
|
|
||||||
|
use tun_tap::Mode;
|
||||||
|
|
||||||
|
use crate::ethernet::{EtherType, EthernetFrame, InvalidEtherType, MacAddr};
|
||||||
|
|
||||||
|
pub struct Iface {
|
||||||
|
iface: tun_tap::Iface,
|
||||||
|
mac: MacAddr,
|
||||||
|
ip: Option<Ipv4Addr>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iface {
|
||||||
|
pub fn new() -> Iface {
|
||||||
|
Self {
|
||||||
|
iface: tun_tap::Iface::without_packet_info("tap0", Mode::Tap).unwrap(),
|
||||||
|
mac: MacAddr::new([0x9A, 0xB1, 0xB5, 0x22, 0x38, 0xAB]),
|
||||||
|
ip: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn recv(&self) -> Result<EthernetFrame, InvalidEtherType> {
|
||||||
|
let mut frame_buf = [0u8; 2048];
|
||||||
|
let frame_len = self.iface.recv(&mut frame_buf).unwrap();
|
||||||
|
EthernetFrame::parse(&frame_buf[0..frame_len])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mac_to_self(&self, mac: MacAddr) -> bool {
|
||||||
|
mac == self.mac || mac.is_broadcast()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send(&self, dst: MacAddr, typ: EtherType, data: &[u8]) {
|
||||||
|
let frame = EthernetFrame::new(dst, self.mac, typ, data).serialize();
|
||||||
|
println!("{}", frame);
|
||||||
|
self.iface.send(&frame).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mac(&self) -> MacAddr {
|
||||||
|
self.mac
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ip(&self) -> Option<Ipv4Addr> {
|
||||||
|
self.ip
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_ip(&mut self, ip: Ipv4Addr) {
|
||||||
|
self.ip = Some(ip);
|
||||||
|
}
|
||||||
|
}
|
686
Cargo.lock
generated
Normal file
686
Cargo.lock
generated
Normal file
@ -0,0 +1,686 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytes"
|
||||||
|
version = "0.4.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"iovec",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.73"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cloudabi"
|
||||||
|
version = "0.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c20ff29ded3204c5106278a81a38f4b482636ed4fa1e6cfbeef193291beb29ed"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"maybe-uninit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"cfg-if 0.1.10",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"lazy_static",
|
||||||
|
"maybe-uninit",
|
||||||
|
"memoffset",
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-queue"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.10",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"maybe-uninit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"cfg-if 0.1.10",
|
||||||
|
"lazy_static",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive-try-from-primitive"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "302ccf094df1151173bb6f5a2282fcd2f45accd5eae1bdf82dcbfefbc501ad5c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuchsia-zircon"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"fuchsia-zircon-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuchsia-zircon-sys"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.1.31"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.1.19"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "internet-checksum"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fc6d6206008e25125b1f97fbe5d309eb7b85141cf9199d52dbd3729a1584dd16"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iovec"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kernel32-sys"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.2.8",
|
||||||
|
"winapi-build",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.126"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.3.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
|
||||||
|
dependencies = [
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log"
|
||||||
|
version = "0.4.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "maybe-uninit"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memoffset"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio"
|
||||||
|
version = "0.6.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.10",
|
||||||
|
"fuchsia-zircon",
|
||||||
|
"fuchsia-zircon-sys",
|
||||||
|
"iovec",
|
||||||
|
"kernel32-sys",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"miow",
|
||||||
|
"net2",
|
||||||
|
"slab",
|
||||||
|
"winapi 0.2.8",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mio-uds"
|
||||||
|
version = "0.6.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0"
|
||||||
|
dependencies = [
|
||||||
|
"iovec",
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "miow"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d"
|
||||||
|
dependencies = [
|
||||||
|
"kernel32-sys",
|
||||||
|
"net2",
|
||||||
|
"winapi 0.2.8",
|
||||||
|
"ws2_32-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mopa"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a785740271256c230f57462d3b83e52f998433a7062fc18f96d5999474a9f915"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "net2"
|
||||||
|
version = "0.2.37"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.10",
|
||||||
|
"libc",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nstack-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"derive-try-from-primitive",
|
||||||
|
"internet-checksum",
|
||||||
|
"mopa",
|
||||||
|
"rhexdump",
|
||||||
|
"tun-tap",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num_cpus"
|
||||||
|
version = "1.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f842b1982eb6c2fe34036a4fbfb06dd185a3f5c8edfaacdf7d1ea10b07de6252"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
"parking_lot_core",
|
||||||
|
"rustc_version",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "parking_lot_core"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b876b1b9e7ac6e1a74a6da34d25c42e17e8862aa409cbbbdcfc8d86c6f3bc62b"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 0.1.10",
|
||||||
|
"cloudabi",
|
||||||
|
"libc",
|
||||||
|
"redox_syscall",
|
||||||
|
"rustc_version",
|
||||||
|
"smallvec",
|
||||||
|
"winapi 0.3.9",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.39"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c54b25569025b7fc9651de43004ae593a75ad88543b17178aa5e1b9c4f15f56f"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_syscall"
|
||||||
|
version = "0.1.57"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rhexdump"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c5e9af64574935e39f24d1c0313a997c8b880ca0e087c888bc6af8af31579847"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scoped-tls"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "332ffa32bf586782a3efaeb58f127980944bbc8c4d6913a86107ac2a5ab24b28"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||||
|
dependencies = [
|
||||||
|
"semver-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver-parser"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "slab"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "smallvec"
|
||||||
|
version = "0.6.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0"
|
||||||
|
dependencies = [
|
||||||
|
"maybe-uninit",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.96"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0748dd251e24453cb8717f0354206b91557e4ec8703673a4b30208f2abaf1ebf"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio"
|
||||||
|
version = "0.1.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5a09c0b5bb588872ab2f09afa13ee6e9dac11e10a0ec9e8e3ba39a5a5d530af6"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures",
|
||||||
|
"mio",
|
||||||
|
"num_cpus",
|
||||||
|
"tokio-codec",
|
||||||
|
"tokio-current-thread",
|
||||||
|
"tokio-executor",
|
||||||
|
"tokio-fs",
|
||||||
|
"tokio-io",
|
||||||
|
"tokio-reactor",
|
||||||
|
"tokio-sync",
|
||||||
|
"tokio-tcp",
|
||||||
|
"tokio-threadpool",
|
||||||
|
"tokio-timer",
|
||||||
|
"tokio-udp",
|
||||||
|
"tokio-uds",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-codec"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25b2998660ba0e70d18684de5d06b70b70a3a747469af9dea7618cc59e75976b"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures",
|
||||||
|
"tokio-io",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-core"
|
||||||
|
version = "0.1.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "87b1395334443abca552f63d4f61d0486f12377c2ba8b368e523f89e828cffd4"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures",
|
||||||
|
"iovec",
|
||||||
|
"log",
|
||||||
|
"mio",
|
||||||
|
"scoped-tls",
|
||||||
|
"tokio",
|
||||||
|
"tokio-executor",
|
||||||
|
"tokio-io",
|
||||||
|
"tokio-reactor",
|
||||||
|
"tokio-timer",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-current-thread"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b1de0e32a83f131e002238d7ccde18211c0a5397f60cbfffcb112868c2e0e20e"
|
||||||
|
dependencies = [
|
||||||
|
"futures",
|
||||||
|
"tokio-executor",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-executor"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fb2d1b8f4548dbf5e1f7818512e9c406860678f29c300cdf0ebac72d1a3a1671"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
"futures",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-fs"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "297a1206e0ca6302a0eed35b700d292b275256f596e2f3fea7729d5e629b6ff4"
|
||||||
|
dependencies = [
|
||||||
|
"futures",
|
||||||
|
"tokio-io",
|
||||||
|
"tokio-threadpool",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-io"
|
||||||
|
version = "0.1.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "57fc868aae093479e3131e3d165c93b1c7474109d13c90ec0dda2a1bbfff0674"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-reactor"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09bc590ec4ba8ba87652da2068d150dcada2cfa2e07faae270a5e0409aa51351"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
"futures",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"mio",
|
||||||
|
"num_cpus",
|
||||||
|
"parking_lot",
|
||||||
|
"slab",
|
||||||
|
"tokio-executor",
|
||||||
|
"tokio-io",
|
||||||
|
"tokio-sync",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-sync"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "edfe50152bc8164fcc456dab7891fa9bf8beaf01c5ee7e1dd43a397c3cf87dee"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"futures",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-tcp"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98df18ed66e3b72e742f185882a9e201892407957e45fbff8da17ae7a7c51f72"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures",
|
||||||
|
"iovec",
|
||||||
|
"mio",
|
||||||
|
"tokio-io",
|
||||||
|
"tokio-reactor",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-threadpool"
|
||||||
|
version = "0.1.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df720b6581784c118f0eb4310796b12b1d242a7eb95f716a8367855325c25f89"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-queue",
|
||||||
|
"crossbeam-utils",
|
||||||
|
"futures",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"num_cpus",
|
||||||
|
"slab",
|
||||||
|
"tokio-executor",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-timer"
|
||||||
|
version = "0.2.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "93044f2d313c95ff1cb7809ce9a7a05735b012288a888b62d4434fd58c94f296"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
"futures",
|
||||||
|
"slab",
|
||||||
|
"tokio-executor",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-udp"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2a0b10e610b39c38b031a2fcab08e4b82f16ece36504988dcbd81dbba650d82"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures",
|
||||||
|
"log",
|
||||||
|
"mio",
|
||||||
|
"tokio-codec",
|
||||||
|
"tokio-io",
|
||||||
|
"tokio-reactor",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-uds"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ab57a4ac4111c8c9dbcf70779f6fc8bc35ae4b2454809febac840ad19bd7e4e0"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures",
|
||||||
|
"iovec",
|
||||||
|
"libc",
|
||||||
|
"log",
|
||||||
|
"mio",
|
||||||
|
"mio-uds",
|
||||||
|
"tokio-codec",
|
||||||
|
"tokio-io",
|
||||||
|
"tokio-reactor",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tun-tap"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "53ccbe9cfffdaa7eefd36538bb26f228d4bd319a8aeca044d54377612a646bcd"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"futures",
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"tokio-core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d22af068fba1eb5edcb4aea19d382b2a3deb4c8f9d475c589b6ada9e0fd493ee"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.2.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-build"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ws2_32-sys"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.2.8",
|
||||||
|
"winapi-build",
|
||||||
|
]
|
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "nstack-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
derive-try-from-primitive = "1.0.0"
|
||||||
|
internet-checksum = "0.2.1"
|
||||||
|
mopa = "0.2.2"
|
||||||
|
rhexdump = "0.1.1"
|
||||||
|
tun-tap = "0.1.2"
|
1
nstack-runner
Symbolic link
1
nstack-runner
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
runner/target/debug/runner
|
47
runner/Cargo.lock
generated
Normal file
47
runner/Cargo.lock
generated
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "capctl"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "526c6a8746a7cfb052c15d20259c4f5c021966affdc7c960c71ca640f824c801"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iptool"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.126"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "runner"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"capctl",
|
||||||
|
"iptool",
|
||||||
|
]
|
10
runner/Cargo.toml
Normal file
10
runner/Cargo.toml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[package]
|
||||||
|
name = "runner"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
capctl = "0.2.1"
|
||||||
|
iptool = { path = "iptool" }
|
17
runner/iptool/.github/workflows/build.yml
vendored
Normal file
17
runner/iptool/.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
name: "Build"
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2.3.4
|
||||||
|
- uses: cachix/install-nix-action@v13
|
||||||
|
with:
|
||||||
|
nix_path: ""
|
||||||
|
- uses: cachix/cachix-action@v10
|
||||||
|
with:
|
||||||
|
name: nyantec
|
||||||
|
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
|
||||||
|
- run: nix-build -A iptool
|
1
runner/iptool/.gitignore
vendored
Normal file
1
runner/iptool/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
215
runner/iptool/Cargo.lock
generated
Normal file
215
runner/iptool/Cargo.lock
generated
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "0.7.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ipnetwork"
|
||||||
|
version = "0.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4088d739b183546b239688ddbc79891831df421773df95e236daf7867866d355"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "iptool"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"pnet",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.104"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b2f96d100e1cf1929e7719b7edb3b90ab5298072638fccd77be9ce942ecdfce"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pnet"
|
||||||
|
version = "0.28.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4b6d2a0409666964722368ef5fb74b9f93fac11c18bef3308693c16c6733f103"
|
||||||
|
dependencies = [
|
||||||
|
"ipnetwork",
|
||||||
|
"pnet_base",
|
||||||
|
"pnet_datalink",
|
||||||
|
"pnet_packet",
|
||||||
|
"pnet_sys",
|
||||||
|
"pnet_transport",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pnet_base"
|
||||||
|
version = "0.28.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "25488cd551a753dcaaa6fffc9f69a7610a412dd8954425bf7ffad5f7d1156fb8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pnet_datalink"
|
||||||
|
version = "0.28.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4d1f8ab1ef6c914cf51dc5dfe0be64088ea5f3b08bbf5a31abc70356d271198"
|
||||||
|
dependencies = [
|
||||||
|
"ipnetwork",
|
||||||
|
"libc",
|
||||||
|
"pnet_base",
|
||||||
|
"pnet_sys",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pnet_macros"
|
||||||
|
version = "0.28.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30490e0852e58402b8fae0d39897b08a24f493023a4d6cf56b2e30f31ed57548"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pnet_macros_support"
|
||||||
|
version = "0.28.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4714e10f30cab023005adce048f2d30dd4ac4f093662abf2220855655ef8f90"
|
||||||
|
dependencies = [
|
||||||
|
"pnet_base",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pnet_packet"
|
||||||
|
version = "0.28.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8588067671d03c9f4254b2e66fecb4d8b93b5d3e703195b84f311cd137e32130"
|
||||||
|
dependencies = [
|
||||||
|
"glob",
|
||||||
|
"pnet_base",
|
||||||
|
"pnet_macros",
|
||||||
|
"pnet_macros_support",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pnet_sys"
|
||||||
|
version = "0.28.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d9a3f32b0df45515befd19eed04616f6b56a488da92afc61164ef455e955f07f"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pnet_transport"
|
||||||
|
version = "0.28.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "932b2916d693bcc5fa18443dc99142e0a6fd31a6ce75a511868f7174c17e2bce"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"pnet_base",
|
||||||
|
"pnet_packet",
|
||||||
|
"pnet_sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.30"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.130"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.80"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
21
runner/iptool/Cargo.toml
Normal file
21
runner/iptool/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[package]
|
||||||
|
name = "iptool"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Finn Behrens <fin@nyantec.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
repository = "https://github.com/nyantec/iptool"
|
||||||
|
license = "MirOS"
|
||||||
|
keywords = [ "linux", "network" ]
|
||||||
|
categories = [ "hardware-support" ]
|
||||||
|
description = "Rust linux iptool helpers for network interfaces"
|
||||||
|
homepage = "https://github.com/nyantec/iptool"
|
||||||
|
readme = "README.md"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = "0.2"
|
||||||
|
|
||||||
|
pnet = { version = "0.28", optional = true }
|
8
runner/iptool/LICENSE
Normal file
8
runner/iptool/LICENSE
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
Copyright 2020 nyantec GmbH <oss@nyantec.com>
|
||||||
|
|
||||||
|
Authors:
|
||||||
|
Finn Behrens <fin@nyantec.com>
|
||||||
|
|
||||||
|
Provided that these terms and disclaimer and all copyright notices are retained or reproduced in an accompanying document, permission is granted to deal in this work without restriction, including unlimited rights to use, publicly perform, distribute, sell, modify, merge, give away, or sublicence.
|
||||||
|
|
||||||
|
This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to the utmost extent permitted by applicable law, neither express nor implied; without malicious intent or gross negligence. In no event may a licensor, author or contributor be held liable for indirect, direct, other damage, loss, or other issues arising in any way out of dealing in the work, even if advised of the possibility of such damage or existence of a defect, except proven that it results out of said person's immediate fault when using the work as intended.
|
3
runner/iptool/README.md
Normal file
3
runner/iptool/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# IpTool
|
||||||
|
|
||||||
|
Rust linux iptool helpers for network interfaces.
|
8
runner/iptool/default.nix
Normal file
8
runner/iptool/default.nix
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
(import (
|
||||||
|
fetchTarball {
|
||||||
|
url = "https://github.com/edolstra/flake-compat/archive/12c64ca55c1014cdc1b16ed5a804aa8576601ff2.tar.gz";
|
||||||
|
sha256 = "0jm6nzb83wa6ai17ly9fzpqc40wg1viib8klq8lby54agpl213w5";
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
src = ./.;
|
||||||
|
}).defaultNix.packages.x86_64-linux
|
25
runner/iptool/flake.lock
Normal file
25
runner/iptool/flake.lock
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1635150109,
|
||||||
|
"narHash": "sha256-KTtyOdjFY8AqQRKytamEuLgPJYRjQSDivyNlZtT/2Zs=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "8e18c70837aa01ade3718cd0fd35b649b3a2cf52",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"id": "nixpkgs",
|
||||||
|
"type": "indirect"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
28
runner/iptool/flake.nix
Normal file
28
runner/iptool/flake.nix
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
description = "iptool";
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs }: let
|
||||||
|
version = self.shortRev or (toString self.lastModifiedDate);
|
||||||
|
overlay = final: prev: {
|
||||||
|
iptool = final.callPackage (
|
||||||
|
{ rustPlatform }: rustPlatform.buildRustPackage {
|
||||||
|
pname = "iptool";
|
||||||
|
inherit version;
|
||||||
|
src = self;
|
||||||
|
cargoLock.lockFile = ./Cargo.lock;
|
||||||
|
cargoBuildFlags = [ "--all-features" ];
|
||||||
|
}
|
||||||
|
) {};
|
||||||
|
};
|
||||||
|
pkgs = import nixpkgs {
|
||||||
|
system = "x86_64-linux";
|
||||||
|
overlays = [ overlay ];
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
inherit overlay;
|
||||||
|
packages.x86_64-linux = {
|
||||||
|
inherit (pkgs) iptool;
|
||||||
|
};
|
||||||
|
defaultPackage.x86_64-linux = self.packages.x86_64-linux.iptool;
|
||||||
|
};
|
||||||
|
}
|
133
runner/iptool/src/lib.rs
Normal file
133
runner/iptool/src/lib.rs
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
use std::io::{Error, Result};
|
||||||
|
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||||
|
|
||||||
|
#[cfg(feature = "pnet")]
|
||||||
|
use pnet::datalink::MacAddr;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod linux;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub use linux::{Ifreq, SIOCGIFINDEX};
|
||||||
|
|
||||||
|
// TODO: macOS
|
||||||
|
|
||||||
|
/// Only use it for a short amount of time, as it does not close it's ioctl socket
|
||||||
|
pub struct IpTool {
|
||||||
|
#[cfg(target_family = "unix")]
|
||||||
|
fd: RawFd,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
TODO: is it already non send?
|
||||||
|
impl !Send for IpTool {}
|
||||||
|
impl !Sync for IpTool {}
|
||||||
|
*/
|
||||||
|
|
||||||
|
impl AsRawFd for IpTool {
|
||||||
|
fn as_raw_fd(&self) -> RawFd {
|
||||||
|
self.fd
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromRawFd for IpTool {
|
||||||
|
unsafe fn from_raw_fd(fd: RawFd) -> Self {
|
||||||
|
Self { fd }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function
|
||||||
|
#[allow(dead_code)] // not used yet on non linux
|
||||||
|
pub(crate) fn copy_slice(dst: &mut [u8], src: &[u8]) -> usize {
|
||||||
|
let mut c = 0;
|
||||||
|
|
||||||
|
for (d, s) in dst.iter_mut().zip(src.iter()) {
|
||||||
|
*d = *s;
|
||||||
|
c += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
c
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_mac_addr(mac: &str) -> Result<[libc::c_char; 14]> {
|
||||||
|
let mut addr: [libc::c_char; 14] = [0; 14];
|
||||||
|
let mac_vec: Vec<&str> = mac.split(':').collect();
|
||||||
|
if mac_vec.len() != 6 {
|
||||||
|
// TODO: unlikly (https://doc.rust-lang.org/nightly/std/intrinsics/fn.unlikely.html)
|
||||||
|
return Err(Error::from_raw_os_error(libc::EINVAL));
|
||||||
|
}
|
||||||
|
for x in 0..6 {
|
||||||
|
if let Ok(data) = u8::from_str_radix(mac_vec[x], 16) {
|
||||||
|
addr[x] = data as i8;
|
||||||
|
} else {
|
||||||
|
// TODO: unlikly (https://doc.rust-lang.org/nightly/std/intrinsics/fn.unlikely.html)
|
||||||
|
return Err(Error::from_raw_os_error(libc::EINVAL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ext traits
|
||||||
|
#[cfg(feature = "pnet")]
|
||||||
|
pub trait MacAddrLinxExt: From<[u8; 6]> {
|
||||||
|
fn from_interface(interface: &str) -> Result<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "pnet")]
|
||||||
|
impl MacAddrLinxExt for MacAddr {
|
||||||
|
fn from_interface(interface: &str) -> Result<Self> {
|
||||||
|
let tool = IpTool::new()?;
|
||||||
|
|
||||||
|
let hwaddr = tool.get_mac_sa_data(interface)?;
|
||||||
|
//let hwaddr: [u8; 6] = hwaddr.try_into()?;
|
||||||
|
let hwaddr = unsafe { *(&hwaddr as *const _ as *const [u8; 6]) };
|
||||||
|
|
||||||
|
let hwaddr: [u8; 6] = hwaddr.into();
|
||||||
|
|
||||||
|
Ok(hwaddr.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub use linux::IpAddrLinkExt;
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
#[inline]
|
||||||
|
#[allow(dead_code)] // not used yet on non linux
|
||||||
|
/// Own (cold) function to optimize if statements
|
||||||
|
fn last_err() -> Error {
|
||||||
|
Error::last_os_error()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[allow(overflowing_literals)]
|
||||||
|
fn parse_mac_addr() {
|
||||||
|
let addr = "5A:E6:60:8F:5F:DE";
|
||||||
|
let mut addr_vec: [libc::c_char; 14] = [0; 14];
|
||||||
|
addr_vec[0] = 0x5A;
|
||||||
|
addr_vec[1] = 0xE6;
|
||||||
|
addr_vec[2] = 0x60;
|
||||||
|
addr_vec[3] = 0x8F;
|
||||||
|
addr_vec[4] = 0x5F;
|
||||||
|
addr_vec[5] = 0xDE;
|
||||||
|
|
||||||
|
assert_eq!(super::parse_mac_addr(addr).unwrap(), addr_vec);
|
||||||
|
|
||||||
|
// not long enough address
|
||||||
|
super::parse_mac_addr("5A:3B:2D").unwrap_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "pnet")]
|
||||||
|
#[test]
|
||||||
|
fn macaddr_from_interface() {
|
||||||
|
use super::MacAddrLinxExt;
|
||||||
|
|
||||||
|
assert!(pnet::util::MacAddr::from_interface("lo").unwrap().is_zero());
|
||||||
|
}
|
||||||
|
}
|
369
runner/iptool/src/linux.rs
Normal file
369
runner/iptool/src/linux.rs
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
use std::io::{Error, Result};
|
||||||
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
|
|
||||||
|
use libc::{
|
||||||
|
c_int, c_short, c_uchar, close, in6_addr, ioctl, sockaddr, sockaddr_in, sockaddr_in6, socket,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::{copy_slice, last_err, parse_mac_addr, IpTool};
|
||||||
|
|
||||||
|
pub const SIOCGIFINDEX: u64 = 0x8933;
|
||||||
|
|
||||||
|
// Helper traits
|
||||||
|
pub trait IpAddrLinkExt
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
fn from_interface(dev: &str) -> Result<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IpAddrLinkExt for Ipv4Addr {
|
||||||
|
fn from_interface(dev: &str) -> Result<Self> {
|
||||||
|
let iptool = IpTool::new()?;
|
||||||
|
|
||||||
|
iptool.get_address(dev)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IpTool {
|
||||||
|
pub fn new() -> Result<Self> {
|
||||||
|
let fd = Self::get_ctl_fd()?;
|
||||||
|
|
||||||
|
Ok(Self { fd })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_up(&self, dev: &str, up: bool) -> Result<()> {
|
||||||
|
let mut ifr = Ifreq::new(dev);
|
||||||
|
|
||||||
|
ifr.ioctl(&self, libc::SIOCGIFFLAGS as _)?;
|
||||||
|
|
||||||
|
let flag_val = libc::IFF_UP as i16;
|
||||||
|
|
||||||
|
// SAFETY: union
|
||||||
|
unsafe {
|
||||||
|
ifr.ifr_ifru.ifru_flags = if up {
|
||||||
|
ifr.ifr_ifru.ifru_flags | flag_val
|
||||||
|
} else {
|
||||||
|
ifr.ifr_ifru.ifru_flags & (!flag_val)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
ifr.ioctl(&self, libc::SIOCSIFFLAGS as _)?;
|
||||||
|
|
||||||
|
if self.get_up(dev)? != up {
|
||||||
|
return Err(Error::from_raw_os_error(libc::ENOTRECOVERABLE));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_up(&self, dev: &str) -> Result<bool> {
|
||||||
|
let mut ifr = Ifreq::new(dev);
|
||||||
|
|
||||||
|
ifr.ioctl(&self, libc::SIOCGIFFLAGS as _)?;
|
||||||
|
|
||||||
|
// unions
|
||||||
|
let flags: i16 = unsafe { ifr.ifr_ifru.ifru_flags };
|
||||||
|
Ok((flags & libc::IFF_UP as i16) == 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_mtu(&self, dev: &str, mtu: u32) -> Result<()> {
|
||||||
|
let mut ifr = Ifreq::new(dev);
|
||||||
|
ifr.ifr_ifru.ifru_mtu = mtu as i32;
|
||||||
|
|
||||||
|
ifr.ioctl(&self, libc::SIOCSIFMTU as _)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mtu(&self, dev: &str) -> Result<u32> {
|
||||||
|
let mut ifr = Ifreq::new(dev);
|
||||||
|
|
||||||
|
ifr.ioctl(&self, libc::SIOCGIFMTU as _)?;
|
||||||
|
|
||||||
|
let mtu = unsafe { ifr.ifr_ifru.ifru_mtu as u32 };
|
||||||
|
Ok(mtu)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_index(&self, dev: &str) -> Result<c_int> {
|
||||||
|
let mut ifr = Ifreq::new(dev);
|
||||||
|
|
||||||
|
ifr.ioctl(&self, SIOCGIFINDEX as _)?;
|
||||||
|
|
||||||
|
Ok(unsafe { ifr.ifr_ifru.ifru_ivalue })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_address(&self, dev: &str, address: &IpAddr, prefix_length: u32) -> Result<()> {
|
||||||
|
let index = self.get_index(dev)?;
|
||||||
|
match address {
|
||||||
|
IpAddr::V4(addr) => {
|
||||||
|
if prefix_length > 32 {
|
||||||
|
return Err(Error::from_raw_os_error(libc::EINVAL));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ifr = Ifreq::new(dev);
|
||||||
|
ifr.ifr_ifru.ifru_addr_v4.sin_family = libc::AF_INET as _;
|
||||||
|
ifr.ifr_ifru.ifru_addr_v4.sin_addr.s_addr = u32::from_ne_bytes(addr.octets());
|
||||||
|
|
||||||
|
ifr.ioctl(&self, libc::SIOCSIFADDR as _)?;
|
||||||
|
|
||||||
|
ifr.ifr_ifru.ifru_addr_v4.sin_addr.s_addr = u32::MAX >> (32 - prefix_length);
|
||||||
|
|
||||||
|
ifr.ioctl(&self, libc::SIOCSIFNETMASK as _)?;
|
||||||
|
}
|
||||||
|
IpAddr::V6(addr) => {
|
||||||
|
let mut ifr = Ifreq6 {
|
||||||
|
prefix_length,
|
||||||
|
ifindex: index as _,
|
||||||
|
addr: in6_addr {
|
||||||
|
s6_addr: addr.octets(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
ifr.ioctl(&self, libc::SIOCSIFADDR as _)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
//let mut ifr = Ifreq::new(dev);
|
||||||
|
/*match address {
|
||||||
|
IpAddr::V4(addr) => {
|
||||||
|
ifr.ifr_ifru.ifru_addr_v4.sin_family = libc::AF_INET as libc::sa_family_t;
|
||||||
|
ifr.ifr_ifru.ifru_addr_v4.sin_addr.s_addr = u32::from_ne_bytes(addr.octets());
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
/*let res = unsafe { libc::ioctl(self.fd, libc::SIOCSIFADDR as _, &mut ifr) };*/
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_address(&self, dev: &str) -> Result<Ipv4Addr> {
|
||||||
|
let mut ifr = Ifreq::new(dev);
|
||||||
|
|
||||||
|
ifr.ioctl(&self, libc::SIOCGIFADDR as _)?;
|
||||||
|
|
||||||
|
// SAFETY: union
|
||||||
|
let addr: Ipv4Addr = unsafe {
|
||||||
|
if ifr.ifr_ifru.ifru_addr_v4.sin_family != libc::AF_INET as _ {
|
||||||
|
return Err(Error::from_raw_os_error(libc::ENOTRECOVERABLE));
|
||||||
|
}
|
||||||
|
ifr.ifr_ifru.ifru_addr_v4.sin_addr.s_addr
|
||||||
|
}
|
||||||
|
.into();
|
||||||
|
|
||||||
|
Ok(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_mac(&self, dev: &str, mac: &str) -> Result<()> {
|
||||||
|
self.set_mac_sa_data(dev, parse_mac_addr(mac)?)
|
||||||
|
}
|
||||||
|
pub fn set_mac_sa_data(&self, dev: &str, mac: [libc::c_char; 14]) -> Result<()> {
|
||||||
|
let mut ifr = Ifreq::new(dev);
|
||||||
|
ifr.ifr_ifru.ifru_hwaddr.sa_family = libc::ARPHRD_ETHER;
|
||||||
|
ifr.ifr_ifru.ifru_hwaddr.sa_data = mac;
|
||||||
|
|
||||||
|
ifr.ioctl(&self, libc::SIOCSIFHWADDR as _)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mac_sa_data(&self, dev: &str) -> Result<[libc::c_char; 14]> {
|
||||||
|
let mut ifr = Ifreq::new(dev);
|
||||||
|
ifr.ifr_ifru.ifru_hwaddr.sa_family = libc::ARPHRD_ETHER;
|
||||||
|
|
||||||
|
ifr.ioctl(&self, libc::SIOCGIFHWADDR as _)?;
|
||||||
|
|
||||||
|
let sa_data = unsafe { ifr.ifr_ifru.ifru_hwaddr.sa_data };
|
||||||
|
Ok(sa_data)
|
||||||
|
}
|
||||||
|
// TODO: get_mac -> String
|
||||||
|
|
||||||
|
/// Get the hardware type of the given interface
|
||||||
|
pub fn get_arptype(&self, dev: &str) -> Result<libc::sa_family_t> {
|
||||||
|
let mut ifr = Ifreq::new(dev);
|
||||||
|
|
||||||
|
ifr.ioctl(&self, libc::SIOCGIFHWADDR as _)?;
|
||||||
|
|
||||||
|
let arptype = unsafe { ifr.ifr_ifru.ifru_hwaddr.sa_family };
|
||||||
|
return Ok(arptype);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_ctl_fd() -> Result<c_int> {
|
||||||
|
let fd = unsafe { socket(libc::PF_INET, libc::SOCK_DGRAM, 0) };
|
||||||
|
if fd >= 0 {
|
||||||
|
return Ok(fd);
|
||||||
|
}
|
||||||
|
let error = std::io::Error::last_os_error();
|
||||||
|
let fd = unsafe { socket(libc::PF_PACKET, libc::SOCK_DGRAM, 0) };
|
||||||
|
if fd >= 0 {
|
||||||
|
return Ok(fd);
|
||||||
|
}
|
||||||
|
let fd = unsafe { socket(libc::PF_INET6, libc::SOCK_DGRAM, 0) };
|
||||||
|
if fd >= 0 {
|
||||||
|
return Ok(fd);
|
||||||
|
}
|
||||||
|
Err(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for IpTool {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { close(self.fd) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
union IfrIfru {
|
||||||
|
ifru_addr: sockaddr,
|
||||||
|
ifru_hwaddr: sockaddr,
|
||||||
|
ifru_addr_v4: sockaddr_in,
|
||||||
|
ifru_addr_v6: sockaddr_in6,
|
||||||
|
ifru_dstaddr: sockaddr,
|
||||||
|
ifru_broadaddr: sockaddr,
|
||||||
|
ifru_flags: c_short,
|
||||||
|
ifru_metric: c_int,
|
||||||
|
ifru_ivalue: c_int,
|
||||||
|
ifru_mtu: c_int,
|
||||||
|
ifru_phys: c_int,
|
||||||
|
ifru_media: c_int,
|
||||||
|
ifru_intval: c_int,
|
||||||
|
//ifru_data: caddr_t,
|
||||||
|
//ifru_devmtu: ifdevmtu,
|
||||||
|
//ifru_kpi: ifkpi,
|
||||||
|
ifru_wake_flags: u32,
|
||||||
|
ifru_route_refcnt: u32,
|
||||||
|
ifru_cap: [c_int; 2],
|
||||||
|
ifru_functional_type: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
trait IoctlReq {
|
||||||
|
fn ioctl(&mut self, iptool: &IpTool, request: libc::c_ulong) -> Result<()> {
|
||||||
|
let res = unsafe { ioctl(iptool.fd, request as _, self) };
|
||||||
|
if res < 0 {
|
||||||
|
Err(last_err())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Ifreq {
|
||||||
|
ifr_name: [c_uchar; libc::IFNAMSIZ],
|
||||||
|
ifr_ifru: IfrIfru,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ifreq {
|
||||||
|
pub fn new(dev: &str) -> Self {
|
||||||
|
//let mut ifr_name = [0; libc::IF_NAMESIZE];
|
||||||
|
|
||||||
|
//ifr_name[..dev.len()].copy_from_slice(dev.as_bytes().as_ref());
|
||||||
|
|
||||||
|
let s: [u8; core::mem::size_of::<Self>()] = [0; core::mem::size_of::<Self>()];
|
||||||
|
let mut s: Self = unsafe { core::mem::transmute(s) };
|
||||||
|
|
||||||
|
copy_slice(&mut s.ifr_name, dev.as_bytes());
|
||||||
|
|
||||||
|
s
|
||||||
|
/*Self {
|
||||||
|
ifr_name,
|
||||||
|
ifr_ifru: IfrIfru { ifru_flags: 0 },
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IoctlReq for Ifreq {}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Ifreq6 {
|
||||||
|
addr: in6_addr,
|
||||||
|
prefix_length: u32,
|
||||||
|
ifindex: libc::c_uint,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IoctlReq for Ifreq6 {}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::IpTool;
|
||||||
|
use std::net::{IpAddr, Ipv4Addr};
|
||||||
|
|
||||||
|
const TEST_INTERFACE: &str = "loop1";
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn down() {
|
||||||
|
let ip_tool = IpTool::new().unwrap();
|
||||||
|
|
||||||
|
ip_tool.set_up(TEST_INTERFACE, false).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn up() {
|
||||||
|
let ip_tool = IpTool::new().unwrap();
|
||||||
|
|
||||||
|
ip_tool.set_up(TEST_INTERFACE, true).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn sleep_down_and_up() {
|
||||||
|
let ip_tool = IpTool::new().unwrap();
|
||||||
|
|
||||||
|
ip_tool.set_up(TEST_INTERFACE, false).unwrap();
|
||||||
|
|
||||||
|
std::thread::sleep(std::time::Duration::from_secs(5));
|
||||||
|
|
||||||
|
ip_tool.set_up(TEST_INTERFACE, true).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn mtu() {
|
||||||
|
let ip_tool = IpTool::new().unwrap();
|
||||||
|
|
||||||
|
ip_tool.set_mtu(TEST_INTERFACE, 1420).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(ip_tool.get_mtu(TEST_INTERFACE).unwrap(), 1420);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn mac() {
|
||||||
|
let ip_tool = IpTool::new().unwrap();
|
||||||
|
let mac = "5A:E6:60:8F:5F:DE";
|
||||||
|
|
||||||
|
ip_tool.set_mac(TEST_INTERFACE, mac).unwrap();
|
||||||
|
|
||||||
|
let sa_data = ip_tool.get_mac_sa_data(TEST_INTERFACE).unwrap();
|
||||||
|
assert_eq!(sa_data, super::parse_mac_addr(mac).unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn set_ipv4() {
|
||||||
|
let ip_tool = IpTool::new().unwrap();
|
||||||
|
let address: Ipv4Addr = "10.23.42.1".parse().unwrap();
|
||||||
|
|
||||||
|
ip_tool
|
||||||
|
.set_address(TEST_INTERFACE, &IpAddr::V4(address), 24)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let addr_on_link = ip_tool.get_address(TEST_INTERFACE).unwrap();
|
||||||
|
assert_eq!(address, addr_on_link);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn ipv4addr_link_ext() {
|
||||||
|
use super::IpAddrLinkExt;
|
||||||
|
let address: Ipv4Addr = "10.23.42.1".parse().unwrap();
|
||||||
|
|
||||||
|
let addr_if = Ipv4Addr::from_interface(TEST_INTERFACE).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(address, addr_if);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_arptype() {
|
||||||
|
let iptool = IpTool::new().unwrap();
|
||||||
|
let arptype = iptool.get_arptype("lo").unwrap();
|
||||||
|
assert_eq!(arptype, libc::ARPHRD_LOOPBACK);
|
||||||
|
}
|
||||||
|
}
|
2
runner/rust-toolchain.toml
Normal file
2
runner/rust-toolchain.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "nightly"
|
33
runner/src/main.rs
Normal file
33
runner/src/main.rs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#![feature(io_error_uncategorized)]
|
||||||
|
|
||||||
|
use iptool::IpTool;
|
||||||
|
use std::{io, net::Ipv4Addr, process::Command, str::FromStr};
|
||||||
|
|
||||||
|
use capctl::{Cap, FileCaps};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
Command::new("cargo")
|
||||||
|
.arg("build")
|
||||||
|
.spawn()
|
||||||
|
.unwrap()
|
||||||
|
.wait()
|
||||||
|
.unwrap();
|
||||||
|
let mut caps = FileCaps::empty();
|
||||||
|
caps.effective = true;
|
||||||
|
caps.permitted.add(Cap::NET_ADMIN);
|
||||||
|
caps.permitted.add(Cap::NET_RAW);
|
||||||
|
caps.set_for_file("target/debug/nstack-rs").unwrap();
|
||||||
|
let mut nstack = Command::new("target/debug/nstack-rs").spawn().unwrap();
|
||||||
|
let iptool = IpTool::new().unwrap();
|
||||||
|
while let Err(x) =
|
||||||
|
iptool.set_address("tap0", &(Ipv4Addr::from_str("10.0.0.2").unwrap().into()), 8)
|
||||||
|
{
|
||||||
|
if x.kind() == io::ErrorKind::Uncategorized {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
Err::<(), _>(x).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iptool.set_up("tap0", true).unwrap();
|
||||||
|
nstack.wait().unwrap();
|
||||||
|
}
|
217
src/arp.rs
Normal file
217
src/arp.rs
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
|
collections::{hash_map::Entry, HashMap},
|
||||||
|
fmt::Display,
|
||||||
|
net::Ipv4Addr,
|
||||||
|
};
|
||||||
|
|
||||||
|
use derive_try_from_primitive::TryFromPrimitive;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ethernet::{EthernetHeader, MacAddr},
|
||||||
|
iface::Iface,
|
||||||
|
packet::{Header, HeaderParse, Packet},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum ArpParseError {
|
||||||
|
HardwareType(u16),
|
||||||
|
ProtocolType(u16),
|
||||||
|
ProtocolAddressLen(u8),
|
||||||
|
HardwareAddressLen(u8),
|
||||||
|
Opcode(u16),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, TryFromPrimitive, PartialEq, Eq)]
|
||||||
|
#[repr(u16)]
|
||||||
|
pub enum ArpOpcode {
|
||||||
|
Request = 1,
|
||||||
|
Reply = 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct ArpMessage {
|
||||||
|
op: ArpOpcode,
|
||||||
|
sha: MacAddr,
|
||||||
|
spa: Ipv4Addr,
|
||||||
|
tha: MacAddr,
|
||||||
|
tpa: Ipv4Addr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for ArpMessage {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self.op {
|
||||||
|
ArpOpcode::Request => f.write_fmt(format_args!(
|
||||||
|
"ARP request from {} ({}) for {}",
|
||||||
|
self.spa, self.sha, self.tpa,
|
||||||
|
)),
|
||||||
|
ArpOpcode::Reply => f.write_fmt(format_args!(
|
||||||
|
"ARP response for {} ({}) to {} ({})",
|
||||||
|
self.spa, self.sha, self.tpa, self.tha,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArpMessage {
|
||||||
|
pub fn new(op: ArpOpcode, sha: MacAddr, spa: Ipv4Addr, tha: MacAddr, tpa: Ipv4Addr) -> Self {
|
||||||
|
Self {
|
||||||
|
op,
|
||||||
|
sha,
|
||||||
|
spa,
|
||||||
|
tha,
|
||||||
|
tpa,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn op(&self) -> ArpOpcode {
|
||||||
|
self.op
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn sha(&self) -> MacAddr {
|
||||||
|
self.sha
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn spa(&self) -> Ipv4Addr {
|
||||||
|
self.spa
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn tha(&self) -> MacAddr {
|
||||||
|
self.tha
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn tpa(&self) -> Ipv4Addr {
|
||||||
|
self.tpa
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Header for ArpMessage {
|
||||||
|
fn serialize_stage1(&self, buf: &mut Vec<u8>) {
|
||||||
|
buf.extend_from_slice(&1u16.to_be_bytes());
|
||||||
|
buf.extend_from_slice(&0x800u16.to_be_bytes());
|
||||||
|
buf.push(6);
|
||||||
|
buf.push(4);
|
||||||
|
buf.extend_from_slice(&(self.op as u16).to_be_bytes());
|
||||||
|
buf.extend_from_slice(self.sha.octets());
|
||||||
|
buf.extend_from_slice(&self.spa.octets());
|
||||||
|
buf.extend_from_slice(self.tha.octets());
|
||||||
|
buf.extend_from_slice(&self.tpa.octets());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HeaderParse for ArpMessage {
|
||||||
|
type ParseError = ArpParseError;
|
||||||
|
fn parse(message: &[u8]) -> Result<(Self, &[u8]), ArpParseError> {
|
||||||
|
let hrd = u16::from_be_bytes((&message[0..2]).try_into().unwrap());
|
||||||
|
if hrd != 1 {
|
||||||
|
return Err(ArpParseError::HardwareType(hrd));
|
||||||
|
}
|
||||||
|
let pro = u16::from_be_bytes((&message[2..4]).try_into().unwrap());
|
||||||
|
if pro != 0x800 {
|
||||||
|
return Err(ArpParseError::ProtocolType(pro));
|
||||||
|
}
|
||||||
|
let hln = u8::from_be_bytes((&message[4..5]).try_into().unwrap());
|
||||||
|
if hln != 6 {
|
||||||
|
return Err(ArpParseError::HardwareAddressLen(hln));
|
||||||
|
}
|
||||||
|
let pln = u8::from_be_bytes((&message[5..6]).try_into().unwrap());
|
||||||
|
if pln != 4 {
|
||||||
|
return Err(ArpParseError::ProtocolAddressLen(pln));
|
||||||
|
}
|
||||||
|
let op = ArpOpcode::try_from(u16::from_be_bytes((&message[6..8]).try_into().unwrap()))
|
||||||
|
.map_err(ArpParseError::Opcode)?;
|
||||||
|
let sha = MacAddr::new((&message[8..14]).try_into().unwrap());
|
||||||
|
let spa = Ipv4Addr::from(<[u8; 4]>::try_from(&message[14..18]).unwrap());
|
||||||
|
let tha = MacAddr::new((&message[18..24]).try_into().unwrap());
|
||||||
|
let tpa = Ipv4Addr::from(<[u8; 4]>::try_from(&message[24..28]).unwrap());
|
||||||
|
Ok((
|
||||||
|
Self {
|
||||||
|
op,
|
||||||
|
sha,
|
||||||
|
spa,
|
||||||
|
tha,
|
||||||
|
tpa,
|
||||||
|
},
|
||||||
|
&[],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ArpCallback<'a> = Box<dyn FnOnce(MacAddr) + 'a>;
|
||||||
|
|
||||||
|
pub struct ArpClient<'a> {
|
||||||
|
iface: &'a Iface,
|
||||||
|
cache: RefCell<HashMap<Ipv4Addr, MacAddr>>,
|
||||||
|
waiting: RefCell<HashMap<Ipv4Addr, Vec<ArpCallback<'a>>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ArpClient<'a> {
|
||||||
|
pub fn new(iface: &'a Iface) -> Self {
|
||||||
|
Self {
|
||||||
|
iface,
|
||||||
|
cache: RefCell::new(HashMap::new()),
|
||||||
|
waiting: RefCell::new(HashMap::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn handle_message(&self, message: Packet<EthernetHeader, ArpMessage>) {
|
||||||
|
match message.l3().op() {
|
||||||
|
ArpOpcode::Request => {
|
||||||
|
if let Some(ip) = self.iface.ip() {
|
||||||
|
if message.l3().tpa() == ip {
|
||||||
|
self.iface.send(
|
||||||
|
message.l3().sha(),
|
||||||
|
Packet::new(Vec::new()).set_l3(ArpMessage::new(
|
||||||
|
ArpOpcode::Reply,
|
||||||
|
self.iface.mac(),
|
||||||
|
ip,
|
||||||
|
message.l3().sha(),
|
||||||
|
message.l3().spa(),
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ArpOpcode::Reply => {
|
||||||
|
self.cache
|
||||||
|
.borrow_mut()
|
||||||
|
.insert(message.l3().spa(), message.l3().sha());
|
||||||
|
if let Some(callback_vec) = self.waiting.borrow_mut().remove(&message.l3().spa()) {
|
||||||
|
for callback in callback_vec {
|
||||||
|
callback(message.l3().sha());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mac(&self, ip: Ipv4Addr, callback: Box<dyn FnOnce(MacAddr) + 'a>) {
|
||||||
|
if let Some(&mac) = self.cache.borrow().get(&ip) {
|
||||||
|
callback(mac);
|
||||||
|
} else {
|
||||||
|
match self.waiting.borrow_mut().entry(ip) {
|
||||||
|
Entry::Vacant(e) => {
|
||||||
|
e.insert(vec![callback]);
|
||||||
|
self.iface.send(
|
||||||
|
MacAddr::broadcast(),
|
||||||
|
Packet::new(Vec::new()).set_l3(ArpMessage::new(
|
||||||
|
ArpOpcode::Request,
|
||||||
|
self.iface.mac(),
|
||||||
|
self.iface.ip().unwrap_or(Ipv4Addr::new(0, 0, 0, 0)),
|
||||||
|
MacAddr::broadcast(),
|
||||||
|
ip,
|
||||||
|
)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Entry::Occupied(mut e) => {
|
||||||
|
e.get_mut().push(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
124
src/ethernet.rs
Normal file
124
src/ethernet.rs
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
use derive_try_from_primitive::TryFromPrimitive;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
arp::ArpMessage,
|
||||||
|
from_header_type_impl,
|
||||||
|
ip::IpHeader,
|
||||||
|
packet::{Header, HeaderParse},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct MacAddr([u8; 6]);
|
||||||
|
|
||||||
|
impl Display for MacAddr {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
for byte in &self.0[0..5] {
|
||||||
|
f.write_fmt(format_args!("{:0>2x}:", byte))?;
|
||||||
|
}
|
||||||
|
f.write_fmt(format_args!("{:0>2x}", self.0[5]))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MacAddr {
|
||||||
|
pub fn new(mac: [u8; 6]) -> MacAddr {
|
||||||
|
Self(mac)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_broadcast(&self) -> bool {
|
||||||
|
self == &Self::broadcast()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn octets(&self) -> &[u8] {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn broadcast() -> MacAddr {
|
||||||
|
Self([0xFFu8; 6])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(TryFromPrimitive)]
|
||||||
|
#[repr(u16)]
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum EtherType {
|
||||||
|
Ipv4 = 0x800,
|
||||||
|
Arp = 0x806,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for EtherType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Ipv4 => f.write_str("IPv4"),
|
||||||
|
Self::Arp => f.write_str("ARP"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from_header_type_impl!(EtherType, IpHeader => Self::Ipv4, ArpMessage => Self::Arp);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct InvalidEtherType(pub u16);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct EthernetHeader {
|
||||||
|
dst: MacAddr,
|
||||||
|
src: MacAddr,
|
||||||
|
typ: EtherType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for EthernetHeader {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_fmt(format_args!(
|
||||||
|
"{} from {} to {}",
|
||||||
|
self.typ, self.src, self.dst,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EthernetHeader {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn new(dst: MacAddr, src: MacAddr, typ: EtherType) -> Self {
|
||||||
|
Self { dst, src, typ }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn dst(&self) -> MacAddr {
|
||||||
|
self.dst
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn src(&self) -> MacAddr {
|
||||||
|
self.src
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn typ(&self) -> EtherType {
|
||||||
|
self.typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Header for EthernetHeader {
|
||||||
|
fn serialize_stage1(&self, buf: &mut Vec<u8>) {
|
||||||
|
buf.extend_from_slice(self.dst.octets());
|
||||||
|
buf.extend_from_slice(self.src.octets());
|
||||||
|
buf.extend_from_slice(&(self.typ as u16).to_be_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HeaderParse for EthernetHeader {
|
||||||
|
type ParseError = InvalidEtherType;
|
||||||
|
fn parse(frame: &[u8]) -> Result<(Self, &[u8]), InvalidEtherType> {
|
||||||
|
Ok((
|
||||||
|
Self {
|
||||||
|
dst: MacAddr(frame[0..6].try_into().unwrap()),
|
||||||
|
src: MacAddr(frame[6..12].try_into().unwrap()),
|
||||||
|
typ: EtherType::try_from(u16::from_be_bytes(frame[12..14].try_into().unwrap()))
|
||||||
|
.map_err(InvalidEtherType)?,
|
||||||
|
},
|
||||||
|
&frame[14..],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
94
src/icmp.rs
Normal file
94
src/icmp.rs
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
use crate::packet::{Header, HeaderParse};
|
||||||
|
use derive_try_from_primitive::TryFromPrimitive;
|
||||||
|
use internet_checksum::Checksum;
|
||||||
|
use std::fmt::Display;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
pub struct InvalidType(pub u8);
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, TryFromPrimitive)]
|
||||||
|
#[repr(u8)]
|
||||||
|
enum IcmpHeaderType {
|
||||||
|
EchoRequest = 8,
|
||||||
|
EchoReply = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum IcmpHeader {
|
||||||
|
EchoRequest { id: u16, seq: u16 },
|
||||||
|
EchoReply { id: u16, seq: u16 },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IcmpHeader {
|
||||||
|
fn typ(&self) -> IcmpHeaderType {
|
||||||
|
match self {
|
||||||
|
Self::EchoRequest { .. } => IcmpHeaderType::EchoRequest,
|
||||||
|
Self::EchoReply { .. } => IcmpHeaderType::EchoReply,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for IcmpHeader {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::EchoRequest { id, seq } => f.write_fmt(format_args!(
|
||||||
|
"ICMP Echo Request with ID {} and seq number {}",
|
||||||
|
id, seq,
|
||||||
|
)),
|
||||||
|
Self::EchoReply { id, seq } => f.write_fmt(format_args!(
|
||||||
|
"ICMP Echo Reply with ID {} and seq number {}.",
|
||||||
|
id, seq,
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Header for IcmpHeader {
|
||||||
|
fn serialize_stage1(&self, buf: &mut Vec<u8>) {
|
||||||
|
buf.extend_from_slice(&[self.typ() as u8, 0]);
|
||||||
|
buf.extend_from_slice(&[0, 0]); // Filled with checksum in stage 2
|
||||||
|
match self {
|
||||||
|
Self::EchoRequest { id, seq } => {
|
||||||
|
buf.extend_from_slice(&id.to_be_bytes());
|
||||||
|
buf.extend_from_slice(&seq.to_be_bytes());
|
||||||
|
}
|
||||||
|
Self::EchoReply { id, seq } => {
|
||||||
|
buf.extend_from_slice(&id.to_be_bytes());
|
||||||
|
buf.extend_from_slice(&seq.to_be_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_stage2(hdr: &mut [u8], data: &[u8]) {
|
||||||
|
let mut checksum = Checksum::new();
|
||||||
|
checksum.add_bytes(hdr);
|
||||||
|
checksum.add_bytes(data);
|
||||||
|
hdr[2..4].copy_from_slice(&checksum.checksum());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HeaderParse for IcmpHeader {
|
||||||
|
type ParseError = InvalidType;
|
||||||
|
fn parse(message: &[u8]) -> Result<(Self, &[u8]), InvalidType> {
|
||||||
|
let typ = IcmpHeaderType::try_from(message[0]).map_err(InvalidType)?;
|
||||||
|
let _code = message[1];
|
||||||
|
let rest = &message[4..];
|
||||||
|
match typ {
|
||||||
|
IcmpHeaderType::EchoRequest => Ok((
|
||||||
|
Self::EchoRequest {
|
||||||
|
id: u16::from_be_bytes(rest[0..2].try_into().unwrap()),
|
||||||
|
seq: u16::from_be_bytes(rest[2..4].try_into().unwrap()),
|
||||||
|
},
|
||||||
|
&rest[4..],
|
||||||
|
)),
|
||||||
|
|
||||||
|
IcmpHeaderType::EchoReply => Ok((
|
||||||
|
Self::EchoReply {
|
||||||
|
id: u16::from_be_bytes(rest[0..2].try_into().unwrap()),
|
||||||
|
seq: u16::from_be_bytes(rest[2..4].try_into().unwrap()),
|
||||||
|
},
|
||||||
|
&rest[4..],
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
62
src/iface.rs
Normal file
62
src/iface.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
use std::{cell::Cell, net::Ipv4Addr};
|
||||||
|
|
||||||
|
use tun_tap::Mode;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ethernet::{EtherType, EthernetHeader, InvalidEtherType, MacAddr},
|
||||||
|
packet::{FromHeaderType, Header, NoHeader, OptionalHeader, Packet},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Iface {
|
||||||
|
iface: tun_tap::Iface,
|
||||||
|
mac: MacAddr,
|
||||||
|
ip: Cell<Option<Ipv4Addr>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iface {
|
||||||
|
pub fn new() -> Iface {
|
||||||
|
Self {
|
||||||
|
iface: tun_tap::Iface::without_packet_info("tap0", Mode::Tap).unwrap(),
|
||||||
|
mac: MacAddr::new([0x9A, 0xB1, 0xB5, 0x22, 0x38, 0xAB]),
|
||||||
|
ip: Cell::new(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn recv(&self) -> Result<Packet<EthernetHeader>, InvalidEtherType> {
|
||||||
|
let mut frame_buf = [0u8; 2048];
|
||||||
|
let frame_len = self.iface.recv(&mut frame_buf).unwrap();
|
||||||
|
let packet = Packet::new(frame_buf[0..frame_len].to_vec());
|
||||||
|
packet.parse::<EthernetHeader>().map_err(|(_pkt, err)| err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mac_to_self(&self, mac: MacAddr) -> bool {
|
||||||
|
mac == self.mac || mac.is_broadcast()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send<L3, L4, L7>(&self, dst: MacAddr, packet: Packet<NoHeader, L3, L4, L7>)
|
||||||
|
where
|
||||||
|
L3: Header,
|
||||||
|
L4: OptionalHeader,
|
||||||
|
L7: OptionalHeader,
|
||||||
|
EtherType: FromHeaderType<L3>,
|
||||||
|
{
|
||||||
|
let eth_header = EthernetHeader::new(dst, self.mac, EtherType::from_hdr_type());
|
||||||
|
let packet = packet.set_l2(eth_header);
|
||||||
|
println!("------------------");
|
||||||
|
println!("{}", packet);
|
||||||
|
println!("------------------");
|
||||||
|
self.iface.send(&packet.serialize()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mac(&self) -> MacAddr {
|
||||||
|
self.mac
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ip(&self) -> Option<Ipv4Addr> {
|
||||||
|
self.ip.get()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_ip(&self, ip: Ipv4Addr) {
|
||||||
|
self.ip.set(Some(ip))
|
||||||
|
}
|
||||||
|
}
|
170
src/ip.rs
Normal file
170
src/ip.rs
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
use std::{fmt::Display, net::Ipv4Addr, rc::Rc};
|
||||||
|
|
||||||
|
use derive_try_from_primitive::TryFromPrimitive;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
arp::ArpClient,
|
||||||
|
from_header_type_impl,
|
||||||
|
icmp::IcmpHeader,
|
||||||
|
iface::Iface,
|
||||||
|
packet::{FromHeaderType, Header, HeaderParse, NoHeader, OptionalHeader, Packet},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ParseError {
|
||||||
|
FragmentsUnsupported,
|
||||||
|
OptionsUnsupported,
|
||||||
|
InvalidVersion,
|
||||||
|
InvaliidProtocol(u8),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive)]
|
||||||
|
pub enum IpProtocolType {
|
||||||
|
Icmp = 1,
|
||||||
|
Tcp = 6,
|
||||||
|
Udp = 17,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for IpProtocolType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Icmp => f.write_str("ICMP"),
|
||||||
|
Self::Tcp => f.write_str("TCP"),
|
||||||
|
Self::Udp => f.write_str("UDP"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from_header_type_impl!(IpProtocolType, IcmpHeader => Self::Icmp);
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct IpHeader {
|
||||||
|
ttl: u8,
|
||||||
|
protocol: IpProtocolType,
|
||||||
|
src: Ipv4Addr,
|
||||||
|
dst: Ipv4Addr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for IpHeader {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_fmt(format_args!(
|
||||||
|
"IP datagram from {} to {} ({})",
|
||||||
|
self.src, self.dst, self.protocol,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IpHeader {
|
||||||
|
pub fn new(protocol: IpProtocolType, src: Ipv4Addr, dst: Ipv4Addr) -> Self {
|
||||||
|
Self {
|
||||||
|
ttl: 255,
|
||||||
|
protocol,
|
||||||
|
src,
|
||||||
|
dst,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn ttl(&self) -> u8 {
|
||||||
|
self.ttl
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn protocol(&self) -> IpProtocolType {
|
||||||
|
self.protocol
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn dst(&self) -> Ipv4Addr {
|
||||||
|
self.dst
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn src(&self) -> Ipv4Addr {
|
||||||
|
self.src
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Header for IpHeader {
|
||||||
|
fn serialize_stage1(&self, buf: &mut Vec<u8>) {
|
||||||
|
buf.extend_from_slice(&[0x45, 0]);
|
||||||
|
buf.extend_from_slice(&[0, 0]); // Filled with length in stage 2
|
||||||
|
buf.extend_from_slice(&[0, 0]);
|
||||||
|
buf.extend_from_slice(&[0, 0]);
|
||||||
|
buf.push(self.ttl);
|
||||||
|
buf.push(self.protocol as u8);
|
||||||
|
buf.extend_from_slice(&[0, 0]); // Filled with checksum in stage 2
|
||||||
|
buf.extend_from_slice(&self.src.octets());
|
||||||
|
buf.extend_from_slice(&self.dst.octets());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_stage2(hdr: &mut [u8], data: &[u8]) {
|
||||||
|
hdr[2..4].copy_from_slice(&(20u16 + data.len() as u16).to_be_bytes());
|
||||||
|
let checksum = internet_checksum::checksum(hdr);
|
||||||
|
hdr[10..12].copy_from_slice(&checksum);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HeaderParse for IpHeader {
|
||||||
|
type ParseError = ParseError;
|
||||||
|
fn parse(datagram: &[u8]) -> Result<(Self, &[u8]), ParseError> {
|
||||||
|
if datagram[0] & 0xf0 != 0x40 {
|
||||||
|
return Err(ParseError::InvalidVersion);
|
||||||
|
}
|
||||||
|
if datagram[0] & 0xf != 0x5 {
|
||||||
|
return Err(ParseError::OptionsUnsupported);
|
||||||
|
}
|
||||||
|
if datagram[6] & 0x20 != 0x0 {
|
||||||
|
return Err(ParseError::FragmentsUnsupported);
|
||||||
|
}
|
||||||
|
let ttl = datagram[8];
|
||||||
|
let protocol =
|
||||||
|
IpProtocolType::try_from(datagram[9]).map_err(ParseError::InvaliidProtocol)?;
|
||||||
|
let src = Ipv4Addr::from(<[u8; 4]>::try_from(&datagram[12..16]).unwrap());
|
||||||
|
let dst = Ipv4Addr::from(<[u8; 4]>::try_from(&datagram[16..20]).unwrap());
|
||||||
|
Ok((
|
||||||
|
Self {
|
||||||
|
ttl,
|
||||||
|
protocol,
|
||||||
|
src,
|
||||||
|
dst,
|
||||||
|
},
|
||||||
|
&datagram[20..],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct IpSender<'a, 'b> {
|
||||||
|
iface: Rc<Iface>,
|
||||||
|
arp_client: &'a ArpClient<'b>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> IpSender<'a, 'b> {
|
||||||
|
pub fn new(iface: Rc<Iface>, arp_client: &'a ArpClient<'b>) -> Self {
|
||||||
|
Self { iface, arp_client }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send<L4: 'b, L7: 'b>(&self, dst: Ipv4Addr, packet: Packet<NoHeader, NoHeader, L4, L7>)
|
||||||
|
where
|
||||||
|
L4: Header,
|
||||||
|
L7: OptionalHeader,
|
||||||
|
IpProtocolType: FromHeaderType<L4>,
|
||||||
|
{
|
||||||
|
let header = IpHeader::new(
|
||||||
|
IpProtocolType::from_hdr_type(),
|
||||||
|
self.iface.ip().unwrap(),
|
||||||
|
dst,
|
||||||
|
);
|
||||||
|
let packet = packet.set_l3(header);
|
||||||
|
let iface = Rc::downgrade(&self.iface);
|
||||||
|
self.arp_client.get_mac(
|
||||||
|
dst,
|
||||||
|
Box::new(move |mac| {
|
||||||
|
if let Some(iface) = iface.upgrade() {
|
||||||
|
iface.send(mac, packet);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
67
src/main.rs
Normal file
67
src/main.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
mod arp;
|
||||||
|
mod ethernet;
|
||||||
|
mod icmp;
|
||||||
|
mod iface;
|
||||||
|
mod ip;
|
||||||
|
mod packet;
|
||||||
|
|
||||||
|
use std::{net::Ipv4Addr, rc::Rc};
|
||||||
|
|
||||||
|
use arp::ArpClient;
|
||||||
|
use iface::Iface;
|
||||||
|
use ip::{IpHeader, IpSender};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
arp::ArpMessage,
|
||||||
|
ethernet::{EtherType, InvalidEtherType},
|
||||||
|
icmp::IcmpHeader,
|
||||||
|
ip::IpProtocolType,
|
||||||
|
packet::Packet,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let iface = Rc::new(Iface::new());
|
||||||
|
iface.set_ip(Ipv4Addr::new(10, 0, 0, 1));
|
||||||
|
let arp_client = ArpClient::new(&iface);
|
||||||
|
let ip_sender = IpSender::new(iface.clone(), &arp_client);
|
||||||
|
loop {
|
||||||
|
let packet = match iface.recv() {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(InvalidEtherType(_typ)) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if !iface.mac_to_self(packet.l2().dst()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match packet.l2().typ() {
|
||||||
|
EtherType::Arp => {
|
||||||
|
let packet = packet.parse::<ArpMessage>().unwrap();
|
||||||
|
println!("------------------");
|
||||||
|
println!("{}", packet);
|
||||||
|
println!("------------------");
|
||||||
|
arp_client.handle_message(packet);
|
||||||
|
}
|
||||||
|
EtherType::Ipv4 => {
|
||||||
|
let packet = packet.parse::<IpHeader>().unwrap();
|
||||||
|
#[allow(clippy::single_match)]
|
||||||
|
match packet.l3().protocol() {
|
||||||
|
IpProtocolType::Icmp => {
|
||||||
|
let packet = packet.parse::<IcmpHeader>().unwrap();
|
||||||
|
println!("------------------");
|
||||||
|
println!("{}", packet);
|
||||||
|
println!("------------------");
|
||||||
|
if let &IcmpHeader::EchoRequest { id, seq } = packet.l4() {
|
||||||
|
ip_sender.send(
|
||||||
|
packet.l3().src(),
|
||||||
|
Packet::new(packet.body().to_vec())
|
||||||
|
.set_l4(IcmpHeader::EchoReply { id, seq }),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
306
src/packet.rs
Normal file
306
src/packet.rs
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
#![allow(clippy::transmute_ptr_to_ref)] // Shut up a warning generated by mopafy!
|
||||||
|
|
||||||
|
use std::fmt::{Debug, Display};
|
||||||
|
|
||||||
|
pub trait FromHeaderType<T: Header> {
|
||||||
|
fn from_hdr_type() -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! from_header_type_impl {
|
||||||
|
($t: ty, $($ht: ty => $ex: expr),+) => {
|
||||||
|
$(
|
||||||
|
impl $crate::packet::FromHeaderType<$ht> for $t {
|
||||||
|
fn from_hdr_type() -> Self {
|
||||||
|
$ex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Header: Display + Debug {
|
||||||
|
fn serialize_stage1(&self, buf: &mut Vec<u8>);
|
||||||
|
fn serialize_stage2(_hdr: &mut [u8], _data: &[u8]) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait HeaderParse: Header {
|
||||||
|
type ParseError;
|
||||||
|
fn parse(message: &[u8]) -> Result<(Self, &[u8]), Self::ParseError>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait OptionalHeader: Display + Debug {
|
||||||
|
fn is_some(&self) -> bool;
|
||||||
|
fn serialize_stage1(&self, buf: &mut Vec<u8>);
|
||||||
|
fn serialize_stage2(_hdr: &mut [u8], _data: &[u8]) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Header> OptionalHeader for T {
|
||||||
|
fn is_some(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
fn serialize_stage1(&self, buf: &mut Vec<u8>) {
|
||||||
|
<Self as Header>::serialize_stage1(self, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn serialize_stage2(hdr: &mut [u8], data: &[u8]) {
|
||||||
|
<Self as Header>::serialize_stage2(hdr, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NoHeader;
|
||||||
|
|
||||||
|
impl Display for NoHeader {
|
||||||
|
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OptionalHeader for NoHeader {
|
||||||
|
fn is_some(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
fn serialize_stage1(&self, _buf: &mut Vec<u8>) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Packet<
|
||||||
|
L2: OptionalHeader = NoHeader,
|
||||||
|
L3: OptionalHeader = NoHeader,
|
||||||
|
L4: OptionalHeader = NoHeader,
|
||||||
|
L7: OptionalHeader = NoHeader,
|
||||||
|
> {
|
||||||
|
l2: L2,
|
||||||
|
l3: L3,
|
||||||
|
l4: L4,
|
||||||
|
l7: L7,
|
||||||
|
body: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L2: OptionalHeader, L3: OptionalHeader, L4: OptionalHeader, L7: OptionalHeader> Display
|
||||||
|
for Packet<L2, L3, L4, L7>
|
||||||
|
{
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let mut printed = false;
|
||||||
|
if self.l2.is_some() {
|
||||||
|
f.write_fmt(format_args!("{}", self.l2))?;
|
||||||
|
printed = true;
|
||||||
|
}
|
||||||
|
if self.l3.is_some() {
|
||||||
|
if printed {
|
||||||
|
f.write_str("\n")?;
|
||||||
|
}
|
||||||
|
f.write_fmt(format_args!("{}", self.l3))?;
|
||||||
|
printed = true;
|
||||||
|
}
|
||||||
|
if self.l4.is_some() {
|
||||||
|
if printed {
|
||||||
|
f.write_str("\n")?;
|
||||||
|
}
|
||||||
|
f.write_fmt(format_args!("{}", self.l4))?;
|
||||||
|
printed = true;
|
||||||
|
}
|
||||||
|
if self.l7.is_some() {
|
||||||
|
if printed {
|
||||||
|
f.write_str("\n")?;
|
||||||
|
}
|
||||||
|
f.write_fmt(format_args!("{}", self.l7))?;
|
||||||
|
printed = true;
|
||||||
|
}
|
||||||
|
if !self.body.is_empty() {
|
||||||
|
if printed {
|
||||||
|
f.write_str("\n")?;
|
||||||
|
}
|
||||||
|
f.write_fmt(format_args!("{}", rhexdump::hexdump(&self.body)))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Packet {
|
||||||
|
pub fn new(body: Vec<u8>) -> Self {
|
||||||
|
Self {
|
||||||
|
l2: NoHeader,
|
||||||
|
l3: NoHeader,
|
||||||
|
l4: NoHeader,
|
||||||
|
l7: NoHeader,
|
||||||
|
body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<L2: OptionalHeader, L3: OptionalHeader, L4: OptionalHeader, L7: OptionalHeader>
|
||||||
|
Packet<L2, L3, L4, L7>
|
||||||
|
{
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn body(&self) -> &[u8] {
|
||||||
|
self.body.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn set_l2<T: Header>(self, l2: T) -> Packet<T, L3, L4, L7> {
|
||||||
|
Packet {
|
||||||
|
l2,
|
||||||
|
l3: self.l3,
|
||||||
|
l4: self.l4,
|
||||||
|
l7: self.l7,
|
||||||
|
body: self.body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn set_l3<T: Header>(self, l3: T) -> Packet<L2, T, L4, L7> {
|
||||||
|
Packet {
|
||||||
|
l2: self.l2,
|
||||||
|
l3,
|
||||||
|
l4: self.l4,
|
||||||
|
l7: self.l7,
|
||||||
|
body: self.body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn set_l4<T: Header>(self, l4: T) -> Packet<L2, L3, T, L7> {
|
||||||
|
Packet {
|
||||||
|
l2: self.l2,
|
||||||
|
l3: self.l3,
|
||||||
|
l4,
|
||||||
|
l7: self.l7,
|
||||||
|
body: self.body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn set_l7<T: Header>(self, l7: T) -> Packet<L2, L3, L4, T> {
|
||||||
|
Packet {
|
||||||
|
l2: self.l2,
|
||||||
|
l3: self.l3,
|
||||||
|
l4: self.l4,
|
||||||
|
l7,
|
||||||
|
body: self.body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn serialize(&self) -> Vec<u8> {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
self.l2.serialize_stage1(&mut buf);
|
||||||
|
let l3_start = buf.len();
|
||||||
|
self.l3.serialize_stage1(&mut buf);
|
||||||
|
let l4_start = buf.len();
|
||||||
|
self.l4.serialize_stage1(&mut buf);
|
||||||
|
let l7_start = buf.len();
|
||||||
|
self.l7.serialize_stage1(&mut buf);
|
||||||
|
let body_start = buf.len();
|
||||||
|
buf.extend_from_slice(&self.body);
|
||||||
|
let (l7_hdr, l7_body) = &mut buf[l7_start..].split_at_mut(body_start - l7_start);
|
||||||
|
L7::serialize_stage2(l7_hdr, l7_body);
|
||||||
|
let (l4_hdr, l4_body) = &mut buf[l4_start..].split_at_mut(l7_start - l4_start);
|
||||||
|
L4::serialize_stage2(l4_hdr, l4_body);
|
||||||
|
let (l3_hdr, l3_body) = &mut buf[l3_start..].split_at_mut(l4_start - l3_start);
|
||||||
|
L3::serialize_stage2(l3_hdr, l3_body);
|
||||||
|
let (l2_hdr, l2_body) = &mut buf.split_at_mut(l3_start);
|
||||||
|
L2::serialize_stage2(l2_hdr, l2_body);
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L2: Header, L3: OptionalHeader, L4: OptionalHeader, L7: OptionalHeader>
|
||||||
|
Packet<L2, L3, L4, L7>
|
||||||
|
{
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn l2(&self) -> &L2 {
|
||||||
|
&self.l2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<L2: OptionalHeader, L3: Header, L4: OptionalHeader, L7: OptionalHeader>
|
||||||
|
Packet<L2, L3, L4, L7>
|
||||||
|
{
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn l3(&self) -> &L3 {
|
||||||
|
&self.l3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<L2: OptionalHeader, L3: OptionalHeader, L4: Header, L7: OptionalHeader>
|
||||||
|
Packet<L2, L3, L4, L7>
|
||||||
|
{
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn l4(&self) -> &L4 {
|
||||||
|
&self.l4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<L2: OptionalHeader, L3: OptionalHeader, L4: OptionalHeader, L7: Header>
|
||||||
|
Packet<L2, L3, L4, L7>
|
||||||
|
{
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn l7(&self) -> &L7 {
|
||||||
|
&self.l7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Packet {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn parse<T: HeaderParse>(self) -> Result<Packet<T>, (Self, T::ParseError)> {
|
||||||
|
match T::parse(&self.body) {
|
||||||
|
Ok((header, body)) => Ok(Packet {
|
||||||
|
l2: header,
|
||||||
|
l3: self.l3,
|
||||||
|
l4: self.l4,
|
||||||
|
l7: self.l7,
|
||||||
|
body: body.to_vec(),
|
||||||
|
}),
|
||||||
|
Err(err) => Err((self, err)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L2: Header> Packet<L2> {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn parse<T: HeaderParse>(self) -> Result<Packet<L2, T>, (Self, T::ParseError)> {
|
||||||
|
match T::parse(&self.body) {
|
||||||
|
Ok((header, body)) => Ok(Packet {
|
||||||
|
l2: self.l2,
|
||||||
|
l3: header,
|
||||||
|
l4: self.l4,
|
||||||
|
l7: self.l7,
|
||||||
|
body: body.to_vec(),
|
||||||
|
}),
|
||||||
|
Err(err) => Err((self, err)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L2: Header, L3: Header> Packet<L2, L3> {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn parse<T: HeaderParse>(self) -> Result<Packet<L2, L3, T>, (Self, T::ParseError)> {
|
||||||
|
match T::parse(&self.body) {
|
||||||
|
Ok((header, body)) => Ok(Packet {
|
||||||
|
l2: self.l2,
|
||||||
|
l3: self.l3,
|
||||||
|
l4: header,
|
||||||
|
l7: self.l7,
|
||||||
|
body: body.to_vec(),
|
||||||
|
}),
|
||||||
|
Err(err) => Err((self, err)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L2: Header, L3: Header, L4: Header> Packet<L2, L3, L4> {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
pub fn parse<T: HeaderParse>(self) -> Result<Packet<L2, L3, L4, T>, (Self, T::ParseError)> {
|
||||||
|
match T::parse(&self.body) {
|
||||||
|
Ok((header, body)) => Ok(Packet {
|
||||||
|
l2: self.l2,
|
||||||
|
l3: self.l3,
|
||||||
|
l4: self.l4,
|
||||||
|
l7: header,
|
||||||
|
body: body.to_vec(),
|
||||||
|
}),
|
||||||
|
Err(err) => Err((self, err)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user