Compare commits
155 Commits
main
...
tap-versio
| Author | SHA1 | Date | |
|---|---|---|---|
| 465648c5df | |||
| 2e823188ba | |||
| ea85aa1b76 | |||
| 03151cc90e | |||
| 0a40eac55e | |||
| f070bc713b | |||
| ee4d4f9cf6 | |||
| e8f4498806 | |||
| d0ec1e29c6 | |||
| 1de337f3d6 | |||
| 84022d5e34 | |||
| 2f11cef7d0 | |||
| eb18e2edde | |||
| 87a3643d56 | |||
| aa36c65194 | |||
| 9c75e81e63 | |||
| 4c2cbc9c09 | |||
| a361675d73 | |||
| 7767096909 | |||
| a3977ba52a | |||
| 503a9b9b6c | |||
| f58ead2d29 | |||
| b5227a9887 | |||
| 520ab964f3 | |||
| 9d0a5289ea | |||
| 0c045dd828 | |||
| 1789c8f62c | |||
| cb0e4cfbd5 | |||
| 85190447be | |||
| 4c0f101be8 | |||
| 6e2376e3d0 | |||
| 2101745c80 | |||
| 12dda5d35c | |||
| 6874bc5291 | |||
| 7e7a9ca2e6 | |||
| c3355db8ee | |||
| 73da6a8610 | |||
| 68867b8e55 | |||
| cb26b985dd | |||
| cf847e3241 | |||
| 95b43c73af | |||
| c5f5ea433a | |||
| 777d3bbc63 | |||
| 5136e9427b | |||
| 150d1c8e67 | |||
| 97a7403015 | |||
| 5594ce639b | |||
| 4cccf58b2e | |||
| 51cdeb285e | |||
| 03b8da17dc | |||
| 2774431d49 | |||
| 732b3f4a0a | |||
| f5e1cfff67 | |||
| 74a549dea9 | |||
| 240b60636f | |||
| 4aa6406428 | |||
| 2cff146f8c | |||
| e2d5b465f7 | |||
| caf62fe079 | |||
| be401afc7b | |||
| b10c721179 | |||
| 9cba4a976d | |||
| 1f46a15b11 | |||
| 2261d83e18 | |||
| 78351e0493 | |||
| f8c213c8c7 | |||
| 372afd470b | |||
| c6df26ac85 | |||
| fbe2aa12fb | |||
| 33a995e7dd | |||
| 321146f6ac | |||
| fa8a60a737 | |||
| 5249a271c5 | |||
| 814ab9181a | |||
| de8bf7cd31 | |||
| 46536fc587 | |||
| a52df35198 | |||
| 50db315c79 | |||
| c8c618015a | |||
| 651b9ba5cd | |||
| 46170feb5b | |||
| 31845c6707 | |||
| e8e8655100 | |||
| 00c5c67307 | |||
| 8902c27b1d | |||
| 8645b65534 | |||
| 9d52223f84 | |||
| be71e8404a | |||
| 13c287e089 | |||
| 6f895c3404 | |||
| c5b04eb843 | |||
| 214e73a0b9 | |||
| 6ef685420c | |||
| f9c33b564f | |||
| d8c908a0b2 | |||
| 2fc246a1fe | |||
| 89f7db657d | |||
| 43c2b01fca | |||
| b36f526cfc | |||
| 2096c3faac | |||
| 0300eb9ade | |||
| 6672e78c42 | |||
| b343124821 | |||
| fad345becb | |||
| 41a21188bf | |||
| 9861c4e850 | |||
| e003584463 | |||
| 6ab4f7042b | |||
| 1e429ad874 | |||
| c2a3048427 | |||
| 8db61a7bbb | |||
| 6f996d020a | |||
| df06803004 | |||
| d9b1329e6a | |||
| ec68a54957 | |||
| 9a72c29870 | |||
| 7fa2ffa04d | |||
| 6e8f4583d6 | |||
| 3f6da7c65e | |||
| 7e5792e190 | |||
| 4aa33a4e26 | |||
| 4fdcfa5bee | |||
| c070d345ad | |||
| 51c323008a | |||
| 63805d9a47 | |||
| 240a3d263e | |||
| c098a3c421 | |||
| b51c78bdfb | |||
| 966f054a63 | |||
| f3cbc1def2 | |||
| 7a75c1d171 | |||
| 93f3dbdcf9 | |||
| c28c9e2911 | |||
| 2b8a536151 | |||
| e32c640466 | |||
| 07c6536d5a | |||
| d1e30370fc | |||
| 69255d7907 | |||
| e7352c7b67 | |||
| c4c1f109b1 | |||
| cb71c53102 | |||
| 06d7819fd1 | |||
| 220cee4a89 | |||
| 9b1710e590 | |||
| 7c4bb64bd4 | |||
| 6dd3d8694c | |||
| 1a5e1d8be3 | |||
| c130dcc83e | |||
| 53b0be35a7 | |||
| 893eef5871 | |||
| 32bbdc4f78 | |||
| af56edd331 | |||
| 3e4fe82a59 | |||
| 302300294a | |||
| 56d6a35fea |
13
.cargo/config.toml
Executable file
13
.cargo/config.toml
Executable file
@ -0,0 +1,13 @@
|
||||
[target.x86_64-unknown-linux-musl]
|
||||
linker = "musl-gcc"
|
||||
|
||||
[target.aarch64-unknown-linux-musl]
|
||||
linker = "aarch64-linux-musl-gcc"
|
||||
ar = "aarch64-linux-musl-gcc"
|
||||
strip = { path = "aarch64-linux-musl-strip" }
|
||||
|
||||
[target.x86_64-pc-windows-gnu]
|
||||
linker = "x86_64-w64-mingw32-gcc"
|
||||
|
||||
# [build]
|
||||
# target = "x86_64-unknown-linux-gnu"
|
||||
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
debian/postinst text eol=lf
|
||||
debian/postrm text eol=lf
|
||||
*.sh text eol=lf
|
||||
*.service text eol=lf
|
||||
12
.gitignore
vendored
Normal file → Executable file
12
.gitignore
vendored
Normal file → Executable file
@ -4,3 +4,15 @@
|
||||
.client
|
||||
.output
|
||||
sdlan
|
||||
sdlan.exe
|
||||
*.bak
|
||||
*.a
|
||||
*.o
|
||||
*.tar
|
||||
*.tar.gz
|
||||
*.tgz
|
||||
/punchnet
|
||||
/ca
|
||||
.access_token
|
||||
.host
|
||||
.config
|
||||
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
// "rust-analyzer.cargo.target": "x86_64-pc-windows-gnu",
|
||||
// "rust-analyzer.cargo.target": "x86_64-unknown-linux-gnu",
|
||||
// "rust-analyzer.cargo.features": ["tun"]
|
||||
}
|
||||
3805
Cargo.lock
generated
3805
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
70
Cargo.toml
Normal file → Executable file
70
Cargo.toml
Normal file → Executable file
@ -1,9 +1,13 @@
|
||||
[package]
|
||||
name = "sdlan-rs"
|
||||
version = "0.1.0"
|
||||
name = "punchnet"
|
||||
version = "1.2.7"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
description = "punchnet client"
|
||||
|
||||
[dependencies]
|
||||
crc = "3.2.1"
|
||||
crc32fast = "1.4.2"
|
||||
dashmap = "6.0.1"
|
||||
dns-lookup = "2.0.4"
|
||||
etherparse = "0.15.0"
|
||||
@ -13,13 +17,71 @@ num_enum = "0.7.2"
|
||||
once_cell = "1.19.0"
|
||||
prost = "0.12.6"
|
||||
prost-build = "0.12.6"
|
||||
rand = "0.8.5"
|
||||
reqwest = { version = "0.12.24", default-features = false, features = ["json", "rustls-tls"] }
|
||||
rsa = "0.9.6"
|
||||
sdlan-sn-rs = { git = "ssh://git@git.asxalex.pw/sdlan-v2/sdlan-rs.git" }
|
||||
# sdlan-sn-rs = { git = "ssh://git@git2.asxalex.pw/sdlan-v2/sdlan-rs.git" }
|
||||
sdlan-sn-rs = {git = "https://gitea.s5s8.com/punchnet/sdlan-rs.git"}
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
# sdlan-sn-rs = {path = "../sdlan-rs"}
|
||||
# sdlan-sn-rs = { git = "https://git.asxalex.pw/sdlan-v2/sdlan-rs.git" }
|
||||
structopt = "0.3.26"
|
||||
tokio = { version = "1.38.0", futures = ["full"] }
|
||||
tokio = { version = "1.38.0", features = ["full"] }
|
||||
tokio-util = "0.7.11"
|
||||
tracing = "0.1.40"
|
||||
myactor = { git = "https://gitea.s5s8.com/rust/actor-rs.git" }
|
||||
bytes = "1.11.1"
|
||||
quinn = "0.11.9"
|
||||
rustls = { version = "0.23.37", features = ["ring", "std"]}
|
||||
rustls-pemfile = "2.2.0"
|
||||
clap = { version = "4.5.60", features = ["derive", "env"] }
|
||||
rpassword = "7.4.0"
|
||||
serde_json = "1.0.149"
|
||||
chacha20poly1305 = "0.10.1"
|
||||
hmac = "0.12.1"
|
||||
md-5 = "0.10.6"
|
||||
hex = "0.4.3"
|
||||
ahash = "0.8.12"
|
||||
ipnet = "2.12.0"
|
||||
arc-swap = "1.9.0"
|
||||
rustls-native-certs = "0.8.3"
|
||||
simple-dns = "0.11.2"
|
||||
default-net = "0.22.0"
|
||||
socket2 = "0.6.3"
|
||||
hostname = "0.4.2"
|
||||
sysinfo = "0.38.4"
|
||||
tun-rs = { version = "2.8.5", features = ["async"] }
|
||||
# rolling-file = { path = "../rolling-file" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2.178"
|
||||
daemonize = "0.5.0"
|
||||
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
wintun = "0.4.0"
|
||||
winapi = "0.3.9"
|
||||
windows-sys = "0.61.2"
|
||||
|
||||
[features]
|
||||
tun = []
|
||||
|
||||
[build-dependencies]
|
||||
cargo-deb = "3.6.2"
|
||||
|
||||
[package.metadata.deb]
|
||||
maintainer = "alex <asxalex@163.com>"
|
||||
copyright = "2025, alex"
|
||||
# license-file = ["MIT"]
|
||||
depends = "$auto"
|
||||
|
||||
## assets
|
||||
assets = [
|
||||
# executable
|
||||
["target/release/punchnet", "usr/local/punchnet/punchnet", "755"],
|
||||
["punchnet.service", "etc/systemd/system/punchnet.service", "755"],
|
||||
# lib
|
||||
# ["libtuntap.so", "usr/lib/", "755"],
|
||||
]
|
||||
|
||||
maintainer-scripts = "debian"
|
||||
|
||||
44
Makefile
Normal file → Executable file
44
Makefile
Normal file → Executable file
@ -1,9 +1,47 @@
|
||||
linux:
|
||||
self: libtun-so
|
||||
RUSTFLAGS="-L ." cargo build --release
|
||||
|
||||
linux: libtun-so
|
||||
RUSTFLAGS="-L ." cargo build --release --target x86_64-unknown-linux-musl
|
||||
|
||||
aarch64: libtun-so-aarch64
|
||||
RUSTFLAGS="-L ." cargo build --release --target aarch64-unknown-linux-gnu
|
||||
|
||||
|
||||
linux-tun: libtun-so
|
||||
RUSTFLAGS="-L ." cargo build --features "tun" --release
|
||||
|
||||
win:
|
||||
cargo build --release --target x86_64-pc-windows-gnu
|
||||
|
||||
pb:
|
||||
cargo run --bin build_pb
|
||||
mv src/pb/_.rs src/pb/message.rs
|
||||
# mv src/pb/_.rs src/pb/message.rs
|
||||
|
||||
libtun-so-clang:
|
||||
cd src/network && clang -fPIC -shared -o libtuntap.so tuntap.c && cd -
|
||||
|
||||
libtun-so:
|
||||
cd src/network && gcc -fPIC -shared -o libtuntap.so tuntap.c && cd -
|
||||
rm libtuntap.* && cd src/network && gcc -fPIC -shared -o libtuntap.so tuntap.c && cp libtuntap.so ../.. && cd -
|
||||
|
||||
libtun-aarch64-musl:
|
||||
rm -rf libtuntap.* && touch libtuntap.so && cd src/network && aarch64-linux-musl-gcc -c tuntap.c -o libtuntap.o && aarch64-linux-musl-ar rcs libtuntap.a libtuntap.o && cp libtuntap.a ../.. && cd -
|
||||
|
||||
pack:
|
||||
tar -czvf punchnet.tar.gz punchnet punchnet.service libtuntap.so install.sh
|
||||
|
||||
libtun-musl:
|
||||
rm libtuntap.* && touch libtuntap.so && cd src/network && musl-gcc -c tuntap.c -o libtuntap.o && ar rcs libtuntap.a libtuntap.o && cp libtuntap.a ../.. && cd -
|
||||
|
||||
|
||||
deb-musl:
|
||||
cargo deb --target x86_64-unknown-linux-musl --deb-revision="2-acl-static"
|
||||
|
||||
deb: libtun-so
|
||||
RUSTFLAGS="-L ." cargo deb --deb-revision="1-dynamic"
|
||||
|
||||
deb-aarch64-musl: libtun-aarch64-musl
|
||||
RUSTFLAGS="-L ." cargo deb --target aarch64-unknown-linux-musl --deb-revision="1-static"
|
||||
|
||||
deb-aarch64:
|
||||
cargo deb --target aarch64-unknown-linux-musl --deb-revision="2-acl-static"
|
||||
|
||||
121
code_review_report.md
Normal file
121
code_review_report.md
Normal file
@ -0,0 +1,121 @@
|
||||
# Punchnet (sdlan-lib-rs) Code Review Report
|
||||
|
||||
This report provides a detailed, comprehensive review of the `punchnet` SD-LAN (Software-Defined LAN / VPN) client repository. It covers the overall architecture, highlights key design strengths, details security and code quality risks, and suggests practical refactoring opportunities.
|
||||
|
||||
---
|
||||
|
||||
## 1. Repository Overview & Architecture
|
||||
|
||||
`punchnet` is a virtual networking client written in Rust designed to establish secure, peer-to-peer Software-Defined LAN connections.
|
||||
|
||||
The architecture consists of two main planes:
|
||||
1. **Control Plane (QUIC)**: Communicates with a centralized supernode over QUIC (via `quinn`) to handle registration (`RegisterSuper`), policy exchange, peer discovery, and NAT type discovery.
|
||||
2. **Data Plane (UDP)**: Encrypted virtual network traffic is sent directly between peer nodes using UDP. It performs NAT traversal (using STUN-like hole punching) to establish direct P2P connections, falling back to relaying via the supernode if direct routing fails.
|
||||
|
||||
### Core Components
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[OS Virtual NIC: TUN/TAP] <-->|Read/Write L3/L2 Packets| B[Packet Routing Engine: packet.rs]
|
||||
B <-->|Encrypt/Decrypt: AES or ChaCha20| C[UDP Socket Engine: socks.rs]
|
||||
D[Control Plane: quic.rs] -->|Session Keys / ACL Rules| B
|
||||
D <-->|QUIC Protocol| E[Supernode Server]
|
||||
C <-->|P2P Encrypted Data| F[Remote Peers]
|
||||
B -->|Local Domain Filter| G[DNS Hijacking Proxy: dns.rs]
|
||||
G <-->|Proxy Queries| H[AliDNS 223.5.5.5 / Supernode DNS]
|
||||
```
|
||||
|
||||
* **Platform-specific NIC Adapters** (`src/network/tun_win.rs` & `src/network/tun_linux.rs`): Handles raw packet read/write. Windows implements L3 TUN using the Wintun driver (relying on `wintun.dll`), simulating Ethernet frames (L2) locally. Linux binds directly to `/dev/net/tun` via `ioctl` (supported by helper C-code `tuntap.c`).
|
||||
* **Routing & Encapsulation Engine** (`src/network/packet.rs` & `src/network/route.rs`): Identifies packet destination MACs and resolves them to peer IP/ports using a custom ARP table. Packs IP/Ethernet payloads into protobuf messages (`SdlData`), encrypts them, and forwards them. Uses a custom lock-free IP Trie (`src/utils/system_action.rs`) for prefix route resolution.
|
||||
* **DNS Proxy Engine** (`src/utils/dns.rs`): Automatically intercepts DNS queries. Queries for private virtual domains (e.g. `*.network_domain`) are sent to the supernode's DNS server (`server_ip:15353`), while other public queries are proxied to AliDNS (`223.5.5.5:53`) over dynamically rotated UDP sockets.
|
||||
|
||||
---
|
||||
|
||||
## 2. Key Design Highlights (Strengths)
|
||||
|
||||
* **Lock-Free Concurrent Routing Lookups**:
|
||||
Outbound lookups on the routing table (`RouteTable2`) happen on every single packet. The project handles this very efficiently by implementing an `IpTrie` wrapped in an `ArcSwap`. Write operations (inserting or clearing routes) clone the trie and perform an atomic pointer swap, enabling read threads to query routes concurrently without locking.
|
||||
* **Dynamic DNS Socket Rotation**:
|
||||
To prevent firewalls or stateful NAT routers from blocking persistent DNS proxy connections, `DynamicDNSClient` rotates its local UDP socket either every 60 seconds or after 100 queries. It spawns a new receiver thread on a fresh port and allows the old socket to time out and close gracefully, which is a highly resilient approach to DNS proxying.
|
||||
* **Clean Separation of Control and Data Planes**:
|
||||
Using QUIC for signaling and standard UDP for data plane routing provides a robust control connection (resilient to head-of-line blocking and network changes) while keeping the overhead of data forwarding as low as possible.
|
||||
|
||||
---
|
||||
|
||||
## 3. Security Vulnerabilities & Risks
|
||||
|
||||
> [!CAUTION]
|
||||
> ### 1. ACL/Firewall Rules Completely Bypassed
|
||||
> In [acl_session.rs](file:///d:/work/rust/sdlan-lib-rs/src/utils/acl_session.rs#L141-L146), the core firewall verification method `is_identity_ok` unconditionally returns `(true, false)`:
|
||||
> ```rust
|
||||
> pub fn is_identity_ok(&self, allow_routing: bool, identity: IdentityID, info: FiveTuple) -> (bool, ShouldRenew) {
|
||||
> if allow_routing {
|
||||
> return (true, false);
|
||||
> }
|
||||
> return (true, false);
|
||||
>
|
||||
> error!("is identity ok? {:?}", info);
|
||||
> // ... remaining validation logic is dead code ...
|
||||
> }
|
||||
> ```
|
||||
> **Risk**: Any peer on the SD-LAN network can send traffic to any port of the client, bypassing all security group policies pushed from the supernode server.
|
||||
|
||||
> [!WARNING]
|
||||
> ### 2. Hardcoded Signature Key for HTTP API
|
||||
> In [api/mod.rs](file:///d:/work/rust/sdlan-lib-rs/src/bin/punchnet/api/mod.rs#L10), the signing key for REST APIs is hardcoded:
|
||||
> ```rust
|
||||
> const DIGEST_KEY: &'static str = "H6p*2RfEu4ITcL";
|
||||
> ```
|
||||
> **Risk**: This key is used to sign requests sent to the central controller via HMAC-MD5 (sent as `X-sign`). Since the key is hardcoded in the client, an attacker could reverse-engineer the binary, retrieve the key, and forge authenticated API requests.
|
||||
|
||||
> [!WARNING]
|
||||
> ### 3. Weak Directory Permissions for Local Credentials
|
||||
> In [node.rs](file:///d:/work/rust/sdlan-lib-rs/src/network/node.rs#L64-L73) and [main.rs](file:///d:/work/rust/sdlan-lib-rs/src/bin/punchnet/main.rs#L344), the client directory `.client` is created inside the base directory to hold the generated RSA key pair (`id_rsa`/`id_rsa.pub`). The code does not restrict folder permissions on Unix.
|
||||
> **Risk**: If the app is run as root (needed to configure TUN/TAP) and saves files in a shared base directory (e.g. `/usr/local/punchnet`), other non-root local users might be able to read the private key file `id_rsa` if the default `umask` is weak.
|
||||
|
||||
---
|
||||
|
||||
## 4. Code Quality & Potential Bugs
|
||||
|
||||
### 1. DNS Settings Corruption on Improper Exit
|
||||
In [main.rs](file:///d:/work/rust/sdlan-lib-rs/src/bin/punchnet/main.rs#L268-L280), the code restores system DNS settings on `ctrl_c` by invoking `restore_dns`.
|
||||
* **Bug**: If the process crashes, panics, is killed via `kill -9` (`SIGKILL`), or system shutdown happens without triggering the tokio runtime signal handler, the system's DNS settings (e.g., `/etc/resolv.conf` on Linux or static DNS addresses on Windows) will remain pointed to the hijacked address `100.100.100.100`. This will break the host machine's internet connectivity until manually fixed.
|
||||
|
||||
### 2. PID File Race Conditions & False Positives
|
||||
In [main.rs](file:///d:/work/rust/sdlan-lib-rs/src/bin/punchnet/main.rs#L633-L645), `is_process_running()` checks for the PID file `/tmp/punchnet.pid` and verifies whether that process ID exists in the system:
|
||||
```rust
|
||||
fn is_pid_running(pid: u32) -> bool {
|
||||
let mut sys = sysinfo::System::new_all();
|
||||
sys.refresh_all();
|
||||
sys.process(sysinfo::Pid::from_u32(pid)).is_some()
|
||||
}
|
||||
```
|
||||
* **Bug**: If `punchnet` crashes, the PID file is left over. If another completely unrelated application starts later and gets assigned the exact same process ID, `punchnet` will refuse to launch, erroneously reporting `"process is running, aborting..."`. It is safer to verify if the process name matches `"punchnet"` or check the binary executable path.
|
||||
|
||||
### 3. Blocking Operations on Async Thread Pool
|
||||
In `async_main.rs` and `node.rs`, there are multiple instances where filesystem operations (`fs::read_to_string`, `fs::write`) or platform calls (`Command::new("netsh")`, `Command::new("route")`) are executed synchronously within async tokio contexts instead of using `tokio::fs` or `spawn_blocking`.
|
||||
* **Performance Impact**: These blocking system operations can stall the Tokio worker threads, reducing throughput and leading to increased latency in packet forwarding.
|
||||
|
||||
---
|
||||
|
||||
## 5. Suggestions for Improvement & Refactoring
|
||||
|
||||
1. **Fix the ACL Shortcut**:
|
||||
Remove the early `return (true, false)` from `is_identity_ok` in `src/utils/acl_session.rs` and fully integrate the rules received from the server to secure the client against unauthorized peer access.
|
||||
2. **Move Secret Keys to Environment/Config**:
|
||||
Remove `DIGEST_KEY` from source files. Instead, use a key derivation mechanism or load it from a secure local environment variable or configuration file.
|
||||
3. **Ensure Atomic & Safe File Permissions**:
|
||||
When creating folders or writing key pairs on Unix, restrict the directory permissions explicitly to `0700` and files to `0600` using standard library extensions:
|
||||
```rust
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::DirBuilderExt;
|
||||
let mut builder = std::fs::DirBuilder::new();
|
||||
builder.mode(0o700);
|
||||
builder.create(rsa_path)?;
|
||||
}
|
||||
```
|
||||
4. **Use Subprocess Wrappers and Async Routines**:
|
||||
Migrate blocking file I/O and process spawns to async counterparts (e.g. `tokio::fs` and `tokio::process::Command`) to prevent worker thread starvation.
|
||||
5. **Graceful Recovery of System DNS Settings**:
|
||||
Consider writing a secondary daemon / sentinel wrapper, or register a shell-level script trap to ensure that system DNS settings are automatically restored even under fatal panics/crashes.
|
||||
9
debian/postinst
vendored
Normal file
9
debian/postinst
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
#DEBHELPER#
|
||||
|
||||
ln -sf /usr/local/punchnet/punchnet /usr/bin/punchnet
|
||||
|
||||
exit 0;
|
||||
9
debian/postrm
vendored
Normal file
9
debian/postrm
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
#DEBHELPER#
|
||||
|
||||
rm -rf /usr/lib/libswscale.so.5
|
||||
|
||||
exit 0;
|
||||
13
install.sh
Normal file
13
install.sh
Normal file
@ -0,0 +1,13 @@
|
||||
uid=`id -u`
|
||||
|
||||
if [ "$uid" != "0" ]; then
|
||||
echo "need to be root"
|
||||
exit -1
|
||||
fi
|
||||
|
||||
mkdir -p /usr/local/punchnet
|
||||
cp punchnet /usr/local/punchnet
|
||||
cp libtuntap.so /usr/lib/
|
||||
cp punchnet.service /etc/systemd/system
|
||||
systemctl enable punchnet
|
||||
systemctl start punchnet
|
||||
158
message.proto
158
message.proto
@ -1,158 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
// 基础公共类型定义
|
||||
|
||||
message SDLV4Info {
|
||||
uint32 port = 1;
|
||||
bytes v4 = 2;
|
||||
uint32 nat_type = 3;
|
||||
}
|
||||
|
||||
message SDLV6Info {
|
||||
uint32 port = 1;
|
||||
bytes v6 = 2;
|
||||
}
|
||||
|
||||
// 设备网络地址信息
|
||||
message SDLDevAddr {
|
||||
uint32 network_id = 1;
|
||||
uint32 net_addr = 2;
|
||||
uint32 net_bit_len = 3;
|
||||
}
|
||||
|
||||
// tcp通讯消息
|
||||
message SDLEmpty {
|
||||
|
||||
}
|
||||
|
||||
message SDLRegisterSuper {
|
||||
uint32 version = 1;
|
||||
string installed_channel = 2;
|
||||
string client_id = 3;
|
||||
SDLDevAddr dev_addr = 4;
|
||||
string pub_key = 5;
|
||||
string token = 6;
|
||||
}
|
||||
|
||||
message SDLRegisterSuperAck {
|
||||
SDLDevAddr dev_addr = 1;
|
||||
bytes aes_key = 2;
|
||||
bytes known_ips = 3;
|
||||
uint32 upgrade_type = 4;
|
||||
optional string upgrade_prompt = 5;
|
||||
optional string upgrade_address = 6;
|
||||
}
|
||||
|
||||
message SDLRegisterSuperNak {
|
||||
uint32 error_code = 1;
|
||||
string error_message = 2;
|
||||
}
|
||||
|
||||
// 网络地址查询
|
||||
|
||||
message SDLQueryInfo {
|
||||
uint32 dst_ip = 1;
|
||||
}
|
||||
|
||||
message SDLPeerInfo {
|
||||
uint32 dst_ip = 1;
|
||||
SDLV4Info v4_info = 2;
|
||||
optional SDLV6Info v6_info = 3;
|
||||
}
|
||||
|
||||
// 事件定义
|
||||
|
||||
message SDLKnownIpEvent {
|
||||
uint32 ip = 1;
|
||||
}
|
||||
|
||||
message SDLDropIpEvent {
|
||||
uint32 ip = 1;
|
||||
}
|
||||
|
||||
message SDLNatChangedEvent {
|
||||
uint32 ip = 1;
|
||||
}
|
||||
|
||||
message SDLSendRegisterEvent {
|
||||
uint32 dst_ip = 1;
|
||||
uint32 nat_ip = 2;
|
||||
uint32 nat_port = 3;
|
||||
optional SDLV6Info v6_info = 4;
|
||||
}
|
||||
|
||||
message SDLNetworkShutdownEvent {
|
||||
string message = 1;
|
||||
}
|
||||
|
||||
// 命令定义
|
||||
|
||||
message SDLChangeNetworkCommand {
|
||||
SDLDevAddr dev_addr = 1;
|
||||
bytes aes_key = 2;
|
||||
bytes known_ips = 3;
|
||||
}
|
||||
|
||||
message SDLCommandAck {
|
||||
// status = true, 表示成功;status = false 表示失败,message是失败原因描述
|
||||
bool status = 1;
|
||||
optional string message = 2;
|
||||
}
|
||||
|
||||
message SDLFlows {
|
||||
// 服务器转发流量
|
||||
uint32 forward_num = 1;
|
||||
// p2p直接流量
|
||||
uint32 p2p_num = 2;
|
||||
// 接收的流量
|
||||
uint32 inbound_num = 3;
|
||||
}
|
||||
|
||||
// UDP通讯消息
|
||||
|
||||
message SDLStunRequest {
|
||||
uint32 cookie = 1;
|
||||
string client_id = 2;
|
||||
uint32 network_id = 3;
|
||||
uint32 ip = 4;
|
||||
uint32 nat_type = 5;
|
||||
optional SDLV6Info v6_info = 6;
|
||||
}
|
||||
|
||||
message SDLStunReply {
|
||||
uint32 cookie = 1;
|
||||
}
|
||||
|
||||
message SDLData {
|
||||
uint32 network_id = 1;
|
||||
uint32 src_ip = 2;
|
||||
uint32 dst_ip = 3;
|
||||
bool is_p2p = 4;
|
||||
uint32 ttl = 5;
|
||||
bytes data = 6;
|
||||
}
|
||||
|
||||
message SDLRegister {
|
||||
uint32 network_id = 1;
|
||||
uint32 src_ip = 2;
|
||||
uint32 dst_ip = 3;
|
||||
}
|
||||
|
||||
message SDLRegisterAck {
|
||||
uint32 network_id = 1;
|
||||
uint32 src_ip = 2;
|
||||
uint32 dst_ip = 3;
|
||||
}
|
||||
|
||||
// 网络类型探测
|
||||
|
||||
message SDLStunProbe {
|
||||
uint32 cookie = 1;
|
||||
uint32 attr = 2;
|
||||
}
|
||||
|
||||
message SDLStunProbeReply {
|
||||
uint32 cookie = 1;
|
||||
uint32 port = 2;
|
||||
uint32 ip = 3;
|
||||
}
|
||||
216
nat-simulator.sh
Executable file
216
nat-simulator.sh
Executable file
@ -0,0 +1,216 @@
|
||||
#!/bin/bash
|
||||
|
||||
## check for root
|
||||
|
||||
NAT_TYPE=
|
||||
UDP_PORT=
|
||||
IP=
|
||||
DELETE=0
|
||||
|
||||
OUTER_INTERFACE=eth0
|
||||
|
||||
OUTER_IP=`ifconfig ${OUTER_INTERFACE} | grep -e "\binet\b" | awk '{print $2}'`
|
||||
|
||||
options=$(getopt -o t:p:i: --long type:,port:,ip:,delete -- "$@")
|
||||
eval set -- "$options"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-t|--type)
|
||||
NAT_TYPE=$2
|
||||
shift 2
|
||||
;;
|
||||
-p|--port)
|
||||
PORT=$2
|
||||
shift 2
|
||||
;;
|
||||
-i|--ip)
|
||||
IP=$2
|
||||
shift 2
|
||||
;;
|
||||
--delete)
|
||||
DELETE=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo "invalid option: $1"
|
||||
exit -1;
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
port=$PORT
|
||||
ip=$IP
|
||||
|
||||
is_number() {
|
||||
re="^[1-9][0-9]*$"
|
||||
if [[ $1 =~ $re ]]; then
|
||||
return 0;
|
||||
fi
|
||||
return 1;
|
||||
}
|
||||
|
||||
is_valid_ip() {
|
||||
re="^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$"
|
||||
if [[ $1 =~ $re ]]; then
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
if ! is_number $port; then
|
||||
echo "invalid port: $port"
|
||||
exit -1;
|
||||
fi
|
||||
|
||||
if ! is_valid_ip $ip; then
|
||||
echo "invalid ip: $ip"
|
||||
exit -1;
|
||||
fi
|
||||
|
||||
PUNCHNET_TCP_PORT=18083
|
||||
## iptables' tcp POSTROUTING chain name
|
||||
POST_TCP=PUNCHNET-TCP
|
||||
PRE_UDP=PUNCHNET-PRE-UDP
|
||||
|
||||
IPSET_GROUP=allowed_ip_${ip}
|
||||
add_ipset_group() {
|
||||
ipset destroy ${IPSET_GROUP}
|
||||
ipset create ${IPSET_GROUP} hash:ip hashsize 4096
|
||||
}
|
||||
|
||||
add_ipset_group
|
||||
|
||||
ensure_rule() {
|
||||
table_name=$1
|
||||
chain_name=$2
|
||||
arguments=$3
|
||||
|
||||
## create table
|
||||
# iptables -t ${table_name} -N ${chain_name} > /dev/null 2>&1
|
||||
|
||||
## add the rule
|
||||
if ! iptables -t ${table_name} -C ${chain_name} ${arguments} > /dev/null 2>&1; then
|
||||
echo "executing: iptables -t ${table_name} -A ${chain_name} ${arguments} > /dev/null 2>&1"
|
||||
iptables -t ${table_name} -A ${chain_name} ${arguments} > /dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
|
||||
clear_rule() {
|
||||
table_name=$1
|
||||
chain_name=$2
|
||||
arguments=$3
|
||||
|
||||
iptables -t ${table_name} -D ${chain_name} ${arguments} > /dev/null 2>&1
|
||||
}
|
||||
|
||||
drop_user_chian() {
|
||||
table_name=$1
|
||||
chain_name=$2
|
||||
|
||||
iptables -t ${table_name} -F ${chain_name}
|
||||
iptables -t ${table_name} -X ${chain_name}
|
||||
}
|
||||
|
||||
|
||||
ensure_tcp_connection() {
|
||||
# create table anyhow
|
||||
iptables -t nat -N ${POST_TCP} > /dev/null 2>&1
|
||||
|
||||
ensure_rule "nat" "${POST_TCP}" "-p tcp --dport ${PUNCHNET_TCP_PORT} -j MASQUERADE"
|
||||
ensure_rule "nat" "${POST_TCP}" "-j RETURN"
|
||||
ensure_rule "nat" "POSTROUTING" "-p tcp -s ${ip} -j ${POST_TCP}"
|
||||
}
|
||||
|
||||
fullcone() {
|
||||
ensure_tcp_connection
|
||||
|
||||
# POSTROUTING for udp ports
|
||||
ensure_rule "nat" "POSTROUTING" "-o ${OUTER_INTERFACE} -p udp --sport ${port} -j SNAT --to-source ${OUTER_IP}:${port}"
|
||||
|
||||
# prerouting for udp to the very host
|
||||
ensure_rule "nat" "PREROUTING" "-p udp -i ${OUTER_INTERFACE} --dport ${port} -j DNAT --to-destination ${ip}:${port}"
|
||||
}
|
||||
|
||||
fullcone_clear() {
|
||||
clear_rule "nat" "POSTROUTING" "-o ${OUTER_INTERFACE} -p udp --sport ${port} -j MASQUERADE"
|
||||
clear_rule "nat" "PREROUTING" "-i ${OUTER_INTERFACE} -p udp --dport ${port} -j DNAT --to-destination ${ip}"
|
||||
}
|
||||
|
||||
restricted_cone() {
|
||||
fullcone
|
||||
# ensure_rule "filter" "FORWARD" "-i ${OUTER_INTERFACE} -p udp --dport ${port} -m state --state ESTABLISHED,RELATED -j LOG --log-prefix \\\"accept INPUT: \\\""
|
||||
ensure_rule "filter" "FORWARD" "-i ${OUTER_INTERFACE} -p udp --dport ${port} -m state --state ESTABLISHED,RELATED -j SET --add-set ${IPSET_GROUP} src"
|
||||
ensure_rule "filter" "FORWARD" "-i ${OUTER_INTERFACE} -p udp --dport ${port} -m state --state ESTABLISHED,RELATED -j ACCEPT"
|
||||
# ensure_rule "filter" "FORWARD" "-i ${OUTER_INTERFACE} -p udp --dport ${port} -m state --state ESTABLISHED,RELATED -j ACCEPT"
|
||||
# ensure_rule "filter" "FORWARD" "-i ${OUTER_INTERFACE} -p udp --dport ${port} -m state --state NEW -j LOG --log-prefix \"drop INPUT\""
|
||||
# ensure_rule "filter" "FORWARD" "-i ${OUTER_INTERFACE} -p udp --dport ${port} -m state --state NEW -m set ! --match-set allowed_ip src -j LOG"
|
||||
ensure_rule "filter" "FORWARD" "-i ${OUTER_INTERFACE} -p udp --dport ${port} -m state --state NEW -m set ! --match-set ${IPSET_GROUP} src -j DROP"
|
||||
ensure_rule "filter" "FORWARD" "-i ${OUTER_INTERFACE} -p udp --dport ${port} -m state --state NEW -m set --match-set ${IPSET_GROUP} src -j ACCEPT"
|
||||
}
|
||||
|
||||
restricted_cone_clear() {
|
||||
fullcone_clear
|
||||
clear_rule "filter" "FORWARD" "-i ${OUTER_INTERFACE} -p udp --dport ${port} -m state --state ESTABLISHED,RELATED -j ACCEPT"
|
||||
clear_rule "filter" "FORWARD" "-i ${OUTER_INTERFACE} -p udp --dport ${port} -m state --state NEW -j DROP"
|
||||
}
|
||||
|
||||
port_restricted_cone() {
|
||||
ensure_tcp_connection
|
||||
ensure_rule "nat" "POSTROUTING" "-o ${OUTER_INTERFACE} -p udp --sport ${port} -j MASQUERADE"
|
||||
}
|
||||
|
||||
port_restricted_cone_clear() {
|
||||
clear_rule "nat" "POSTROUTING" "-o ${OUTER_INTERFACE} -p udp --sport ${port} -j MASQUERADE"
|
||||
}
|
||||
|
||||
symmetric() {
|
||||
ensure_rule "nat" "POSTROUTING" "-o ${OUTER_INTERFACE} -p udp --sport ${port} -j MASQUERADE --random"
|
||||
}
|
||||
|
||||
symmetric_clear() {
|
||||
clear_rule "nat" "POSTROUTING" "-o ${OUTER_INTERFACE} -p udp --sport ${port} -j MASQUERADE --random"
|
||||
}
|
||||
|
||||
case $NAT_TYPE in
|
||||
"nat1")
|
||||
fullcone
|
||||
echo "full cone"
|
||||
;;
|
||||
"nat2")
|
||||
restricted_cone
|
||||
echo "restricted cone"
|
||||
;;
|
||||
"nat3")
|
||||
port_restricted_cone
|
||||
echo "port restricted cone"
|
||||
;;
|
||||
"nat4")
|
||||
symmetric
|
||||
echo "symmetric nat"
|
||||
;;
|
||||
*)
|
||||
echo "invalid nat type"
|
||||
;;
|
||||
esac
|
||||
|
||||
# iptables -t nat -C POSTROUTING -p tcp -j ${POST_TCP} > /dev/null 2>&1
|
||||
#
|
||||
#
|
||||
# iptables -t nat -F ${POST_TCP} > /dev/null 2&>1
|
||||
# iptables -t nat -N ${POST_TCP}
|
||||
# iptables -t nat -A POSTROUTING -p tcp -j ${POST_TCP}
|
||||
# iptables -t nat -A ${POST_TCP} -p tcp --dport ${PUNCHNET_TCP_PORT}
|
||||
# iptables -t nat -A ${POST_TCP} -j RETURN
|
||||
#
|
||||
# iptables -t nat -D POSTROUTING -p udp -j ${POST_UDP} > /dev/null 2&>1
|
||||
# iptables -t nat -F ${POST_UDP} > /dev/null 2&>1
|
||||
# iptables -t nat -N ${POST_UDP}
|
||||
#
|
||||
# iptables -t nat -A POSTROUTING -o eth0 -p tcp --dport 18083 -j MASQUERADE
|
||||
# iptables -t nat -A POSTROUTING -o eth0 -p udp --sport 7890 -j MASQUERADE
|
||||
# iptables -t nat -A PREROUTING -i eth0 -p udp --dport 7890 -j DNAT --to-destination 172.17.0.2
|
||||
6
proto/empty.proto
Normal file
6
proto/empty.proto
Normal file
@ -0,0 +1,6 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package message;
|
||||
|
||||
message SDLEmpty {
|
||||
}
|
||||
217
proto/message.proto
Normal file
217
proto/message.proto
Normal file
@ -0,0 +1,217 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package message;
|
||||
|
||||
// 基础公共类型定义
|
||||
|
||||
message SDLV4Info {
|
||||
uint32 port = 1;
|
||||
bytes v4 = 2;
|
||||
uint32 nat_type = 3;
|
||||
}
|
||||
|
||||
message SDLV6Info {
|
||||
uint32 port = 1;
|
||||
bytes v6 = 2;
|
||||
}
|
||||
|
||||
// 和super之间采用了quic协议通讯
|
||||
// 传输层采用: <<Len:16, PacketType:8, Payload/binary>>
|
||||
|
||||
message SDLWelcome {
|
||||
uint32 version = 1;
|
||||
// 服务器允许的最大双向流
|
||||
uint32 max_bidi_streams = 2;
|
||||
// 服务器允许的最大包
|
||||
uint32 max_packet_size = 3;
|
||||
// 心跳包的间隔
|
||||
uint32 heartbeat_sec = 4;
|
||||
}
|
||||
|
||||
// 这里修改成了扁平的结构, 否则有些字段不好找放的位置
|
||||
message SDLRegisterSuper {
|
||||
string client_id = 1;
|
||||
// 网络地址信息已经有https请求分配了
|
||||
// 注册的时候需要带上(network_id, mac, ip, mask_len, hostname)
|
||||
uint32 network_id = 2;
|
||||
bytes mac = 3;
|
||||
uint32 ip = 4;
|
||||
uint32 mask_len = 5;
|
||||
string hostname = 6;
|
||||
|
||||
string pub_key = 7;
|
||||
// 客户端使用http协议请求后端,通过token或者账号密码登录时, 统一返回一个access_token;
|
||||
// RegisterSuper的时候,验证凭证是否合法 (access_token)
|
||||
string access_token = 8;
|
||||
}
|
||||
|
||||
// 客户端的升级逻辑,在https的接口里面去完成
|
||||
// 部分逻辑会脱离quic去通讯,增加session_token校验
|
||||
message SDLRegisterSuperAck {
|
||||
// 目前支持aes, chacha20
|
||||
string algorithm = 1;
|
||||
bytes key = 2;
|
||||
// 逻辑分段,chacha20加密算法需要使用该字段
|
||||
uint32 region_id = 3;
|
||||
bytes session_token = 4;
|
||||
}
|
||||
|
||||
message SDLRegisterSuperNak {
|
||||
uint32 error_code = 1;
|
||||
string error_message = 2;
|
||||
}
|
||||
|
||||
// 网络地址查询
|
||||
message SDLQueryInfo {
|
||||
bytes dst_mac = 1;
|
||||
}
|
||||
|
||||
message SDLPeerInfo {
|
||||
bytes dst_mac = 1;
|
||||
optional SDLV4Info v4_info = 2;
|
||||
optional SDLV6Info v6_info = 3;
|
||||
}
|
||||
|
||||
// ARP查询相关
|
||||
// 真实的arp请求是通过广播的形式获取到的,但是针对于macos这种tun的实现;是能够分析出arp请求包的;
|
||||
// 对于当前网络来说,服务端是知道mac对应的ip地址的,因此没有必要广播;直接通过服务器端返回
|
||||
message SDLArpRequest {
|
||||
uint32 target_ip = 1;
|
||||
uint32 origin_ip = 2;
|
||||
bytes context = 3;
|
||||
}
|
||||
|
||||
message SDLArpResponse {
|
||||
uint32 target_ip = 1;
|
||||
bytes target_mac = 2;
|
||||
uint32 origin_ip = 3;
|
||||
bytes context = 4;
|
||||
}
|
||||
|
||||
// 权限请求查询相关
|
||||
message SDLPolicyRequest {
|
||||
uint32 src_identity_id = 1;
|
||||
uint32 dst_identity_id = 2;
|
||||
uint32 version = 3;
|
||||
}
|
||||
|
||||
// 基于quic通讯,rules部分已经没有了长度限制
|
||||
message SDLPolicyResponse {
|
||||
uint32 src_identity_id = 1;
|
||||
uint32 dst_identity_id = 2;
|
||||
// 版本号,客户端需要比较版本号确定是否覆盖; 请求端自己去管理版本号,服务端只是原样回写
|
||||
uint32 version = 3;
|
||||
// 1 + 2稀疏序列化规则, 按照: <<Proto:8, Port:16>> 这个格式序列号所有的规则信息; 下发的数据默认都是allow,deny规则的服务器端已经屏蔽
|
||||
bytes rules = 4;
|
||||
}
|
||||
|
||||
// 事件定义
|
||||
|
||||
message SDLEvent {
|
||||
// nat映射变化
|
||||
message NatChanged {
|
||||
bytes mac = 1;
|
||||
uint32 ip = 2;
|
||||
}
|
||||
|
||||
// 发送register消息
|
||||
message SendRegister {
|
||||
bytes dst_mac = 1;
|
||||
uint32 nat_ip = 2;
|
||||
uint32 nat_port = 3;
|
||||
uint32 nat_type = 4;
|
||||
optional SDLV6Info v6_info = 5;
|
||||
}
|
||||
|
||||
// 网络关闭
|
||||
message NetworkShutdown {
|
||||
string message = 1;
|
||||
}
|
||||
|
||||
oneof event {
|
||||
NatChanged nat_changed = 1;
|
||||
SendRegister send_register = 2;
|
||||
NetworkShutdown shutdown = 3;
|
||||
}
|
||||
}
|
||||
|
||||
// Command指令
|
||||
message SDLCommand {
|
||||
uint32 pkt_id = 1;
|
||||
// 出口节点控制
|
||||
message ExitNodeControl {
|
||||
int32 action = 1; // 必选:操作类型
|
||||
string remark = 2; // 可选:备注(方便日志/调试)
|
||||
}
|
||||
|
||||
oneof command {
|
||||
ExitNodeControl exit_node = 2;
|
||||
}
|
||||
}
|
||||
|
||||
message SDLCommandAck {
|
||||
uint32 pkt_id = 1;
|
||||
int32 code = 2;
|
||||
string message = 3;
|
||||
bytes data = 4;
|
||||
}
|
||||
|
||||
// UDP通讯消息
|
||||
|
||||
// client和stun之间的心跳包,客户端需要和super的udp之间的存活逻辑
|
||||
message SDLStunRequest {
|
||||
string client_id = 1;
|
||||
uint32 network_id = 2;
|
||||
bytes mac = 3;
|
||||
uint32 ip = 4;
|
||||
uint32 nat_type = 5;
|
||||
optional SDLV6Info v6_info = 6;
|
||||
bytes session_token = 7;
|
||||
}
|
||||
|
||||
message SDLStunReply {
|
||||
}
|
||||
|
||||
message SDLData {
|
||||
uint32 network_id = 1;
|
||||
bytes src_mac = 2;
|
||||
bytes dst_mac = 3;
|
||||
bool is_p2p = 4;
|
||||
uint32 ttl = 5;
|
||||
bytes data = 6;
|
||||
bytes session_token = 7;
|
||||
// 端通过https登录的时候,服务端会分配该端对应的权限标识
|
||||
// 后续的请求过程中需要带上这个值,对端通过这个值要判断对数据包是否放行
|
||||
uint32 identity_id = 8;
|
||||
}
|
||||
|
||||
// 网络类型探测
|
||||
|
||||
message SDLStunProbe {
|
||||
uint32 cookie = 1;
|
||||
uint32 attr = 2;
|
||||
// 增加step是为了方便端上判断,收到的请求和响应之间的映射关系;服务器端原样返回
|
||||
uint32 step = 3;
|
||||
}
|
||||
|
||||
message SDLStunProbeReply {
|
||||
uint32 cookie = 1;
|
||||
// 增加step是为了方便端上判断,收到的请求和响应之间的映射关系;服务器端原样返回
|
||||
uint32 step = 2;
|
||||
uint32 port = 3;
|
||||
uint32 ip = 4;
|
||||
}
|
||||
|
||||
// Node-Node之间的握手逻辑, 是基于udp传输的
|
||||
|
||||
message SDLRegister {
|
||||
uint32 network_id = 1;
|
||||
bytes src_mac = 2;
|
||||
bytes dst_mac = 3;
|
||||
}
|
||||
|
||||
message SDLRegisterAck {
|
||||
uint32 network_id = 1;
|
||||
bytes src_mac = 2;
|
||||
bytes dst_mac = 3;
|
||||
}
|
||||
12
punchnet.service
Normal file
12
punchnet.service
Normal file
@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=punchnet client
|
||||
After=network.target network.service
|
||||
Requires=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/usr/local/punchnet
|
||||
ExecStart=/usr/local/punchnet/punchnet start -f
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
4
remote.record
Normal file
4
remote.record
Normal file
@ -0,0 +1,4 @@
|
||||
origin git@git.asxalex.pw:rust/sdlan-lib-rs.git (fetch)
|
||||
origin git@git.asxalex.pw:rust/sdlan-lib-rs.git (push)
|
||||
origin2 http://101.43.184.190:3000/xlf/sdlan-lib-rs.git (fetch)
|
||||
origin2 http://101.43.184.190:3000/xlf/sdlan-lib-rs.git (push)
|
||||
3
src/bin/build_pb/main.rs
Normal file → Executable file
3
src/bin/build_pb/main.rs
Normal file → Executable file
@ -1,8 +1,9 @@
|
||||
fn main() {
|
||||
prost_build::Config::new()
|
||||
.out_dir("src/pb")
|
||||
.bytes(&[".message.SDLData.data"])
|
||||
// .out_dir("../tcp_mock/pb")
|
||||
.protoc_arg("--experimental_allow_proto3_optional")
|
||||
.compile_protos(&["message.proto"], &["."])
|
||||
.compile_protos(&["message.proto", "empty.proto"], &["./proto"])
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
402
src/bin/punchnet/api/mod.rs
Normal file
402
src/bin/punchnet/api/mod.rs
Normal file
@ -0,0 +1,402 @@
|
||||
use hmac::{Hmac, Mac as HamcMac};
|
||||
use md5::Md5;
|
||||
use punchnet::{
|
||||
get_hostname, set_access_token, CachedLoginInfo, ExitNodeConfiguration, TokenLogin,
|
||||
};
|
||||
use reqwest::Client;
|
||||
use sdlan_sn_rs::utils::{Mac, Result, SDLanError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::warn;
|
||||
|
||||
pub const TEST_PREFIX: &'static str = "https://test.punchsky.com/api";
|
||||
|
||||
const DIGEST_KEY: &'static str = "H6p*2RfEu4ITcL";
|
||||
type HmacMd5 = Hmac<Md5>;
|
||||
|
||||
trait HMacCalculator {
|
||||
fn calculate_hmac(&self) -> Result<String>;
|
||||
}
|
||||
|
||||
fn calculate_hmac<T: HMacCalculator>(cal: T) -> Result<String> {
|
||||
cal.calculate_hmac()
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct TokenLoginData<'a> {
|
||||
client_id: &'a str,
|
||||
token: &'a str,
|
||||
mac: &'a str,
|
||||
system: &'a str,
|
||||
version: &'a str,
|
||||
hostname: &'a str,
|
||||
}
|
||||
|
||||
fn do_calculate(data: &[u8]) -> Result<String> {
|
||||
let Ok(mut mac) = HmacMd5::new_from_slice(DIGEST_KEY.as_bytes()) else {
|
||||
return Err(SDLanError::IOError("failed to new hmac".to_owned()));
|
||||
};
|
||||
mac.update(data);
|
||||
let result = mac.finalize().into_bytes();
|
||||
Ok(hex::encode(result))
|
||||
}
|
||||
|
||||
impl<'a> HMacCalculator for TokenLoginData<'a> {
|
||||
fn calculate_hmac(&self) -> Result<String> {
|
||||
let data = format!(
|
||||
"client_id={}&hostname={}&mac={}&system={}&token={}&version={}",
|
||||
self.client_id, self.hostname, self.mac, self.system, self.token, self.version,
|
||||
);
|
||||
do_calculate(data.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct UserPassLoginData<'a> {
|
||||
client_id: &'a str,
|
||||
username: &'a str,
|
||||
password: &'a str,
|
||||
mac: &'a str,
|
||||
system: &'a str,
|
||||
version: &'a str,
|
||||
hostname: &'a str,
|
||||
}
|
||||
|
||||
impl HMacCalculator for UserPassLoginData<'_> {
|
||||
fn calculate_hmac(&self) -> Result<String> {
|
||||
let data = format!(
|
||||
"client_id={}&hostname={}&mac={}&password={}&system={}&username={}&version={}",
|
||||
self.client_id,
|
||||
self.hostname,
|
||||
self.mac,
|
||||
self.password,
|
||||
self.system,
|
||||
self.username,
|
||||
self.version,
|
||||
);
|
||||
do_calculate(data.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryInto<LoginData> for LoginResponse {
|
||||
type Error = SDLanError;
|
||||
fn try_into(self) -> std::result::Result<LoginData, Self::Error> {
|
||||
if self.code != 0 {
|
||||
return Err(SDLanError::IOError(format!("code error: {}", self.message)));
|
||||
}
|
||||
match self.data {
|
||||
Some(data) => {
|
||||
if let Err(_e) = set_access_token(&CachedLoginInfo {
|
||||
access_token: data.access_token.clone(),
|
||||
username: data.username.clone(),
|
||||
user_type: data.user_type.clone(),
|
||||
audit: data.audit,
|
||||
network_id: data.network_id,
|
||||
network_name: data.network_name.clone(),
|
||||
network_domain: data.network_domain.clone(),
|
||||
}) {
|
||||
eprintln!("failed to save access_token");
|
||||
}
|
||||
Ok(data)
|
||||
}
|
||||
None => Err(SDLanError::IOError(format!(
|
||||
"data is none: {}",
|
||||
self.message
|
||||
))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct LoginResponse {
|
||||
pub code: i32,
|
||||
pub message: String,
|
||||
pub data: Option<LoginData>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct NetworkListInfo {
|
||||
pub network_id: u32,
|
||||
pub network_name: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct LoginData {
|
||||
pub access_token: String,
|
||||
pub username: String,
|
||||
pub user_type: String,
|
||||
pub audit: u32,
|
||||
pub network_id: u32,
|
||||
pub network_name: String,
|
||||
pub network_domain: String,
|
||||
pub my_network_list: Vec<NetworkListInfo>,
|
||||
// pub exit_node: Vec<ExitNode>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct ExitNode {
|
||||
pub node_id: u32,
|
||||
pub node_name: String,
|
||||
pub gateway: String,
|
||||
pub target_network: String,
|
||||
}
|
||||
|
||||
async fn post_with_data<T, R>(url: &str, data: T) -> Result<R>
|
||||
where
|
||||
T: Serialize + HMacCalculator,
|
||||
R: for<'de> Deserialize<'de>,
|
||||
{
|
||||
let client = Client::new();
|
||||
|
||||
let Ok(hmac) = data.calculate_hmac() else {
|
||||
return Err(SDLanError::IOError("failed to calculate hmac".to_owned()));
|
||||
};
|
||||
|
||||
let mut retry_times = 0;
|
||||
let mut response_ok = false;
|
||||
loop {
|
||||
let response = match client
|
||||
.post(url)
|
||||
.header("X-sign", hmac.clone())
|
||||
.json(&data)
|
||||
.send()
|
||||
.await
|
||||
{
|
||||
Ok(response) => {
|
||||
response_ok = true;
|
||||
response
|
||||
}
|
||||
Err(e) => {
|
||||
retry_times += 1;
|
||||
if retry_times > 3 {
|
||||
return Err(SDLanError::IOError(format!("failed to do request: {}", e)));
|
||||
}
|
||||
warn!("retry connecting api in 5 seconds");
|
||||
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
// println!("status: {}", response.status());
|
||||
// let text = response.text().await.unwrap();
|
||||
let text = match response.text().await {
|
||||
Ok(text) => text,
|
||||
Err(e) => {
|
||||
return Err(SDLanError::IOError(format!(
|
||||
"failed to get response text: {}",
|
||||
e
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
||||
let data = match serde_json::from_str(&text) {
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
return Err(SDLanError::IOError(format!(
|
||||
"failed to deserialize text: {}",
|
||||
e
|
||||
)))
|
||||
}
|
||||
};
|
||||
return Ok(data);
|
||||
}
|
||||
|
||||
// println!("got test: {}", text);
|
||||
// let data = serde_json::from_str(&text).unwrap();
|
||||
|
||||
// println!("response: {}", response.text().await.unwrap());
|
||||
// let Ok(data) = response.json().await else {
|
||||
// return Err(SDLanError::IOError("failed to jsonify response".to_owned()));
|
||||
// };
|
||||
// println!("got respose: {:?}", data);
|
||||
|
||||
// Ok(data)
|
||||
}
|
||||
|
||||
pub async fn login_with_user_pass(
|
||||
url_prefix: &str,
|
||||
client_id: &str,
|
||||
username: &str,
|
||||
password: &str,
|
||||
mac: Mac,
|
||||
system: &str,
|
||||
version: &str,
|
||||
) -> Result<LoginResponse> {
|
||||
let mac = format!(
|
||||
"{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
|
||||
);
|
||||
|
||||
let hostname = get_hostname();
|
||||
|
||||
let post_data = UserPassLoginData {
|
||||
client_id,
|
||||
username,
|
||||
password,
|
||||
mac: &mac,
|
||||
system,
|
||||
version,
|
||||
hostname: &hostname,
|
||||
};
|
||||
|
||||
post_with_data(&format!("{}/auth/login", url_prefix), post_data).await
|
||||
}
|
||||
|
||||
pub async fn login_with_token(
|
||||
url_prefix: &str,
|
||||
client_id: &str,
|
||||
token: &str,
|
||||
mac: Mac,
|
||||
system: &str,
|
||||
version: &str,
|
||||
) -> Result<LoginResponse> {
|
||||
let mac = format!(
|
||||
"{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
|
||||
);
|
||||
|
||||
let hostname = get_hostname();
|
||||
|
||||
let post_data = TokenLoginData {
|
||||
client_id,
|
||||
token,
|
||||
mac: &mac,
|
||||
system,
|
||||
version,
|
||||
hostname: &hostname,
|
||||
};
|
||||
|
||||
post_with_data(&format!("{}/auth/token", url_prefix), post_data).await
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ConnectDisconnectRequest<'a> {
|
||||
client_id: &'a str,
|
||||
access_token: &'a str,
|
||||
}
|
||||
|
||||
impl HMacCalculator for ConnectDisconnectRequest<'_> {
|
||||
fn calculate_hmac(&self) -> Result<String> {
|
||||
let data = format!(
|
||||
"access_token={}&client_id={}",
|
||||
self.access_token, self.client_id
|
||||
);
|
||||
do_calculate(data.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct ConnectResponse {
|
||||
pub code: i32,
|
||||
pub message: String,
|
||||
pub data: Option<ConnectData>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct ConnectData {
|
||||
pub ip: String,
|
||||
pub mask_len: u8,
|
||||
pub hostname: String,
|
||||
pub identity_id: u32,
|
||||
pub resource_list: Vec<ResourceList>,
|
||||
pub node_list: Vec<NodeList>,
|
||||
// pub acl: Vec<ACL>,
|
||||
pub exit_node: Vec<ExitNode>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct ResourceList {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub url: String,
|
||||
pub connection_status: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct NodeList {
|
||||
pub name: String,
|
||||
pub connection_status: String,
|
||||
pub ip: String,
|
||||
pub system: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct DisconnectResponse {
|
||||
pub code: i32,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
pub async fn connect(
|
||||
url_prefix: &str,
|
||||
client_id: &str,
|
||||
access_token: &str,
|
||||
) -> Result<ConnectResponse> {
|
||||
let url = format!("{}/connect", url_prefix);
|
||||
let data = ConnectDisconnectRequest {
|
||||
client_id,
|
||||
access_token,
|
||||
};
|
||||
post_with_data(&url, data).await
|
||||
}
|
||||
|
||||
pub async fn disconnect(
|
||||
url_prefix: &str,
|
||||
client_id: &str,
|
||||
access_token: &str,
|
||||
) -> Result<DisconnectResponse> {
|
||||
let url = format!("{}/disconnect", url_prefix);
|
||||
let data = ConnectDisconnectRequest {
|
||||
client_id,
|
||||
access_token,
|
||||
};
|
||||
post_with_data(&url, data).await
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct GetResourceRequest<'a> {
|
||||
client_id: &'a str,
|
||||
access_token: &'a str,
|
||||
id: i32,
|
||||
}
|
||||
|
||||
impl HMacCalculator for GetResourceRequest<'_> {
|
||||
fn calculate_hmac(&self) -> Result<String> {
|
||||
let data = format!(
|
||||
"access_token={}&client_id={}&id={}",
|
||||
self.access_token, self.client_id, self.id,
|
||||
);
|
||||
do_calculate(data.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct GetResourceResponse {
|
||||
pub code: i32,
|
||||
pub message: String,
|
||||
pub data: Option<ResourceData>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct ResourceData {
|
||||
pub id: i32,
|
||||
pub name: String,
|
||||
pub ip: String,
|
||||
pub system: String,
|
||||
pub connection_status: String,
|
||||
pub resource_list: Vec<ResourceList>,
|
||||
}
|
||||
|
||||
pub async fn get_node_resource(
|
||||
url_prefix: &str,
|
||||
client_id: &str,
|
||||
access_token: &str,
|
||||
id: i32,
|
||||
) -> Result<GetResourceResponse> {
|
||||
let data = GetResourceRequest {
|
||||
client_id,
|
||||
access_token,
|
||||
id,
|
||||
};
|
||||
|
||||
let url = format!("{}/get_node_resource", url_prefix);
|
||||
post_with_data(&url, data).await
|
||||
}
|
||||
390
src/bin/punchnet/local_udp_info.rs
Normal file
390
src/bin/punchnet/local_udp_info.rs
Normal file
@ -0,0 +1,390 @@
|
||||
use std::{net::SocketAddr, sync::atomic::Ordering, time::Duration};
|
||||
|
||||
use ipnet::Ipv4Net;
|
||||
use num_enum::TryFromPrimitive;
|
||||
use punchnet::{LocalConfigInfo, RouteInfo, get_edge, load_configuration, set_route_from_net, store_configuration};
|
||||
use sdlan_sn_rs::{config, utils::{Mac, Result, SDLanError}};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::{net::UdpSocket, time::sleep};
|
||||
|
||||
|
||||
/// 这里是punchnet list等命令与punchnet主进程通信的消息结构
|
||||
/// data-type: u8
|
||||
/// data-len: u16
|
||||
/// data: json
|
||||
#[derive(TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum InfoFuncCode {
|
||||
// query for info
|
||||
Info = 0x00,
|
||||
// query for exit node list,
|
||||
ExitNodeList = 0x01,
|
||||
// set exit node
|
||||
ExitNodeSet = 0x02,
|
||||
ExitNodeStop = 0x03,
|
||||
|
||||
InfoFeedback = 0x80,
|
||||
ExitNodeListFeedback = 0x81,
|
||||
ExitNodeSetFeedback = 0x82,
|
||||
ExitNodeStopFeedback = 0x83,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct SingleExitNodeInfo {
|
||||
pub in_use: bool,
|
||||
pub id: u32,
|
||||
pub name: String,
|
||||
pub gw: String,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ExitNodeSetFeedback {
|
||||
pub ok: bool,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ExitNodeSetQuery {
|
||||
pub id: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ExitNodeListFeedback {
|
||||
pub nodes: Vec<SingleExitNodeInfo>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct InfoFeedback {
|
||||
pub mac: Mac,
|
||||
pub ip: u32,
|
||||
pub tx_p2p: u64,
|
||||
pub rx_p2p: u64,
|
||||
|
||||
pub tx_sup: u64,
|
||||
pub rx_sup: u64,
|
||||
}
|
||||
|
||||
pub async fn do_query<T: serde::de::DeserializeOwned>(data_type: u8, data: &[u8], timeout: Duration) -> Result<T>{
|
||||
let Ok(udp) = UdpSocket::bind("127.0.0.1:0").await else {
|
||||
eprintln!("failed to create");
|
||||
return Err(SDLanError::IOError("failed to create".to_owned()));
|
||||
};
|
||||
|
||||
let remote = format!("127.0.0.1:{}", LOCAL_INFO_UDP_PORT).parse::<SocketAddr>().unwrap();
|
||||
|
||||
let mut buf = Vec::with_capacity(1+2 + data.len());
|
||||
buf.push(data_type);
|
||||
buf.extend_from_slice(&(data.len() as u16).to_be_bytes());
|
||||
buf.extend_from_slice(data);
|
||||
|
||||
if let Err(e) = udp.send_to(buf.as_slice(), remote).await {
|
||||
eprintln!("failed to send query info");
|
||||
return Err(SDLanError::IOError("failed to send query".to_owned()));
|
||||
}
|
||||
|
||||
let mut buf = vec![0;1024];
|
||||
tokio::select! {
|
||||
data = udp.recv_from(&mut buf) => {
|
||||
if let Ok((size, _from)) = data {
|
||||
if size < 3 {
|
||||
eprintln!("no byte received");
|
||||
return Err(SDLanError::IOError("no bytes received".to_owned()));
|
||||
}
|
||||
buf.truncate(size);
|
||||
|
||||
let Ok(_typecode) = InfoFuncCode::try_from_primitive(buf[0]) else {
|
||||
eprintln!("invalid type: {}", buf[0]);
|
||||
return Err(SDLanError::IOError("invalid byte".to_owned()));
|
||||
};
|
||||
|
||||
let size = u16::from_be_bytes([buf[1], buf[2]]);
|
||||
if buf.len() as u16 != size + 3 {
|
||||
eprintln!("info length error: buf.len={}, size={}", buf.len(), size);
|
||||
return Err(SDLanError::IOError("info length error".to_owned()));
|
||||
}
|
||||
|
||||
let result = serde_json::from_slice::<T>(&buf[3..])?;
|
||||
return Ok(result);
|
||||
} else {
|
||||
eprintln!("failed to recv from punchnet");
|
||||
}
|
||||
return Err(SDLanError::IOError("failed to recv from punchnet".to_owned()));
|
||||
}
|
||||
_ = sleep(timeout) => {
|
||||
eprintln!("query timed out, is punchnet running?");
|
||||
return Err(SDLanError::IOError("timed out".to_owned()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn query_for_exit_node_stop() {
|
||||
let result = do_query::<ExitNodeSetFeedback>(InfoFuncCode::ExitNodeStop as u8, &[], Duration::from_secs(3)).await;
|
||||
match result {
|
||||
Ok(data) => {
|
||||
if data.ok {
|
||||
println!("set exit-node ok");
|
||||
} else {
|
||||
println!("failed to set exit-node: {}", data.message);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("failed to set exit-node: {}", e.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub async fn query_for_exit_node_set(nodeid: u32) {
|
||||
let data = ExitNodeSetQuery {
|
||||
id: nodeid,
|
||||
};
|
||||
let data = serde_json::to_vec(&data).unwrap();
|
||||
let result = do_query::<ExitNodeSetFeedback>(InfoFuncCode::ExitNodeSet as u8, &data, Duration::from_secs(3)).await;
|
||||
match result {
|
||||
Ok(data) => {
|
||||
if data.ok {
|
||||
println!("set exit-node ok");
|
||||
} else {
|
||||
println!("failed to set exit-node: {}", data.message);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("failed to set exit-node: {}", e.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn query_for_exit_node_list() {
|
||||
let result = do_query::<ExitNodeListFeedback>(InfoFuncCode::ExitNodeList as u8, &[], Duration::from_secs(3)).await;
|
||||
match result {
|
||||
Ok(data) => {
|
||||
println!("exit nodes:");
|
||||
for node in &data.nodes {
|
||||
if node.in_use {
|
||||
println!(" * id: {}, name: {}, gw: {}", node.id, node.name, node.gw);
|
||||
} else {
|
||||
println!(" id: {}, name: {}, gw: {}", node.id, node.name, node.gw);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("failed to query for exit node list: {}", e.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn query_for_info() {
|
||||
let result = do_query::<InfoFeedback>(InfoFuncCode::Info as u8, &[], Duration::from_secs(3)).await;
|
||||
match result {
|
||||
Ok(data) => {
|
||||
println!("punchnet info:");
|
||||
let ip = data.ip;
|
||||
println!(" ip: {}.{}.{}.{}",
|
||||
((ip>>24) & 0xff) as u8,
|
||||
((ip>>16) & 0xff) as u8,
|
||||
((ip>>8) & 0xff) as u8,
|
||||
((ip) & 0xff) as u8,
|
||||
);
|
||||
let mac = data.mac;
|
||||
println!(" mac: {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
|
||||
);
|
||||
|
||||
println!(" p2p tx: {} bytes", data.tx_p2p);
|
||||
println!(" p2p rx: {} bytes", data.rx_p2p);
|
||||
|
||||
println!(" super tx: {} bytes", data.tx_sup);
|
||||
println!(" super rx: {} bytes", data.rx_sup);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("failed to query for info: {}", e.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const LOCAL_INFO_UDP_PORT: u16 = 1789;
|
||||
|
||||
pub async fn handle_query_for_info_info() {
|
||||
let Ok(udp) = UdpSocket::bind(format!("127.0.0.1:{}", LOCAL_INFO_UDP_PORT)).await else {
|
||||
eprintln!("failed to create");
|
||||
return;
|
||||
};
|
||||
|
||||
let mut buf = vec![0u8; 1024];
|
||||
loop {
|
||||
if let Ok((size, from)) = udp.recv_from(&mut buf).await {
|
||||
let current_data = &buf[..size];
|
||||
handle_query_info(&udp, &buf[..size], from).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_query_info(udp: &UdpSocket, buf: &[u8], from: SocketAddr) {
|
||||
if buf.len() < 3 {
|
||||
return;
|
||||
}
|
||||
let tp = buf[0];
|
||||
|
||||
let Ok(typecode) = InfoFuncCode::try_from_primitive(tp) else {
|
||||
eprintln!("invalid type: {}", tp);
|
||||
return;
|
||||
};
|
||||
let size_field = u16::from_be_bytes([buf[1], buf[2]]);
|
||||
if size_field as usize + 3 != buf.len() {
|
||||
// error!("got invalid size");
|
||||
eprintln!("got invalid size");
|
||||
return;
|
||||
}
|
||||
|
||||
match typecode {
|
||||
InfoFuncCode::Info => {
|
||||
send_info_back(udp, from).await;
|
||||
}
|
||||
InfoFuncCode::ExitNodeList => {
|
||||
send_exit_node_back(udp, from).await;
|
||||
}
|
||||
InfoFuncCode::ExitNodeSet => {
|
||||
if let Ok(data) = serde_json::from_slice::<ExitNodeSetQuery>(&buf[3..]) {
|
||||
handle_set_exit_node(udp, from, data).await;
|
||||
}
|
||||
}
|
||||
InfoFuncCode::ExitNodeStop => {
|
||||
println!("got exit node stop");
|
||||
handle_stop_exit_node(udp, from).await;
|
||||
}
|
||||
_other => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_exit_node_back(udp: &UdpSocket, from: SocketAddr) {
|
||||
let config = load_configuration();
|
||||
|
||||
let mut temp = Vec::new();
|
||||
for (id, node) in &config.exit_node {
|
||||
temp.push(SingleExitNodeInfo {
|
||||
name: node.node_name.clone(),
|
||||
gw: node.gateway.clone(),
|
||||
id: node.node_id,
|
||||
in_use: node.in_use,
|
||||
})
|
||||
}
|
||||
let res = ExitNodeListFeedback {
|
||||
nodes: temp
|
||||
};
|
||||
let value = serde_json::to_string(&res).unwrap();
|
||||
|
||||
let mut content = Vec::with_capacity(value.len() + 3);
|
||||
content.push(InfoFuncCode::ExitNodeListFeedback as u8);
|
||||
|
||||
let size = value.len() as u16;
|
||||
let size_buf = size.to_be_bytes();
|
||||
|
||||
content.extend_from_slice(&size_buf);
|
||||
content.extend_from_slice(value.as_bytes());
|
||||
udp.send_to(content.as_slice(), from).await;
|
||||
}
|
||||
|
||||
fn handle_set_exit_node_inner(data: u32) -> std::result::Result<LocalConfigInfo, String>{
|
||||
let configuration = load_configuration();
|
||||
|
||||
let Some(data) = configuration.exit_node.get(&data) else {
|
||||
return Err("no such id found".to_string());
|
||||
};
|
||||
let Ok(ip) = data.gateway.parse() else {
|
||||
return Err("no such id found".to_string());
|
||||
};
|
||||
|
||||
let net = "0.0.0.0/0".parse::<Ipv4Net>().unwrap();
|
||||
set_route_from_net(vec![RouteInfo{
|
||||
net,
|
||||
gw: ip,
|
||||
}]);
|
||||
|
||||
return Ok(configuration);
|
||||
}
|
||||
|
||||
async fn _handle_set_exit_node(udp: &UdpSocket, from: SocketAddr, data:Option<ExitNodeSetQuery>, result_code: InfoFuncCode) {
|
||||
let result = if data.is_none() {
|
||||
let mut configuration = load_configuration();
|
||||
for (node, value) in configuration.exit_node.iter_mut() {
|
||||
value.in_use = false;
|
||||
}
|
||||
store_configuration(&configuration);
|
||||
set_route_from_net(vec![]);
|
||||
|
||||
ExitNodeSetFeedback {
|
||||
ok: true,
|
||||
message: "ok".to_string(),
|
||||
}
|
||||
} else {
|
||||
let data = data.unwrap();
|
||||
let result = match handle_set_exit_node_inner(data.id) {
|
||||
Ok(mut configuration) => {
|
||||
if let Some(conf) = configuration.exit_node.get_mut(&data.id) {
|
||||
conf.in_use = true;
|
||||
}
|
||||
store_configuration(&configuration);
|
||||
ExitNodeSetFeedback {
|
||||
ok: true,
|
||||
message: "ok".to_string(),
|
||||
}
|
||||
}
|
||||
Err(estr) => {
|
||||
ExitNodeSetFeedback {
|
||||
ok: false,
|
||||
message: estr,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
result
|
||||
};
|
||||
|
||||
let value = serde_json::to_string(&result).unwrap();
|
||||
|
||||
let mut content = Vec::with_capacity(value.len() + 3);
|
||||
content.push(result_code as u8);
|
||||
|
||||
let size = value.len() as u16;
|
||||
let size_buf = size.to_be_bytes();
|
||||
|
||||
content.extend_from_slice(&size_buf);
|
||||
content.extend_from_slice(value.as_bytes());
|
||||
udp.send_to(content.as_slice(), from).await;
|
||||
}
|
||||
|
||||
async fn handle_stop_exit_node(udp: &UdpSocket, from: SocketAddr) {
|
||||
_handle_set_exit_node(udp, from, None, InfoFuncCode::ExitNodeStopFeedback).await;
|
||||
}
|
||||
|
||||
async fn handle_set_exit_node(udp: &UdpSocket, from: SocketAddr, data:ExitNodeSetQuery) {
|
||||
_handle_set_exit_node(udp, from, Some(data), InfoFuncCode::ExitNodeSetFeedback).await;
|
||||
}
|
||||
|
||||
async fn send_info_back(udp: &UdpSocket, from: SocketAddr) {
|
||||
let edge = get_edge();
|
||||
let ip = edge.device_config.get_ip();
|
||||
let mac = edge.device_config.get_mac();
|
||||
let feedback = InfoFeedback {
|
||||
ip,
|
||||
mac,
|
||||
tx_p2p: edge.stats.tx_p2p.load(Ordering::Relaxed),
|
||||
rx_p2p: edge.stats.rx_p2p.load(Ordering::Relaxed),
|
||||
|
||||
tx_sup: edge.stats.tx_sup.load(Ordering::Relaxed),
|
||||
rx_sup: edge.stats.rx_sup.load(Ordering::Relaxed),
|
||||
};
|
||||
let value = serde_json::to_string(&feedback).unwrap();
|
||||
|
||||
let mut content = Vec::with_capacity(value.len() + 3);
|
||||
content.push(InfoFuncCode::InfoFeedback as u8);
|
||||
|
||||
let size = value.len() as u16;
|
||||
let size_buf = size.to_be_bytes();
|
||||
|
||||
content.extend_from_slice(&size_buf);
|
||||
content.extend_from_slice(value.as_bytes());
|
||||
udp.send_to(content.as_slice(), from).await;
|
||||
}
|
||||
669
src/bin/punchnet/main.rs
Executable file
669
src/bin/punchnet/main.rs
Executable file
@ -0,0 +1,669 @@
|
||||
mod api;
|
||||
mod local_udp_info;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
use std::time::Duration;
|
||||
|
||||
use clap::Parser;
|
||||
use punchnet::store_configuration;
|
||||
use punchnet::ExitNodeConfiguration;
|
||||
use sdlan_sn_rs::utils::SDLanError;
|
||||
use std::env;
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
use daemonize::Daemonize;
|
||||
|
||||
use futures_util::io;
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
use libc::{kill, SIGTERM};
|
||||
use punchnet::get_access_token;
|
||||
use punchnet::get_base_dir;
|
||||
use punchnet::get_edge;
|
||||
use punchnet::ip_string_to_u32;
|
||||
use punchnet::load_configuration;
|
||||
use punchnet::restore_dns;
|
||||
use punchnet::run_sdlan;
|
||||
use punchnet::set_access_token;
|
||||
use punchnet::set_base_dir;
|
||||
use punchnet::CachedLoginInfo;
|
||||
use punchnet::CommandLine;
|
||||
use punchnet::CommandLineInput2;
|
||||
use punchnet::Commands;
|
||||
use punchnet::ExitNodeCmd;
|
||||
use sdlan_sn_rs::log;
|
||||
|
||||
use sdlan_sn_rs::utils::create_or_load_uuid;
|
||||
use sdlan_sn_rs::utils::Mac;
|
||||
use sdlan_sn_rs::utils::Result;
|
||||
use tokio::runtime::Runtime;
|
||||
use tracing::debug;
|
||||
use tracing::error;
|
||||
|
||||
use std::net::ToSocketAddrs;
|
||||
|
||||
use crate::api::connect;
|
||||
use crate::api::login_with_token;
|
||||
use crate::api::login_with_user_pass;
|
||||
use crate::api::ConnectData;
|
||||
use crate::api::ConnectResponse;
|
||||
use crate::api::LoginData;
|
||||
use crate::api::LoginResponse;
|
||||
use crate::api::TEST_PREFIX;
|
||||
use crate::local_udp_info::handle_query_for_info_info;
|
||||
use crate::local_udp_info::query_for_exit_node_list;
|
||||
use crate::local_udp_info::query_for_exit_node_set;
|
||||
use crate::local_udp_info::query_for_exit_node_stop;
|
||||
use crate::local_udp_info::query_for_info;
|
||||
|
||||
const APP_USER_ENV_NAME: &str = "PUNCH_USER";
|
||||
const APP_PASS_ENV_NAME: &str = "PUNCH_PASS";
|
||||
const APP_TOKEN_ENV_NAME: &str = "PUNCH_TOKEN";
|
||||
|
||||
const PID_FILE: &str = "/tmp/punchnet.pid";
|
||||
|
||||
fn parse_connect_result(res: Result<ConnectResponse>) -> ConnectData {
|
||||
match res {
|
||||
Err(e) => {
|
||||
eprintln!("failed to connect: {}", e.as_str());
|
||||
process::exit(-3);
|
||||
}
|
||||
Ok(data) => {
|
||||
if data.code != 0 {
|
||||
eprintln!("failed to connect: {}", data.message);
|
||||
process::exit(-3);
|
||||
}
|
||||
match data.data {
|
||||
None => {
|
||||
eprintln!("connect empty response");
|
||||
process::exit(-3);
|
||||
}
|
||||
Some(data) => data,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_login_result(res: Result<LoginResponse>) -> LoginData {
|
||||
match res {
|
||||
Err(e) => {
|
||||
eprintln!("login error: {}", e.as_str());
|
||||
process::exit(-1);
|
||||
}
|
||||
Ok(data) => {
|
||||
if data.code != 0 {
|
||||
eprintln!("login error: {}", data.message);
|
||||
process::exit(-1);
|
||||
}
|
||||
let Some(data) = data.data else {
|
||||
eprintln!("login error: empty data");
|
||||
process::exit(-1);
|
||||
};
|
||||
|
||||
if let Err(_e) = set_access_token(&CachedLoginInfo {
|
||||
access_token: data.access_token.clone(),
|
||||
username: data.username.clone(),
|
||||
user_type: data.user_type.clone(),
|
||||
audit: data.audit,
|
||||
network_id: data.network_id,
|
||||
network_name: data.network_name.clone(),
|
||||
network_domain: data.network_domain.clone(),
|
||||
}) {
|
||||
eprintln!("failed to save access_token");
|
||||
}
|
||||
data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn daemonize_me(
|
||||
allow_routing: bool,
|
||||
connect_info: ConnectData,
|
||||
remembered: CachedLoginInfo,
|
||||
client_id: String,
|
||||
route_file: String,
|
||||
route_str: String,
|
||||
mac: Mac,
|
||||
take_over_dns: bool,
|
||||
) {
|
||||
let _guard = log::init_log(&format!("{}/.output", get_base_dir()));
|
||||
|
||||
let self_host_name = connect_info.hostname;
|
||||
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
let hostname = "test.punchsky.com".to_owned();
|
||||
let host = format!("{}:80", hostname);
|
||||
let mut server = String::new();
|
||||
if let Ok(addrs) = host.to_socket_addrs() {
|
||||
for addr in addrs {
|
||||
let h = addr.to_string().split(":").take(1).collect::<Vec<_>>()[0].to_owned();
|
||||
server = h
|
||||
}
|
||||
}
|
||||
if server.is_empty() {
|
||||
println!("failed to resolv host ip");
|
||||
return;
|
||||
}
|
||||
|
||||
// mod_hostname::get_hostname();
|
||||
/*
|
||||
let hostname = if cmd.hostname.len() == 0 {
|
||||
mod_hostname::get_hostname()
|
||||
} else {
|
||||
Some(cmd.hostname)
|
||||
};
|
||||
*/
|
||||
/*
|
||||
let hostname = if cmd.hostname.len() == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(cmd.hostname)
|
||||
};
|
||||
// let hostname = mod_hostname::get_hostname();
|
||||
println!("hostname = {:?}", hostname);
|
||||
*/
|
||||
|
||||
if let Err(e) = run_sdlan(
|
||||
client_id,
|
||||
mac,
|
||||
CommandLine {
|
||||
sn: server.clone() + ":1365",
|
||||
quic: server.clone() + ":443",
|
||||
quic_domain: hostname,
|
||||
nat_server1: server.clone() + ":1365",
|
||||
// nat_server2: "47.98.178.3:1265".to_owned(),
|
||||
nat_server2: server.clone() + ":1366",
|
||||
allow_routing: allow_routing,
|
||||
_drop_multicast: true,
|
||||
register_ttl: 1,
|
||||
mtu: 1400,
|
||||
name: "tau".to_owned(),
|
||||
tos: 0,
|
||||
local_port: 0,
|
||||
// token: cmd.token.clone(),
|
||||
// network_code: cmd.network_code.clone(),
|
||||
allow_p2p: true,
|
||||
route_file,
|
||||
route_str,
|
||||
take_dns: take_over_dns,
|
||||
},
|
||||
tx,
|
||||
&punchnet::get_install_channel(),
|
||||
server,
|
||||
Some(self_host_name),
|
||||
None,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
panic!("failed to run_sdlan: {}", e.as_str());
|
||||
};
|
||||
|
||||
let _ = rx.recv();
|
||||
|
||||
tokio::spawn(async {
|
||||
handle_query_for_info_info().await;
|
||||
});
|
||||
|
||||
let edge = get_edge();
|
||||
// let res = edge.start_without_feedback(cmd.token).await;
|
||||
/*
|
||||
let res = match edge
|
||||
.start_with_feedback(cmd.token, Duration::from_secs(3))
|
||||
.await
|
||||
{
|
||||
Ok(res) => res,
|
||||
Err(e) => {
|
||||
error!("failed to start: {:?}", e);
|
||||
exit(0);
|
||||
}
|
||||
};
|
||||
|
||||
debug!("here1");
|
||||
|
||||
if res.result != 0 {
|
||||
println!("failed to start: {}", res.message);
|
||||
if res.should_exit {
|
||||
exit(0);
|
||||
}
|
||||
// edge.stop().await;
|
||||
// exit(0);
|
||||
}
|
||||
*/
|
||||
let Ok(ip_net) = ip_string_to_u32(&connect_info.ip) else {
|
||||
eprintln!("got ip from network is invlid: {}", connect_info.ip);
|
||||
process::exit(-5);
|
||||
};
|
||||
if let Err(e) = edge
|
||||
.start_without_feedback(
|
||||
remembered.access_token.clone(),
|
||||
// String::new(),
|
||||
remembered.network_id,
|
||||
&remembered.network_domain,
|
||||
ip_net,
|
||||
connect_info.mask_len,
|
||||
connect_info.identity_id,
|
||||
// 0,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
{
|
||||
error!("failed to start: {:?}", e);
|
||||
}
|
||||
|
||||
/*
|
||||
tokio::time::sleep(Duration::from_secs(20)).await;
|
||||
edge.stop().await;
|
||||
*/
|
||||
|
||||
/*
|
||||
let mut stream =
|
||||
tokio::signal::unix::signal(tokio::signal::unix::SignalKind::user_defined1()).unwrap();
|
||||
|
||||
let mut started = true;
|
||||
*/
|
||||
println!("waiting for ctrl-c");
|
||||
match tokio::signal::ctrl_c().await {
|
||||
Ok(()) => {
|
||||
edge.quic_endpoint.close(0u32.into(), "bye".as_bytes());
|
||||
println!("quic is quitting");
|
||||
delete_pid_file();
|
||||
let _ = restore_dns(edge.take_over_dns);
|
||||
debug!("restored dns");
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("failed to listen for shutdown signal: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
std::process::exit(0);
|
||||
|
||||
// loop {
|
||||
// tokio::time::sleep(Duration::from_secs(10)).await;
|
||||
// /*
|
||||
// let sig = stream.recv().await;
|
||||
// if started {
|
||||
// edge.stop().await;
|
||||
// } else {
|
||||
// edge.start("0".to_owned()).await;
|
||||
// }
|
||||
// started = !started;
|
||||
// */
|
||||
// }
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
const SYSTEM: &'static str = "windows";
|
||||
#[cfg(target_os = "windows")]
|
||||
const DEFAULT_BASE_DIR: &'static str = ".";
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
const SYSTEM: &'static str = "linux";
|
||||
#[cfg(target_os = "linux")]
|
||||
const DEFAULT_BASE_DIR: &'static str = "/usr/local/punchnet";
|
||||
|
||||
async fn login_with_token_or_user_pass(
|
||||
client_id: &str,
|
||||
mac: Mac,
|
||||
system: &str,
|
||||
version: &str,
|
||||
token: &Option<String>,
|
||||
user: &Option<String>,
|
||||
pass: &Option<String>,
|
||||
) -> Result<LoginData> {
|
||||
if let Some(ref tk) = token {
|
||||
login_with_token(TEST_PREFIX, client_id, tk, mac, system, version)
|
||||
.await?
|
||||
.try_into()
|
||||
} else {
|
||||
if let (Some(ref user), Some(ref pass)) = (&user, &pass) {
|
||||
login_with_user_pass(TEST_PREFIX, &client_id, &user, &pass, mac, system, version)
|
||||
.await?
|
||||
.try_into()
|
||||
} else {
|
||||
// eprintln!("invalid argument, use --help for help");
|
||||
Err(SDLanError::IOError(
|
||||
"Invalid argument, use --help for help".to_string(),
|
||||
))
|
||||
// process::exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
unsafe {
|
||||
libc::umask(0o077);
|
||||
}
|
||||
|
||||
set_base_dir(DEFAULT_BASE_DIR);
|
||||
// let _guard = log::init_log(&format!("{}/.output", get_base_dir()));
|
||||
|
||||
let client_id = create_or_load_uuid(&format!("{}/.id", get_base_dir()), None).unwrap();
|
||||
|
||||
let local_config = load_configuration();
|
||||
|
||||
let mac = local_config.mac.unwrap();
|
||||
let allow_routing = local_config.allow_routing.unwrap_or(false);
|
||||
|
||||
let system = SYSTEM;
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
|
||||
// let cmd = CommandLineInput::from_args();
|
||||
let cmd = CommandLineInput2::parse();
|
||||
// println!("port is {}", cmd.port);
|
||||
|
||||
let mut should_daemonize = true;
|
||||
match &cmd.cmd {
|
||||
Commands::Version => {
|
||||
println!("punchnet version: {}", env!("CARGO_PKG_VERSION"));
|
||||
process::exit(0);
|
||||
}
|
||||
Commands::Login(user) => {
|
||||
let rt = Runtime::new().unwrap();
|
||||
rt.block_on(async move {
|
||||
if is_process_running() {
|
||||
eprintln!("process is running, aborting...");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
|
||||
if let Err(e) = login_with_token_or_user_pass(
|
||||
&client_id,
|
||||
mac,
|
||||
system,
|
||||
version,
|
||||
&user.token,
|
||||
&user.username,
|
||||
&user.password,
|
||||
)
|
||||
.await
|
||||
{
|
||||
eprintln!("failed to login: {}", e.as_str());
|
||||
std::process::exit(-1);
|
||||
}
|
||||
println!("login ok");
|
||||
});
|
||||
process::exit(0);
|
||||
}
|
||||
/*
|
||||
Commands::TokenLogin(tk) => {
|
||||
let rt = Runtime::new().unwrap();
|
||||
rt.block_on(async move {
|
||||
let _ = parse_login_result(
|
||||
login_with_token(TEST_PREFIX, &client_id, &tk.token, mac, system, version)
|
||||
.await,
|
||||
);
|
||||
println!("login ok");
|
||||
});
|
||||
process::exit(0);
|
||||
}
|
||||
*/
|
||||
Commands::ExitNode(cmd) => {
|
||||
let rt = Runtime::new().unwrap();
|
||||
rt.block_on(async move {
|
||||
match cmd {
|
||||
ExitNodeCmd::List => {
|
||||
query_for_exit_node_list().await;
|
||||
}
|
||||
ExitNodeCmd::Start(info) => {
|
||||
query_for_exit_node_set(info.id).await;
|
||||
}
|
||||
ExitNodeCmd::Stop => {
|
||||
query_for_exit_node_stop().await;
|
||||
}
|
||||
}
|
||||
});
|
||||
process::exit(0);
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
Commands::Stop => {
|
||||
match fs::read_to_string(PID_FILE) {
|
||||
Ok(content) => {
|
||||
let pid: i32 = match content.trim().parse() {
|
||||
Ok(value) => value,
|
||||
Err(_e) => {
|
||||
eprintln!("failed to parse value: {}", content);
|
||||
process::exit(-4);
|
||||
}
|
||||
};
|
||||
let result = unsafe {
|
||||
use libc::SIGINT;
|
||||
|
||||
kill(pid, SIGINT)
|
||||
};
|
||||
if result != 0 {
|
||||
let err = io::Error::last_os_error();
|
||||
eprintln!("failed to kill: {}", err);
|
||||
process::exit(-5);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("failed to read pid: {}", e);
|
||||
process::exit(-6);
|
||||
}
|
||||
}
|
||||
process::exit(0);
|
||||
}
|
||||
Commands::Info => {
|
||||
let rt = Runtime::new().unwrap();
|
||||
rt.block_on(async move {
|
||||
query_for_info().await;
|
||||
});
|
||||
process::exit(0);
|
||||
}
|
||||
Commands::AutoRun(runinfo) => {
|
||||
if is_process_running() {
|
||||
eprintln!("process is running, aborting...");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
if runinfo.foreground {
|
||||
should_daemonize = false;
|
||||
}
|
||||
}
|
||||
Commands::Start(startinfo) => {
|
||||
if is_process_running() {
|
||||
eprintln!("process is running, aborting...");
|
||||
std::process::exit(-1);
|
||||
}
|
||||
if startinfo.foreground {
|
||||
should_daemonize = false;
|
||||
}
|
||||
}
|
||||
_other => {
|
||||
// just fall through to next code
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
if should_daemonize {
|
||||
use std::fs::OpenOptions;
|
||||
let out = OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.open("/tmp/punchnet.out")
|
||||
.unwrap();
|
||||
let err = OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.open("/tmp/punchnet.err")
|
||||
.unwrap();
|
||||
|
||||
let daemonize = Daemonize::new()
|
||||
.pid_file(PID_FILE)
|
||||
.chown_pid_file(true)
|
||||
.working_directory(get_base_dir())
|
||||
.stdout(out)
|
||||
.stderr(err)
|
||||
.privileged_action(|| {});
|
||||
|
||||
match daemonize.start() {
|
||||
Ok(_) => {
|
||||
run_it(cmd, client_id, allow_routing, mac, system, version);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("failed to daemonize: {}", e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let pid = std::process::id();
|
||||
record_pid_file(pid);
|
||||
run_it(cmd, client_id, allow_routing, mac, system, version);
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
run_it(cmd, client_id, allow_routing, mac, system, version);
|
||||
}
|
||||
|
||||
fn record_exit_node(connect_info: &ConnectData) {
|
||||
let mut local_configuration = load_configuration();
|
||||
let mut temp = HashMap::new();
|
||||
for node in &connect_info.exit_node {
|
||||
temp.insert(
|
||||
node.node_id,
|
||||
ExitNodeConfiguration {
|
||||
in_use: false,
|
||||
node_id: node.node_id,
|
||||
node_name: node.node_name.clone(),
|
||||
gateway: node.gateway.clone(),
|
||||
target_network: node.target_network.clone(),
|
||||
},
|
||||
);
|
||||
}
|
||||
local_configuration.exit_node = temp;
|
||||
if let Err(e) = store_configuration(&local_configuration) {
|
||||
error!("failed to store configuration");
|
||||
}
|
||||
}
|
||||
|
||||
fn run_it(
|
||||
cmd: CommandLineInput2,
|
||||
client_id: String,
|
||||
allow_routing: bool,
|
||||
mac: Mac,
|
||||
system: &str,
|
||||
version: &str,
|
||||
) {
|
||||
let rt = Runtime::new().unwrap();
|
||||
match &cmd.cmd {
|
||||
Commands::Start(rtinfo) => rt.block_on(async move {
|
||||
let remembered_token = get_access_token();
|
||||
if remembered_token.is_none() {
|
||||
eprintln!("not logged in, should login with user/pass or token first");
|
||||
process::exit(-2);
|
||||
}
|
||||
let remembered = remembered_token.unwrap();
|
||||
let connect_info = parse_connect_result(
|
||||
connect(TEST_PREFIX, &client_id, &remembered.access_token).await,
|
||||
);
|
||||
|
||||
record_exit_node(&connect_info);
|
||||
daemonize_me(
|
||||
rtinfo.allow_routing || allow_routing,
|
||||
connect_info,
|
||||
remembered,
|
||||
client_id.clone(),
|
||||
rtinfo.route_file.clone(),
|
||||
rtinfo.route.clone(),
|
||||
mac,
|
||||
rtinfo.take_dns,
|
||||
)
|
||||
.await;
|
||||
}),
|
||||
Commands::AutoRun(tk) => rt.block_on(async move {
|
||||
loop {
|
||||
let data = match login_with_token_or_user_pass(
|
||||
&client_id,
|
||||
mac,
|
||||
system,
|
||||
version,
|
||||
&tk.token,
|
||||
&tk.username,
|
||||
&tk.password,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
eprintln!("failed to login: {}, will try in 10 seconds", e.as_str());
|
||||
tokio::time::sleep(Duration::from_secs(10)).await;
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let remembered_token = Some(CachedLoginInfo {
|
||||
access_token: data.access_token,
|
||||
username: data.username,
|
||||
user_type: data.user_type,
|
||||
audit: data.audit,
|
||||
network_id: data.network_id,
|
||||
network_name: data.network_name,
|
||||
network_domain: data.network_domain,
|
||||
});
|
||||
|
||||
let remembered = remembered_token.unwrap();
|
||||
|
||||
let connect_info = parse_connect_result(
|
||||
connect(TEST_PREFIX, &client_id, &remembered.access_token).await,
|
||||
);
|
||||
|
||||
record_exit_node(&connect_info);
|
||||
daemonize_me(
|
||||
tk.allow_routing || allow_routing,
|
||||
connect_info,
|
||||
remembered,
|
||||
client_id,
|
||||
tk.route_file.clone(),
|
||||
tk.route.clone(),
|
||||
mac,
|
||||
tk.take_dns,
|
||||
)
|
||||
.await;
|
||||
break;
|
||||
}
|
||||
}),
|
||||
|
||||
_other => {
|
||||
eprintln!("should not comes here");
|
||||
process::exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn record_pid_file(pid: u32) {
|
||||
fs::write(PID_FILE, pid.to_string());
|
||||
}
|
||||
|
||||
pub fn delete_pid_file() {
|
||||
fs::remove_file("/tmp/punchnet.pid");
|
||||
}
|
||||
|
||||
fn read_pid_file() -> Option<u32> {
|
||||
let content = fs::read_to_string(PID_FILE).ok()?;
|
||||
content.trim().parse().ok()
|
||||
}
|
||||
|
||||
fn is_pid_running(pid: u32) -> bool {
|
||||
let mut sys = sysinfo::System::new_all();
|
||||
sys.refresh_all();
|
||||
sys.process(sysinfo::Pid::from_u32(pid)).is_some()
|
||||
}
|
||||
|
||||
pub fn is_process_running() -> bool {
|
||||
if Path::new(PID_FILE).exists() {
|
||||
if let Some(pid) = read_pid_file() {
|
||||
if !is_pid_running(pid) {
|
||||
fs::remove_file(PID_FILE);
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
@ -1,82 +0,0 @@
|
||||
use sdlan_rs::get_edge;
|
||||
use sdlan_rs::run_sdlan;
|
||||
use sdlan_rs::CommandLine;
|
||||
use sdlan_rs::CommandLineInput;
|
||||
use sdlan_sn_rs::log;
|
||||
|
||||
use std::process::exit;
|
||||
use std::time::Duration;
|
||||
use structopt::StructOpt;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let _guard = log::init_log();
|
||||
|
||||
let cmd = CommandLineInput::from_args();
|
||||
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
let _ = run_sdlan(
|
||||
CommandLine {
|
||||
sn: "39.98.184.67:1265".to_owned(),
|
||||
tcp: "39.98.184.67:18083".to_owned(),
|
||||
nat_server1: "39.98.184.67:1265".to_owned(),
|
||||
nat_server2: "47.98.178.3:1265".to_owned(),
|
||||
_allow_routing: true,
|
||||
register_ttl: 1,
|
||||
mtu: 1290,
|
||||
name: "tau".to_owned(),
|
||||
tos: 0,
|
||||
token: "".to_owned(),
|
||||
allow_p2p: true,
|
||||
},
|
||||
tx,
|
||||
&sdlan_rs::get_install_channel(),
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
|
||||
let _ = rx.recv();
|
||||
|
||||
let edge = get_edge();
|
||||
// let res = edge.start_without_feedback(cmd.token).await;
|
||||
let Ok(res) = edge
|
||||
.start_with_feedback(cmd.token, Duration::from_secs(3))
|
||||
.await
|
||||
else {
|
||||
println!("failed to start1");
|
||||
exit(0);
|
||||
};
|
||||
|
||||
if res.result != 0 {
|
||||
println!("failed to start: {}", res.message);
|
||||
if res.should_exit {
|
||||
exit(0);
|
||||
}
|
||||
// edge.stop().await;
|
||||
// exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
tokio::time::sleep(Duration::from_secs(20)).await;
|
||||
edge.stop().await;
|
||||
*/
|
||||
|
||||
/*
|
||||
let mut stream =
|
||||
tokio::signal::unix::signal(tokio::signal::unix::SignalKind::user_defined1()).unwrap();
|
||||
|
||||
let mut started = true;
|
||||
*/
|
||||
loop {
|
||||
tokio::time::sleep(Duration::from_secs(10)).await;
|
||||
/*
|
||||
let sig = stream.recv().await;
|
||||
if started {
|
||||
edge.stop().await;
|
||||
} else {
|
||||
edge.start("0".to_owned()).await;
|
||||
}
|
||||
started = !started;
|
||||
*/
|
||||
}
|
||||
}
|
||||
30
src/config/mod.rs
Normal file → Executable file
30
src/config/mod.rs
Normal file → Executable file
@ -1,4 +1,9 @@
|
||||
pub const REGISTER_INTERVAL: u8 = 20;
|
||||
use std::fs;
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use sdlan_sn_rs::utils::Mac;
|
||||
|
||||
pub const REGISTER_INTERVAL: u8 = 3;
|
||||
pub const REGISTER_SUPER_INTERVAL: u16 = 20;
|
||||
|
||||
pub const MULITCAST_V4: [u8; 4] = [224, 0, 0, 69];
|
||||
@ -7,3 +12,26 @@ pub const MULTICAST_PORT: u16 = 1970;
|
||||
// pub const SUPER_ATTEMPTS_DEFAULT: u8 = 3;
|
||||
|
||||
pub const TCP_PING_TIME: u64 = 7;
|
||||
|
||||
pub const NULL_MAC: Mac = [0, 0, 0, 0, 0, 0];
|
||||
|
||||
pub static BASE_DIR: OnceCell<String> = OnceCell::new();
|
||||
|
||||
pub fn get_base_dir() -> &'static str {
|
||||
match BASE_DIR.get() {
|
||||
Some(data) => {
|
||||
data
|
||||
}
|
||||
None => {
|
||||
"."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_base_dir(base_dir: &str) {
|
||||
fs::create_dir_all(base_dir).unwrap();
|
||||
let base = base_dir.trim_end_matches("/");
|
||||
if let Err(_) = BASE_DIR.set(base.to_owned()) {
|
||||
println!("failed to set base dir");
|
||||
}
|
||||
}
|
||||
159
src/lib.rs
Normal file → Executable file
159
src/lib.rs
Normal file → Executable file
@ -3,24 +3,45 @@ mod network;
|
||||
mod pb;
|
||||
mod tcp;
|
||||
mod utils;
|
||||
mod quic;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::{sync::atomic::AtomicU8, time::Duration};
|
||||
|
||||
use std::net::{SocketAddr, ToSocketAddrs};
|
||||
|
||||
pub use network::get_edge;
|
||||
use network::{async_main, init_edge, NodeConfig};
|
||||
pub use network::get_install_channel;
|
||||
pub use network::{async_main, init_edge, NodeConfig, restore_dns};
|
||||
pub use network::{RouteInfo, set_route_from_net};
|
||||
use sdlan_sn_rs::utils::{Mac, save_to_file};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::net::{TcpListener, UdpSocket};
|
||||
use tokio::sync::mpsc::{channel, Sender};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{debug, error};
|
||||
pub use utils::{CommandLine, CommandLineInput};
|
||||
pub use network::get_install_channel;
|
||||
pub use utils::*;
|
||||
|
||||
pub use config::{get_base_dir, set_base_dir};
|
||||
|
||||
use sdlan_sn_rs::{
|
||||
peer::SdlanSock,
|
||||
utils::{create_or_load_uuid, get_sdlan_sock_from_socketaddr, Result, SDLanError},
|
||||
};
|
||||
|
||||
use crate::utils::DynamicDNSClient;
|
||||
|
||||
#[cfg(all(feature = "tun", target_os = "windows"))]
|
||||
compile_error!("tun feature is not supported on windows");
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ConnectionInfo {
|
||||
ConnState(ConnectionState),
|
||||
IPInfo(String),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[repr(u8)]
|
||||
pub enum ConnectionState {
|
||||
@ -30,28 +51,80 @@ pub enum ConnectionState {
|
||||
}
|
||||
|
||||
pub async fn run_sdlan(
|
||||
edge_uuid: String,
|
||||
mac: Mac,
|
||||
|
||||
args: CommandLine,
|
||||
sender: std::sync::mpsc::Sender<bool>,
|
||||
install_channel: &str,
|
||||
|
||||
connecting_chan: Option<Sender<ConnectionState>>
|
||||
// start_stop_sender: Sender<String>,
|
||||
// start_stop_receiver: Receiver<String>,
|
||||
server_ip: String,
|
||||
|
||||
hostname: Option<String>,
|
||||
connecting_chan: Option<Sender<ConnectionInfo>>,
|
||||
// report error to
|
||||
error_report_channel: Option<Sender<ErrorReport>>,
|
||||
) -> Result<()> {
|
||||
let (start_stop_sender, start_stop_chan) = channel(20);
|
||||
let edge_uuid = create_or_load_uuid("")?;
|
||||
// let edge_uuid = create_or_load_uuid(&format!("{}/.id", get_base_dir()), None)?;
|
||||
let node_conf = parse_config(edge_uuid, &args).await?;
|
||||
|
||||
if let Err(e) = init_edge(&args.token, node_conf, args.tos, start_stop_sender).await {
|
||||
panic!("failed to init edge: {:?}", e);
|
||||
|
||||
let hostfile = format!("{}/.host", get_base_dir());
|
||||
let host = create_or_load_uuid(&hostfile, Some(8))?;
|
||||
|
||||
|
||||
let hostname = hostname.unwrap_or(host);
|
||||
let _ = save_to_file(&hostfile, &hostname);
|
||||
|
||||
let sock_dns = Arc::new(UdpSocket::bind("0.0.0.0:0").await?);
|
||||
let udp_sock_for_global_dns = Arc::new(UdpSocket::bind("0.0.0.0:0").await?);
|
||||
|
||||
let exclusive_tcp = match TcpListener::bind("127.0.0.1:7653").await {
|
||||
Ok(tcp) => tcp,
|
||||
Err(e) => {
|
||||
if let Some(ref chan) = error_report_channel {
|
||||
println!("sending report channel for exclusive tcp");
|
||||
chan.send(ErrorReport { severity: ErrorSeverity::Panic, message: "bind tcp failed, is process already running?".to_string() }).await;
|
||||
return Err(e.into());
|
||||
} else {
|
||||
panic!("new iface failed: {}", e.to_string());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let (global_dns_tx, global_dns_rx) = channel(10);
|
||||
let udp_sock_for_global_dns = DynamicDNSClient::new("223.5.5.5:53".parse().unwrap(), global_dns_tx).await.unwrap();
|
||||
|
||||
init_edge(
|
||||
// &args.token,
|
||||
// &args.network_code,
|
||||
&args,
|
||||
mac,
|
||||
node_conf,
|
||||
// args.tos,
|
||||
start_stop_sender,
|
||||
// args.mtu,
|
||||
connecting_chan.clone(),
|
||||
sock_dns,
|
||||
udp_sock_for_global_dns,
|
||||
hostname,
|
||||
server_ip,
|
||||
install_channel.to_owned(),
|
||||
error_report_channel,
|
||||
exclusive_tcp,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let _ = sender.send(true);
|
||||
debug!("edge inited");
|
||||
|
||||
let cancel = CancellationToken::new();
|
||||
let install_chan = install_channel.to_owned();
|
||||
// let install_chan = install_channel.to_owned();
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = async_main(install_chan, args, start_stop_chan, cancel, connecting_chan).await {
|
||||
if let Err(e) =
|
||||
async_main(args, start_stop_chan, cancel, connecting_chan, global_dns_rx).await
|
||||
{
|
||||
error!("failed to run async main: {}", e.as_str());
|
||||
}
|
||||
});
|
||||
@ -108,14 +181,14 @@ async fn parse_config(uuid: String, args: &CommandLine) -> Result<NodeConfig> {
|
||||
|
||||
let node_conf = NodeConfig {
|
||||
name: args.name.to_owned(),
|
||||
allow_routing: true,
|
||||
_drop_multicast: false,
|
||||
allow_routing: AtomicBool::new(args.allow_routing),
|
||||
_drop_multicast: args._drop_multicast,
|
||||
allow_p2p: args.allow_p2p,
|
||||
mtu: args.mtu,
|
||||
_tos: 0,
|
||||
_register_super_interval: config::REGISTER_SUPER_INTERVAL,
|
||||
register_ttl,
|
||||
_local_port: 0,
|
||||
_local_port: args.local_port as u16,
|
||||
node_uuid: uuid,
|
||||
super_nodes: sockaddr,
|
||||
super_node_index: AtomicU8::new(0),
|
||||
@ -160,6 +233,64 @@ fn parse_sns<'a>(sns: &'a Vec<&'a str>) -> (Vec<&'a str>, Vec<SdlanSock>) {
|
||||
(correct_sns, result)
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct NetworkReq<'a> {
|
||||
client_id: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct NetworkRespSingleNetwork {
|
||||
pub name: String,
|
||||
pub code: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct NetworkRespNetworks {
|
||||
pub network: Vec<NetworkRespSingleNetwork>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct NetworkResp {
|
||||
pub result: NetworkRespNetworks,
|
||||
}
|
||||
|
||||
pub async fn get_my_networks(url: &str) -> Result<Vec<NetworkRespSingleNetwork>>{
|
||||
let edge_uuid = create_or_load_uuid(&format!("{}/.id", get_base_dir()), None)?;
|
||||
|
||||
let req_data = NetworkReq {
|
||||
client_id: &edge_uuid,
|
||||
};
|
||||
|
||||
let Ok(client) = reqwest::Client::builder()
|
||||
.timeout(Duration::from_secs(5)).build() else {
|
||||
return Err(SDLanError::NormalError("failed to create client"));
|
||||
};
|
||||
// match client.post("https://punchnet.s5s8.com/api/get_user_network")
|
||||
match client.post(url)
|
||||
.json(&req_data)
|
||||
.send().await {
|
||||
|
||||
Ok(response) => {
|
||||
let Ok(network) = response.json::<NetworkResp>().await else {
|
||||
return Err(SDLanError::NormalError("failed to jsonify get_user_network "));
|
||||
};
|
||||
|
||||
Ok(network.result.network)
|
||||
|
||||
}
|
||||
Err(e) => {
|
||||
error!("failed to send get_user_network: {}", e);
|
||||
return Err(SDLanError::NormalError("failed to get_user_network"));
|
||||
}
|
||||
}
|
||||
|
||||
// let Ok(response) = client.post("https://punchnet.aioe.tech/api/get_all_networks")
|
||||
// .json(&req_data)
|
||||
// .send().await else {
|
||||
// return Err(SDLanError::NormalError("failed to send get_all_networks"));
|
||||
// };
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use sdlan_sn_rs::config::AF_INET;
|
||||
|
||||
578
src/network/arp.rs
Executable file
578
src/network/arp.rs
Executable file
@ -0,0 +1,578 @@
|
||||
#![allow(unused)]
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{
|
||||
atomic::{AtomicU8, Ordering},
|
||||
Arc,
|
||||
},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use dashmap::DashMap;
|
||||
use tracing::{debug, error};
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use sdlan_sn_rs::{
|
||||
config::SDLAN_DEFAULT_TTL,
|
||||
utils::{
|
||||
get_current_timestamp, ip_to_string, mac_to_string, net_bit_len_to_mask, Mac,
|
||||
BROADCAST_MAC, MULTICAST_MAC,
|
||||
},
|
||||
};
|
||||
use tokio::sync::{
|
||||
mpsc::{channel, Receiver, Sender},
|
||||
oneshot,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
network::{form_ethernet_packet, send_packet_to_net},
|
||||
pb::{encode_to_udp_message, SdlData},
|
||||
tcp::PacketType,
|
||||
};
|
||||
|
||||
use super::get_edge;
|
||||
|
||||
/*
|
||||
static GLOBAL_ARP: OnceCell<ArpActor> = OnceCell::new();
|
||||
pub fn init_arp() {
|
||||
init_arp_wait_list();
|
||||
let actor = ArpActor::new();
|
||||
GLOBAL_ARP.set(actor).unwrap();
|
||||
}
|
||||
|
||||
pub fn get_arp() -> &'static ArpActor {
|
||||
GLOBAL_ARP.get().unwrap()
|
||||
}
|
||||
*/
|
||||
|
||||
const ETHER_TYPE_ARP: u16 = 0x0806;
|
||||
const ETHER_TYPE_IP: u16 = 0x0800;
|
||||
#[allow(unused)]
|
||||
const ETHER_TYPE_IP6: u16 = 0x86dd;
|
||||
|
||||
const ARP_MAX_AGE: u8 = 128;
|
||||
|
||||
const ARP_HWTYPE_ETH: u16 = 1;
|
||||
pub const ARP_REQUEST: u16 = 1;
|
||||
pub const ARP_REPLY: u16 = 2;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct EthHdr {
|
||||
pub dest: [u8; 6],
|
||||
pub src: [u8; 6],
|
||||
pub eth_type: u16,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct ArpHdr {
|
||||
pub ethhdr: EthHdr,
|
||||
pub hwtype: u16,
|
||||
pub protocol: u16,
|
||||
pub hwlen: u8,
|
||||
pub protolen: u8,
|
||||
pub opcode: u16,
|
||||
pub shwaddr: [u8; 6],
|
||||
pub sipaddr: [u16; 2],
|
||||
pub dhwaddr: [u8; 6],
|
||||
pub dipaddr: [u16; 2],
|
||||
}
|
||||
|
||||
impl ArpHdr {
|
||||
pub fn from_slice(data: &[u8]) -> Self {
|
||||
if data.len() < 42 {
|
||||
panic!("data size error");
|
||||
}
|
||||
Self {
|
||||
ethhdr: EthHdr {
|
||||
dest: data[0..6].try_into().unwrap(),
|
||||
src: data[6..12].try_into().unwrap(),
|
||||
eth_type: u16::from_be_bytes(data[12..14].try_into().unwrap()),
|
||||
},
|
||||
hwtype: u16::from_be_bytes(data[14..16].try_into().unwrap()),
|
||||
protocol: u16::from_be_bytes(data[16..18].try_into().unwrap()),
|
||||
hwlen: data[18],
|
||||
protolen: data[19],
|
||||
opcode: u16::from_be_bytes(data[20..22].try_into().unwrap()),
|
||||
shwaddr: data[22..28].try_into().unwrap(),
|
||||
sipaddr: [
|
||||
u16::from_be_bytes(data[28..30].try_into().unwrap()),
|
||||
u16::from_be_bytes(data[30..32].try_into().unwrap()),
|
||||
],
|
||||
dhwaddr: data[32..38].try_into().unwrap(),
|
||||
dipaddr: [
|
||||
u16::from_be_bytes(data[38..40].try_into().unwrap()),
|
||||
u16::from_be_bytes(data[40..42].try_into().unwrap()),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn marshal_to_bytes(&self) -> Vec<u8> {
|
||||
let mut result = Vec::with_capacity(64);
|
||||
result.extend_from_slice(&self.ethhdr.dest);
|
||||
result.extend_from_slice(&self.ethhdr.src);
|
||||
result.extend_from_slice(&self.ethhdr.eth_type.to_be_bytes());
|
||||
result.extend_from_slice(&self.hwtype.to_be_bytes());
|
||||
result.extend_from_slice(&self.protocol.to_be_bytes());
|
||||
result.push(self.hwlen);
|
||||
result.push(self.protolen);
|
||||
result.extend_from_slice(&self.opcode.to_be_bytes());
|
||||
result.extend_from_slice(&self.shwaddr);
|
||||
result.extend_from_slice(&self.sipaddr[0].to_be_bytes());
|
||||
result.extend_from_slice(&self.sipaddr[1].to_be_bytes());
|
||||
|
||||
result.extend_from_slice(&self.dhwaddr);
|
||||
result.extend_from_slice(&self.dipaddr[0].to_be_bytes());
|
||||
result.extend_from_slice(&self.dipaddr[1].to_be_bytes());
|
||||
// result.extend_from_slice(&[0; 18]);
|
||||
|
||||
// let crc = CRC_HASH.checksum(&result).to_be_bytes();
|
||||
// result.extend_from_slice(&crc);
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
ethhdr: EthHdr {
|
||||
dest: [0; 6],
|
||||
src: [0; 6],
|
||||
eth_type: ETHER_TYPE_ARP,
|
||||
},
|
||||
hwtype: ARP_HWTYPE_ETH,
|
||||
protocol: ETHER_TYPE_IP,
|
||||
hwlen: 6,
|
||||
protolen: 4,
|
||||
opcode: ARP_REQUEST,
|
||||
shwaddr: [0; 6],
|
||||
sipaddr: [0; 2],
|
||||
dhwaddr: [0; 6],
|
||||
dipaddr: [0; 2],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// const ARP_TABLE_SIZE: usize = 8;
|
||||
static ARPTIME: AtomicU8 = AtomicU8::new(0);
|
||||
const BROADCAST_IPADDR: u32 = 0xffffffff;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(unused)]
|
||||
pub struct ArpEntry {
|
||||
// ip_addr: u32,
|
||||
last_seen: Instant,
|
||||
hw_addr: Mac,
|
||||
}
|
||||
|
||||
/*
|
||||
impl ArpEntry {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
ip_addr: 0,
|
||||
arptime: 0,
|
||||
hw_addr: [0; 6],
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
pub struct ArpTable {
|
||||
entries: Arc<DashMap<u32, ArpEntry>>,
|
||||
ttl: Duration,
|
||||
pending_packet_buffer: ArpWaitList,
|
||||
}
|
||||
|
||||
impl ArpTable {
|
||||
pub fn new() -> Self {
|
||||
let entries = Arc::new(DashMap::new());
|
||||
let res = Self {
|
||||
entries: entries.clone(),
|
||||
ttl: Duration::from_secs(100),
|
||||
pending_packet_buffer: ArpWaitList::new(),
|
||||
};
|
||||
|
||||
let mut bytes = BytesMut::with_capacity(1024 + 20);
|
||||
|
||||
let ttl = res.ttl;
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
tokio::time::sleep(Duration::from_secs(100)).await;
|
||||
let now = Instant::now();
|
||||
entries.retain(|_, entry| now.duration_since(entry.last_seen) < ttl);
|
||||
}
|
||||
});
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
pub async fn arp_arrived(&self, ip: u32, mac: Mac) {
|
||||
self.pending_packet_buffer.arp_arrived(ip, mac).await;
|
||||
}
|
||||
|
||||
/// origin_data is layer3(ip) packet
|
||||
pub fn add_to_arp_wait_list(&self, ip: u32, origin_data: BytesMut) {
|
||||
self.pending_packet_buffer.add_to_wait_list(ip, origin_data);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get(&self, ip: u32) -> Option<Mac> {
|
||||
self.entries.get(&ip).map(|entry| entry.hw_addr)
|
||||
}
|
||||
|
||||
pub fn set(&self, ip: u32, mac: Mac) {
|
||||
self.entries.insert(
|
||||
ip,
|
||||
ArpEntry {
|
||||
last_seen: Instant::now(),
|
||||
hw_addr: mac,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
pub struct ArpInfo {
|
||||
// host_ip: AtomicU32,
|
||||
// ip representation of mask
|
||||
// host_netmask: AtomicU32,
|
||||
entry: HashMap<u32, ArpEntry>,
|
||||
// entry: [ArpEntry; ARP_TABLE_SIZE],
|
||||
}
|
||||
|
||||
impl ArpInfo {
|
||||
fn lookup_ip_mac(&self, ip: u32) -> Option<([u8; 6], u32, bool)> {
|
||||
if ip == BROADCAST_IPADDR {
|
||||
return Some((BROADCAST_MAC, ip, false));
|
||||
}
|
||||
let edge = get_edge();
|
||||
let netbit = edge.device_config.get_net_bit();
|
||||
let host_netmask = net_bit_len_to_mask(netbit);
|
||||
let host_netmask_reverse = !host_netmask;
|
||||
|
||||
if (ip & host_netmask_reverse) == host_netmask_reverse {
|
||||
return Some((BROADCAST_MAC, ip, false));
|
||||
}
|
||||
|
||||
let first_ip = (ip >> 24) as u8 & 0xff;
|
||||
if first_ip >= 224 && first_ip <= 239 {
|
||||
let mut multi = MULTICAST_MAC;
|
||||
multi[3] = (ip >> 16) as u8 & 0x7f;
|
||||
multi[4] = (ip >> 8) as u8 & 0xff;
|
||||
multi[5] = (ip) as u8 & 0xff;
|
||||
return Some((multi, ip, false));
|
||||
}
|
||||
let mut target_ip = 0;
|
||||
|
||||
let host_ip = edge.device_config.get_ip();
|
||||
|
||||
if (ip & host_netmask) == (host_ip & host_netmask) {
|
||||
target_ip = ip;
|
||||
}
|
||||
|
||||
if target_ip == 0 {
|
||||
// let route_table = get_route_table();
|
||||
if let Some((_prefix, gateway_ip)) = edge.route_table.route_table.lookup(ip) {
|
||||
// if let Some(gateway_ip) = route_table.get_gateway_ip(ip) {
|
||||
target_ip = gateway_ip.into();
|
||||
}
|
||||
}
|
||||
if target_ip == 0 {
|
||||
error!("target should not route to me: ip = {}", ip_to_string(&ip));
|
||||
return None;
|
||||
}
|
||||
if let Some(entry) = self.entry.get(&target_ip) {
|
||||
return Some((entry.hw_addr, target_ip, false));
|
||||
}
|
||||
/*
|
||||
for i in 0..ARP_TABLE_SIZE {
|
||||
let item = &self.entry[i];
|
||||
if item.ip_addr == target_ip {
|
||||
return (item.hw_addr, target_ip, false);
|
||||
}
|
||||
}
|
||||
*/
|
||||
return Some((BROADCAST_MAC, target_ip, true));
|
||||
}
|
||||
|
||||
fn set_arp(&mut self, mac: [u8; 6], ip: u32) {
|
||||
// println!("setting ip: {:?} is at {:?}", ip.to_be_bytes(), mac);
|
||||
let nowage = ARPTIME.load(Ordering::Relaxed);
|
||||
self.entry.insert(
|
||||
ip,
|
||||
ArpEntry {
|
||||
ip_addr: ip,
|
||||
hw_addr: mac,
|
||||
arptime: nowage,
|
||||
},
|
||||
);
|
||||
/*
|
||||
for i in 0..ARP_TABLE_SIZE {
|
||||
let item = &mut self.entry[i];
|
||||
if item.ip_addr != 0 {
|
||||
if item.ip_addr == ip {
|
||||
item.hw_addr = mac;
|
||||
let nowage = ARPTIME.load(Ordering::Relaxed);
|
||||
item.arptime = nowage;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
/*
|
||||
println!("set_arp 1");
|
||||
|
||||
let mut itemindex = ARP_TABLE_SIZE;
|
||||
for i in 0..ARP_TABLE_SIZE {
|
||||
let temp = &self.entry[i];
|
||||
println!("set arp 2");
|
||||
if temp.ip_addr == 0 {
|
||||
itemindex = i;
|
||||
println!("set arp 3: itemindex = {}", itemindex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
let arptime = ARPTIME.load(Ordering::Relaxed);
|
||||
if itemindex == ARP_TABLE_SIZE {
|
||||
println!("set_arp 4");
|
||||
let mut tmpage = 0;
|
||||
let mut idx = 0;
|
||||
for i in 0..ARP_TABLE_SIZE {
|
||||
let temp = &self.entry[i];
|
||||
if (arptime - temp.arptime) > tmpage {
|
||||
tmpage = arptime - temp.arptime;
|
||||
idx = i;
|
||||
}
|
||||
}
|
||||
itemindex = idx;
|
||||
}
|
||||
println!("set_arp 5");
|
||||
let temp = &mut self.entry[itemindex];
|
||||
temp.arptime = arptime;
|
||||
temp.ip_addr = ip;
|
||||
temp.hw_addr = mac;
|
||||
println!("set arp 6: idx={} => {:?}", itemindex, temp);
|
||||
*/
|
||||
}
|
||||
|
||||
fn timer(&mut self) {
|
||||
let timer = ARPTIME.fetch_add(1, Ordering::Relaxed);
|
||||
let mut todelete = vec![];
|
||||
for (ip, entry) in &self.entry {
|
||||
if (timer - entry.arptime) >= ARP_MAX_AGE {
|
||||
todelete.push(*ip);
|
||||
}
|
||||
}
|
||||
for ip in todelete {
|
||||
self.entry.remove(&ip);
|
||||
}
|
||||
/*
|
||||
for i in 0..ARP_TABLE_SIZE {
|
||||
let item = &mut self.entry[i];
|
||||
if item.ip_addr != 0 && (timer - item.arptime) >= ARP_MAX_AGE {
|
||||
item.ip_addr = 0;
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
pub enum ArpRequestInfo {
|
||||
Lookup { ip: u32 },
|
||||
Set { ip: u32, mac: Mac },
|
||||
}
|
||||
|
||||
pub struct ArpRequest {
|
||||
req: ArpRequestInfo,
|
||||
tx: oneshot::Sender<ArpResponse>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[allow(unused)]
|
||||
pub enum ArpResponse {
|
||||
LookupResp {
|
||||
mac: Mac,
|
||||
ip: u32,
|
||||
do_arp_request: bool,
|
||||
},
|
||||
SetResp {
|
||||
ok: bool,
|
||||
},
|
||||
ArpRespError {
|
||||
msg: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ArpActor {
|
||||
pub tx: Sender<ArpRequest>,
|
||||
}
|
||||
|
||||
impl ArpActor {
|
||||
pub fn new() -> Self {
|
||||
let (tx, rx) = channel(20);
|
||||
tokio::spawn(loop_arp_info(rx));
|
||||
Self { tx }
|
||||
}
|
||||
}
|
||||
|
||||
async fn loop_arp_info(mut rx: Receiver<ArpRequest>) {
|
||||
let mut arp = ArpInfo {
|
||||
// entry: array::from_fn(|_i| ArpEntry::new()),
|
||||
entry: HashMap::new(),
|
||||
};
|
||||
loop {
|
||||
tokio::select! {
|
||||
data = rx.recv() => {
|
||||
if let Some(d) = data {
|
||||
match d.req {
|
||||
ArpRequestInfo::Lookup{ip} => {
|
||||
let Some(mac) = arp.lookup_ip_mac(ip) else {
|
||||
continue;
|
||||
};
|
||||
if let Err(e) = d.tx.send(ArpResponse::LookupResp {
|
||||
mac: mac.0, ip: mac.1, do_arp_request: mac.2 }
|
||||
) {
|
||||
error!("failed to send back arp lookup feedback: {:?}", e);
|
||||
};
|
||||
}
|
||||
ArpRequestInfo::Set{ip, mac} => {
|
||||
arp.set_arp(mac, ip);
|
||||
if let Err(e) = d.tx.send(ArpResponse::SetResp { ok: true }) {
|
||||
error!("failed to send back arp set feedback: {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = tokio::time::sleep(Duration::from_secs(10)) => {
|
||||
arp.timer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_arp_request(data: ArpRequestInfo) -> ArpResponse {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let arp = get_arp();
|
||||
if let Err(e) = arp.tx.send(ArpRequest { tx, req: data }).await {
|
||||
error!("failed to send arp request: {}", e);
|
||||
return ArpResponse::ArpRespError {
|
||||
msg: "failed to send arp request".to_owned(),
|
||||
};
|
||||
}
|
||||
match rx.await {
|
||||
Ok(res) => res,
|
||||
Err(e) => ArpResponse::ArpRespError { msg: e.to_string() },
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn generate_arp_request(srcmac: [u8; 6], dstip: u32, srcip: u32) -> Vec<u8> {
|
||||
let mut arphdr = ArpHdr::new();
|
||||
arphdr.ethhdr.dest = [0xff; 6];
|
||||
arphdr.dhwaddr = [0; 6];
|
||||
arphdr.ethhdr.src = srcmac;
|
||||
arphdr.shwaddr = srcmac;
|
||||
|
||||
arphdr.dipaddr = [(dstip >> 16) as u16, (dstip & 0xffff) as u16];
|
||||
arphdr.sipaddr = [(srcip >> 16) as u16, (srcip & 0xffff) as u16];
|
||||
|
||||
/*
|
||||
println!(
|
||||
"arphdr.sipaddr: {:?}, {:?}",
|
||||
arphdr.sipaddr[0].to_be_bytes(),
|
||||
arphdr.sipaddr[1].to_be_bytes()
|
||||
);
|
||||
println!(
|
||||
"arphdr.dipaddr: {:?}, {:?}",
|
||||
arphdr.dipaddr[0].to_be_bytes(),
|
||||
arphdr.dipaddr[1].to_be_bytes()
|
||||
);
|
||||
*/
|
||||
|
||||
arphdr.marshal_to_bytes()
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ArpWaitInfo {
|
||||
timestamp: u64,
|
||||
// origin data is from the tun or tap device
|
||||
origin_data: BytesMut,
|
||||
}
|
||||
|
||||
pub const MAX_WAIT_PACKETS: usize = 100;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ArpWaitList {
|
||||
content: DashMap<u32, Vec<ArpWaitInfo>>,
|
||||
}
|
||||
|
||||
impl ArpWaitList {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
content: DashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_to_wait_list(&self, ip: u32, origin_data: BytesMut) {
|
||||
let mut entry = self.content.entry(ip).or_insert(vec![]);
|
||||
if entry.len() < MAX_WAIT_PACKETS {
|
||||
entry.push(ArpWaitInfo {
|
||||
timestamp: get_current_timestamp(),
|
||||
origin_data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn arp_arrived(&self, ip: u32, mac: Mac) {
|
||||
debug!(
|
||||
"arp for {} arrived: {}",
|
||||
ip_to_string(&ip),
|
||||
mac_to_string(&mac)
|
||||
);
|
||||
let Some(items) = self.content.remove(&ip) else {
|
||||
return;
|
||||
};
|
||||
let edge = get_edge();
|
||||
// just remove the items
|
||||
if !edge.is_authorized() {
|
||||
return;
|
||||
}
|
||||
|
||||
// let encrypt_key = edge.get_encrypt_key();
|
||||
let network_id = edge.network_id.load(Ordering::Relaxed);
|
||||
|
||||
let src_mac = edge.device_config.get_mac();
|
||||
let now = get_current_timestamp();
|
||||
for item in items.1 {
|
||||
if (now - item.timestamp) > 5 {
|
||||
continue;
|
||||
}
|
||||
let mut packet = form_ethernet_packet(src_mac, mac, item.origin_data);
|
||||
|
||||
let pkt_size = packet.len();
|
||||
|
||||
let encryptor = edge.encryptor.load();
|
||||
if let Err(e) = encryptor.encrypt(&mut packet) {
|
||||
error!("failed to encrypt packet request: {:?}", e);
|
||||
return;
|
||||
}
|
||||
let data_bytes = packet.freeze();
|
||||
let data = SdlData {
|
||||
is_p2p: true,
|
||||
network_id,
|
||||
ttl: SDLAN_DEFAULT_TTL as u32,
|
||||
src_mac: Vec::from(src_mac),
|
||||
dst_mac: Vec::from(mac),
|
||||
data: data_bytes,
|
||||
identity_id: edge.identity_id.load(),
|
||||
session_token: edge.session_token.get(),
|
||||
};
|
||||
let msg = encode_to_udp_message(Some(data), PacketType::Data as u8).unwrap();
|
||||
send_packet_to_net(edge, mac, &msg, pkt_size as u64).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
600
src/network/async_main.rs
Normal file → Executable file
600
src/network/async_main.rs
Normal file → Executable file
@ -1,321 +1,48 @@
|
||||
use std::net::IpAddr;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::config::TCP_PING_TIME;
|
||||
use crate::network::ipv6::run_ipv6;
|
||||
use crate::network::{get_edge, ping_to_sn, read_and_parse_packet, RegisterSuperFeedback};
|
||||
use crate::pb::{
|
||||
encode_to_tcp_message, encode_to_udp_message, SdlData, SdlDevAddr, SdlRegisterSuper,
|
||||
SdlRegisterSuperAck, SdlRegisterSuperNak, SdlSendRegisterEvent, SdlStunRequest, Sdlv6Info,
|
||||
};
|
||||
use crate::tcp::{init_tcp_conn, EventType, NakMsgCode, PacketType, SdlanTcp};
|
||||
use crate::network::{get_edge, ping_to_sn, read_and_parse_packet, TunTapPacketHandler};
|
||||
use crate::tcp::{init_quic_conn, send_stun_request};
|
||||
use crate::utils::{send_to_sock, CommandLine};
|
||||
use crate::ConnectionState;
|
||||
use etherparse::IpHeaders;
|
||||
use sdlan_sn_rs::config::{AF_INET, AF_INET6, SDLAN_DEFAULT_TTL};
|
||||
use sdlan_sn_rs::peer::{SdlanSock, V6Info};
|
||||
use sdlan_sn_rs::utils::Result;
|
||||
use sdlan_sn_rs::utils::{
|
||||
aes_encrypt, get_current_timestamp, ip_to_string, is_multi_broadcast, rsa_decrypt,
|
||||
};
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use crate::ConnectionInfo;
|
||||
use bytes::BytesMut;
|
||||
use etherparse::PacketBuilder;
|
||||
use sdlan_sn_rs::peer::SdlanSock;
|
||||
use sdlan_sn_rs::utils::{get_current_timestamp, ip_to_string, is_multi_broadcast};
|
||||
use sdlan_sn_rs::utils::{Mac, Result};
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::sync::mpsc::{channel, Receiver, Sender};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
|
||||
use super::{check_peer_registration_needed, packet, Node, StartStopInfo};
|
||||
use super::{Node, StartStopInfo};
|
||||
use crate::utils::Socket;
|
||||
|
||||
use prost::Message;
|
||||
use tracing::{debug, error, info};
|
||||
|
||||
async fn handle_tcp_message(msg: SdlanTcp) {
|
||||
let edge = get_edge();
|
||||
|
||||
// let now = get_current_timestamp();
|
||||
// edge.tcp_pong.store(now, Ordering::Relaxed);
|
||||
|
||||
debug!("got tcp message: {:?}", msg.packet_type);
|
||||
match msg.packet_type {
|
||||
PacketType::RegisterSuperACK => {
|
||||
edge.send_register_super_feedback(
|
||||
msg._packet_id,
|
||||
RegisterSuperFeedback {
|
||||
result: 0,
|
||||
message: "".to_owned(),
|
||||
should_exit: false,
|
||||
},
|
||||
);
|
||||
let Ok(ack) = SdlRegisterSuperAck::decode(&msg.current_packet[..]) else {
|
||||
error!("failed to decode REGISTER_SUPER_ACK");
|
||||
return;
|
||||
};
|
||||
debug!("got register super ack: {:?}", ack);
|
||||
let Ok(aes) = rsa_decrypt(&edge.rsa_private, &ack.aes_key) else {
|
||||
error!("failed to rsa decrypt aes key");
|
||||
return;
|
||||
};
|
||||
let Some(dev) = ack.dev_addr else {
|
||||
error!("no dev_addr is specified");
|
||||
return;
|
||||
};
|
||||
|
||||
let ip = ip_to_string(&dev.net_addr);
|
||||
debug!("aes key is {:?}, ip is {}/{}", aes, ip, dev.net_bit_len,);
|
||||
edge.device_config
|
||||
.ip
|
||||
.net_addr
|
||||
.store(dev.net_addr, Ordering::Relaxed);
|
||||
edge.device_config
|
||||
.ip
|
||||
.net_bit_len
|
||||
.store(dev.net_bit_len as u8, Ordering::Relaxed);
|
||||
edge.device.reload_config(&edge.device_config);
|
||||
edge.network_id.store(dev.network_id, Ordering::Relaxed);
|
||||
|
||||
edge.set_authorized(true, aes);
|
||||
send_stun_request(edge).await;
|
||||
tokio::spawn(async {
|
||||
let nattype = edge.probe_nat_type().await;
|
||||
println!("nat type is: {:?}", nattype);
|
||||
});
|
||||
}
|
||||
PacketType::RegisterSuperNAK => {
|
||||
let Ok(_nak) = SdlRegisterSuperNak::decode(&msg.current_packet[..]) else {
|
||||
error!("failed to decode REGISTER_SUPER_NAK");
|
||||
edge.send_register_super_feedback(
|
||||
msg._packet_id,
|
||||
RegisterSuperFeedback {
|
||||
result: 1,
|
||||
message: "failed to decode REGISTER SUPER NAK".to_owned(),
|
||||
should_exit: false,
|
||||
},
|
||||
);
|
||||
return;
|
||||
};
|
||||
|
||||
let Ok(error_code) = NakMsgCode::try_from(_nak.error_code as u8) else {
|
||||
edge.send_register_super_feedback(
|
||||
msg._packet_id,
|
||||
RegisterSuperFeedback {
|
||||
result: 2,
|
||||
message: "error_code not recognized".to_owned(),
|
||||
should_exit: false,
|
||||
},
|
||||
);
|
||||
return;
|
||||
};
|
||||
match error_code {
|
||||
NakMsgCode::InvalidToken => {
|
||||
edge.send_register_super_feedback(
|
||||
msg._packet_id,
|
||||
RegisterSuperFeedback {
|
||||
result: 3,
|
||||
message: "invalid token".to_owned(),
|
||||
should_exit: true,
|
||||
},
|
||||
);
|
||||
edge.stop().await;
|
||||
}
|
||||
NakMsgCode::NodeDisabled => {
|
||||
edge.send_register_super_feedback(
|
||||
msg._packet_id,
|
||||
RegisterSuperFeedback {
|
||||
result: 4,
|
||||
message: "Node is disabled".to_owned(),
|
||||
should_exit: true,
|
||||
},
|
||||
);
|
||||
edge.stop().await;
|
||||
}
|
||||
_other => {
|
||||
edge.send_register_super_feedback(
|
||||
msg._packet_id,
|
||||
RegisterSuperFeedback {
|
||||
result: 0,
|
||||
message: "".to_owned(),
|
||||
should_exit: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
/*
|
||||
edge.send_register_super_feedback(msg._packet_id, RegisterSuperFeedback {
|
||||
result: 1,
|
||||
message: "failed to decode REGISTER SUPER NAK".to_owned(),
|
||||
});
|
||||
*/
|
||||
edge.set_authorized(false, Vec::new());
|
||||
// std::process::exit(0);
|
||||
}
|
||||
PacketType::Command => {
|
||||
if msg.current_packet.len() < 1 {
|
||||
error!("malformed COMMAND received");
|
||||
return;
|
||||
}
|
||||
handle_tcp_command(edge, msg.current_packet[0], &msg.current_packet[1..]).await;
|
||||
}
|
||||
PacketType::Event => {
|
||||
if msg.current_packet.len() < 1 {
|
||||
error!("malformed EVENT received");
|
||||
return;
|
||||
}
|
||||
let Ok(event) = msg.current_packet[0].try_into() else {
|
||||
error!("failed to parse event type");
|
||||
return;
|
||||
};
|
||||
handle_tcp_event(edge, event, &msg.current_packet[1..]).await;
|
||||
}
|
||||
PacketType::PeerInfo => {
|
||||
let _ = packet::handle_packet_peer_info(edge, &msg.current_packet[..]).await;
|
||||
}
|
||||
PacketType::Pong => {
|
||||
debug!("tcp pong received");
|
||||
let now = get_current_timestamp();
|
||||
edge.tcp_pong.store(now, Ordering::Relaxed);
|
||||
}
|
||||
other => {
|
||||
debug!("tcp not handling {:?}", other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_tcp_command(_edge: &Node, _cmdtype: u8, _cmdprotobuf: &[u8]) {}
|
||||
|
||||
async fn handle_tcp_event(edge: &Node, eventtype: EventType, eventprotobuf: &[u8]) {
|
||||
match eventtype {
|
||||
EventType::SendRegister => {
|
||||
let Ok(reg) = SdlSendRegisterEvent::decode(eventprotobuf) else {
|
||||
error!("failed to decode SendRegister Event");
|
||||
return;
|
||||
};
|
||||
let v4 = reg.nat_ip.to_be_bytes();
|
||||
let mut v6_sock = None;
|
||||
if let Some(v6_info) = reg.v6_info {
|
||||
if let Ok(v6_bytes) = v6_info.v6.try_into() {
|
||||
v6_sock = Some(V6Info {
|
||||
port: v6_info.port as u16,
|
||||
v6: v6_bytes,
|
||||
});
|
||||
}
|
||||
}
|
||||
check_peer_registration_needed(
|
||||
edge,
|
||||
true,
|
||||
reg.dst_ip,
|
||||
// &v6_sock,
|
||||
&v6_sock,
|
||||
&SdlanSock {
|
||||
family: AF_INET,
|
||||
port: reg.nat_port as u16,
|
||||
v4,
|
||||
v6: [0; 16],
|
||||
},
|
||||
)
|
||||
.await;
|
||||
}
|
||||
other => {
|
||||
debug!("unhandled event {:?}", other);
|
||||
}
|
||||
}
|
||||
}
|
||||
use tracing::{debug, error, warn};
|
||||
|
||||
pub async fn async_main(
|
||||
install_channel: String,
|
||||
args: CommandLine,
|
||||
start_stop_chan: Receiver<StartStopInfo>,
|
||||
cancel: CancellationToken,
|
||||
connecting_chan: Option<Sender<ConnectionState>>,
|
||||
connecting_chan: Option<Sender<ConnectionInfo>>,
|
||||
global_dns_rx: Receiver<(Vec<u8>, SocketAddr)>,
|
||||
) -> Result<()> {
|
||||
// let _ = PidRecorder::new(".pid");
|
||||
|
||||
// // gen public key
|
||||
// gen_rsa_keys(".client");
|
||||
// let mut pubkey = String::new();
|
||||
// File::open(".client/id_rsa.pub")?.read_to_string(&mut pubkey)?;
|
||||
// let privatekey = load_private_key_file(".client/id_rsa")?;
|
||||
|
||||
// // init sock
|
||||
// if args.token.len() == 0 {
|
||||
// println!("failed to load token");
|
||||
// return Ok(());
|
||||
// }
|
||||
// let sock_v4 = Socket::build(0, true, true, args.tos).await?;
|
||||
|
||||
// // allow multicast
|
||||
|
||||
// // TODO: set the sn's tcp socket
|
||||
// // let tcpsock = TCPSocket::build("121.4.79.234:1234").await?;
|
||||
// let tcp_pong = Arc::new(AtomicU64::new(0));
|
||||
// let edge = Node::new(
|
||||
// pubkey,
|
||||
// node_conf,
|
||||
// sock_v4,
|
||||
// &args.token,
|
||||
// privatekey,
|
||||
// tcp_pong.clone(),
|
||||
// );
|
||||
|
||||
let edge = get_edge();
|
||||
|
||||
// let token = args.token.clone();
|
||||
let cancel_tcp = cancel.clone();
|
||||
let (ipv6_network_restarter, rx) = channel(10);
|
||||
tokio::spawn(run_ipv6(edge, rx));
|
||||
init_tcp_conn(
|
||||
|
||||
init_quic_conn(
|
||||
cancel_tcp,
|
||||
&args.tcp,
|
||||
move |stream, pkt_id| {
|
||||
let installed_channel = install_channel.to_owned();
|
||||
Box::pin(async move {
|
||||
let token = edge._token.lock().unwrap().clone();
|
||||
// let edge = get_edge();
|
||||
// let edge = get_edge();
|
||||
// let token = args.token.clone();
|
||||
if let Ok(ipaddr) = stream.local_addr() {
|
||||
match ipaddr.ip() {
|
||||
IpAddr::V4(v4) => {
|
||||
let ip = v4.into();
|
||||
// println!("outer ip is {} => {}", v4, ip);
|
||||
edge.outer_ip_v4.store(ip, Ordering::Relaxed);
|
||||
}
|
||||
_other => {}
|
||||
}
|
||||
}
|
||||
let register_super = SdlRegisterSuper {
|
||||
version: 1,
|
||||
installed_channel,
|
||||
client_id: edge.config.node_uuid.clone(),
|
||||
dev_addr: Some(SdlDevAddr {
|
||||
net_addr: 0,
|
||||
network_id: 0,
|
||||
net_bit_len: 0,
|
||||
}),
|
||||
pub_key: edge.rsa_pubkey.clone(),
|
||||
token,
|
||||
};
|
||||
// debug!("send register super: {:?}", register_super);
|
||||
let packet_id = match pkt_id {
|
||||
Some(id) => id,
|
||||
None => edge.get_next_packet_id(),
|
||||
};
|
||||
// let packet_id = edge.get_next_packet_id();
|
||||
let data = encode_to_tcp_message(
|
||||
Some(register_super),
|
||||
packet_id,
|
||||
PacketType::RegisterSuper as u8,
|
||||
)
|
||||
.unwrap();
|
||||
if let Err(e) = stream.write(&data).await {
|
||||
error!("failed to write to tcp: {}", e.to_string());
|
||||
}
|
||||
})
|
||||
},
|
||||
|| async {
|
||||
edge.set_authorized(false, vec![]);
|
||||
},
|
||||
|msg| handle_tcp_message(msg),
|
||||
&args.quic,
|
||||
&args.quic_domain,
|
||||
// |msg| handle_tcp_message(msg),
|
||||
edge.tcp_pong.clone(),
|
||||
// tcp_pong,
|
||||
start_stop_chan,
|
||||
@ -323,11 +50,6 @@ pub async fn async_main(
|
||||
Some(ipv6_network_restarter),
|
||||
);
|
||||
|
||||
// tcp_conn.send("hello".as_bytes()).await;
|
||||
// tokio::spawn(handle_tcp_message(tcp_conn.data_from_tcp));
|
||||
|
||||
// tcp_conn.send("".as_bytes());
|
||||
|
||||
debug!("waiting for authorization...");
|
||||
|
||||
/*
|
||||
@ -352,7 +74,7 @@ pub async fn async_main(
|
||||
{
|
||||
let cancel = cancel.clone();
|
||||
tokio::spawn(async move {
|
||||
run_edge_loop(edge, cancel).await;
|
||||
run_edge_loop(edge, global_dns_rx, cancel).await;
|
||||
});
|
||||
}
|
||||
|
||||
@ -396,7 +118,11 @@ pub async fn async_main(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_edge_loop(eee: &'static Node, cancel: CancellationToken) {
|
||||
async fn run_edge_loop(
|
||||
eee: &'static Node,
|
||||
global_dns_rx: Receiver<(Vec<u8>, SocketAddr)>,
|
||||
cancel: CancellationToken,
|
||||
) {
|
||||
ping_to_sn().await;
|
||||
{
|
||||
let cancel2 = cancel.clone();
|
||||
@ -404,48 +130,19 @@ async fn run_edge_loop(eee: &'static Node, cancel: CancellationToken) {
|
||||
tokio::spawn(async move {
|
||||
loop_socket_v4(eee, &eee.udp_sock_v4, cancel, false).await;
|
||||
});
|
||||
tokio::spawn(async move {
|
||||
loop_socket_v4(eee, &eee.udp_sock_multicast, cancel2, true).await;
|
||||
});
|
||||
if let Some(ref multicast) = eee.udp_sock_multicast {
|
||||
loop_socket_v4(eee, &multicast, cancel2, true).await;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
tokio::spawn(async move {
|
||||
loop_tap(eee, cancel).await;
|
||||
loop_tap(eee, global_dns_rx, cancel).await;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_stun_request(eee: &Node) {
|
||||
let sdl_v6_info = match *eee.ipv6.read().unwrap() {
|
||||
Some(ref l) => Some(Sdlv6Info {
|
||||
port: l.port as u32,
|
||||
v6: Vec::from(l.v6),
|
||||
}),
|
||||
None => None,
|
||||
};
|
||||
let req = SdlStunRequest {
|
||||
cookie: 0,
|
||||
client_id: eee.config.node_uuid.clone(),
|
||||
network_id: eee.network_id.load(Ordering::Relaxed),
|
||||
ip: eee.device_config.get_ip(),
|
||||
nat_type: eee.get_nat_type() as u32,
|
||||
v6_info: sdl_v6_info,
|
||||
};
|
||||
debug!("stun request: {:?}", req);
|
||||
let msg = encode_to_udp_message(Some(req), PacketType::StunRequest as u8).unwrap();
|
||||
if let Err(e) = send_to_sock(
|
||||
eee,
|
||||
&msg,
|
||||
&eee.config.super_nodes[eee.config.super_node_index.load(Ordering::Relaxed) as usize],
|
||||
)
|
||||
.await
|
||||
{
|
||||
error!("failed to send to sock: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn loop_socket_v6(eee: &Node, socket: Arc<Socket>, cancel: CancellationToken) {
|
||||
pub async fn loop_socket_v6(eee: &'static Node, socket: Arc<Socket>, cancel: CancellationToken) {
|
||||
debug!("loop sock v6");
|
||||
loop {
|
||||
tokio::select! {
|
||||
@ -473,41 +170,57 @@ pub async fn loop_socket_v6(eee: &Node, socket: Arc<Socket>, cancel: Cancellatio
|
||||
}
|
||||
|
||||
pub async fn loop_socket_v4(
|
||||
eee: &Node,
|
||||
eee: &'static Node,
|
||||
socket: &Socket,
|
||||
cancel: CancellationToken,
|
||||
is_multicast_sock: bool,
|
||||
) {
|
||||
debug!("loop sock v4");
|
||||
let cancel_clone = cancel.clone();
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = cancel_clone.cancelled() => {
|
||||
break;
|
||||
}
|
||||
_ = tokio::time::sleep(Duration::from_secs(10)) => {
|
||||
if !is_multicast_sock {
|
||||
send_stun_request(eee).await;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = cancel.cancelled() => {
|
||||
break;
|
||||
}
|
||||
_ = read_and_parse_packet(eee, socket, Some(Duration::from_secs(10)), is_multicast_sock) => { }
|
||||
_ = tokio::time::sleep(Duration::from_secs(10)) => {
|
||||
if !is_multicast_sock {
|
||||
send_stun_request(eee).await;
|
||||
}
|
||||
/*
|
||||
let req = SdlStunRequest {
|
||||
cookie: 0,
|
||||
client_id: eee.config.node_uuid.clone(),
|
||||
network_id: eee.network_id.load(Ordering::Relaxed),
|
||||
ip: eee.device_config.get_ip(),
|
||||
nat_type: 0,
|
||||
};
|
||||
let msg = encode_to_udp_message(Some(req), PacketType::StunRequest as u8).unwrap();
|
||||
if let Err(e) = send_to_sock(eee, &msg, &eee.config.super_nodes[eee.config.super_node_index.load(Ordering::Relaxed) as usize]).await {
|
||||
error!("failed to send to sock: {:?}", e);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
debug!("loop_socket_v4 exited");
|
||||
}
|
||||
|
||||
async fn loop_tap(eee: &'static Node, cancel: CancellationToken) {
|
||||
async fn receive_dns_reply(sock: &Arc<UdpSocket>) -> Option<Vec<u8>> {
|
||||
let mut reply = vec![0; 1024];
|
||||
if let Ok((size, _from)) = sock.recv_from(&mut reply).await {
|
||||
if size == 0 {
|
||||
// closed
|
||||
return None;
|
||||
}
|
||||
reply.truncate(size);
|
||||
return Some(reply);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
async fn loop_tap(
|
||||
eee: &'static Node,
|
||||
mut dns_rx: Receiver<(Vec<u8>, SocketAddr)>,
|
||||
cancel: CancellationToken,
|
||||
) {
|
||||
debug!("loop tap");
|
||||
let (tx, mut rx) = channel(10);
|
||||
tokio::spawn(async {
|
||||
@ -520,6 +233,60 @@ async fn loop_tap(eee: &'static Node, cancel: CancellationToken) {
|
||||
drop(rx);
|
||||
break;
|
||||
}
|
||||
global_reply_global = dns_rx.recv() => {
|
||||
// global reply, only dns payload
|
||||
if let Some(data) = global_reply_global {
|
||||
if let Ok(mut dns) = simple_dns::Packet::parse(&data.0) {
|
||||
let transaction_id = dns.id();
|
||||
if let Some((ip, port, origin_transaction_id)) = eee.dns_matcher.get_client_info(transaction_id) {
|
||||
warn!("got dns reply from global 223.5.5.5, will send to {}:{}",
|
||||
ip_to_string(&ip), port);
|
||||
|
||||
|
||||
let dstmac = eee.device_config.get_mac();
|
||||
let srcmac = eee.device_config.dns_mac;
|
||||
|
||||
dns.set_id(origin_transaction_id);
|
||||
let builder = PacketBuilder::ethernet2(srcmac, dstmac)
|
||||
.ipv4([100, 100, 100, 100], ip.to_be_bytes(), 64)
|
||||
.udp(53, port);
|
||||
|
||||
let new_dns_resp = dns.build_bytes_vec().unwrap();
|
||||
let mut response_packet = Vec::with_capacity(builder.size(new_dns_resp.len()));
|
||||
if let Ok(_) = builder.write(&mut response_packet, &new_dns_resp) {
|
||||
// warn!("will send to tun with packet: {:?}", response_packet);
|
||||
if let Err(_e) = eee.device.handle_packet_from_net(&response_packet).await {
|
||||
error!("failed to write dns packet to device");
|
||||
}
|
||||
} else {
|
||||
error!("failed to write dns body");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
reply = receive_dns_reply(&eee.udp_sock_for_dns) => {
|
||||
if reply.is_none() {
|
||||
drop(rx);
|
||||
break;
|
||||
}
|
||||
let reply = reply.unwrap();
|
||||
warn!("got packet from 15353: {:?}", reply);
|
||||
let dstmac = eee.device_config.get_mac();
|
||||
let srcmac = eee.device_config.dns_mac;
|
||||
let mut packet = Vec::with_capacity(14+reply.len());
|
||||
packet.extend_from_slice(&dstmac);
|
||||
packet.extend_from_slice(&srcmac);
|
||||
packet.push(0x08);
|
||||
packet.push(0x00);
|
||||
packet.extend_from_slice(&reply);
|
||||
|
||||
// TODO: check the packet should
|
||||
if let Err(_e) = eee.device.handle_packet_from_net(&packet).await {
|
||||
error!("failed to write dns packet to device");
|
||||
}
|
||||
}
|
||||
buf = rx.recv() => {
|
||||
if buf.is_none() {
|
||||
break;
|
||||
@ -528,23 +295,48 @@ async fn loop_tap(eee: &'static Node, cancel: CancellationToken) {
|
||||
}
|
||||
}
|
||||
}
|
||||
debug!("loop_tap exited");
|
||||
error!("loop_tap exited");
|
||||
}
|
||||
|
||||
async fn get_tun_flow(eee: &'static Node, tx: Sender<Vec<u8>>) {
|
||||
loop {
|
||||
let buf = tokio::task::spawn_blocking(|| {
|
||||
let mut buf = vec![0; 1800];
|
||||
let Ok(size) = eee.device.recv(&mut buf) else {
|
||||
return vec![];
|
||||
#[cfg(any(feature = "tun", target_os = "windows"))]
|
||||
async fn get_data_from_tun_with_layer2_zeroed(eee: &Node) -> BytesMut {
|
||||
let mut temp = BytesMut::zeroed(1514);
|
||||
// let mut temp = BytesMut::with_capacity(1514);
|
||||
let mut data_buf = temp.split_off(14);
|
||||
let Ok(size) = eee.device.recv(&mut data_buf).await else {
|
||||
error!("failed to receive");
|
||||
return BytesMut::new();
|
||||
};
|
||||
warn!("got {} bytes from tun with layer 2", size);
|
||||
|
||||
data_buf.truncate(size);
|
||||
temp.unsplit(data_buf);
|
||||
temp
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "tun"))]
|
||||
async fn get_data_from_tap_with_layer2(eee: &Node) -> BytesMut {
|
||||
let mut buf = BytesMut::zeroed(1514);
|
||||
let Ok(size) = eee.device.recv(&mut buf).await else {
|
||||
return BytesMut::new();
|
||||
};
|
||||
buf.truncate(size);
|
||||
buf
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
async fn get_tun_flow(eee: &'static Node, tx: Sender<BytesMut>) {
|
||||
loop {
|
||||
let buf = {
|
||||
#[cfg(any(feature = "tun", target_os = "windows"))]
|
||||
let data = get_data_from_tun_with_layer2_zeroed(eee).await;
|
||||
#[cfg(all(not(feature = "tun"), not(target_os = "windows")))]
|
||||
let data = get_data_from_tap_with_layer2(eee).await;
|
||||
|
||||
data
|
||||
};
|
||||
|
||||
if buf.len() == 0 {
|
||||
error!("buf length is zero, quitting loop");
|
||||
return;
|
||||
}
|
||||
if let Err(e) = tx.send(buf).await {
|
||||
@ -554,7 +346,7 @@ async fn get_tun_flow(eee: &'static Node, tx: Sender<Vec<u8>>) {
|
||||
}
|
||||
}
|
||||
|
||||
async fn read_and_parse_tun_packet(eee: &'static Node, buf: Vec<u8>) {
|
||||
async fn read_and_parse_tun_packet(eee: &'static Node, buf: BytesMut) {
|
||||
/*
|
||||
if !eee.is_authorized() {
|
||||
debug!("drop packet before authorized");
|
||||
@ -569,71 +361,31 @@ async fn read_and_parse_tun_packet(eee: &'static Node, buf: Vec<u8>) {
|
||||
*/
|
||||
// buf.truncate(size);
|
||||
|
||||
edge_send_packet_to_net(eee, &buf).await;
|
||||
edge_send_packet_to_net(eee, buf).await;
|
||||
}
|
||||
|
||||
async fn edge_send_packet_to_net(eee: &Node, data: &[u8]) {
|
||||
debug!("edge send packet to net({} bytes): {:?}", data.len(), data);
|
||||
match IpHeaders::from_slice(&data) {
|
||||
Ok((iphdr, _payload)) => {
|
||||
let Some(ipv4hdr) = iphdr.ipv4() else {
|
||||
debug!("ipv6 packet ignored");
|
||||
return;
|
||||
};
|
||||
let dstip = u32::from_be_bytes(ipv4hdr.0.destination);
|
||||
debug!("packet dst ip: {:?}", ipv4hdr.0.destination);
|
||||
let src = u32::from_be_bytes(ipv4hdr.0.source);
|
||||
debug!("packet src ip: {:?}", ipv4hdr.0.source);
|
||||
// packet should be sent to dev
|
||||
debug!("got {} bytes from tun", data.len());
|
||||
if (!eee.config.allow_routing) && (src != eee.device_config.get_ip()) {
|
||||
info!("dropping routed packet");
|
||||
return;
|
||||
}
|
||||
if !eee.is_authorized() {
|
||||
debug!("drop tun packet due to not authed");
|
||||
return;
|
||||
}
|
||||
async fn edge_send_packet_to_net(eee: &Node, data: BytesMut) {
|
||||
// debug!("edge send packet to net({} bytes): {:?}", data.len(), data);
|
||||
|
||||
/*
|
||||
let encrypt_key = eee.get_encrypt_key();
|
||||
if encrypt_key.len() == 0 {
|
||||
error!("drop tun packet due to encrypt key len is 0");
|
||||
return;
|
||||
}
|
||||
let msg_size = data.len() as u64;
|
||||
let Ok(encrypted_flow) = aes_encrypt(encrypt_key.as_slice(), data) else {
|
||||
error!("failed to encrypt flow");
|
||||
return;
|
||||
};
|
||||
|
||||
let message = SdlData {
|
||||
// TODO: network id should be stored in
|
||||
network_id: eee.network_id.load(Ordering::Relaxed),
|
||||
src_ip: eee.device_config.get_ip(),
|
||||
dst_ip: dstip,
|
||||
is_p2p: true,
|
||||
ttl: SDLAN_DEFAULT_TTL as u32,
|
||||
data: encrypted_flow,
|
||||
};
|
||||
debug!("sending SdlData: {:?}", message);
|
||||
let Ok(flow) = encode_to_udp_message(Some(message), PacketType::Data as u8) else {
|
||||
error!("failed to encode to udp message");
|
||||
return;
|
||||
};
|
||||
send_packet_to_net(eee, dstip, &flow, msg_size).await;
|
||||
}
|
||||
Err(e) => {
|
||||
error!("failed to parse ip packet: {}", e.to_string());
|
||||
}
|
||||
*/
|
||||
if let Err(e) = eee.device.handle_packet_from_device(data).await {
|
||||
error!("failed to handle packet from device: {}", e.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_packet_to_net(eee: &Node, dst_ip: u32, pkt: &[u8], size: u64) {
|
||||
let (dest_sock, is_p2p) = find_peer_destination(eee, dst_ip).await;
|
||||
pub async fn send_packet_to_net(eee: &'static Node, dst_mac: Mac, pkt: &[u8], size: u64) {
|
||||
let (dest_sock, is_p2p) = find_peer_destination(eee, dst_mac).await;
|
||||
if is_p2p {
|
||||
eee.stats.tx_p2p.fetch_add(size, Ordering::Relaxed);
|
||||
} else {
|
||||
eee.stats.tx_sup.fetch_add(size, Ordering::Relaxed);
|
||||
if is_multi_broadcast(dst_ip) {
|
||||
if is_multi_broadcast(&dst_mac) {
|
||||
eee.stats.tx_broadcast.fetch_add(size, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
@ -643,8 +395,8 @@ async fn send_packet_to_net(eee: &Node, dst_ip: u32, pkt: &[u8], size: u64) {
|
||||
}
|
||||
}
|
||||
|
||||
async fn find_peer_destination(eee: &Node, dst_ip: u32) -> (SdlanSock, bool) {
|
||||
if is_multi_broadcast(dst_ip) {
|
||||
async fn find_peer_destination(eee: &'static Node, dst_mac: Mac) -> (SdlanSock, bool) {
|
||||
if is_multi_broadcast(&dst_mac) {
|
||||
return (
|
||||
eee.config.super_nodes[eee.config.super_node_index.load(Ordering::Relaxed) as usize]
|
||||
.deepcopy(),
|
||||
@ -653,28 +405,36 @@ async fn find_peer_destination(eee: &Node, dst_ip: u32) -> (SdlanSock, bool) {
|
||||
}
|
||||
let mut is_p2p = false;
|
||||
let result: SdlanSock;
|
||||
if let Some(dst) = eee.known_peers.get_peer(&dst_ip) {
|
||||
let mut need_delete_from_known_peers = false;
|
||||
if let Some(dst) = eee.known_peers.peers.get_mut(&dst_mac) {
|
||||
let now = get_current_timestamp();
|
||||
if now - dst.last_p2p.load(Ordering::Relaxed) >= ((dst.timeout / 2) as u64) {
|
||||
// too much time elapsed since we saw the peer, need to register again
|
||||
eee.known_peers.delete_peer_with_ip(&dst_ip);
|
||||
error!("last p2p is too old, deleting from known_peers");
|
||||
need_delete_from_known_peers = true;
|
||||
// eee.known_peers.delete_peer_with_mac(&dst_mac);
|
||||
debug!("deleted from known");
|
||||
result = eee.config.super_nodes
|
||||
[eee.config.super_node_index.load(Ordering::Relaxed) as usize]
|
||||
.deepcopy();
|
||||
} else {
|
||||
// dst.last_seen.store(now, Ordering::Relaxed);
|
||||
is_p2p = true;
|
||||
result = dst.sock.read().unwrap().deepcopy();
|
||||
result = dst.sock.deepcopy();
|
||||
}
|
||||
} else {
|
||||
result = eee.config.super_nodes
|
||||
[eee.config.super_node_index.load(Ordering::Relaxed) as usize]
|
||||
.deepcopy();
|
||||
}
|
||||
|
||||
if need_delete_from_known_peers {
|
||||
eee.known_peers.delete_peer_with_mac(&dst_mac);
|
||||
}
|
||||
// println!("find peer_destination: {}", is_p2p);
|
||||
if !is_p2p {
|
||||
debug!("check_query_peer_info");
|
||||
super::packet::check_query_peer_info(eee, dst_ip).await;
|
||||
super::packet::check_query_peer_info(eee, dst_mac).await;
|
||||
}
|
||||
return (result, is_p2p);
|
||||
}
|
||||
|
||||
47
src/network/device.rs
Normal file → Executable file
47
src/network/device.rs
Normal file → Executable file
@ -1,13 +1,35 @@
|
||||
use sdlan_sn_rs::peer::IpSubnet;
|
||||
use std::{net::Ipv4Addr, sync::{Arc, atomic::{AtomicU32, Ordering}}};
|
||||
|
||||
use crate::utils::mac_to_string;
|
||||
use arc_swap::ArcSwap;
|
||||
use ipnet::Ipv4Net;
|
||||
use sdlan_sn_rs::{peer::IpSubnet, utils::{Mac, Result, SDLanError}};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::utils::generate_mac_address;
|
||||
|
||||
pub struct DeviceConfig {
|
||||
pub ip: IpSubnet,
|
||||
pub mtu: u32,
|
||||
mac: Mac,
|
||||
ip: IpSubnet,
|
||||
|
||||
self_ip_net: ArcSwap<Ipv4Net>,
|
||||
|
||||
pub dns_mac: Mac,
|
||||
}
|
||||
|
||||
impl DeviceConfig {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(mac: Mac, mtu: u32) -> Self {
|
||||
// let mac = generate_mac_address();
|
||||
let dns_mac = generate_mac_address();
|
||||
println!("self mac: {}", mac_to_string(&mac));
|
||||
debug!("self mac: {}", mac_to_string(&mac));
|
||||
DeviceConfig {
|
||||
mtu,
|
||||
mac: mac,
|
||||
ip: IpSubnet::new(0, 0),
|
||||
dns_mac,
|
||||
self_ip_net: ArcSwap::new(Arc::new(Ipv4Net::new_assert(Ipv4Addr::from(0), 32))),
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,12 +44,31 @@ impl DeviceConfig {
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn contains(&self, ip: &Ipv4Addr) -> bool {
|
||||
self.self_ip_net.load().contains(ip)
|
||||
}
|
||||
|
||||
pub fn set_ip(&self, ip_net_addr: u32, ip_net_bit_len: u8) -> Result<()>{
|
||||
if ip_net_bit_len > 32 {
|
||||
return Err(SDLanError::NormalError("invalid ip net bit len"));
|
||||
}
|
||||
self.ip.net_addr.store(ip_net_addr, Ordering::Relaxed);
|
||||
self.ip.net_bit_len.store(ip_net_bit_len, Ordering::Relaxed);
|
||||
self.self_ip_net.store(Arc::new(Ipv4Net::new(Ipv4Addr::from(ip_net_addr), ip_net_bit_len).unwrap()));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_ip(&self) -> u32 {
|
||||
self.ip.net_addr()
|
||||
}
|
||||
pub fn get_net_bit(&self) -> u8 {
|
||||
self.ip.net_bit_len()
|
||||
}
|
||||
|
||||
pub fn get_mac(&self) -> Mac {
|
||||
let mac = self.mac;
|
||||
mac
|
||||
}
|
||||
}
|
||||
|
||||
/// The mode in which open the virtual network adapter.
|
||||
|
||||
37
src/network/ipv6.rs
Normal file → Executable file
37
src/network/ipv6.rs
Normal file → Executable file
@ -1,25 +1,34 @@
|
||||
use std::{net::{IpAddr, Ipv6Addr}, time::Duration};
|
||||
use std::sync::Mutex;
|
||||
use std::{
|
||||
net::{IpAddr, Ipv6Addr},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use sdlan_sn_rs::{config::AF_INET6, peer::SdlanSock};
|
||||
use tokio::{net::UdpSocket, sync::mpsc::Receiver};
|
||||
use std::sync::Arc;
|
||||
use tokio::{sync::mpsc::Receiver};
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::error;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{network::{loop_socket_v4, loop_socket_v6}, utils::Socket};
|
||||
use crate::{network::loop_socket_v6, utils::Socket};
|
||||
|
||||
use super::Node;
|
||||
|
||||
pub async fn run_ipv6(edge: &'static Node, mut v6_may_change: Receiver<bool>) {
|
||||
v6_may_change.recv().await;
|
||||
loop {
|
||||
error!("v6_may_change received");
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
let ipv6 = get_current_ipv6();
|
||||
let mut ipv6 = get_current_ipv6();
|
||||
error!("got v6: {:?}", ipv6);
|
||||
if ipv6.is_none() {
|
||||
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||
ipv6 = get_current_ipv6();
|
||||
if ipv6.is_none() {
|
||||
v6_may_change.recv().await;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// ipv6 is not none
|
||||
let ipv6 = ipv6.unwrap();
|
||||
|
||||
/*
|
||||
@ -32,12 +41,17 @@ pub async fn run_ipv6(edge: &'static Node, mut v6_may_change: Receiver<bool>) {
|
||||
println!("ipv6: {}:{}", addr, port);
|
||||
*/
|
||||
|
||||
let socket = Arc::new(Socket::build_v6(ipv6, 0).await.unwrap());
|
||||
let Ok(v6_sock) = Socket::build_v6(ipv6, 0).await else{
|
||||
v6_may_change.recv().await;
|
||||
continue;
|
||||
};
|
||||
println!("sock ipv6: {}", ipv6);
|
||||
let socket = Arc::new(v6_sock);
|
||||
|
||||
let port = socket.get_local_port();
|
||||
let socket_clone = socket.clone();
|
||||
let cancel = CancellationToken::new();
|
||||
|
||||
|
||||
*edge.ipv6.write().unwrap() = Some(SdlanSock {
|
||||
family: AF_INET6,
|
||||
port,
|
||||
@ -57,6 +71,7 @@ pub async fn run_ipv6(edge: &'static Node, mut v6_may_change: Receiver<bool>) {
|
||||
}
|
||||
|
||||
pub fn get_current_ipv6() -> Option<Ipv6Addr> {
|
||||
// return None;
|
||||
let Ok(ips) = local_ip_address::list_afinet_netifas() else {
|
||||
error!("failed to get ip address");
|
||||
return None;
|
||||
@ -68,9 +83,9 @@ pub fn get_current_ipv6() -> Option<Ipv6Addr> {
|
||||
continue;
|
||||
}
|
||||
IpAddr::V6(ipv6) => {
|
||||
if (ipv6.octets()[0] & 0x70 == 0x20) {
|
||||
println!("got global ip: {}", ipv6);
|
||||
return Some(ipv6)
|
||||
if ipv6.octets()[0] & 0x70 == 0x20 {
|
||||
// println!("got global ip: {}", ipv6);
|
||||
return Some(ipv6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
17
src/network/mod.rs
Normal file → Executable file
17
src/network/mod.rs
Normal file → Executable file
@ -9,10 +9,23 @@ mod ipv6;
|
||||
mod packet;
|
||||
pub use packet::*;
|
||||
|
||||
mod arp;
|
||||
pub use arp::*;
|
||||
|
||||
mod route;
|
||||
pub use route::*;
|
||||
|
||||
mod tuntap;
|
||||
pub use tuntap::*;
|
||||
|
||||
pub const DNS_IP: u32 = (100<<24) + (100<<16) + (100<<8) + 100;
|
||||
|
||||
#[cfg_attr(target_os = "linux", path = "tun_linux.rs")]
|
||||
#[cfg_attr(target_os = "windows", path = "tun_win.rs")]
|
||||
mod tun;
|
||||
pub use tun::get_install_channel;
|
||||
pub use tun::{get_install_channel, restore_dns, arp_reply_arrived};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub use tun::{set_allow_routing, set_disallow_routing};
|
||||
|
||||
mod device;
|
||||
|
||||
|
||||
659
src/network/node.rs
Normal file → Executable file
659
src/network/node.rs
Normal file → Executable file
@ -1,23 +1,36 @@
|
||||
use arc_swap::ArcSwap;
|
||||
use dashmap::DashMap;
|
||||
use default_net::Interface;
|
||||
use prost::Message;
|
||||
use quinn::Endpoint;
|
||||
use rsa::RsaPrivateKey;
|
||||
use sdlan_sn_rs::config::{AF_INET, AF_INET6};
|
||||
use std::collections::HashMap;
|
||||
use std::any::Any;
|
||||
use std::future::Future;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, AtomicU8, Ordering};
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
use std::time::Duration;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio::net::{TcpListener, UdpSocket};
|
||||
use tokio::sync::mpsc::Sender;
|
||||
use tokio::sync::oneshot;
|
||||
use tracing::error;
|
||||
use tracing::{debug, error, warn};
|
||||
|
||||
use crate::network::{ArpTable, RouteTable2};
|
||||
use crate::pb::{
|
||||
encode_to_tcp_message, encode_to_udp_message, SdlEmpty, SdlStunProbe, SdlStunProbeReply,
|
||||
encode_to_tcp_message, encode_to_udp_message, SdlArpRequest, SdlEmpty, SdlStunProbe,
|
||||
SdlStunProbeReply,
|
||||
};
|
||||
use crate::quic::quic_init;
|
||||
use crate::tcp::{get_quic_write_conn, NatType, PacketType, StunProbeAttr};
|
||||
use crate::utils::DynamicDNSClient;
|
||||
use crate::utils::Socket;
|
||||
use crate::{
|
||||
get_base_dir, get_default_interface, CommandLine, ConnectionInfo, DNSMatcher, ErrorReport,
|
||||
ErrorSeverity, MyEncryptor, RuleCache,
|
||||
};
|
||||
use crate::tcp::{get_tcp_conn, NatType, PacketType, StunProbeAttr};
|
||||
use crate::utils::{PidRecorder, Socket};
|
||||
|
||||
use sdlan_sn_rs::peer::{is_sdlan_sock_equal, IpSubnet, V6Info};
|
||||
use sdlan_sn_rs::peer::{IpSubnet, V6Info};
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
@ -25,53 +38,135 @@ use super::device::{DeviceConfig, Mode};
|
||||
use super::tun::{new_iface, Iface};
|
||||
use tokio::fs::File;
|
||||
|
||||
use sdlan_sn_rs::utils::{gen_rsa_keys, load_private_key_file};
|
||||
use sdlan_sn_rs::utils::{gen_rsa_keys, load_private_key_file, save_to_file, Mac};
|
||||
use sdlan_sn_rs::utils::{Result, SDLanError};
|
||||
|
||||
static EDGE: OnceCell<Node> = OnceCell::new();
|
||||
|
||||
pub async fn init_edge(
|
||||
token: &str,
|
||||
// token: &str,
|
||||
// network_code: &str,
|
||||
args: &CommandLine,
|
||||
mac: Mac,
|
||||
node_conf: NodeConfig,
|
||||
tos: u32,
|
||||
// tos: u32,
|
||||
start_stop: Sender<StartStopInfo>,
|
||||
// mtu: u32,
|
||||
connecting_chan: Option<Sender<ConnectionInfo>>,
|
||||
udpsock_for_dns: Arc<UdpSocket>,
|
||||
udp_sock_for_global_dns: DynamicDNSClient,
|
||||
hostname: String,
|
||||
server_ip: String,
|
||||
install_channel: String,
|
||||
error_report_channel: Option<Sender<ErrorReport>>,
|
||||
exclusive_tcp: TcpListener,
|
||||
) -> Result<()> {
|
||||
let _ = PidRecorder::new(".pid");
|
||||
|
||||
// gen public key
|
||||
gen_rsa_keys(".client");
|
||||
let rsa_path = format!("{}/.client", get_base_dir());
|
||||
gen_rsa_keys(&rsa_path);
|
||||
let mut pubkey = String::new();
|
||||
|
||||
File::open(".client/id_rsa.pub")
|
||||
// File::open(".client/id_rsa.pub")
|
||||
File::open(&format!("{}/id_rsa.pub", rsa_path))
|
||||
.await?
|
||||
.read_to_string(&mut pubkey)
|
||||
.await?;
|
||||
let privatekey = load_private_key_file(".client/id_rsa")?;
|
||||
let privatekey = load_private_key_file(&format!("{}/id_rsa", rsa_path))?;
|
||||
|
||||
// init sock
|
||||
// let edge_uuid = create_or_load_uuid("")?;
|
||||
//let node_conf = parse_config(edge_uuid, &args).await?;
|
||||
|
||||
let sock_v4 = Socket::build(0, true, false, tos).await?;
|
||||
let default_interface = match get_default_interface() {
|
||||
Ok(interface) => {
|
||||
println!("get default interface: {:?}", interface);
|
||||
Some(interface)
|
||||
}
|
||||
Err(e) => {
|
||||
println!("failed to get default net interface: {}", e.as_str());
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let sock_multicast = Socket::build(MULTICAST_PORT, true, true, 0).await?;
|
||||
let Ok(sock_v4) = Socket::build(
|
||||
node_conf._local_port,
|
||||
true,
|
||||
false,
|
||||
args.tos,
|
||||
&default_interface,
|
||||
)
|
||||
.await
|
||||
else {
|
||||
panic!("failed to build sock for sock v4");
|
||||
};
|
||||
|
||||
let mut sock_multicast = None;
|
||||
if !node_conf._drop_multicast {
|
||||
sock_multicast =
|
||||
Some(Socket::build(MULTICAST_PORT, true, true, 0, &default_interface).await?);
|
||||
}
|
||||
// let sock_multicast = Socket::build(MULTICAST_PORT, true, true, 0).await?;
|
||||
// allow multicast
|
||||
|
||||
// TODO: set the sn's tcp socket
|
||||
// let tcpsock = TCPSocket::build("121.4.79.234:1234").await?;
|
||||
let tcp_pong = Arc::new(AtomicU64::new(0));
|
||||
|
||||
let iface = match new_iface("dev") {
|
||||
Ok(iface) => iface,
|
||||
Err(e) => {
|
||||
if let Some(ref chan) = error_report_channel {
|
||||
println!("sending one panic");
|
||||
chan.send(ErrorReport {
|
||||
severity: ErrorSeverity::Panic,
|
||||
message: e.to_string(),
|
||||
})
|
||||
.await;
|
||||
return Err(e.into());
|
||||
} else {
|
||||
panic!("new iface failed: {}", e.to_string());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let edge = Node::new(
|
||||
mac,
|
||||
pubkey,
|
||||
node_conf,
|
||||
sock_v4,
|
||||
&default_interface,
|
||||
sock_multicast,
|
||||
token,
|
||||
// token,
|
||||
// network_code,
|
||||
privatekey,
|
||||
tcp_pong.clone(),
|
||||
start_stop,
|
||||
args.mtu,
|
||||
connecting_chan,
|
||||
hostname,
|
||||
udpsock_for_dns,
|
||||
udp_sock_for_global_dns,
|
||||
server_ip,
|
||||
install_channel,
|
||||
args.take_dns,
|
||||
error_report_channel,
|
||||
iface,
|
||||
exclusive_tcp,
|
||||
);
|
||||
|
||||
edge.route_table
|
||||
.parse_and_add_route(&args.route_file, &args.route_str);
|
||||
let matcher = Arc::clone(&edge.dns_matcher);
|
||||
|
||||
do_init_edge(edge)?;
|
||||
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
tokio::time::sleep(Duration::from_secs(10)).await;
|
||||
matcher.retain();
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -102,26 +197,82 @@ pub struct StartStopInfo {
|
||||
pub pkt_id: Option<u32>,
|
||||
}
|
||||
|
||||
pub struct StringToken<T>(RwLock<T>);
|
||||
|
||||
impl<T: Clone> StringToken<T> {
|
||||
pub fn new(value: T) -> Self {
|
||||
Self(RwLock::new(value))
|
||||
}
|
||||
|
||||
pub fn get(&self) -> T {
|
||||
self.0.read().unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn set(&self, token: T) {
|
||||
*self.0.write().unwrap() = token;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IdentityID(AtomicU32);
|
||||
|
||||
impl IdentityID {
|
||||
pub fn new(id: u32) -> Self {
|
||||
Self(AtomicU32::new(id))
|
||||
}
|
||||
|
||||
pub fn load(&self) -> u32 {
|
||||
self.0.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn store(&self, id: u32) {
|
||||
self.0.store(id, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Node {
|
||||
packet_id: AtomicU32,
|
||||
|
||||
pub encryptor: ArcSwap<MyEncryptor>,
|
||||
|
||||
pub network_id: AtomicU32,
|
||||
pub network_domain: ArcSwap<String>,
|
||||
|
||||
pub identity_id: IdentityID,
|
||||
|
||||
// rule is identity-to-identity ok
|
||||
pub rule_cache: RuleCache,
|
||||
|
||||
// route_table stores the routes
|
||||
pub route_table: RouteTable2,
|
||||
|
||||
pub access_token: StringToken<String>,
|
||||
pub session_token: StringToken<Vec<u8>>,
|
||||
|
||||
pub hostname: RwLock<String>,
|
||||
|
||||
pub quic_endpoint: Endpoint,
|
||||
|
||||
pub udp_sock_for_dns: Arc<UdpSocket>,
|
||||
pub udp_sock_for_global_dns: DynamicDNSClient,
|
||||
pub dns_matcher: Arc<DNSMatcher>,
|
||||
|
||||
pub server_ip: String,
|
||||
|
||||
pub tcp_pong: Arc<AtomicU64>,
|
||||
|
||||
start_stop_sender: Sender<StartStopInfo>,
|
||||
pub connection_chan: Option<Sender<ConnectionInfo>>,
|
||||
|
||||
// user token info
|
||||
pub _token: Mutex<String>,
|
||||
|
||||
// pub _token: Mutex<String>,
|
||||
// pub network_code: Mutex<String>,
|
||||
pub device_config: DeviceConfig,
|
||||
pub device: Iface,
|
||||
|
||||
// authorize related
|
||||
pub authorized: AtomicBool,
|
||||
// pub header_key: RwLock<Arc<Vec<u8>>>,
|
||||
pub encrypt_key: RwLock<Arc<Vec<u8>>>,
|
||||
|
||||
// pub encrypt_key: RwLock<Arc<Vec<u8>>>,
|
||||
pub rsa_pubkey: String,
|
||||
pub rsa_private: RsaPrivateKey,
|
||||
|
||||
@ -131,10 +282,16 @@ pub struct Node {
|
||||
|
||||
// store pending, and known peers
|
||||
pub pending_peers: PeerMap,
|
||||
|
||||
pub take_over_dns: bool,
|
||||
|
||||
#[cfg(any(feature = "tun", target_os = "windows"))]
|
||||
pub arp_table: ArpTable,
|
||||
|
||||
pub known_peers: PeerMap,
|
||||
|
||||
// pub tcp_sock_v4: TCPSocket,
|
||||
pub udp_sock_multicast: Socket,
|
||||
pub udp_sock_multicast: Option<Socket>,
|
||||
pub udp_sock_v4: Socket,
|
||||
pub outer_ip_v4: AtomicU32,
|
||||
pub udp_sock_v6: RwLock<Option<Arc<Socket>>>,
|
||||
@ -149,34 +306,63 @@ pub struct Node {
|
||||
// last register super time, in unix
|
||||
pub _last_register_req: AtomicU64,
|
||||
|
||||
pub install_channel: String,
|
||||
|
||||
nat_type: Mutex<NatType>,
|
||||
|
||||
nat_cookie: AtomicU32,
|
||||
cookie_match: DashMap<u32, oneshot::Sender<SdlStunProbeReply>>,
|
||||
|
||||
packet_id_match: DashMap<u32, oneshot::Sender<RegisterSuperFeedback>>,
|
||||
error_report_channel: Option<Sender<ErrorReport>>,
|
||||
|
||||
//cookie_match: DashMap<u32, oneshot::Sender<SdlStunProbeReply>>,
|
||||
pub cookie_match: Queryer,
|
||||
// packet_id_match: DashMap<u32, oneshot::Sender<RegisterSuperFeedback>>,
|
||||
exclusive_tcp: TcpListener,
|
||||
}
|
||||
|
||||
unsafe impl Sync for Node {}
|
||||
|
||||
impl Node {
|
||||
pub fn send_register_super_feedback(&self, pktid: u32, feed: RegisterSuperFeedback) {
|
||||
match self.packet_id_match.remove(&pktid) {
|
||||
Some(sender) => {
|
||||
let _ = sender.1.send(feed);
|
||||
}
|
||||
None => {
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.cookie_match.write_feedback(pktid, Box::new(feed));
|
||||
}
|
||||
|
||||
pub fn get_nat_type(&self) -> NatType {
|
||||
self.nat_type.lock().unwrap().clone()
|
||||
}
|
||||
|
||||
pub async fn start_without_feedback(&self, token: String) -> Result<()> {
|
||||
*self._token.lock().unwrap() = token;
|
||||
pub async fn start_without_feedback(
|
||||
&self,
|
||||
access_token: String,
|
||||
network_id: u32,
|
||||
network_domain: &String,
|
||||
ip_net: u32,
|
||||
ip_net_bit_len: u8,
|
||||
identity_id: u32,
|
||||
hostname: Option<String>,
|
||||
) -> Result<()> {
|
||||
if let Some(host) = hostname {
|
||||
let idfile = format!("{}/.host", get_base_dir());
|
||||
let _ = save_to_file(&idfile, &host);
|
||||
*self.hostname.write().unwrap() = host;
|
||||
}
|
||||
|
||||
self.access_token.set(access_token);
|
||||
|
||||
if let Err(_e) = self.device_config.set_ip(ip_net, ip_net_bit_len) {
|
||||
error!("failed to set ip");
|
||||
}
|
||||
// self.device_config.ip.net_addr.store(ip_net, Ordering::Relaxed);
|
||||
// self.device_config.ip.net_bit_len.store(ip_net_bit_len, Ordering::Relaxed);
|
||||
// *self.device_config.mac.write().unwrap() = create_or_load_mac();
|
||||
self.network_id.store(network_id, Ordering::Relaxed);
|
||||
self.network_domain.store(Arc::new(network_domain.clone()));
|
||||
// self.network_domain.write().unwrap().clone_from(network_domain);
|
||||
// self.network_domain = network_domain;
|
||||
|
||||
self.identity_id.store(identity_id);
|
||||
// *self._token.lock().unwrap() = token;
|
||||
// *self.network_code.lock().unwrap() = network_code;
|
||||
let _ = self
|
||||
.start_stop_sender
|
||||
.send(StartStopInfo {
|
||||
@ -189,13 +375,45 @@ impl Node {
|
||||
|
||||
pub async fn start_with_feedback(
|
||||
&self,
|
||||
token: String,
|
||||
|
||||
access_token: String,
|
||||
|
||||
network_id: u32,
|
||||
network_domain: &String,
|
||||
|
||||
ip_net: u32,
|
||||
ip_net_bit_len: u8,
|
||||
identity_id: u32,
|
||||
// token: String,
|
||||
// network_code: String,
|
||||
hostname: Option<String>,
|
||||
timeout: Duration,
|
||||
) -> Result<RegisterSuperFeedback> {
|
||||
*self._token.lock().unwrap() = token;
|
||||
let (tx, rx) = oneshot::channel();
|
||||
if let Some(host) = hostname {
|
||||
let idfile = format!("{}/.host", get_base_dir());
|
||||
let _ = save_to_file(&idfile, &host);
|
||||
*self.hostname.write().unwrap() = host;
|
||||
}
|
||||
|
||||
self.access_token.set(access_token);
|
||||
if let Err(_e) = self.device_config.set_ip(ip_net, ip_net_bit_len) {
|
||||
error!("failed to set ip");
|
||||
}
|
||||
// *self.device_config.mac.write().unwrap() = create_or_load_mac();
|
||||
self.network_id.store(network_id, Ordering::Relaxed);
|
||||
// self.network_domain.write().unwrap().clone_from(network_domain);
|
||||
self.network_domain.store(Arc::new(network_domain.clone()));
|
||||
|
||||
self.identity_id.store(identity_id);
|
||||
// *self._token.lock().unwrap() = token;
|
||||
// *self.network_code.lock().unwrap() = network_code;
|
||||
let id = self.get_next_packet_id();
|
||||
self.packet_id_match.insert(id, tx);
|
||||
|
||||
let res = self
|
||||
.cookie_match
|
||||
.do_action_and_wait_for(
|
||||
0,
|
||||
|| async {
|
||||
let _ = self
|
||||
.start_stop_sender
|
||||
.send(StartStopInfo {
|
||||
@ -203,24 +421,24 @@ impl Node {
|
||||
pkt_id: Some(id),
|
||||
})
|
||||
.await;
|
||||
debug!("start with feedback");
|
||||
},
|
||||
timeout,
|
||||
)
|
||||
.await?;
|
||||
|
||||
tokio::select! {
|
||||
rx_info = rx => {
|
||||
if let Ok(result) = rx_info {
|
||||
self.packet_id_match.remove(&id);
|
||||
Ok(result)
|
||||
if let Ok(res) = res.downcast() {
|
||||
Ok(*res)
|
||||
} else {
|
||||
Err(SDLanError::NormalError("rx closed"))
|
||||
}
|
||||
}
|
||||
_ = tokio::time::sleep(timeout) => {
|
||||
Err(SDLanError::NormalError("timed out"))
|
||||
}
|
||||
Err(SDLanError::ConvertError(
|
||||
"failed to convert feedback to RSFeedback".to_owned(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn stop(&self) {
|
||||
*self._token.lock().unwrap() = "".to_owned();
|
||||
// *self._token.lock().unwrap() = "".to_owned();
|
||||
self.session_token.set(vec![]);
|
||||
let _ = self
|
||||
.start_stop_sender
|
||||
.send(StartStopInfo {
|
||||
@ -231,32 +449,71 @@ impl Node {
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
mac: Mac,
|
||||
pubkey: String,
|
||||
config: NodeConfig,
|
||||
sock: Socket,
|
||||
multicast_sock: Socket,
|
||||
iface: &Option<Interface>,
|
||||
multicast_sock: Option<Socket>,
|
||||
// tcpsock: TCPSocket,
|
||||
token: &str,
|
||||
// token: &str,
|
||||
// network_code: &str,
|
||||
private: RsaPrivateKey,
|
||||
tcp_pong: Arc<AtomicU64>,
|
||||
start_stop: Sender<StartStopInfo>,
|
||||
mtu: u32,
|
||||
connecting_chan: Option<Sender<ConnectionInfo>>,
|
||||
hostname: String,
|
||||
udpsock_for_dns: Arc<UdpSocket>,
|
||||
udp_sock_for_global_dns: DynamicDNSClient,
|
||||
server_ip: String,
|
||||
install_channel: String,
|
||||
take_over_dns: bool,
|
||||
// 是否需要上报错误信息
|
||||
error_report_info: Option<Sender<ErrorReport>>,
|
||||
virtual_iface: Iface,
|
||||
tcp_listener: TcpListener,
|
||||
) -> Self {
|
||||
Self {
|
||||
packet_id: AtomicU32::new(1),
|
||||
network_id: AtomicU32::new(0),
|
||||
_token: Mutex::new(token.to_owned()),
|
||||
#[cfg(any(feature = "tun", target_os = "windows"))]
|
||||
arp_table: ArpTable::new(),
|
||||
|
||||
packet_id: AtomicU32::new(1),
|
||||
encryptor: ArcSwap::from(Arc::new(MyEncryptor::new())),
|
||||
// encryptor: RwLock::new(MyEncryptor::new()),
|
||||
network_id: AtomicU32::new(0),
|
||||
hostname: RwLock::new(hostname),
|
||||
|
||||
rule_cache: RuleCache::new(),
|
||||
|
||||
route_table: RouteTable2::new(),
|
||||
|
||||
network_domain: ArcSwap::new(Arc::new(String::new())),
|
||||
|
||||
udp_sock_for_dns: udpsock_for_dns,
|
||||
udp_sock_for_global_dns: udp_sock_for_global_dns,
|
||||
dns_matcher: Arc::new(DNSMatcher::new()),
|
||||
|
||||
quic_endpoint: quic_init(iface),
|
||||
|
||||
identity_id: IdentityID::new(0),
|
||||
access_token: StringToken::new(String::new()),
|
||||
session_token: StringToken::new(Vec::new()),
|
||||
|
||||
// _token: Mutex::new(token.to_owned()),
|
||||
// network_code: Mutex::new(network_code.to_owned()),
|
||||
start_stop_sender: start_stop,
|
||||
connection_chan: connecting_chan,
|
||||
|
||||
tcp_pong,
|
||||
|
||||
nat_type: Mutex::new(NatType::Blocked),
|
||||
|
||||
device_config: DeviceConfig::new(),
|
||||
device: new_iface("dev", Mode::Tun),
|
||||
device_config: DeviceConfig::new(mac, mtu),
|
||||
device: virtual_iface,
|
||||
|
||||
authorized: AtomicBool::new(false),
|
||||
encrypt_key: RwLock::new(Arc::new(Vec::new())),
|
||||
// encrypt_key: RwLock::new(Arc::new(Vec::new())),
|
||||
// rsa_pubkey:
|
||||
rsa_pubkey: pubkey,
|
||||
rsa_private: private,
|
||||
@ -284,9 +541,14 @@ impl Node {
|
||||
stats: NodeStats::new(),
|
||||
_last_register_req: AtomicU64::new(0),
|
||||
|
||||
packet_id_match: DashMap::new(),
|
||||
// packet_id_match: DashMap::new(),
|
||||
nat_cookie: AtomicU32::new(1),
|
||||
cookie_match: DashMap::new(),
|
||||
cookie_match: Queryer::new(),
|
||||
error_report_channel: error_report_info,
|
||||
server_ip,
|
||||
install_channel,
|
||||
take_over_dns,
|
||||
exclusive_tcp: tcp_listener,
|
||||
}
|
||||
}
|
||||
|
||||
@ -299,9 +561,9 @@ impl Node {
|
||||
self.authorized.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
pub fn set_authorized(&self, authorized: bool, encrypt_key: Vec<u8>) {
|
||||
pub fn set_authorized(&self, authorized: bool) {
|
||||
self.authorized.store(authorized, Ordering::Relaxed);
|
||||
*(self.encrypt_key.write().unwrap()) = Arc::new(encrypt_key);
|
||||
// *(self.encrypt_key.write().unwrap()) = Arc::new(encrypt_key);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -310,9 +572,11 @@ impl Node {
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
pub fn get_encrypt_key(&self) -> Arc<Vec<u8>> {
|
||||
self.encrypt_key.read().unwrap().clone()
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
pub fn sn_is_known(&self, sock: &SdlanSock) -> bool {
|
||||
@ -363,11 +627,21 @@ impl Node {
|
||||
}
|
||||
*/
|
||||
|
||||
pub async fn send_nat_probe_reply(&self, cookie: u32, buf: SdlStunProbeReply) {
|
||||
if let Some((_key, chan)) = self.cookie_match.remove(&cookie) {
|
||||
let _ = chan.send(buf);
|
||||
pub async fn send_arp_request(&self, gw_ip: u32, real_ip: u32) -> Result<()> {
|
||||
let arp_request = SdlArpRequest {
|
||||
// pkt_id: id,
|
||||
target_ip: gw_ip,
|
||||
origin_ip: real_ip,
|
||||
context: Vec::new(),
|
||||
};
|
||||
let msg = encode_to_tcp_message(Some(arp_request), PacketType::ArpRequest as u8).unwrap();
|
||||
let conn = get_quic_write_conn();
|
||||
let _ = conn.send(msg).await;
|
||||
Ok(())
|
||||
}
|
||||
error!("failed to get such cookie stun probe");
|
||||
|
||||
pub async fn send_nat_probe_reply(&self, cookie: u32, buf: SdlStunProbeReply) {
|
||||
self.cookie_match.write_feedback(cookie, Box::new(buf));
|
||||
}
|
||||
|
||||
pub async fn probe_nat_type(&self) -> NatType {
|
||||
@ -385,32 +659,38 @@ impl Node {
|
||||
return NatType::Blocked;
|
||||
};
|
||||
|
||||
let Ok(reply2) = self
|
||||
if reply1.ip == self.outer_ip_v4.load(Ordering::Relaxed) {
|
||||
let Ok(_reply2) = self
|
||||
.send_and_wait_for_probe_reply(StunProbeAttr::Peer, &self.config.nat_server1)
|
||||
.await
|
||||
else {
|
||||
*self.nat_type.lock().unwrap() = NatType::Symmetric;
|
||||
return NatType::Symmetric;
|
||||
};
|
||||
*self.nat_type.lock().unwrap() = NatType::NoNat;
|
||||
return NatType::NoNat;
|
||||
}
|
||||
|
||||
if let Ok(_reply2_2) = self
|
||||
.send_and_wait_for_probe_reply(StunProbeAttr::Peer, &self.config.nat_server1)
|
||||
.await
|
||||
{
|
||||
*self.nat_type.lock().unwrap() = NatType::FullCone;
|
||||
return NatType::FullCone;
|
||||
}
|
||||
|
||||
let Ok(reply3) = self
|
||||
.send_and_wait_for_probe_reply(StunProbeAttr::None, &self.config.nat_server2)
|
||||
.await
|
||||
else {
|
||||
*self.nat_type.lock().unwrap() = NatType::Blocked;
|
||||
return NatType::Blocked;
|
||||
};
|
||||
|
||||
if reply1.ip == self.outer_ip_v4.load(Ordering::Relaxed) {
|
||||
*self.nat_type.lock().unwrap() = NatType::NoNat;
|
||||
return NatType::NoNat;
|
||||
}
|
||||
|
||||
if reply1.ip != reply2.ip || reply1.port != reply2.port {
|
||||
if reply3.ip != reply1.ip || reply3.port != reply1.port {
|
||||
*self.nat_type.lock().unwrap() = NatType::Symmetric;
|
||||
return NatType::Symmetric;
|
||||
}
|
||||
|
||||
if let Ok(_reply3) = self
|
||||
.send_and_wait_for_probe_reply(StunProbeAttr::Peer, &self.config.nat_server1)
|
||||
.await
|
||||
{
|
||||
*self.nat_type.lock().unwrap() = NatType::FullCone;
|
||||
return NatType::FullCone;
|
||||
}
|
||||
|
||||
if let Ok(_reply4) = self
|
||||
.send_and_wait_for_probe_reply(StunProbeAttr::Port, &self.config.nat_server1)
|
||||
.await
|
||||
@ -425,10 +705,10 @@ impl Node {
|
||||
|
||||
pub async fn send_unregister_super(&self) -> Result<()> {
|
||||
let content =
|
||||
encode_to_tcp_message::<SdlEmpty>(None, 0, PacketType::UnRegisterSuper as u8).unwrap();
|
||||
encode_to_tcp_message::<SdlEmpty>(None, PacketType::UnRegisterSuper as u8).unwrap();
|
||||
|
||||
let conn = get_tcp_conn();
|
||||
let _ = conn.send(&content).await;
|
||||
let conn = get_quic_write_conn();
|
||||
let _ = conn.send(content).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -442,38 +722,33 @@ impl Node {
|
||||
let probe = SdlStunProbe {
|
||||
attr: msgattr as u32,
|
||||
cookie,
|
||||
step: 0,
|
||||
};
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.cookie_match.insert(cookie, tx);
|
||||
// let cookie = msg.cookie;
|
||||
let msg = encode_to_udp_message(Some(probe), PacketType::StunProbe as u8).unwrap();
|
||||
if let Err(_e) = self.udp_sock_v4.send_to(&msg, to_server).await {
|
||||
self.cookie_match.remove(&cookie);
|
||||
return Err(SDLanError::NormalError("send error"));
|
||||
}
|
||||
let result = self
|
||||
.cookie_match
|
||||
.send_message_to_udp_and_wait_for(
|
||||
&self.udp_sock_v4,
|
||||
cookie,
|
||||
probe,
|
||||
PacketType::StunProbe as u8,
|
||||
to_server,
|
||||
Duration::from_secs(3),
|
||||
)
|
||||
.await?;
|
||||
|
||||
tokio::select! {
|
||||
_ = tokio::time::sleep(Duration::from_secs(3)) => {
|
||||
self.cookie_match.remove(&cookie);
|
||||
return Err(SDLanError::NormalError("timed out"));
|
||||
}
|
||||
reply = rx => {
|
||||
self.cookie_match.remove(&cookie);
|
||||
if let Ok(reply) = reply {
|
||||
// reply received,
|
||||
return Ok(reply);
|
||||
// println!("got nat ip: {}:{}", ip_to_string(&reply.ip), reply.port);
|
||||
}
|
||||
return Err(SDLanError::NormalError("reply recv error"));
|
||||
// step 1 received
|
||||
}
|
||||
if let Ok(res) = result.downcast() {
|
||||
return Ok(*res);
|
||||
}
|
||||
return Err(SDLanError::ConvertError(
|
||||
"failed to convert to StunprobeReply".to_owned(),
|
||||
));
|
||||
// println!("==> sending probe request: {:?}", probe);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PeerMap {
|
||||
pub peers: DashMap<u32, Arc<EdgePeer>>,
|
||||
pub peers: DashMap<Mac, EdgePeer>,
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
@ -484,37 +759,38 @@ impl PeerMap {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_peer(&self, ip: &u32) -> Option<Arc<EdgePeer>> {
|
||||
if let Some(v) = self.peers.get(ip) {
|
||||
/*
|
||||
pub fn get_peer(&self, mac: &Mac) -> Option<Arc<EdgePeer>> {
|
||||
if let Some(v) = self.peers.get(mac) {
|
||||
Some(v.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.peers.clear();
|
||||
}
|
||||
|
||||
pub fn get_peer_by_sock(&self, sock: &SdlanSock) -> Option<Arc<EdgePeer>> {
|
||||
/*
|
||||
for s in self.peers.iter() {
|
||||
let m = s.sock.read().unwrap();
|
||||
if is_sdlan_sock_equal(&m, sock) {
|
||||
return Some(s.value().clone());
|
||||
}
|
||||
}
|
||||
*/
|
||||
None
|
||||
}
|
||||
|
||||
pub fn delete_peer_with_ip(&self, ip: &u32) {
|
||||
self.peers.remove(ip);
|
||||
pub fn delete_peer_with_mac(&self, mac: &Mac) -> Option<(Mac, EdgePeer)> {
|
||||
self.peers.remove(mac)
|
||||
}
|
||||
|
||||
pub fn insert_peer(&self, p: Arc<EdgePeer>) {
|
||||
let net_addr = p.dev_addr.net_addr();
|
||||
if net_addr != 0 {
|
||||
self.peers.insert(net_addr, p);
|
||||
}
|
||||
pub fn insert_peer(&self, mac: Mac, p: EdgePeer) {
|
||||
self.peers.insert(mac, p);
|
||||
}
|
||||
}
|
||||
|
||||
@ -548,12 +824,12 @@ impl NodeStats {
|
||||
|
||||
use sdlan_sn_rs::peer::SdlanSock;
|
||||
|
||||
use crate::config::{self, MULTICAST_PORT, REGISTER_INTERVAL};
|
||||
use crate::config::{self, MULTICAST_PORT, REGISTER_SUPER_INTERVAL};
|
||||
pub struct NodeConfig {
|
||||
// node name
|
||||
pub name: String,
|
||||
// 允许路由
|
||||
pub allow_routing: bool,
|
||||
pub allow_routing: AtomicBool,
|
||||
|
||||
// 丢弃多播,广播消息
|
||||
pub _drop_multicast: bool,
|
||||
@ -588,12 +864,15 @@ pub struct NodeConfig {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EdgePeer {
|
||||
// pub mac: Mac,
|
||||
pub dev_addr: IpSubnet,
|
||||
|
||||
pub nat_type: NatType,
|
||||
|
||||
// 对端对外开放的ip和端口信息
|
||||
pub sock: RwLock<SdlanSock>,
|
||||
pub sock: SdlanSock,
|
||||
// peer's ipv6 info
|
||||
pub _v6_info: RwLock<Option<SdlanSock>>,
|
||||
pub _v6_info: Option<SdlanSock>,
|
||||
|
||||
pub timeout: u8,
|
||||
|
||||
@ -613,6 +892,7 @@ pub struct EdgePeer {
|
||||
|
||||
impl EdgePeer {
|
||||
pub fn new(
|
||||
// mac: Mac,
|
||||
net_addr: u32,
|
||||
net_bit_len: u8,
|
||||
sock: &SdlanSock,
|
||||
@ -629,14 +909,141 @@ impl EdgePeer {
|
||||
})
|
||||
}
|
||||
Self {
|
||||
// mac,
|
||||
dev_addr: IpSubnet::new(net_addr, net_bit_len),
|
||||
sock: RwLock::new(sock.deepcopy()),
|
||||
_v6_info: RwLock::new(v6_info),
|
||||
timeout: REGISTER_INTERVAL,
|
||||
sock: sock.deepcopy(),
|
||||
_v6_info: v6_info,
|
||||
timeout: REGISTER_SUPER_INTERVAL as u8,
|
||||
last_p2p: AtomicU64::new(0),
|
||||
last_seen: AtomicU64::new(0),
|
||||
_last_valid_timestamp: AtomicU64::new(now),
|
||||
last_sent_query: AtomicU64::new(0),
|
||||
nat_type: NatType::Invalid,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_nat_type(&self) -> NatType {
|
||||
self.nat_type
|
||||
}
|
||||
}
|
||||
|
||||
type BoxedProstMessage = Box<dyn Any + Send + Sync + 'static>;
|
||||
|
||||
pub struct Queryer {
|
||||
pub mailbox: DashMap<u32, tokio::sync::oneshot::Sender<BoxedProstMessage>>,
|
||||
}
|
||||
|
||||
impl Queryer {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
mailbox: DashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_feedback(&self, id: u32, data: BoxedProstMessage) {
|
||||
if let Some((_, tx)) = self.mailbox.remove(&id) {
|
||||
if let Err(_e) = tx.send(data) {
|
||||
error!("failed to write feedback");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_message_to_udp_and_wait_for<T: Message>(
|
||||
&self,
|
||||
sock: &Socket,
|
||||
id: u32,
|
||||
message: T,
|
||||
packet_type: u8,
|
||||
to_server: &SocketAddr,
|
||||
timeout: Duration,
|
||||
) -> Result<BoxedProstMessage> {
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
self.mailbox.insert(id, tx);
|
||||
|
||||
let content = encode_to_udp_message(Some(message), packet_type)?;
|
||||
|
||||
if let Err(_e) = sock.send_to(&content, to_server).await {
|
||||
self.mailbox.remove(&id);
|
||||
return Err(SDLanError::NormalError("send error"));
|
||||
}
|
||||
|
||||
tokio::select! {
|
||||
data = rx => {
|
||||
if let Ok(data) = data {
|
||||
self.mailbox.remove(&id);
|
||||
Ok(data)
|
||||
} else {
|
||||
self.mailbox.remove(&id);
|
||||
Err(SDLanError::IOError("rx receive failed".to_string()))
|
||||
}
|
||||
}
|
||||
_ = tokio::time::sleep(timeout) => {
|
||||
self.mailbox.remove(&id);
|
||||
Err(SDLanError::IOError("timed out".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn do_action_and_wait_for<T, F>(
|
||||
&self,
|
||||
id: u32,
|
||||
action: T,
|
||||
timeout: Duration,
|
||||
) -> Result<BoxedProstMessage>
|
||||
where
|
||||
F: Future<Output = ()>,
|
||||
T: Fn() -> F,
|
||||
{
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
self.mailbox.insert(id, tx);
|
||||
|
||||
action().await;
|
||||
|
||||
tokio::select! {
|
||||
data = rx => {
|
||||
if let Ok(data) = data {
|
||||
self.mailbox.remove(&id);
|
||||
Ok(data)
|
||||
} else {
|
||||
self.mailbox.remove(&id);
|
||||
Err(SDLanError::IOError("rx receive failed".to_string()))
|
||||
}
|
||||
}
|
||||
_ = tokio::time::sleep(timeout) => {
|
||||
self.mailbox.remove(&id);
|
||||
Err(SDLanError::IOError("timed out".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send_message_to_quic_and_wait_for<T: Message>(
|
||||
&self,
|
||||
id: u32,
|
||||
message: T,
|
||||
packet_type: u8,
|
||||
timeout: Duration,
|
||||
) -> Result<BoxedProstMessage> {
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
self.mailbox.insert(id, tx);
|
||||
|
||||
let content = encode_to_tcp_message(Some(message), packet_type)?;
|
||||
let quic_conn = get_quic_write_conn();
|
||||
quic_conn.send(content).await?;
|
||||
|
||||
tokio::select! {
|
||||
data = rx => {
|
||||
if let Ok(data) = data {
|
||||
self.mailbox.remove(&id);
|
||||
Ok(data)
|
||||
} else {
|
||||
self.mailbox.remove(&id);
|
||||
Err(SDLanError::IOError("rx receive failed".to_string()))
|
||||
}
|
||||
}
|
||||
_ = tokio::time::sleep(timeout) => {
|
||||
self.mailbox.remove(&id);
|
||||
Err(SDLanError::IOError("timed out".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
804
src/network/packet.rs
Normal file → Executable file
804
src/network/packet.rs
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
205
src/network/route.rs
Executable file
205
src/network/route.rs
Executable file
@ -0,0 +1,205 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fs,
|
||||
io::{BufRead, BufReader},
|
||||
net::Ipv4Addr,
|
||||
sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use ahash::RandomState;
|
||||
use arc_swap::ArcSwap;
|
||||
use dashmap::DashMap;
|
||||
use ipnet::Ipv4Net;
|
||||
use sdlan_sn_rs::utils::{Result, SDLanError};
|
||||
|
||||
use tokio::{
|
||||
io::ReadBuf,
|
||||
sync::oneshot::{channel, Receiver, Sender},
|
||||
};
|
||||
use tracing::{debug, error, warn};
|
||||
|
||||
use crate::{
|
||||
network::tun::{add_route, del_route},
|
||||
pb::{SdlArpResponse, SdlStunReply},
|
||||
RouteTableTrie,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RouteInfo {
|
||||
pub net: Ipv4Net,
|
||||
pub gw: Ipv4Addr,
|
||||
}
|
||||
|
||||
pub struct RouteTable2 {
|
||||
pub default_gw: ArcSwap<Option<Ipv4Addr>>,
|
||||
pub cache_table: DashMap<(Ipv4Net, Ipv4Addr), AtomicBool, RandomState>,
|
||||
pub route_table: RouteTableTrie,
|
||||
}
|
||||
|
||||
impl RouteTable2 {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
default_gw: ArcSwap::new(Arc::new(None)),
|
||||
cache_table: DashMap::with_hasher(RandomState::new()),
|
||||
route_table: RouteTableTrie::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lookup(&self, ip: u32) -> Option<(u8, Ipv4Addr)> {
|
||||
let res = self.route_table.lookup(ip);
|
||||
if res.is_some() {
|
||||
res
|
||||
} else {
|
||||
if self.default_gw.load().is_some() {
|
||||
Some((0, self.default_gw.load().unwrap()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_and_add_routes(&self, routes: Vec<RouteInfo>) -> Result<()> {
|
||||
for route in self.cache_table.iter() {
|
||||
let (net, gw) = route.key();
|
||||
if net.addr().to_bits() == 0 {
|
||||
// default gateway
|
||||
self.default_gw.store(Arc::new(Some(gw.clone())));
|
||||
}
|
||||
if route.value().load(Ordering::Relaxed) {
|
||||
if let Err(e) = del_route(net, gw) {
|
||||
error!("failed to del route: {}", e.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
self.cache_table.clear();
|
||||
self.route_table.clear();
|
||||
|
||||
for route in routes {
|
||||
self.cache_table
|
||||
.insert((route.net, route.gw), AtomicBool::new(false));
|
||||
warn!("inserting route {} gw {}", route.net, route.gw);
|
||||
self.route_table
|
||||
.insert(route.net.addr().into(), route.net.prefix_len(), route.gw);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// parse the route_file or route_str and add to the route table.
|
||||
pub fn parse_and_add_route(&self, route_file: &str, route_str: &str) -> Result<()> {
|
||||
let routes = match true {
|
||||
_ if route_str.len() != 0 => parse_route(route_str),
|
||||
_ if route_file.len() != 0 => load_route_file(route_file),
|
||||
_ => Ok(HashMap::new()),
|
||||
}?;
|
||||
for route in routes.keys() {
|
||||
if self.cache_table.get(route).is_some() {
|
||||
error!("route {} {} has been added", route.0.to_string(), route.1);
|
||||
return Err(SDLanError::IOError(format!(
|
||||
"route {} already added",
|
||||
route.0.to_string()
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
for route in self.cache_table.iter() {
|
||||
let (net, gw) = route.key();
|
||||
if route.value().load(Ordering::Relaxed) {
|
||||
if let Err(e) = del_route(net, gw) {
|
||||
error!("failed to del route: {}", e.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
self.cache_table.clear();
|
||||
self.route_table.clear();
|
||||
|
||||
for route in routes.keys() {
|
||||
debug!("got route: {} gw {}", route.0, route.1);
|
||||
self.cache_table.insert(*route, AtomicBool::new(false));
|
||||
|
||||
warn!(
|
||||
"inserting to route table: {} mask {} gw {}",
|
||||
route.0.addr(),
|
||||
route.0.prefix_len(),
|
||||
route.1
|
||||
);
|
||||
self.route_table.insert(
|
||||
u32::from_be_bytes(route.0.addr().octets()),
|
||||
route.0.prefix_len(),
|
||||
route.1,
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn apply_system(&self, if_idx: u32) {
|
||||
debug!("applying system");
|
||||
for route in &self.cache_table {
|
||||
let (net, gw) = route.key();
|
||||
debug!("route is {} gw {}", net, gw);
|
||||
if !route.value().load(Ordering::Relaxed) {
|
||||
debug!("adding route: {} gw {}", net, gw);
|
||||
if let Err(e) = add_route(net, gw, if_idx) {
|
||||
error!("failed to add route: {}", e.as_str());
|
||||
} else {
|
||||
route.value().store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
// should add to system
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 路由文件,应该是以下格式,一行一条
|
||||
/// network-cidr gw
|
||||
/// network-cidr gw
|
||||
pub fn load_route_file(filename: &str) -> Result<HashMap<(Ipv4Net, Ipv4Addr), bool>> {
|
||||
let mut result = HashMap::new();
|
||||
let fp = fs::File::open(filename)?;
|
||||
|
||||
let buf = BufReader::new(fp);
|
||||
for line in buf.lines() {
|
||||
let line = line?;
|
||||
if let Ok(rt) = parse_a_route(&line) {
|
||||
result.insert(rt, true);
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn parse_a_route(route: &str) -> Result<(Ipv4Net, Ipv4Addr)> {
|
||||
let route_info: Vec<_> = route.trim().split_whitespace().collect();
|
||||
if route_info.len() != 2 {
|
||||
error!("route info format error: {}", route);
|
||||
return Err(SDLanError::IOError("route format error".to_string()));
|
||||
}
|
||||
debug!("got route info: {:?}", route_info);
|
||||
|
||||
let Ok(gateway) = route_info[1].parse::<Ipv4Addr>() else {
|
||||
error!("failed to parse gw: {}", route_info[1]);
|
||||
return Err(SDLanError::IOError("failed to parse gw".to_string()));
|
||||
};
|
||||
|
||||
let cidr = route_info[0];
|
||||
let Ok(net) = cidr.parse::<Ipv4Net>() else {
|
||||
error!("failed to parse cidr: {}, skipping", cidr);
|
||||
return Err(SDLanError::IOError("failed to parse cidr".to_string()));
|
||||
};
|
||||
Ok((net, gateway))
|
||||
}
|
||||
|
||||
// ip, mask, gateway, cidr gateway,cidr2 gateway2
|
||||
pub fn parse_route(route: &str) -> Result<HashMap<(Ipv4Net, Ipv4Addr), bool>> {
|
||||
let mut result = HashMap::new();
|
||||
// let routes: Vec<_> = route.split(",").collect();
|
||||
|
||||
for route in route.trim().split(",") {
|
||||
if let Ok(rt) = parse_a_route(route) {
|
||||
result.insert(rt, true);
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
1143
src/network/tun_linux.rs
Normal file → Executable file
1143
src/network/tun_linux.rs
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
834
src/network/tun_win.rs
Normal file → Executable file
834
src/network/tun_win.rs
Normal file → Executable file
@ -1,20 +1,168 @@
|
||||
use sdlan_sn_rs::utils::{ip_to_string, net_bit_len_to_mask};
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use etherparse::ether_type::ARP;
|
||||
use etherparse::{Ethernet2Header, IpHeaders, NetSlice, SlicedPacket, TransportSlice};
|
||||
use ipnet::Ipv4Net;
|
||||
use sdlan_sn_rs::config::SDLAN_DEFAULT_TTL;
|
||||
use sdlan_sn_rs::utils::{
|
||||
aes_encrypt, ip_to_string, is_multi_broadcast, net_bit_len_to_mask, Result, SDLanError,
|
||||
BROADCAST_MAC,
|
||||
};
|
||||
use std::io::{Error, ErrorKind};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::os::windows::process::CommandExt;
|
||||
use std::process::Command;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use tracing::{debug, error, info};
|
||||
use tun_rs::{AsyncDevice, SyncDevice};
|
||||
use wintun;
|
||||
use tracing::{error, debug};
|
||||
|
||||
use super::device::{Mode, DeviceConfig};
|
||||
use crate::network::{
|
||||
form_ethernet_packet, generate_arp_request, parse_dns_payload, send_packet_to_net, ArpHdr,
|
||||
Node, ARP_REPLY, ARP_REQUEST, DNS_IP, LAYER,
|
||||
};
|
||||
use crate::pb::{encode_to_udp_message, SdlArpResponse, SdlData};
|
||||
use crate::tcp::PacketType;
|
||||
use crate::utils::mac_to_string;
|
||||
use crate::{caculate_crc, get_edge};
|
||||
|
||||
use super::device::{DeviceConfig, Mode};
|
||||
use super::TunTapPacketHandler;
|
||||
|
||||
pub struct Iface {
|
||||
device: AsyncDevice,
|
||||
if_idx: u32,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl Iface {
|
||||
fn new(_path: &str, name: &str) -> Self {
|
||||
println!("layer = {:?}", LAYER);
|
||||
let dev = tun_rs::DeviceBuilder::new()
|
||||
// .wintun_file(path.to_string())
|
||||
.name(name)
|
||||
// .ipv4(Ipv4Addr::new(10, 10, 4, 39), Ipv4Addr::new(255, 255, 255, 0), None)
|
||||
.layer(tun_rs::Layer::L3)
|
||||
.mtu(1280)
|
||||
// .enable(true)
|
||||
.build_async()
|
||||
.expect("failed to create tun");
|
||||
|
||||
let idx = dev.if_index().expect("failed to get if index");
|
||||
println!("index = {}", idx);
|
||||
Self {
|
||||
device: dev,
|
||||
if_idx: idx,
|
||||
name: name.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iface {
|
||||
pub fn get_if_idx(&self) -> u32 {
|
||||
self.if_idx
|
||||
}
|
||||
|
||||
pub async fn recv(&self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
self.device.recv(buf).await
|
||||
}
|
||||
|
||||
pub async fn send(&self, content: &[u8]) -> std::io::Result<usize> {
|
||||
self.device.send(content).await
|
||||
}
|
||||
|
||||
pub fn reload_config(&self, node: &Node, device_config: &DeviceConfig, network_domain: &str) {
|
||||
let netbit = device_config.get_net_bit();
|
||||
let ip = device_config.get_ip();
|
||||
if netbit == 0 || ip == 0 {
|
||||
error!("reload config's ip is 0");
|
||||
return;
|
||||
}
|
||||
|
||||
let mask = net_bit_len_to_mask(netbit);
|
||||
let ip = ip_to_string(&ip);
|
||||
|
||||
let netbit = ip_to_string(&net_bit_len_to_mask(netbit));
|
||||
|
||||
let mut cmd = Command::new("netsh");
|
||||
debug!("name={}, addr={}, mask={}", self.name, ip, netbit);
|
||||
let command = cmd
|
||||
.creation_flags(0x08000000)
|
||||
.arg("interface")
|
||||
.arg("ip")
|
||||
.arg("set")
|
||||
.arg("address")
|
||||
.arg(&format!("name=\"{}\"", self.name))
|
||||
.arg("source=static")
|
||||
.arg(&format!("addr={}", ip))
|
||||
.arg(&format!("mask={}", netbit));
|
||||
|
||||
let res = command.status();
|
||||
// let res = command.output();
|
||||
|
||||
match res {
|
||||
Ok(r) => {
|
||||
if r.success() {
|
||||
debug!("netsh ok");
|
||||
} else {
|
||||
error!("failed to run netsh, returned {:?}", r.code())
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("failed to run netsh: {}", e.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
let mut cmd = Command::new("netsh");
|
||||
let command = cmd
|
||||
.creation_flags(0x08000000)
|
||||
.arg("interface")
|
||||
.arg("ipv4")
|
||||
.arg("set")
|
||||
.arg("subinterface")
|
||||
.arg(&format!("\"{}\"", self.name))
|
||||
.arg(format!("mtu={}", device_config.mtu))
|
||||
.arg("store=persistent");
|
||||
|
||||
let res = command.status();
|
||||
|
||||
match res {
|
||||
Ok(r) => {
|
||||
if r.success() {
|
||||
debug!("netsh2 ok");
|
||||
} else {
|
||||
error!("failed to run netsh set mtu, returned {:?}", r.code())
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("failed to run netsh2: {}", e.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
// let gw = ip_to_string(&default_gw);
|
||||
// debug!("gw = {}", ip);
|
||||
if let Err(e) = set_dns(&self.name, network_domain, &ip, self.if_idx) {
|
||||
error!("failed to set dns: {:?}", e);
|
||||
} else {
|
||||
debug!("set dns ok");
|
||||
}
|
||||
|
||||
node.route_table.apply_system(self.if_idx);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IfaceOld {
|
||||
if_idx: u32,
|
||||
name: String,
|
||||
_adapter: Arc<wintun::Adapter>,
|
||||
session: Arc<wintun::Session>,
|
||||
}
|
||||
|
||||
impl Iface {
|
||||
impl IfaceOld {
|
||||
pub fn get_if_idx(&self) -> u32 {
|
||||
self.if_idx
|
||||
}
|
||||
|
||||
pub fn recv(&self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
let Ok(pkt) = self.session.receive_blocking() else {
|
||||
return Err(Error::new(ErrorKind::Other, "failed to receive"));
|
||||
@ -31,24 +179,30 @@ impl Iface {
|
||||
}
|
||||
|
||||
pub fn send(&self, content: &[u8]) -> std::io::Result<usize> {
|
||||
let mut pkt = self
|
||||
.session
|
||||
.allocate_send_packet(content.len() as u16)
|
||||
.unwrap();
|
||||
let Ok(mut pkt) = self.session.allocate_send_packet(content.len() as u16) else {
|
||||
error!("failed to allocate send packet");
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"failed to allocate send packet",
|
||||
));
|
||||
};
|
||||
let buf: &mut [u8] = pkt.bytes_mut();
|
||||
buf.copy_from_slice(content);
|
||||
self.session.send_packet(pkt);
|
||||
Ok(content.len())
|
||||
}
|
||||
|
||||
pub fn reload_config(&self, device_config: &DeviceConfig) {
|
||||
pub fn reload_config(&self, node: &Node, device_config: &DeviceConfig, network_domain: &str) {
|
||||
let netbit = device_config.get_net_bit();
|
||||
let ip = device_config.get_ip();
|
||||
if netbit == 0 || ip == 0 {
|
||||
error!("reload config's ip is 0");
|
||||
return;
|
||||
}
|
||||
|
||||
let mask = net_bit_len_to_mask(netbit);
|
||||
let ip = ip_to_string(&ip);
|
||||
|
||||
let netbit = ip_to_string(&net_bit_len_to_mask(netbit));
|
||||
|
||||
let mut cmd = Command::new("netsh");
|
||||
@ -64,36 +218,678 @@ impl Iface {
|
||||
.arg(&format!("addr={}", ip))
|
||||
.arg(&format!("mask={}", netbit));
|
||||
|
||||
let res = command.output();
|
||||
let res = command.status();
|
||||
// let res = command.output();
|
||||
|
||||
match res {
|
||||
Ok(r) => {
|
||||
debug!("netsh ok: [{:?}]", String::from_utf8_lossy(&r.stdout));
|
||||
if r.success() {
|
||||
debug!("netsh ok");
|
||||
} else {
|
||||
error!("failed to run netsh, returned {:?}", r.code())
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("failed to run netsh: {}", e.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
let mut cmd = Command::new("netsh");
|
||||
let command = cmd
|
||||
.creation_flags(0x08000000)
|
||||
.arg("interface")
|
||||
.arg("ipv4")
|
||||
.arg("set")
|
||||
.arg("subinterface")
|
||||
.arg(&format!("\"{}\"", self.name))
|
||||
.arg(format!("mtu={}", device_config.mtu))
|
||||
.arg("store=persistent");
|
||||
|
||||
let res = command.status();
|
||||
|
||||
match res {
|
||||
Ok(r) => {
|
||||
if r.success() {
|
||||
debug!("netsh2 ok");
|
||||
} else {
|
||||
error!("failed to run netsh set mtu, returned {:?}", r.code())
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("failed to run netsh2: {}", e.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
fn create_wintun(path: &str,name: &str) -> Iface {
|
||||
// let gw = ip_to_string(&default_gw);
|
||||
// debug!("gw = {}", ip);
|
||||
if let Err(e) = set_dns(&self.name, network_domain, &ip, self.if_idx) {
|
||||
error!("failed to set dns: {:?}", e);
|
||||
} else {
|
||||
debug!("set dns ok");
|
||||
}
|
||||
|
||||
node.route_table.apply_system(self.if_idx);
|
||||
}
|
||||
}
|
||||
|
||||
impl TunTapPacketHandler for Iface {
|
||||
async fn handle_packet_from_net(&self, data: &[u8]) -> std::io::Result<()> {
|
||||
match Ethernet2Header::from_slice(&data) {
|
||||
Ok((hdr, rest)) => {
|
||||
use etherparse::ether_type::ARP;
|
||||
use sdlan_sn_rs::utils::is_multi_broadcast;
|
||||
|
||||
if rest.len() < 4 {
|
||||
error!("payload length error");
|
||||
return Ok(());
|
||||
}
|
||||
// let crc_code = &rest[(rest.len() - 4)..rest.len()];
|
||||
// let rest = &rest[..(rest.len() - 4)];
|
||||
|
||||
// let crc_hash: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_CKSUM);
|
||||
// let ck = caculate_crc(&data[..(data.len() - 4)]);
|
||||
// let sent_ck = u32::from_be_bytes(crc_code.try_into().unwrap());
|
||||
// debug!("ck = {}, sent_ck = {}", ck, sent_ck);
|
||||
|
||||
debug!("ip size is {}", rest.len());
|
||||
let edge = get_edge();
|
||||
let self_mac = edge.device_config.get_mac();
|
||||
|
||||
if hdr.destination != self_mac && !is_multi_broadcast(&hdr.destination) {
|
||||
use sdlan_sn_rs::utils::mac_to_string;
|
||||
|
||||
error!(
|
||||
"packet to [{:?}] not direct to us",
|
||||
mac_to_string(&hdr.destination)
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if hdr.ether_type == ARP {
|
||||
use crate::network::ArpHdr;
|
||||
|
||||
let mut arp = ArpHdr::from_slice(&data);
|
||||
let self_ip = edge.device_config.get_ip();
|
||||
|
||||
// println!("self_ip: {:?}", self_ip.to_be_bytes());
|
||||
let from_ip = ((arp.sipaddr[0] as u32) << 16) + arp.sipaddr[1] as u32;
|
||||
// println!("from_ip: {:?}", from_ip.to_be_bytes());
|
||||
let dest_ip = ((arp.dipaddr[0] as u32) << 16) + arp.dipaddr[1] as u32;
|
||||
// println!("dest_ip: {:?}", dest_ip.to_be_bytes());
|
||||
|
||||
match arp.opcode {
|
||||
ARP_REQUEST => {
|
||||
// handle ARP REQUEST
|
||||
debug!("got ARP REQUEST");
|
||||
if arp.ethhdr.dest != [0xff; 6] {
|
||||
debug!("ARP REQUEST not broadcast");
|
||||
return Ok(());
|
||||
}
|
||||
if dest_ip == self_ip {
|
||||
use bytes::Bytes;
|
||||
use sdlan_sn_rs::utils::mac_to_string;
|
||||
|
||||
use crate::network::ARP_REPLY;
|
||||
|
||||
edge.arp_table.set(from_ip, arp.shwaddr);
|
||||
|
||||
/*
|
||||
use crate::network::{ARP_REPLY, ArpRequestInfo, send_arp_request};
|
||||
|
||||
send_arp_request(ArpRequestInfo::Set {
|
||||
ip: from_ip,
|
||||
mac: arp.shwaddr,
|
||||
})
|
||||
.await;
|
||||
*/
|
||||
|
||||
// target to us
|
||||
arp.opcode = ARP_REPLY;
|
||||
arp.dhwaddr = arp.shwaddr;
|
||||
arp.shwaddr = self_mac;
|
||||
arp.ethhdr.src = self_mac;
|
||||
arp.ethhdr.dest = arp.dhwaddr;
|
||||
|
||||
arp.dipaddr = arp.sipaddr;
|
||||
|
||||
arp.sipaddr =
|
||||
[((self_ip >> 16) & 0xffff) as u16, (self_ip & 0xffff) as u16];
|
||||
|
||||
let mut data_buf = BytesMut::from(arp.marshal_to_bytes().as_slice());
|
||||
let encryptor = edge.encryptor.load();
|
||||
if let Err(e) = encryptor.encrypt(&mut data_buf) {
|
||||
error!("failed to encrypt arp reply: {:?}", e);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let data_bytes = data_buf.freeze();
|
||||
|
||||
let data = SdlData {
|
||||
is_p2p: true,
|
||||
ttl: 2,
|
||||
network_id: edge.network_id.load(Ordering::Relaxed),
|
||||
src_mac: Vec::from(self_mac),
|
||||
dst_mac: Vec::from(arp.dhwaddr),
|
||||
data: data_bytes,
|
||||
session_token: edge.session_token.get(),
|
||||
identity_id: edge.identity_id.load(),
|
||||
};
|
||||
|
||||
let v = encode_to_udp_message(Some(data), PacketType::Data as u8)
|
||||
.unwrap();
|
||||
debug!(
|
||||
"xxxx send arp reply to [{}], selfmac=[{}]",
|
||||
mac_to_string(&arp.dhwaddr),
|
||||
mac_to_string(&self_mac)
|
||||
);
|
||||
send_packet_to_net(edge, arp.dhwaddr, &v, 0).await;
|
||||
// send_to_sock(edge, &v, from_sock);
|
||||
// edge.sock.send(v).await;
|
||||
}
|
||||
}
|
||||
ARP_REPLY => {
|
||||
debug!("mac {:?} is at {:?}", arp.shwaddr, from_ip.to_be_bytes());
|
||||
if dest_ip == self_ip {
|
||||
/*
|
||||
use crate::network::{ArpRequestInfo, arp_arrived, send_arp_request};
|
||||
|
||||
send_arp_request(ArpRequestInfo::Set {
|
||||
ip: from_ip,
|
||||
mac: arp.shwaddr,
|
||||
})
|
||||
.await;
|
||||
*/
|
||||
|
||||
// use crate::network::arp_arrived;
|
||||
edge.arp_table.set(from_ip, arp.shwaddr);
|
||||
edge.arp_table.arp_arrived(from_ip, arp.shwaddr).await;
|
||||
}
|
||||
}
|
||||
_other => {
|
||||
error!("unknown arp type info");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
use etherparse::IpHeaders;
|
||||
|
||||
match IpHeaders::from_slice(rest) {
|
||||
Ok((iphdr, _)) => {
|
||||
let Some(ipv4) = iphdr.ipv4() else {
|
||||
error!("not ipv4, dropping");
|
||||
return Ok(());
|
||||
};
|
||||
let ip = u32::from_be_bytes(ipv4.0.source);
|
||||
let mac = hdr.source;
|
||||
if !is_multi_broadcast(&mac) {
|
||||
//use crate::network::{ArpRequestInfo, send_arp_request};
|
||||
|
||||
edge.arp_table.set(ip, mac);
|
||||
// send_arp_request(ArpRequestInfo::Set { ip, mac }).await;
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
error!("failed to parse ip header, dropping");
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// println!("got ip packet");
|
||||
// println!("got data: {:?}", rest);
|
||||
match edge.device.send(rest).await {
|
||||
Ok(size) => {
|
||||
debug!("send to tun {} bytes", size);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("failed to send to device: {}", e.to_string());
|
||||
}
|
||||
}
|
||||
// edge.tun.send_data_to_tun(Vec::from(hdr.1)).await;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("failed to parse tun packet: {}", e);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_packet_from_device(&self, mut header: BytesMut) -> std::io::Result<()> {
|
||||
let eee = get_edge();
|
||||
|
||||
let src_mac = eee.device_config.get_mac();
|
||||
|
||||
let data = header.split_off(14);
|
||||
|
||||
let Ok(sliced_packet) = SlicedPacket::from_ip(&data) else {
|
||||
error!("failed to parse ip packet");
|
||||
return Ok(());
|
||||
};
|
||||
let Some(net) = sliced_packet.net else {
|
||||
error!("failed to get ip packet");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
match net {
|
||||
NetSlice::Ipv4(ipv4) => {
|
||||
let dstip = u32::from_be_bytes(ipv4.header().destination());
|
||||
// let dstip = u32::from_be_bytes(ipv4hdr.0.destination);
|
||||
debug!("packet dst ip: {:?}", ip_to_string(&dstip));
|
||||
let src = u32::from_be_bytes(ipv4.header().source());
|
||||
//let src = u32::from_be_bytes(ipv4hdr.0.source);
|
||||
debug!("packet src ip: {:?}", ip_to_string(&src));
|
||||
// packet should be sent to dev
|
||||
debug!("got {} bytes from tun", data.len());
|
||||
if (!eee.config.allow_routing.load(Ordering::Relaxed))
|
||||
&& (src != eee.device_config.get_ip())
|
||||
{
|
||||
info!("dropping routed packet");
|
||||
return Ok(());
|
||||
}
|
||||
if !eee.is_authorized() {
|
||||
debug!("drop tun packet due to not authed");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(transport) = sliced_packet.transport {
|
||||
match transport {
|
||||
TransportSlice::Tcp(tcp) => {
|
||||
use crate::FiveTuple;
|
||||
use std::net::IpAddr;
|
||||
|
||||
let out_five_tuple = FiveTuple {
|
||||
src_ip: IpAddr::V4(ipv4.header().source_addr()),
|
||||
dst_ip: IpAddr::V4(ipv4.header().destination_addr()),
|
||||
src_port: tcp.source_port(),
|
||||
dst_port: tcp.destination_port(),
|
||||
proto: etherparse::IpNumber::TCP.0,
|
||||
};
|
||||
eee.rule_cache.touch_packet(out_five_tuple);
|
||||
}
|
||||
TransportSlice::Udp(udp) => {
|
||||
if dstip == DNS_IP {
|
||||
// should do the dns request
|
||||
// println!("request for dns");
|
||||
|
||||
parse_dns_payload(
|
||||
eee,
|
||||
udp.payload(),
|
||||
&data,
|
||||
src,
|
||||
udp.source_port(),
|
||||
)
|
||||
.await;
|
||||
// edge.udp_sock_for_dns.send_to()
|
||||
return Ok(());
|
||||
} else {
|
||||
use crate::FiveTuple;
|
||||
use std::net::IpAddr;
|
||||
|
||||
let out_five_tuple = FiveTuple {
|
||||
src_ip: IpAddr::V4(ipv4.header().source_addr()),
|
||||
dst_ip: IpAddr::V4(ipv4.header().destination_addr()),
|
||||
src_port: udp.source_port(),
|
||||
dst_port: udp.destination_port(),
|
||||
proto: etherparse::IpNumber::UDP.0,
|
||||
};
|
||||
eee.rule_cache.touch_packet(out_five_tuple);
|
||||
}
|
||||
}
|
||||
_other => {}
|
||||
}
|
||||
}
|
||||
|
||||
match eee.arp_table.get(dstip) {
|
||||
Some(mac) => {
|
||||
let pkt_size = data.len() + 14;
|
||||
let mut etherheader = Ethernet2Header::default();
|
||||
etherheader.destination = mac;
|
||||
etherheader.ether_type = etherparse::EtherType::IPV4;
|
||||
etherheader.source = src_mac;
|
||||
// let mut packet = Vec::with_capacity(14 + data.len() + 4);
|
||||
|
||||
header.copy_from_slice(ðerheader.to_bytes()[..]);
|
||||
|
||||
let crc = caculate_crc(&data);
|
||||
header.unsplit(data);
|
||||
|
||||
// packet.extend_from_slice(ðerheader.to_bytes()[..]);
|
||||
// packet.extend_from_slice(&data);
|
||||
header.extend_from_slice(&crc.to_be_bytes());
|
||||
// packet.extend_from_slice(&crc.to_be_bytes());
|
||||
|
||||
// let pkt_size = packet.len();
|
||||
// println!("sending data with mac");
|
||||
|
||||
let encryptor = eee.encryptor.load();
|
||||
if let Err(e) = encryptor.encrypt(&mut header) {
|
||||
error!("failed to encrypt packet request: {:?}", e);
|
||||
return Ok(());
|
||||
}
|
||||
let data_bytes = header.freeze();
|
||||
|
||||
let data = SdlData {
|
||||
is_p2p: true,
|
||||
network_id: eee.network_id.load(Ordering::Relaxed),
|
||||
ttl: SDLAN_DEFAULT_TTL as u32,
|
||||
src_mac: Vec::from(src_mac),
|
||||
dst_mac: Vec::from(mac),
|
||||
data: data_bytes,
|
||||
session_token: eee.session_token.get(),
|
||||
identity_id: eee.identity_id.load(),
|
||||
};
|
||||
let msg =
|
||||
encode_to_udp_message(Some(data), PacketType::Data as u8).unwrap();
|
||||
let size = msg.len();
|
||||
send_packet_to_net(eee, mac, &msg, pkt_size as u64).await;
|
||||
}
|
||||
None => {
|
||||
header.unsplit(data);
|
||||
debug!(
|
||||
"find ip: {:?} => {:?}",
|
||||
src.to_be_bytes(),
|
||||
dstip.to_be_bytes()
|
||||
);
|
||||
debug!(
|
||||
"no mac found for ip {:?}, sending arp request",
|
||||
dstip.to_be_bytes()
|
||||
);
|
||||
// let _ = eee.send_arp_request(dstip, dstip).await;
|
||||
|
||||
if eee.device_config.contains(&Ipv4Addr::from_bits(dstip)) {
|
||||
debug!("contains dst ip {}", ip_to_string(&dstip));
|
||||
eee.arp_table.add_to_arp_wait_list(dstip, header);
|
||||
let _ = eee.send_arp_request(dstip, dstip).await;
|
||||
} else {
|
||||
debug!("try to lookup ip: {}", ip_to_string(&dstip));
|
||||
if let Some((_, real_ip)) = eee.route_table.lookup(dstip) {
|
||||
eee.arp_table.add_to_arp_wait_list(
|
||||
u32::from_be_bytes(real_ip.octets()),
|
||||
header,
|
||||
);
|
||||
error!("got target route: {}", real_ip);
|
||||
let real_ip = u32::from_be_bytes(real_ip.octets());
|
||||
let _ = eee.send_arp_request(real_ip, dstip).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
NetSlice::Ipv6(ipv6) => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/*
|
||||
async fn handle_packet_from_device(
|
||||
&self,
|
||||
mut header: BytesMut,
|
||||
) -> std::io::Result<()> {
|
||||
use etherparse::IpHeaders;
|
||||
|
||||
let eee = get_edge();
|
||||
|
||||
let src_mac = eee.device_config.get_mac();
|
||||
|
||||
let data = header.split_off(14);
|
||||
|
||||
debug!("got {} bytes from tun", data.len());
|
||||
match IpHeaders::from_slice(&data) {
|
||||
Ok((iphdr, _payload)) => {
|
||||
//use crate::network::{ArpRequestInfo, ArpResponse, send_arp_request};
|
||||
|
||||
let Some(ipv4hdr) = iphdr.ipv4() else {
|
||||
debug!("ipv6 packet ignored");
|
||||
return Ok(());
|
||||
};
|
||||
let dstip = u32::from_be_bytes(ipv4hdr.0.destination);
|
||||
debug!("packet dst ip: {:?}", ipv4hdr.0.destination);
|
||||
let src = u32::from_be_bytes(ipv4hdr.0.source);
|
||||
debug!("packet src ip: {:?}", ipv4hdr.0.source);
|
||||
// packet should be sent to dev
|
||||
debug!("got {} bytes from tun", data.len());
|
||||
if (!eee.config.allow_routing.load(Ordering::Relaxed)) && (src != eee.device_config.get_ip()) {
|
||||
info!("dropping routed packet");
|
||||
return Ok(());
|
||||
}
|
||||
if !eee.is_authorized() {
|
||||
debug!("drop tun packet due to not authed");
|
||||
return Ok(());
|
||||
}
|
||||
if dstip == DNS_IP {
|
||||
// should do the dns request
|
||||
// println!("request for dns");
|
||||
let addr = format!("{}:15353", eee.server_ip);
|
||||
// println!("send dns to {}", addr);
|
||||
if let Err(e) = eee.udp_sock_for_dns.send_to(&data, &addr).await {
|
||||
error!("failed to send request to 15353: {}", e);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
match eee.arp_table.get(dstip) {
|
||||
Some(mac) => {
|
||||
|
||||
let pkt_size = data.len() + 14;
|
||||
let mut etherheader = Ethernet2Header::default();
|
||||
etherheader.destination = mac;
|
||||
etherheader.ether_type = etherparse::EtherType::IPV4;
|
||||
etherheader.source = src_mac;
|
||||
// let mut packet = Vec::with_capacity(14 + data.len() + 4);
|
||||
|
||||
header.copy_from_slice(ðerheader.to_bytes()[..]);
|
||||
|
||||
let crc = caculate_crc(&data);
|
||||
header.unsplit(data);
|
||||
|
||||
|
||||
// packet.extend_from_slice(ðerheader.to_bytes()[..]);
|
||||
// packet.extend_from_slice(&data);
|
||||
header.extend_from_slice(&crc.to_be_bytes());
|
||||
// packet.extend_from_slice(&crc.to_be_bytes());
|
||||
|
||||
// let pkt_size = packet.len();
|
||||
// println!("sending data with mac");
|
||||
|
||||
// let Ok(encrypted) = aes_encrypt(&encrypt_key, &packet) else {
|
||||
let Ok(encrypted) = eee.encryptor.load().encrypt(&header) else {
|
||||
error!("failed to encrypt packet request");
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
let data = SdlData {
|
||||
is_p2p: true,
|
||||
network_id: eee.network_id.load(Ordering::Relaxed),
|
||||
ttl: SDLAN_DEFAULT_TTL as u32,
|
||||
src_mac: Vec::from(src_mac),
|
||||
dst_mac: Vec::from(mac),
|
||||
data: Bytes::from(encrypted),
|
||||
session_token: eee.session_token.get(),
|
||||
identity_id: eee.identity_id.load(),
|
||||
};
|
||||
let msg =
|
||||
encode_to_udp_message(Some(data), PacketType::Data as u8).unwrap();
|
||||
let size = msg.len();
|
||||
send_packet_to_net(eee, mac, &msg, pkt_size as u64).await;
|
||||
}
|
||||
None => {
|
||||
header.unsplit(data);
|
||||
eee.arp_table.add_to_arp_wait_list(dstip, header);
|
||||
debug!(
|
||||
"find ip: {:?} => {:?}",
|
||||
src.to_be_bytes(),
|
||||
dstip.to_be_bytes()
|
||||
);
|
||||
debug!("no mac found for ip {:?}, sending arp request", dstip.to_be_bytes());
|
||||
// let _ = eee.send_arp_request(dstip, dstip).await;
|
||||
|
||||
if eee.device_config.contains(&Ipv4Addr::from_bits(dstip)) {
|
||||
let _ = eee.send_arp_request(dstip, dstip).await;
|
||||
} else {
|
||||
if let Some((_, real_ip)) = eee.route_table.lookup(dstip) {
|
||||
let real_ip = u32::from_be_bytes(real_ip.octets());
|
||||
let _ = eee.send_arp_request(dstip, real_ip).await;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Err(e) => {
|
||||
error!("failed to parse ip packet: {}", e.to_string());
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}*/
|
||||
}
|
||||
|
||||
fn create_wintun(path: &str, name: &str) -> std::io::Result<Iface> {
|
||||
Ok(Iface::new(path, name))
|
||||
/*
|
||||
let wt = unsafe { wintun::load_from_path(path) }.expect("failed to load wintun");
|
||||
|
||||
let adapter = match wintun::Adapter::open(&wt, name) {
|
||||
Ok(a) => a,
|
||||
Err(_e) => wintun::Adapter::create(&wt, name, "Example", None)
|
||||
.expect("failed to create wintun adapter"),
|
||||
Err(_e) => {
|
||||
let Ok(adapt) = wintun::Adapter::create(&wt, name, "Punchnet", None) else {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"failed to create Punch adapter",
|
||||
));
|
||||
};
|
||||
let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap());
|
||||
Iface { _adapter: adapter, session, name: name.to_owned()}
|
||||
adapt
|
||||
}
|
||||
};
|
||||
let idx = adapter
|
||||
.get_adapter_index()
|
||||
.expect("failed to get adapter index");
|
||||
// println!("idx = {}", idx);
|
||||
let Ok(sess) = adapter.start_session(wintun::MAX_RING_CAPACITY) else {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"failed to start session, maybe one process is running, or not running with admin?",
|
||||
));
|
||||
};
|
||||
let session = Arc::new(sess);
|
||||
Ok(Iface {
|
||||
if_idx: idx,
|
||||
_adapter: adapter,
|
||||
session,
|
||||
name: name.to_owned(),
|
||||
})
|
||||
*/
|
||||
}
|
||||
|
||||
pub fn new_iface(name: &str, _mode: Mode) -> Iface {
|
||||
create_wintun("./wintun.dll", name)
|
||||
pub fn new_iface(name: &str) -> std::io::Result<Iface> {
|
||||
create_wintun("wintun.dll", name)
|
||||
// Ok(Box::new(create_wintun("/path/to/file")))
|
||||
}
|
||||
|
||||
pub fn get_install_channel() -> String {
|
||||
"windows".to_owned()
|
||||
}
|
||||
|
||||
pub fn set_dns(name: &str, _network_domain: &str, gw: &str, ifidx: u32) -> Result<()> {
|
||||
let res = Command::new("ROUTE")
|
||||
.arg("ADD")
|
||||
.arg("100.100.100.100")
|
||||
.arg("MASK")
|
||||
.arg("255.255.255.255")
|
||||
.arg(gw)
|
||||
.arg("IF")
|
||||
.arg(ifidx.to_string())
|
||||
.creation_flags(0x08000000)
|
||||
.status()?;
|
||||
if !res.success() {
|
||||
error!(
|
||||
"failed to add route for dns 100.100.100.100: {:?}",
|
||||
res.code()
|
||||
);
|
||||
return Err(SDLanError::IOError(
|
||||
"failed to add route for dns".to_owned(),
|
||||
));
|
||||
}
|
||||
//println!("res1: {}", res.status.success());
|
||||
|
||||
debug!("route set ok");
|
||||
let res = Command::new("netsh")
|
||||
.arg("dnsclient")
|
||||
.arg("set")
|
||||
.arg("dnsserver")
|
||||
.arg(&format!("name={}", name))
|
||||
.arg("source=static")
|
||||
.arg("address=100.100.100.100")
|
||||
.arg("validate=no")
|
||||
.creation_flags(0x08000000)
|
||||
.status()?;
|
||||
if !res.success() {
|
||||
error!("failed to set dnsserver");
|
||||
return Err(SDLanError::IOError("failed to add dnsserver".to_owned()));
|
||||
}
|
||||
// println!("res2: {}", res.status.success());
|
||||
|
||||
debug!("netsh set ok");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn restore_dns(take_over_dns: bool) {}
|
||||
|
||||
pub fn del_route(net: &Ipv4Net, gw: &Ipv4Addr) -> Result<()> {
|
||||
error!("deleting route: {} gw {}", net, gw);
|
||||
let mask = net.netmask().to_string();
|
||||
let network = net.network().to_string();
|
||||
let res = Command::new("route")
|
||||
.arg("delete")
|
||||
.arg(network)
|
||||
.arg("MASK")
|
||||
.arg(mask)
|
||||
.arg(gw.to_string())
|
||||
.status()?;
|
||||
|
||||
if !res.success() {
|
||||
error!("failed to set dnsserver");
|
||||
return Err(SDLanError::IOError("failed to delete route".to_owned()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn add_route(net: &Ipv4Net, gw: &Ipv4Addr, if_idx: u32) -> Result<()> {
|
||||
let mask = net.netmask().to_string();
|
||||
let network = net.network().to_string();
|
||||
let result = Command::new("route")
|
||||
.arg("add")
|
||||
.arg(network)
|
||||
.arg("MASK")
|
||||
.arg(mask)
|
||||
.arg(gw.to_string())
|
||||
.arg("if")
|
||||
.arg(format!("{}", if_idx))
|
||||
.status()?;
|
||||
if !result.success() {
|
||||
error!("failed to add route: {:?}", result.code());
|
||||
return Err(SDLanError::IOError("failed to add route".to_owned()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn arp_reply_arrived(edge: &Node, data: SdlArpResponse) {
|
||||
debug!("got arp response: {:?}", data);
|
||||
if data.target_mac.len() != 6 {
|
||||
// invalid target_mac
|
||||
error!(
|
||||
"invalid target_mac: {:?}, ip={}",
|
||||
data.target_mac,
|
||||
ip_to_string(&data.target_ip)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let ip = data.origin_ip;
|
||||
let mac = data.target_mac.try_into().unwrap();
|
||||
|
||||
debug!("setting mac {:?} for {}", mac, ip_to_string(&ip));
|
||||
edge.arp_table.set(ip, mac);
|
||||
edge.arp_table.arp_arrived(ip, mac).await;
|
||||
}
|
||||
|
||||
10
src/network/tuntap.c
Normal file → Executable file
10
src/network/tuntap.c
Normal file → Executable file
@ -8,16 +8,16 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#ifdef __linux__
|
||||
#include <linux/if.h>
|
||||
#include <linux/if_tun.h>
|
||||
#else
|
||||
// #ifdef __linux__
|
||||
// #include <linux/if.h>
|
||||
// #include <linux/if_tun.h>
|
||||
// #else
|
||||
#include <net/if.h>
|
||||
#define IFF_TUN 0x0001
|
||||
#define IFF_TAP 0x0002
|
||||
#define IFF_NO_PI 0x1000
|
||||
#define TUNSETIFF _IOW('T', 202, int)
|
||||
#endif
|
||||
// #endif
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
/**
|
||||
|
||||
236
src/network/tuntap.rs
Executable file
236
src/network/tuntap.rs
Executable file
@ -0,0 +1,236 @@
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use dashmap::DashMap;
|
||||
use once_cell::sync::OnceCell;
|
||||
use sdlan_sn_rs::{
|
||||
config::SDLAN_DEFAULT_TTL,
|
||||
utils::{get_current_timestamp, ip_to_string, Mac, Result},
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "tun", target_os = "windows"))]
|
||||
pub const LAYER: tun_rs::Layer = tun_rs::Layer::L3;
|
||||
|
||||
#[cfg(all(not(feature = "tun"), not(target_os = "windows")))]
|
||||
pub const LAYER: tun_rs::Layer = tun_rs::Layer::L2;
|
||||
|
||||
use tracing::{debug, warn};
|
||||
|
||||
use tracing::error;
|
||||
#[cfg(feature = "tun")]
|
||||
use tun_rs::Layer;
|
||||
|
||||
use crate::{
|
||||
network::{form_ethernet_packet, send_packet_to_net, Node, RouteInfo},
|
||||
pb::{encode_to_udp_message, SdlData},
|
||||
tcp::PacketType,
|
||||
utils::mac_to_string,
|
||||
};
|
||||
|
||||
use super::get_edge;
|
||||
|
||||
pub const MAX_WAIT_PACKETS: usize = 100;
|
||||
|
||||
const DEFAULT_DNS_SERVER: u32 = (223 << 24) + (5 << 16) + (5 << 8) + 5; // ali dns
|
||||
|
||||
pub trait TunTapPacketHandler {
|
||||
async fn handle_packet_from_net(&self, data: &[u8]) -> std::io::Result<()>;
|
||||
async fn handle_packet_from_device(&self, data: BytesMut) -> std::io::Result<()>;
|
||||
}
|
||||
|
||||
pub fn set_route_from_net(routes: Vec<RouteInfo>) -> Result<()> {
|
||||
let eee = get_edge();
|
||||
eee.route_table.clear_and_add_routes(routes)?;
|
||||
eee.route_table.apply_system(eee.device.get_if_idx());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/*
|
||||
static ARP_WAIT_LIST: OnceCell<ArpWaitList> = OnceCell::new();
|
||||
|
||||
pub fn init_arp_wait_list() {
|
||||
let waitlist = ArpWaitList {
|
||||
content: DashMap::new(),
|
||||
};
|
||||
ARP_WAIT_LIST.set(waitlist).unwrap();
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ArpWaitInfo {
|
||||
timestamp: u64,
|
||||
// origin data is from the tun or tap device
|
||||
origin_data: BytesMut,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ArpWaitList {
|
||||
content: DashMap<u32, Vec<ArpWaitInfo>>,
|
||||
}
|
||||
|
||||
impl ArpWaitList {
|
||||
fn add_to_wait_list(&self, ip: u32, origin_data_with_zeroed_layer2: BytesMut) {
|
||||
let mut entry = self.content.entry(ip).or_insert(vec![]);
|
||||
if entry.len() < MAX_WAIT_PACKETS {
|
||||
entry.push(ArpWaitInfo {
|
||||
timestamp: get_current_timestamp(),
|
||||
origin_data: origin_data_with_zeroed_layer2,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async fn arp_arrived(&self, ip: u32, mac: Mac) {
|
||||
debug!(
|
||||
"arp for {} arrived: {}",
|
||||
ip_to_string(&ip),
|
||||
mac_to_string(&mac)
|
||||
);
|
||||
let Some(items) = self.content.remove(&ip) else {
|
||||
return;
|
||||
};
|
||||
let edge = get_edge();
|
||||
// just remove the items
|
||||
if !edge.is_authorized() {
|
||||
return;
|
||||
}
|
||||
|
||||
// let encrypt_key = edge.get_encrypt_key();
|
||||
let network_id = edge.network_id.load(Ordering::Relaxed);
|
||||
|
||||
let src_mac = edge.device_config.get_mac();
|
||||
let now = get_current_timestamp();
|
||||
for item in items.1 {
|
||||
if (now - item.timestamp) > 5 {
|
||||
continue;
|
||||
}
|
||||
let mut packet = form_ethernet_packet(src_mac, mac, item.origin_data);
|
||||
|
||||
let pkt_size = packet.len();
|
||||
|
||||
let encryptor = edge.encryptor.load();
|
||||
if let Err(e) = encryptor.encrypt(&mut packet) {
|
||||
error!("failed to encrypt packet request: {:?}", e);
|
||||
return;
|
||||
}
|
||||
let data_bytes = packet.freeze();
|
||||
let data = SdlData {
|
||||
is_p2p: true,
|
||||
network_id,
|
||||
ttl: SDLAN_DEFAULT_TTL as u32,
|
||||
src_mac: Vec::from(src_mac),
|
||||
dst_mac: Vec::from(mac),
|
||||
data: data_bytes,
|
||||
identity_id: edge.identity_id.load(),
|
||||
session_token: edge.session_token.get(),
|
||||
};
|
||||
let msg = encode_to_udp_message(Some(data), PacketType::Data as u8).unwrap();
|
||||
send_packet_to_net(edge, mac, &msg, pkt_size as u64).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_to_arp_wait_list(ip: u32, origin_data_with_zeroed_layer2: BytesMut) {
|
||||
let waitlist = ARP_WAIT_LIST
|
||||
.get()
|
||||
.expect("ARP_WAIT_LIST has not been inited");
|
||||
|
||||
waitlist.add_to_wait_list(ip, origin_data_with_zeroed_layer2);
|
||||
}
|
||||
|
||||
pub async fn arp_arrived(ip: u32, mac: Mac) {
|
||||
let waitlist = ARP_WAIT_LIST
|
||||
.get()
|
||||
.expect("ARP_WAIT_LIST has not been inited");
|
||||
|
||||
waitlist.arp_arrived(ip, mac).await;
|
||||
}
|
||||
*/
|
||||
|
||||
pub fn get_dns_gateway(edge: &Node, payload: &[u8]) -> Option<String> {
|
||||
let dns_packet = match simple_dns::Packet::parse(payload) {
|
||||
Err(e) => {
|
||||
error!("failed to parse dns packet: {}", e);
|
||||
return None;
|
||||
}
|
||||
Ok(p) => p,
|
||||
};
|
||||
|
||||
let mut target_dns: &str = "";
|
||||
if !dns_packet.has_flags(simple_dns::PacketFlag::RESPONSE) && !dns_packet.questions.is_empty() {
|
||||
let question = &dns_packet.questions[0];
|
||||
let domain = question.qname.to_string();
|
||||
|
||||
let self_domain = &edge.network_domain.load();
|
||||
if self_domain.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if domain.ends_with(self_domain.as_str()) {
|
||||
debug!("got dns request for domain {}, which ends with our network domain {}, should send to dns server", domain, self_domain);
|
||||
return Some(domain);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
pub async fn parse_dns_payload(
|
||||
edge: &Node,
|
||||
layer7: &[u8],
|
||||
layer3: &[u8],
|
||||
source_ip: u32,
|
||||
source_port: u16,
|
||||
) {
|
||||
match simple_dns::Packet::parse(layer7) {
|
||||
Ok(mut dns) => {
|
||||
if !dns.has_flags(simple_dns::PacketFlag::RESPONSE) && !dns.questions.is_empty() {
|
||||
let question = &dns.questions[0];
|
||||
let qname = question.qname.to_string();
|
||||
if qname.ends_with(edge.network_domain.load().as_str()) {
|
||||
warn!(
|
||||
"question 15353 for {} from {}:{} with transaction_id = {}",
|
||||
qname,
|
||||
ip_to_string(&source_ip),
|
||||
source_port,
|
||||
dns.id()
|
||||
);
|
||||
// should send to our socket
|
||||
if let Err(e) = edge
|
||||
.udp_sock_for_dns
|
||||
.send_to(layer3, format!("{}:15353", edge.server_ip))
|
||||
.await
|
||||
{
|
||||
error!("failed to send request to 15353: {}", e);
|
||||
}
|
||||
} else {
|
||||
let origin_transaction_id = dns.id();
|
||||
let transaction_id = edge.dns_matcher.generate_transaction_id(
|
||||
source_ip,
|
||||
source_port,
|
||||
origin_transaction_id,
|
||||
);
|
||||
dns.set_id(transaction_id);
|
||||
warn!(
|
||||
"question 223.5.5.5 for {} from {}:{} with transaction_id = {}",
|
||||
qname,
|
||||
ip_to_string(&source_ip),
|
||||
source_port,
|
||||
dns.id()
|
||||
);
|
||||
if let Ok(res) = dns.build_bytes_vec() {
|
||||
if let Err(e) = edge
|
||||
.udp_sock_for_global_dns
|
||||
.send_query(&res)
|
||||
// .send_to(&res, "223.5.5.5:53")
|
||||
.await
|
||||
{
|
||||
error!("failed to query for global dns: {}", e.as_str());
|
||||
}
|
||||
}
|
||||
// edge.udp_sock_for_global_dns.send_to()
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("failed to parse dns packet");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -17,52 +17,61 @@ pub struct Sdlv6Info {
|
||||
#[prost(bytes = "vec", tag = "2")]
|
||||
pub v6: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
/// 设备网络地址信息
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlDevAddr {
|
||||
pub struct SdlWelcome {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub network_id: u32,
|
||||
pub version: u32,
|
||||
/// 服务器允许的最大双向流
|
||||
#[prost(uint32, tag = "2")]
|
||||
pub net_addr: u32,
|
||||
pub max_bidi_streams: u32,
|
||||
/// 服务器允许的最大包
|
||||
#[prost(uint32, tag = "3")]
|
||||
pub net_bit_len: u32,
|
||||
pub max_packet_size: u32,
|
||||
/// 心跳包的间隔
|
||||
#[prost(uint32, tag = "4")]
|
||||
pub heartbeat_sec: u32,
|
||||
}
|
||||
/// tcp通讯消息
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlEmpty {}
|
||||
/// 这里修改成了扁平的结构, 否则有些字段不好找放的位置
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlRegisterSuper {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub version: u32,
|
||||
#[prost(string, tag = "2")]
|
||||
pub installed_channel: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "3")]
|
||||
#[prost(string, tag = "1")]
|
||||
pub client_id: ::prost::alloc::string::String,
|
||||
#[prost(message, optional, tag = "4")]
|
||||
pub dev_addr: ::core::option::Option<SdlDevAddr>,
|
||||
#[prost(string, tag = "5")]
|
||||
pub pub_key: ::prost::alloc::string::String,
|
||||
/// 网络地址信息已经有https请求分配了
|
||||
/// 注册的时候需要带上(network_id, mac, ip, mask_len, hostname)
|
||||
#[prost(uint32, tag = "2")]
|
||||
pub network_id: u32,
|
||||
#[prost(bytes = "vec", tag = "3")]
|
||||
pub mac: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(uint32, tag = "4")]
|
||||
pub ip: u32,
|
||||
#[prost(uint32, tag = "5")]
|
||||
pub mask_len: u32,
|
||||
#[prost(string, tag = "6")]
|
||||
pub token: ::prost::alloc::string::String,
|
||||
pub hostname: ::prost::alloc::string::String,
|
||||
#[prost(string, tag = "7")]
|
||||
pub pub_key: ::prost::alloc::string::String,
|
||||
/// 客户端使用http协议请求后端,通过token或者账号密码登录时, 统一返回一个access_token;
|
||||
/// RegisterSuper的时候,验证凭证是否合法 (access_token)
|
||||
#[prost(string, tag = "8")]
|
||||
pub access_token: ::prost::alloc::string::String,
|
||||
}
|
||||
/// 客户端的升级逻辑,在https的接口里面去完成
|
||||
/// 部分逻辑会脱离quic去通讯,增加session_token校验
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlRegisterSuperAck {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub dev_addr: ::core::option::Option<SdlDevAddr>,
|
||||
/// 目前支持aes, chacha20
|
||||
#[prost(string, tag = "1")]
|
||||
pub algorithm: ::prost::alloc::string::String,
|
||||
#[prost(bytes = "vec", tag = "2")]
|
||||
pub aes_key: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(bytes = "vec", tag = "3")]
|
||||
pub known_ips: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(uint32, tag = "4")]
|
||||
pub upgrade_type: u32,
|
||||
#[prost(string, optional, tag = "5")]
|
||||
pub upgrade_prompt: ::core::option::Option<::prost::alloc::string::String>,
|
||||
#[prost(string, optional, tag = "6")]
|
||||
pub upgrade_address: ::core::option::Option<::prost::alloc::string::String>,
|
||||
pub key: ::prost::alloc::vec::Vec<u8>,
|
||||
/// 逻辑分段,chacha20加密算法需要使用该字段
|
||||
#[prost(uint32, tag = "3")]
|
||||
pub region_id: u32,
|
||||
#[prost(bytes = "vec", tag = "4")]
|
||||
pub session_token: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
@ -72,147 +81,208 @@ pub struct SdlRegisterSuperNak {
|
||||
#[prost(string, tag = "2")]
|
||||
pub error_message: ::prost::alloc::string::String,
|
||||
}
|
||||
/// 网络地址查询
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlQueryInfo {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub dst_ip: u32,
|
||||
#[prost(bytes = "vec", tag = "1")]
|
||||
pub dst_mac: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlPeerInfo {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub dst_ip: u32,
|
||||
#[prost(bytes = "vec", tag = "1")]
|
||||
pub dst_mac: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub v4_info: ::core::option::Option<Sdlv4Info>,
|
||||
#[prost(message, optional, tag = "3")]
|
||||
pub v6_info: ::core::option::Option<Sdlv6Info>,
|
||||
}
|
||||
/// ARP查询相关
|
||||
/// 真实的arp请求是通过广播的形式获取到的,但是针对于macos这种tun的实现;是能够分析出arp请求包的;
|
||||
/// 对于当前网络来说,服务端是知道mac对应的ip地址的,因此没有必要广播;直接通过服务器端返回
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlKnownIpEvent {
|
||||
pub struct SdlArpRequest {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub ip: u32,
|
||||
pub target_ip: u32,
|
||||
#[prost(uint32, tag = "2")]
|
||||
pub origin_ip: u32,
|
||||
#[prost(bytes = "vec", tag = "3")]
|
||||
pub context: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlDropIpEvent {
|
||||
pub struct SdlArpResponse {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub ip: u32,
|
||||
pub target_ip: u32,
|
||||
#[prost(bytes = "vec", tag = "2")]
|
||||
pub target_mac: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(uint32, tag = "3")]
|
||||
pub origin_ip: u32,
|
||||
#[prost(bytes = "vec", tag = "4")]
|
||||
pub context: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
/// 权限请求查询相关
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlPolicyRequest {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub src_identity_id: u32,
|
||||
#[prost(uint32, tag = "2")]
|
||||
pub dst_identity_id: u32,
|
||||
#[prost(uint32, tag = "3")]
|
||||
pub version: u32,
|
||||
}
|
||||
/// 基于quic通讯,rules部分已经没有了长度限制
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlPolicyResponse {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub src_identity_id: u32,
|
||||
#[prost(uint32, tag = "2")]
|
||||
pub dst_identity_id: u32,
|
||||
/// 版本号,客户端需要比较版本号确定是否覆盖; 请求端自己去管理版本号,服务端只是原样回写
|
||||
#[prost(uint32, tag = "3")]
|
||||
pub version: u32,
|
||||
/// 1 + 2稀疏序列化规则, 按照: <<Proto:8, Port:16>> 这个格式序列号所有的规则信息; 下发的数据默认都是allow,deny规则的服务器端已经屏蔽
|
||||
#[prost(bytes = "vec", tag = "4")]
|
||||
pub rules: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlNatChangedEvent {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub ip: u32,
|
||||
pub struct SdlEvent {
|
||||
#[prost(oneof = "sdl_event::Event", tags = "1, 2, 3")]
|
||||
pub event: ::core::option::Option<sdl_event::Event>,
|
||||
}
|
||||
/// Nested message and enum types in `SDLEvent`.
|
||||
pub mod sdl_event {
|
||||
/// nat映射变化
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlSendRegisterEvent {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub dst_ip: u32,
|
||||
pub struct NatChanged {
|
||||
#[prost(bytes = "vec", tag = "1")]
|
||||
pub mac: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(uint32, tag = "2")]
|
||||
pub ip: u32,
|
||||
}
|
||||
/// 发送register消息
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SendRegister {
|
||||
#[prost(bytes = "vec", tag = "1")]
|
||||
pub dst_mac: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(uint32, tag = "2")]
|
||||
pub nat_ip: u32,
|
||||
#[prost(uint32, tag = "3")]
|
||||
pub nat_port: u32,
|
||||
#[prost(message, optional, tag = "4")]
|
||||
pub v6_info: ::core::option::Option<Sdlv6Info>,
|
||||
#[prost(uint32, tag = "4")]
|
||||
pub nat_type: u32,
|
||||
#[prost(message, optional, tag = "5")]
|
||||
pub v6_info: ::core::option::Option<super::Sdlv6Info>,
|
||||
}
|
||||
/// 网络关闭
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlNetworkShutdownEvent {
|
||||
pub struct NetworkShutdown {
|
||||
#[prost(string, tag = "1")]
|
||||
pub message: ::prost::alloc::string::String,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
pub enum Event {
|
||||
#[prost(message, tag = "1")]
|
||||
NatChanged(NatChanged),
|
||||
#[prost(message, tag = "2")]
|
||||
SendRegister(SendRegister),
|
||||
#[prost(message, tag = "3")]
|
||||
Shutdown(NetworkShutdown),
|
||||
}
|
||||
}
|
||||
/// Command指令
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlChangeNetworkCommand {
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub dev_addr: ::core::option::Option<SdlDevAddr>,
|
||||
#[prost(bytes = "vec", tag = "2")]
|
||||
pub aes_key: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(bytes = "vec", tag = "3")]
|
||||
pub known_ips: ::prost::alloc::vec::Vec<u8>,
|
||||
pub struct SdlCommand {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub pkt_id: u32,
|
||||
#[prost(oneof = "sdl_command::Command", tags = "2")]
|
||||
pub command: ::core::option::Option<sdl_command::Command>,
|
||||
}
|
||||
/// Nested message and enum types in `SDLCommand`.
|
||||
pub mod sdl_command {
|
||||
/// 出口节点控制
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ExitNodeControl {
|
||||
/// 必选:操作类型
|
||||
#[prost(int32, tag = "1")]
|
||||
pub action: i32,
|
||||
/// 可选:备注(方便日志/调试)
|
||||
#[prost(string, tag = "2")]
|
||||
pub remark: ::prost::alloc::string::String,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
pub enum Command {
|
||||
#[prost(message, tag = "2")]
|
||||
ExitNode(ExitNodeControl),
|
||||
}
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlCommandAck {
|
||||
/// status = true, 表示成功;status = false 表示失败,message是失败原因描述
|
||||
#[prost(bool, tag = "1")]
|
||||
pub status: bool,
|
||||
#[prost(string, optional, tag = "2")]
|
||||
pub message: ::core::option::Option<::prost::alloc::string::String>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlFlows {
|
||||
/// 服务器转发流量
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub forward_num: u32,
|
||||
/// p2p直接流量
|
||||
#[prost(uint32, tag = "2")]
|
||||
pub p2p_num: u32,
|
||||
/// 接收的流量
|
||||
#[prost(uint32, tag = "3")]
|
||||
pub inbound_num: u32,
|
||||
pub pkt_id: u32,
|
||||
#[prost(int32, tag = "2")]
|
||||
pub code: i32,
|
||||
#[prost(string, tag = "3")]
|
||||
pub message: ::prost::alloc::string::String,
|
||||
#[prost(bytes = "vec", tag = "4")]
|
||||
pub data: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
/// client和stun之间的心跳包,客户端需要和super的udp之间的存活逻辑
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlStunRequest {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub cookie: u32,
|
||||
#[prost(string, tag = "2")]
|
||||
#[prost(string, tag = "1")]
|
||||
pub client_id: ::prost::alloc::string::String,
|
||||
#[prost(uint32, tag = "3")]
|
||||
#[prost(uint32, tag = "2")]
|
||||
pub network_id: u32,
|
||||
#[prost(bytes = "vec", tag = "3")]
|
||||
pub mac: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(uint32, tag = "4")]
|
||||
pub ip: u32,
|
||||
#[prost(uint32, tag = "5")]
|
||||
pub nat_type: u32,
|
||||
#[prost(message, optional, tag = "6")]
|
||||
pub v6_info: ::core::option::Option<Sdlv6Info>,
|
||||
#[prost(bytes = "vec", tag = "7")]
|
||||
pub session_token: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlStunReply {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub cookie: u32,
|
||||
}
|
||||
pub struct SdlStunReply {}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlData {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub network_id: u32,
|
||||
#[prost(uint32, tag = "2")]
|
||||
pub src_ip: u32,
|
||||
#[prost(uint32, tag = "3")]
|
||||
pub dst_ip: u32,
|
||||
#[prost(bytes = "vec", tag = "2")]
|
||||
pub src_mac: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(bytes = "vec", tag = "3")]
|
||||
pub dst_mac: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(bool, tag = "4")]
|
||||
pub is_p2p: bool,
|
||||
#[prost(uint32, tag = "5")]
|
||||
pub ttl: u32,
|
||||
#[prost(bytes = "vec", tag = "6")]
|
||||
pub data: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlRegister {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub network_id: u32,
|
||||
#[prost(uint32, tag = "2")]
|
||||
pub src_ip: u32,
|
||||
#[prost(uint32, tag = "3")]
|
||||
pub dst_ip: u32,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlRegisterAck {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub network_id: u32,
|
||||
#[prost(uint32, tag = "2")]
|
||||
pub src_ip: u32,
|
||||
#[prost(uint32, tag = "3")]
|
||||
pub dst_ip: u32,
|
||||
#[prost(bytes = "bytes", tag = "6")]
|
||||
pub data: ::prost::bytes::Bytes,
|
||||
#[prost(bytes = "vec", tag = "7")]
|
||||
pub session_token: ::prost::alloc::vec::Vec<u8>,
|
||||
/// 端通过https登录的时候,服务端会分配该端对应的权限标识
|
||||
/// 后续的请求过程中需要带上这个值,对端通过这个值要判断对数据包是否放行
|
||||
#[prost(uint32, tag = "8")]
|
||||
pub identity_id: u32,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
@ -221,14 +291,43 @@ pub struct SdlStunProbe {
|
||||
pub cookie: u32,
|
||||
#[prost(uint32, tag = "2")]
|
||||
pub attr: u32,
|
||||
/// 增加step是为了方便端上判断,收到的请求和响应之间的映射关系;服务器端原样返回
|
||||
#[prost(uint32, tag = "3")]
|
||||
pub step: u32,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlStunProbeReply {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub cookie: u32,
|
||||
/// 增加step是为了方便端上判断,收到的请求和响应之间的映射关系;服务器端原样返回
|
||||
#[prost(uint32, tag = "2")]
|
||||
pub port: u32,
|
||||
pub step: u32,
|
||||
#[prost(uint32, tag = "3")]
|
||||
pub port: u32,
|
||||
#[prost(uint32, tag = "4")]
|
||||
pub ip: u32,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlRegister {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub network_id: u32,
|
||||
#[prost(bytes = "vec", tag = "2")]
|
||||
pub src_mac: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(bytes = "vec", tag = "3")]
|
||||
pub dst_mac: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlRegisterAck {
|
||||
#[prost(uint32, tag = "1")]
|
||||
pub network_id: u32,
|
||||
#[prost(bytes = "vec", tag = "2")]
|
||||
pub src_mac: ::prost::alloc::vec::Vec<u8>,
|
||||
#[prost(bytes = "vec", tag = "3")]
|
||||
pub dst_mac: ::prost::alloc::vec::Vec<u8>,
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct SdlEmpty {}
|
||||
|
||||
8
src/pb/mod.rs
Normal file → Executable file
8
src/pb/mod.rs
Normal file → Executable file
@ -7,7 +7,7 @@ use sdlan_sn_rs::utils::Result;
|
||||
// tcp message has two-byte of size at header
|
||||
pub fn encode_to_tcp_message<T: Message>(
|
||||
msg: Option<T>,
|
||||
packet_id: u32,
|
||||
// _packet_id: u32,
|
||||
packet_type: u8,
|
||||
) -> Result<Vec<u8>> {
|
||||
let mut raw_data = Vec::new();
|
||||
@ -16,10 +16,10 @@ pub fn encode_to_tcp_message<T: Message>(
|
||||
msg.encode(&mut raw_data)?;
|
||||
}
|
||||
|
||||
let mut result = Vec::with_capacity(raw_data.len() + 7);
|
||||
let size = u16::to_be_bytes(raw_data.len() as u16 + 5);
|
||||
let mut result = Vec::with_capacity(raw_data.len() + 3);
|
||||
let size = u16::to_be_bytes(raw_data.len() as u16 + 1);
|
||||
result.extend_from_slice(&size);
|
||||
result.extend_from_slice(&u32::to_be_bytes(packet_id));
|
||||
// result.extend_from_slice(&u32::to_be_bytes(packet_id));
|
||||
result.push(packet_type);
|
||||
result.extend_from_slice(&raw_data);
|
||||
Ok(result)
|
||||
|
||||
153
src/quic/mod.rs
Normal file
153
src/quic/mod.rs
Normal file
@ -0,0 +1,153 @@
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
use std::net::SocketAddr;
|
||||
use std::net::UdpSocket;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use default_net::Interface;
|
||||
use quinn::crypto::rustls::QuicClientConfig;
|
||||
use quinn::Endpoint;
|
||||
use quinn::TokioRuntime;
|
||||
use rustls::crypto::ring;
|
||||
use rustls::crypto::CryptoProvider;
|
||||
use rustls::pki_types::CertificateDer;
|
||||
use rustls::pki_types::PrivateKeyDer;
|
||||
|
||||
use rustls::pki_types::ServerName;
|
||||
use rustls_pemfile::{certs, private_key};
|
||||
use socket2::Domain;
|
||||
use socket2::Protocol;
|
||||
|
||||
use crate::set_unicast_if_v4;
|
||||
|
||||
fn convert_to_std_socket(sock: socket2::Socket) -> std::net::UdpSocket {
|
||||
let std_socket: std::net::UdpSocket = sock.into();
|
||||
std_socket.set_nonblocking(true);
|
||||
std_socket
|
||||
}
|
||||
|
||||
|
||||
pub fn quic_init(iface: &Option<Interface>) -> Endpoint {
|
||||
let default_provider = ring::default_provider();
|
||||
CryptoProvider::install_default(default_provider).unwrap();
|
||||
|
||||
/*
|
||||
let certificate_chain = load_certs_from_pem("./ca/ca.crt").unwrap();
|
||||
let mut root_store = rustls::RootCertStore::empty();
|
||||
for cert in certificate_chain {
|
||||
root_store.add(cert).unwrap();
|
||||
}
|
||||
*/
|
||||
|
||||
let mut root_store = rustls::RootCertStore::empty();
|
||||
let certs = rustls_native_certs::load_native_certs()
|
||||
.expect("could not load platform certs")
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
root_store.add_parsable_certificates(certs);
|
||||
|
||||
let mut rustls_config = rustls::ClientConfig::builder()
|
||||
// .with_root_certificates(rustls::RootCertStore::empty())
|
||||
// .with_root_certificates(root_store)
|
||||
.with_root_certificates(root_store)
|
||||
.with_no_client_auth();
|
||||
|
||||
/*
|
||||
let mut rustls_config = rustls::ClientConfig::builder()
|
||||
.dangerous()
|
||||
.with_custom_certificate_verifier(Arc::new(SkipServerVerification{}))
|
||||
.with_no_client_auth();
|
||||
*/
|
||||
|
||||
let socket2 =
|
||||
socket2::Socket::new(Domain::IPV4, socket2::Type::DGRAM, Some(Protocol::UDP)).unwrap();
|
||||
socket2
|
||||
.bind(&"0.0.0.0:0".parse::<SocketAddr>().unwrap().into())
|
||||
.unwrap();
|
||||
|
||||
if let Some(iface) = iface {
|
||||
set_unicast_if_v4(&socket2, iface);
|
||||
}
|
||||
|
||||
let std_socket = convert_to_std_socket(socket2);
|
||||
|
||||
rustls_config.alpn_protocols = vec![b"punchnet/1.0".to_vec()];
|
||||
|
||||
let quinn_client_config =
|
||||
quinn::ClientConfig::new(Arc::new(QuicClientConfig::try_from(rustls_config).unwrap()));
|
||||
|
||||
let quinn_endpoint_config = quinn::EndpointConfig::default();
|
||||
|
||||
let mut endpoint = Endpoint::new(
|
||||
quinn_endpoint_config,
|
||||
None,
|
||||
std_socket,
|
||||
Arc::new(TokioRuntime),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
endpoint.set_default_client_config(quinn_client_config);
|
||||
|
||||
endpoint
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SkipServerVerification;
|
||||
impl rustls::client::danger::ServerCertVerifier for SkipServerVerification {
|
||||
fn verify_server_cert(
|
||||
&self,
|
||||
_: &CertificateDer<'_>,
|
||||
_: &[CertificateDer<'_>],
|
||||
_: &ServerName<'_>,
|
||||
_: &[u8],
|
||||
_: rustls::pki_types::UnixTime,
|
||||
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
|
||||
Ok(rustls::client::danger::ServerCertVerified::assertion())
|
||||
}
|
||||
fn verify_tls12_signature(
|
||||
&self,
|
||||
_: &[u8],
|
||||
_: &CertificateDer<'_>,
|
||||
_: &rustls::DigitallySignedStruct,
|
||||
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
|
||||
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
|
||||
}
|
||||
fn verify_tls13_signature(
|
||||
&self,
|
||||
_: &[u8],
|
||||
_: &CertificateDer<'_>,
|
||||
_: &rustls::DigitallySignedStruct,
|
||||
) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
|
||||
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
|
||||
}
|
||||
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
|
||||
rustls::crypto::ring::default_provider()
|
||||
.signature_verification_algorithms
|
||||
.supported_schemes()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_certs_from_pem(
|
||||
path: impl AsRef<Path>,
|
||||
) -> Result<Vec<CertificateDer<'static>>, Box<dyn std::error::Error>> {
|
||||
let file = File::open(path.as_ref())?;
|
||||
let mut reader = BufReader::new(file);
|
||||
let certs = certs(&mut reader)
|
||||
.into_iter()
|
||||
.map(|it| it.unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(certs)
|
||||
}
|
||||
|
||||
pub fn load_private_key_from_pem(path: impl AsRef<Path>) -> Option<PrivateKeyDer<'static>> {
|
||||
let Ok(file) = File::open(path.as_ref()) else {
|
||||
return None;
|
||||
};
|
||||
let mut reader = BufReader::new(file);
|
||||
if let Ok(key) = private_key(&mut reader) {
|
||||
return key
|
||||
}
|
||||
None
|
||||
}
|
||||
70
src/tcp/identity_cache.rs
Normal file
70
src/tcp/identity_cache.rs
Normal file
@ -0,0 +1,70 @@
|
||||
use std::{collections::{HashMap, HashSet}, sync::{OnceLock}, time::{SystemTime, UNIX_EPOCH}};
|
||||
|
||||
use dashmap::{DashMap};
|
||||
use tracing::debug;
|
||||
|
||||
type IdentityID = u32;
|
||||
type Port = u16;
|
||||
type Proto = u8;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RuleInfo {
|
||||
pub proto: Proto,
|
||||
pub port: Port,
|
||||
}
|
||||
|
||||
static RULE_CACHE: OnceLock<DashMap<IdentityID, (u64, HashMap<Port, HashSet<Proto>>)>> = OnceLock::new();
|
||||
|
||||
// static RULE_CACHE: OnceLock<DashMap<IdentityID, HashMap<Port, HashMap<Proto, AtomicU64>>>> = OnceLock::new();
|
||||
|
||||
pub fn set_identity_cache(identity: IdentityID, infos: Vec<RuleInfo>) {
|
||||
debug!("setting identity cache for identity={}, infos: {:?}", identity, infos);
|
||||
|
||||
let cache = RULE_CACHE.get().expect("should set first");
|
||||
let mut temp = HashMap::new();
|
||||
|
||||
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
|
||||
|
||||
for info in &infos {
|
||||
let mut protomap = HashSet::new();
|
||||
protomap.insert(info.proto);
|
||||
temp.insert(info.port, protomap);
|
||||
}
|
||||
cache.remove(&identity);
|
||||
cache.insert(identity, (now, temp));
|
||||
}
|
||||
|
||||
// result.1 is should renew
|
||||
pub fn is_identity_ok(identity: IdentityID, proto: Proto, port: Port) -> (Option<bool>, bool) {
|
||||
let cache = RULE_CACHE.get().expect("should set first");
|
||||
let mut should_renew = false;
|
||||
let result: Option<bool>;
|
||||
match cache.get(&identity) {
|
||||
Some(data) => {
|
||||
let tm = data.0;
|
||||
let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
|
||||
if tm + 10 < now {
|
||||
should_renew = true;
|
||||
}
|
||||
|
||||
if let Some(proto_info) = data.1.get(&port) {
|
||||
if let Some(_has) = proto_info.get(&proto) {
|
||||
result = Some(true);
|
||||
// return Some(true);
|
||||
} else {
|
||||
result = Some(false);
|
||||
}
|
||||
} else {
|
||||
result = Some(false);
|
||||
|
||||
}
|
||||
}
|
||||
None => {
|
||||
result = None;
|
||||
}
|
||||
}
|
||||
|
||||
return (result, should_renew);
|
||||
|
||||
}
|
||||
|
||||
8
src/tcp/mod.rs
Normal file → Executable file
8
src/tcp/mod.rs
Normal file → Executable file
@ -1,5 +1,9 @@
|
||||
mod tcp_codec;
|
||||
mod tcp_conn;
|
||||
// mod tcp_conn;
|
||||
mod quic;
|
||||
|
||||
pub use tcp_codec::*;
|
||||
pub use tcp_conn::*;
|
||||
pub use quic::*;
|
||||
|
||||
|
||||
// pub use tcp_conn::*;
|
||||
|
||||
816
src/tcp/quic.rs
Normal file
816
src/tcp/quic.rs
Normal file
@ -0,0 +1,816 @@
|
||||
use std::{
|
||||
net::IpAddr,
|
||||
sync::{
|
||||
atomic::{AtomicBool, AtomicU64, Ordering},
|
||||
Arc, OnceLock,
|
||||
},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use futures_util::pin_mut;
|
||||
use prost::Message;
|
||||
use quinn::SendStream;
|
||||
use sdlan_sn_rs::{
|
||||
config::AF_INET,
|
||||
peer::{SdlanSock, V6Info},
|
||||
utils::{get_current_timestamp, ip_to_string, rsa_decrypt, Result, SDLanError},
|
||||
};
|
||||
use tokio::{
|
||||
sync::mpsc::{channel, Receiver, Sender},
|
||||
time::sleep,
|
||||
};
|
||||
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use crate::network::{set_allow_routing, set_disallow_routing};
|
||||
use crate::{
|
||||
config::{NULL_MAC, TCP_PING_TIME},
|
||||
get_edge, load_configuration,
|
||||
network::{
|
||||
arp_reply_arrived, check_peer_registration_needed, handle_packet_peer_info, ArpHdr, EthHdr,
|
||||
Node, RegisterSuperFeedback, StartStopInfo, ARP_REPLY,
|
||||
},
|
||||
pb::{
|
||||
encode_to_tcp_message, sdl_command,
|
||||
sdl_event::{self, Event, SendRegister},
|
||||
SdlArpResponse, SdlCommand, SdlCommandAck, SdlEvent, SdlPolicyResponse, SdlRegisterSuper,
|
||||
SdlRegisterSuperAck, SdlRegisterSuperNak,
|
||||
},
|
||||
store_configuration,
|
||||
tcp::{read_a_packet, send_stun_request, EventType, NakMsgCode, NatType, PacketType, SdlanTcp},
|
||||
AesEncryptor, Chacha20Encryptor, ConnectionInfo, ConnectionState, MyEncryptor, RuleFromServer,
|
||||
};
|
||||
|
||||
static GLOBAL_QUIC_HANDLE: OnceLock<ReadWriterHandle> = OnceLock::new();
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ReadWriterHandle {
|
||||
connected: Arc<AtomicBool>,
|
||||
send_to_tcp: Sender<Vec<u8>>,
|
||||
// pub data_from_tcp: Receiver<SdlanTcp>,
|
||||
}
|
||||
|
||||
impl ReadWriterHandle {
|
||||
pub async fn send(&self, data: Vec<u8>) -> Result<()> {
|
||||
if self.connected.load(Ordering::Relaxed) {
|
||||
// connected, send to it
|
||||
if let Err(e) = self.send_to_tcp.send(data).await {
|
||||
error!("failed to send to send_to_tcp: {}", e.to_string());
|
||||
return Err(SDLanError::NormalError("failed to send"));
|
||||
};
|
||||
// debug!("tcp info sent");
|
||||
} else {
|
||||
error!("tcp not connected, so not sending data");
|
||||
return Err(SDLanError::NormalError("not connected, so not sending"));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn new(
|
||||
cancel: CancellationToken,
|
||||
addr: &str,
|
||||
domain: &str,
|
||||
// on_connected: OnConnectedCallback<'a>,
|
||||
// on_disconnected: T3,
|
||||
// on_message: T2,
|
||||
pong_time: Arc<AtomicU64>,
|
||||
start_stop_chan: Receiver<StartStopInfo>,
|
||||
// cancel: CancellationToken,
|
||||
connecting_chan: Option<Sender<ConnectionInfo>>,
|
||||
ipv6_network_restarter: Option<Sender<bool>>,
|
||||
) -> Self {
|
||||
let (send_to_tcp, to_tcp) = channel(20);
|
||||
let (from_tcp, mut data_from_tcp) = channel(20);
|
||||
|
||||
let connected: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
|
||||
|
||||
let actor = ReadWriteActor::new(
|
||||
cancel,
|
||||
addr,
|
||||
domain,
|
||||
from_tcp,
|
||||
connected.clone(),
|
||||
pong_time,
|
||||
connecting_chan,
|
||||
ipv6_network_restarter,
|
||||
);
|
||||
tokio::spawn(async move {
|
||||
actor
|
||||
.run(
|
||||
true,
|
||||
to_tcp,
|
||||
// on_connected,
|
||||
// on_disconnected,
|
||||
start_stop_chan,
|
||||
)
|
||||
.await
|
||||
});
|
||||
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
if let Some(msg) = data_from_tcp.recv().await {
|
||||
handle_tcp_message(msg).await;
|
||||
} else {
|
||||
error!("data from tcp exited");
|
||||
// println!("data from tcp exited");
|
||||
// eprintln!("data from tcp exited");
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ReadWriterHandle {
|
||||
connected,
|
||||
send_to_tcp,
|
||||
// data_from_tcp,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_quic_write_conn() -> &'static ReadWriterHandle {
|
||||
match GLOBAL_QUIC_HANDLE.get() {
|
||||
Some(v) => v,
|
||||
None => panic!("should call init_tcp_conn first"),
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_tcp_message(msg: SdlanTcp) {
|
||||
let edge = get_edge();
|
||||
|
||||
// let now = get_current_timestamp();
|
||||
// edge.tcp_pong.store(now, Ordering::Relaxed);
|
||||
|
||||
debug!("handling tcp message: {:?}", msg.packet_type);
|
||||
match msg.packet_type {
|
||||
PacketType::RegisterSuperACK => {
|
||||
let Ok(ack) = SdlRegisterSuperAck::decode(&msg.current_packet[..]) else {
|
||||
error!("failed to decode REGISTER_SUPER_ACK");
|
||||
return;
|
||||
};
|
||||
|
||||
edge.send_register_super_feedback(
|
||||
0,
|
||||
RegisterSuperFeedback {
|
||||
result: 0,
|
||||
message: "".to_owned(),
|
||||
should_exit: false,
|
||||
},
|
||||
);
|
||||
debug!("got register super ack");
|
||||
edge.session_token.set(ack.session_token);
|
||||
let Ok(key) = rsa_decrypt(&edge.rsa_private, &ack.key) else {
|
||||
error!("failed to rsa decrypt aes key");
|
||||
return;
|
||||
};
|
||||
|
||||
match ack.algorithm.to_ascii_lowercase().as_str() {
|
||||
"chacha20" => {
|
||||
edge.encryptor
|
||||
.store(Arc::new(MyEncryptor::ChaChao20(Chacha20Encryptor::new(
|
||||
key,
|
||||
ack.region_id,
|
||||
))))
|
||||
// *edge.encryptor.write().unwrap() = MyEncryptor::ChaChao20(Chacha20Encryptor::new(key, ack.region_id));
|
||||
}
|
||||
"aes" => {
|
||||
edge.encryptor
|
||||
.store(Arc::new(MyEncryptor::Aes(AesEncryptor::new(key))))
|
||||
// *edge.encryptor.write().unwrap() = MyEncryptor::Aes(AesEncryptor::new(key));
|
||||
}
|
||||
_other => {}
|
||||
}
|
||||
|
||||
/*
|
||||
let Some(dev) = ack.dev_addr else {
|
||||
error!("no dev_addr is specified");
|
||||
return;
|
||||
};
|
||||
*/
|
||||
|
||||
let ip = ip_to_string(&edge.device_config.get_ip());
|
||||
// debug!("aes key is {:?}, ip is {}/{}", aes, ip, dev.net_bit_len,);
|
||||
println!("assigned ip: {}", ip);
|
||||
debug!("assigned ip: {}", ip);
|
||||
// let hostname = edge.hostname.read().unwrap().clone();
|
||||
// println!("network is: {}.{}", hostname, dev.network_domain);
|
||||
/*
|
||||
edge.device_config
|
||||
.ip
|
||||
.net_addr
|
||||
.store(dev.net_addr, Ordering::Relaxed);
|
||||
*/
|
||||
if let Some(ref chan) = edge.connection_chan {
|
||||
let _ = chan.send(ConnectionInfo::IPInfo(ip)).await;
|
||||
}
|
||||
/*
|
||||
let mac = match dev.mac.try_into() {
|
||||
Err(_) => NULL_MAC,
|
||||
Ok(m) => m,
|
||||
};
|
||||
*/
|
||||
// *edge.device_config.mac.write().unwrap() = mac;
|
||||
/*
|
||||
edge.device_config
|
||||
.ip
|
||||
.net_bit_len
|
||||
.store(dev.net_bit_len as u8, Ordering::Relaxed);
|
||||
edge.network_id.store(dev.network_id, Ordering::Relaxed);
|
||||
*/
|
||||
// edge.device.reload_config(&edge.device_config, &dev.network_domain);
|
||||
edge.device
|
||||
.reload_config(edge, &edge.device_config, &edge.network_domain.load());
|
||||
|
||||
edge.set_authorized(true);
|
||||
|
||||
send_stun_request(edge).await;
|
||||
tokio::spawn(async {
|
||||
let nattype = edge.probe_nat_type().await;
|
||||
debug!("nat type is {:?}", nattype);
|
||||
// println!("nat type is: {:?}", nattype);
|
||||
});
|
||||
}
|
||||
PacketType::ArpResponse => {
|
||||
let Ok(res) = SdlArpResponse::decode(&msg.current_packet[..]) else {
|
||||
error!("failed to decode ARP RESPONSE");
|
||||
return;
|
||||
};
|
||||
|
||||
arp_reply_arrived(edge, res).await;
|
||||
return;
|
||||
}
|
||||
PacketType::PolicyReply => {
|
||||
let Ok(policy) = SdlPolicyResponse::decode(&msg.current_packet[..]) else {
|
||||
error!("failed to decode POLICY RESPONSE");
|
||||
return;
|
||||
};
|
||||
let identity = policy.src_identity_id;
|
||||
let mut infos = Vec::new();
|
||||
let mut start = 0;
|
||||
while start < policy.rules.len() {
|
||||
if start + 3 > policy.rules.len() {
|
||||
break;
|
||||
}
|
||||
let proto = policy.rules[start];
|
||||
let port = u16::from_be_bytes([policy.rules[start + 1], policy.rules[start + 2]]);
|
||||
start += 3;
|
||||
|
||||
infos.push(RuleFromServer { proto, port });
|
||||
}
|
||||
|
||||
edge.rule_cache.set_identity_cache(identity, infos);
|
||||
}
|
||||
|
||||
PacketType::RegisterSuperNAK => {
|
||||
let Ok(_nak) = SdlRegisterSuperNak::decode(&msg.current_packet[..]) else {
|
||||
error!("failed to decode REGISTER_SUPER_NAK");
|
||||
|
||||
/*
|
||||
edge.send_register_super_feedback(
|
||||
msg._packet_id,
|
||||
RegisterSuperFeedback {
|
||||
result: 1,
|
||||
message: "failed to decode REGISTER SUPER NAK".to_owned(),
|
||||
should_exit: false,
|
||||
},
|
||||
);
|
||||
*/
|
||||
return;
|
||||
};
|
||||
|
||||
error!("got nak: {:?}", _nak);
|
||||
// let pkt_id = _nak.pkt_id;
|
||||
|
||||
let Ok(error_code) = NakMsgCode::try_from(_nak.error_code as u8) else {
|
||||
edge.send_register_super_feedback(
|
||||
//msg._packet_id,
|
||||
0,
|
||||
RegisterSuperFeedback {
|
||||
result: 2,
|
||||
message: "error_code not recognized".to_owned(),
|
||||
should_exit: false,
|
||||
},
|
||||
);
|
||||
return;
|
||||
};
|
||||
match error_code {
|
||||
NakMsgCode::InvalidToken => {
|
||||
edge.send_register_super_feedback(
|
||||
// msg._packet_id,
|
||||
0,
|
||||
RegisterSuperFeedback {
|
||||
result: 3,
|
||||
message: "invalid token".to_owned(),
|
||||
should_exit: true,
|
||||
},
|
||||
);
|
||||
edge.stop().await;
|
||||
}
|
||||
NakMsgCode::NodeDisabled => {
|
||||
edge.send_register_super_feedback(
|
||||
// msg._packet_id,
|
||||
0,
|
||||
RegisterSuperFeedback {
|
||||
result: 4,
|
||||
message: "Node is disabled".to_owned(),
|
||||
should_exit: true,
|
||||
},
|
||||
);
|
||||
edge.stop().await;
|
||||
}
|
||||
_other => {
|
||||
edge.send_register_super_feedback(
|
||||
// msg._packet_id,
|
||||
0,
|
||||
RegisterSuperFeedback {
|
||||
result: 0,
|
||||
message: "".to_owned(),
|
||||
should_exit: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
/*
|
||||
edge.send_register_super_feedback(msg._packet_id, RegisterSuperFeedback {
|
||||
result: 1,
|
||||
message: "failed to decode REGISTER SUPER NAK".to_owned(),
|
||||
});
|
||||
*/
|
||||
edge.set_authorized(false);
|
||||
edge.encryptor.store(Arc::new(MyEncryptor::Invalid));
|
||||
// *edge.encryptor.write().unwrap() = MyEncryptor::Invalid;
|
||||
// std::process::exit(0);
|
||||
}
|
||||
PacketType::Command => {
|
||||
if msg.current_packet.len() < 1 {
|
||||
error!("malformed COMMAND received");
|
||||
return;
|
||||
}
|
||||
handle_tcp_command(edge, &msg.current_packet[..]).await;
|
||||
}
|
||||
PacketType::Event => {
|
||||
if msg.current_packet.len() < 1 {
|
||||
error!("malformed EVENT received");
|
||||
return;
|
||||
}
|
||||
handle_tcp_event(edge, &msg.current_packet[..]).await;
|
||||
}
|
||||
PacketType::PeerInfo => {
|
||||
let _ = handle_packet_peer_info(edge, &msg.current_packet[..]).await;
|
||||
}
|
||||
PacketType::Pong => {
|
||||
debug!("tcp pong received");
|
||||
let now = get_current_timestamp();
|
||||
edge.tcp_pong.store(now, Ordering::Relaxed);
|
||||
}
|
||||
other => {
|
||||
debug!("tcp not handling {:?}", other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_tcp_command(edge: &Node, cmdprotobuf: &[u8]) {
|
||||
let Ok(cmd) = SdlCommand::decode(cmdprotobuf) else {
|
||||
error!("failed to decode SdlCommand");
|
||||
return;
|
||||
};
|
||||
|
||||
let pkt_id = cmd.pkt_id;
|
||||
let Some(command) = cmd.command else {
|
||||
error!("command type is none");
|
||||
return;
|
||||
};
|
||||
|
||||
match command {
|
||||
sdl_command::Command::ExitNode(node) => {
|
||||
debug!("got exit node command: {:?}", node);
|
||||
// println!("got exit node command: {:?}", node);
|
||||
// std::process::exit(0);
|
||||
if node.action == 0 {
|
||||
// stop
|
||||
let origin = edge
|
||||
.config
|
||||
.allow_routing
|
||||
.fetch_and(false, Ordering::Relaxed);
|
||||
let mut config = load_configuration();
|
||||
config.allow_routing = Some(false);
|
||||
let _ = store_configuration(&config);
|
||||
if origin {
|
||||
#[cfg(target_os = "linux")]
|
||||
set_disallow_routing();
|
||||
}
|
||||
} else {
|
||||
// start
|
||||
let origin = edge.config.allow_routing.fetch_or(true, Ordering::Relaxed);
|
||||
let mut config = load_configuration();
|
||||
config.allow_routing = Some(true);
|
||||
let _ = store_configuration(&config);
|
||||
if !origin {
|
||||
#[cfg(target_os = "linux")]
|
||||
set_allow_routing();
|
||||
}
|
||||
}
|
||||
|
||||
let ack = SdlCommandAck {
|
||||
pkt_id,
|
||||
code: 0,
|
||||
message: "ok".to_owned(),
|
||||
data: vec![],
|
||||
};
|
||||
let msg = encode_to_tcp_message(Some(ack), PacketType::CommandACK as u8).unwrap();
|
||||
|
||||
let stream = get_quic_write_conn();
|
||||
if let Err(e) = stream.send(msg).await {
|
||||
error!("failed to write command ack to quic: {}", e.as_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_tcp_event(edge: &'static Node, eventprotobuf: &[u8]) {
|
||||
let Ok(sdl_event) = SdlEvent::decode(eventprotobuf) else {
|
||||
error!("failed to decode SdlEvent");
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(event) = sdl_event.event else {
|
||||
error!("event type is none");
|
||||
return;
|
||||
};
|
||||
|
||||
match event {
|
||||
sdl_event::Event::SendRegister(reg) => {
|
||||
let v4 = reg.nat_ip.to_be_bytes();
|
||||
let mut v6_sock = None;
|
||||
if let Some(v6_info) = reg.v6_info {
|
||||
if let Ok(v6_bytes) = v6_info.v6.try_into() {
|
||||
v6_sock = Some(V6Info {
|
||||
port: v6_info.port as u16,
|
||||
v6: v6_bytes,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let dst_mac = match reg.dst_mac.try_into() {
|
||||
Ok(m) => m,
|
||||
Err(_e) => NULL_MAC,
|
||||
};
|
||||
|
||||
let remote_nat_byte = reg.nat_type as u8;
|
||||
let remote_nat = match remote_nat_byte.try_into() {
|
||||
Ok(t) => t,
|
||||
Err(_) => NatType::NoNat,
|
||||
};
|
||||
|
||||
check_peer_registration_needed(
|
||||
edge,
|
||||
false,
|
||||
dst_mac,
|
||||
// &v6_sock,
|
||||
remote_nat,
|
||||
&v6_sock,
|
||||
&SdlanSock {
|
||||
family: AF_INET,
|
||||
port: reg.nat_port as u16,
|
||||
v4,
|
||||
v6: [0; 16],
|
||||
},
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
other => {
|
||||
debug!("unhandled event {:?}", other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_quic_conn(
|
||||
cancel: CancellationToken,
|
||||
addr: &str,
|
||||
domain: &str,
|
||||
// on_connected: OnConnectedCallback<'a>,
|
||||
// on_disconnected: T3,
|
||||
// on_message: T2,
|
||||
pong_time: Arc<AtomicU64>,
|
||||
// cancel: CancellationToken,
|
||||
start_stop_chan: Receiver<StartStopInfo>,
|
||||
connecting_chan: Option<Sender<ConnectionInfo>>,
|
||||
ipv6_network_restarter: Option<Sender<bool>>,
|
||||
)
|
||||
// T2: Fn(SdlanTcp) -> F + Send + 'static,
|
||||
// F: Future<Output = ()> + Send,
|
||||
{
|
||||
let tcp_handle = ReadWriterHandle::new(
|
||||
cancel,
|
||||
addr,
|
||||
domain,
|
||||
// on_connected,
|
||||
// on_disconnected,
|
||||
// on_message,
|
||||
pong_time,
|
||||
start_stop_chan,
|
||||
connecting_chan,
|
||||
ipv6_network_restarter,
|
||||
);
|
||||
|
||||
GLOBAL_QUIC_HANDLE
|
||||
.set(tcp_handle)
|
||||
.expect("failed to set global tcp handle");
|
||||
}
|
||||
|
||||
pub struct ReadWriteActor {
|
||||
// actor接收的发送给tcp的接收端,由handle存放发送端
|
||||
// to_tcp: Receiver<Vec<u8>>,
|
||||
// remote is the ip version of the remote server
|
||||
remote: String,
|
||||
// hostname is the domain name of the remote server
|
||||
domain: String,
|
||||
|
||||
connected: Arc<AtomicBool>,
|
||||
pong_time: Arc<AtomicU64>,
|
||||
// actor收到数据之后,发送给上层的发送端口,接收端由handle保存
|
||||
from_tcp: Sender<SdlanTcp>,
|
||||
_cancel: CancellationToken,
|
||||
connecting_chan: Option<Sender<ConnectionInfo>>,
|
||||
ipv6_network_restarter: Option<Sender<bool>>,
|
||||
}
|
||||
|
||||
impl ReadWriteActor {
|
||||
pub fn new(
|
||||
cancel: CancellationToken,
|
||||
remote: &str,
|
||||
domain: &str,
|
||||
from_tcp: Sender<SdlanTcp>,
|
||||
connected: Arc<AtomicBool>,
|
||||
pong_time: Arc<AtomicU64>,
|
||||
connecting_chan: Option<Sender<ConnectionInfo>>,
|
||||
ipv6_network_restarter: Option<Sender<bool>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
// to_tcp,
|
||||
_cancel: cancel,
|
||||
pong_time,
|
||||
connected,
|
||||
domain: domain.to_owned(),
|
||||
remote: remote.to_owned(),
|
||||
from_tcp,
|
||||
connecting_chan,
|
||||
ipv6_network_restarter,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run<'a>(
|
||||
&self,
|
||||
keep_reconnect: bool,
|
||||
mut to_tcp: Receiver<Vec<u8>>,
|
||||
mut start_stop_chan: Receiver<StartStopInfo>,
|
||||
) {
|
||||
let edge = get_edge();
|
||||
|
||||
let mut started = false;
|
||||
// let mut start_pkt_id = None;
|
||||
loop {
|
||||
if let Some(ref connecting_chan) = self.connecting_chan {
|
||||
let state = ConnectionInfo::ConnState(ConnectionState::NotConnected);
|
||||
let _ = connecting_chan.send(state).await;
|
||||
}
|
||||
self.connected.store(false, Ordering::Relaxed);
|
||||
if !started {
|
||||
// println!("waiting for start");
|
||||
loop {
|
||||
let start_or_stop = start_stop_chan.recv().await;
|
||||
if let Some(m) = start_or_stop {
|
||||
if m.is_start {
|
||||
started = true;
|
||||
// start_pkt_id = m.pkt_id;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// None, just return
|
||||
info!("start or stop is None");
|
||||
return;
|
||||
}
|
||||
}
|
||||
debug!("start stop chan received: {}", started);
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(ref connecting_chan) = self.connecting_chan {
|
||||
let state = ConnectionInfo::ConnState(ConnectionState::Connecting);
|
||||
let _ = connecting_chan.send(state).await;
|
||||
}
|
||||
|
||||
debug!("try connecting to {}", self.domain);
|
||||
|
||||
let conn = match edge
|
||||
.quic_endpoint
|
||||
.connect(self.remote.parse().unwrap(), &self.domain)
|
||||
{
|
||||
Ok(conn) => conn,
|
||||
Err(e) => {
|
||||
error!("failed to connect: {}", e);
|
||||
// println!("failed to connect: {}", e);
|
||||
|
||||
self.connected.store(false, Ordering::Relaxed);
|
||||
if keep_reconnect {
|
||||
tokio::time::sleep(Duration::from_secs(3)).await;
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let conn = match conn.await {
|
||||
Err(e) => {
|
||||
// println!("failed to connect await: {}", e);
|
||||
error!("failed to connect await: {}", e);
|
||||
self.connected.store(false, Ordering::Relaxed);
|
||||
if keep_reconnect {
|
||||
tokio::time::sleep(Duration::from_secs(3)).await;
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
Ok(conn) => conn,
|
||||
};
|
||||
|
||||
let local_ip = conn.local_ip();
|
||||
|
||||
let Ok((mut send, mut recv)) = conn.open_bi().await else {
|
||||
error!("failed to open-bi");
|
||||
self.connected.store(false, Ordering::Relaxed);
|
||||
if keep_reconnect {
|
||||
tokio::time::sleep(Duration::from_secs(3)).await;
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
};
|
||||
|
||||
self.connected.store(true, Ordering::Relaxed);
|
||||
debug!("connected");
|
||||
|
||||
sleep(Duration::from_millis(200)).await;
|
||||
on_connected_callback(local_ip, &mut send).await;
|
||||
|
||||
if let Some(ref connecting_chan) = self.connecting_chan {
|
||||
let state = ConnectionInfo::ConnState(ConnectionState::Connected);
|
||||
let _ = connecting_chan.send(state).await;
|
||||
}
|
||||
if let Some(ref ipv6_restarter) = self.ipv6_network_restarter {
|
||||
let _ = ipv6_restarter.send(true).await;
|
||||
}
|
||||
// stream.write("hello".as_bytes()).await;
|
||||
// let (reader, mut write) = stream.into_split();
|
||||
|
||||
let tcp_pong_timeout = async move {
|
||||
loop {
|
||||
tokio::time::sleep(Duration::from_secs(10)).await;
|
||||
let now = get_current_timestamp();
|
||||
let last = edge.tcp_pong.load(Ordering::Relaxed);
|
||||
if now - last > 30 {
|
||||
println!("tcp pong timedout");
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let read_from_tcp = async move {
|
||||
// let mut buffed_reader = BufReader::new(recv);
|
||||
loop {
|
||||
match read_a_packet(&mut recv).await {
|
||||
Ok(packet) => {
|
||||
warn!("got packet: {:?}", packet.packet_type);
|
||||
if let Err(_e) = self.from_tcp.send(packet).await {
|
||||
error!("failed to receive a packet: {:?}", _e);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("failed to read a packet: {}, reconnecting...", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let write_to_tcp = async {
|
||||
while let Some(data) = to_tcp.recv().await {
|
||||
// debug!("data size = {}", data.len());
|
||||
match send.write(&data).await {
|
||||
Ok(_size) => {
|
||||
// debug!("{} bytes sent to tcp", size);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("failed to write to tcp: {}", e.to_string());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
error!("to_tcp recv None");
|
||||
};
|
||||
|
||||
let check_pong = async {
|
||||
loop {
|
||||
tokio::time::sleep(Duration::from_secs(3600)).await;
|
||||
|
||||
let connected = self.connected.load(Ordering::Relaxed);
|
||||
let now = get_current_timestamp();
|
||||
if connected && now - self.pong_time.load(Ordering::Relaxed) > TCP_PING_TIME * 2
|
||||
{
|
||||
// pong time expire, need to re-connect
|
||||
error!("pong check expired");
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let check_stop = async {
|
||||
loop {
|
||||
match start_stop_chan.recv().await {
|
||||
Some(v) => {
|
||||
if !v.is_start {
|
||||
error!("stop called1");
|
||||
started = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
_other => {
|
||||
// send chan is closed;
|
||||
error!("stop called2");
|
||||
started = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pin_mut!(read_from_tcp, write_to_tcp);
|
||||
|
||||
tokio::select! {
|
||||
_ = read_from_tcp => {},
|
||||
_ = write_to_tcp => {},
|
||||
_ = check_pong => {},
|
||||
_ = check_stop => {},
|
||||
_ = tcp_pong_timeout => {},
|
||||
}
|
||||
on_disconnected_callback().await;
|
||||
conn.close(0u32.into(), "close".as_bytes());
|
||||
debug!("connect retrying");
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
debug!("disconnected");
|
||||
// future::select(read_from_tcp, write_to_tcp).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn on_disconnected_callback() {
|
||||
let edge = get_edge();
|
||||
edge.set_authorized(false);
|
||||
edge.encryptor.store(Arc::new(MyEncryptor::Invalid));
|
||||
}
|
||||
|
||||
async fn on_connected_callback(local_ip: Option<IpAddr>, stream: &mut SendStream) {
|
||||
let edge = get_edge();
|
||||
// let installed_channel = install_channel.to_owned();
|
||||
|
||||
// let token = edge._token.lock().unwrap().clone();
|
||||
// let code = edge.network_code.lock().unwrap().clone();
|
||||
|
||||
// let edge = get_edge();
|
||||
// let edge = get_edge();
|
||||
// let token = args.token.clone();
|
||||
if let Some(ipaddr) = local_ip {
|
||||
match ipaddr {
|
||||
IpAddr::V4(v4) => {
|
||||
let ip = v4.into();
|
||||
// println!("outer ip is {} => {}", v4, ip);
|
||||
edge.outer_ip_v4.store(ip, Ordering::Relaxed);
|
||||
}
|
||||
_other => {}
|
||||
}
|
||||
}
|
||||
let register_super = SdlRegisterSuper {
|
||||
mac: Vec::from(edge.device_config.get_mac()),
|
||||
// pkt_id: edge.get_next_packet_id(),
|
||||
network_id: edge.network_id.load(Ordering::Relaxed),
|
||||
ip: edge.device_config.get_ip(),
|
||||
mask_len: edge.device_config.get_net_bit() as u32,
|
||||
access_token: edge.access_token.get(),
|
||||
|
||||
// installed_channel,
|
||||
client_id: edge.config.node_uuid.clone(),
|
||||
pub_key: edge.rsa_pubkey.clone(),
|
||||
hostname: edge.hostname.read().unwrap().clone(),
|
||||
};
|
||||
|
||||
// println!("register super: {:?}", register_super);
|
||||
|
||||
// debug!("send register super: {:?}", register_super);
|
||||
// let packet_id = edge.get_next_packet_id();
|
||||
let data =
|
||||
encode_to_tcp_message(Some(register_super), PacketType::RegisterSuper as u8).unwrap();
|
||||
if let Err(e) = stream.write(&data).await {
|
||||
error!("failed to write to tcp: {}", e.to_string());
|
||||
}
|
||||
}
|
||||
82
src/tcp/tcp_codec.rs
Normal file → Executable file
82
src/tcp/tcp_codec.rs
Normal file → Executable file
@ -1,14 +1,18 @@
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use quinn::RecvStream;
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, BufReader},
|
||||
net::tcp::OwnedReadHalf,
|
||||
io::{AsyncReadExt},
|
||||
};
|
||||
|
||||
use num_enum::TryFromPrimitive;
|
||||
use tracing::debug;
|
||||
use tracing::{debug, error};
|
||||
|
||||
use crate::{network::Node, pb::{SdlStunRequest, Sdlv6Info, encode_to_udp_message}, utils::send_to_sock};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SdlanTcp {
|
||||
pub _packet_id: u32,
|
||||
// pub _packet_id: u32,
|
||||
pub packet_type: PacketType,
|
||||
pub current_packet: Vec<u8>,
|
||||
}
|
||||
@ -44,6 +48,7 @@ pub enum NatType {
|
||||
PortRestricted = 3,
|
||||
ConeRestricted = 4,
|
||||
Symmetric = 5,
|
||||
Invalid = 0xff,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, TryFromPrimitive)]
|
||||
@ -88,28 +93,65 @@ pub enum PacketType {
|
||||
StunProbe = 0x32,
|
||||
StunProbeReply = 0x33,
|
||||
|
||||
Welcome = 0x4f,
|
||||
|
||||
ArpRequest = 0x50,
|
||||
ArpResponse = 0x51,
|
||||
|
||||
PolicyRequest = 0xb0,
|
||||
PolicyReply = 0xb1,
|
||||
|
||||
Data = 0xff,
|
||||
}
|
||||
|
||||
pub async fn read_a_packet(
|
||||
reader: &mut BufReader<OwnedReadHalf>,
|
||||
) -> Result<SdlanTcp, std::io::Error> {
|
||||
debug!("read a packet");
|
||||
let size = reader.read_u16().await?;
|
||||
debug!("1");
|
||||
let packet_id = reader.read_u32().await?;
|
||||
debug!("2");
|
||||
let packet_type = reader.read_u8().await?;
|
||||
debug!("3");
|
||||
pub async fn send_stun_request(eee: &Node) {
|
||||
let sdl_v6_info = match *eee.ipv6.read().unwrap() {
|
||||
Some(ref l) => Some(Sdlv6Info {
|
||||
port: l.port as u32,
|
||||
v6: Vec::from(l.v6),
|
||||
}),
|
||||
None => None,
|
||||
};
|
||||
let req = SdlStunRequest {
|
||||
session_token: Vec::from(eee.session_token.get()),
|
||||
// cookie: 0,
|
||||
client_id: eee.config.node_uuid.clone(),
|
||||
network_id: eee.network_id.load(Ordering::Relaxed),
|
||||
ip: eee.device_config.get_ip(),
|
||||
mac: Vec::from(eee.device_config.get_mac()),
|
||||
nat_type: eee.get_nat_type() as u32,
|
||||
v6_info: sdl_v6_info,
|
||||
};
|
||||
debug!("stun request: {:?}", req);
|
||||
let msg = encode_to_udp_message(Some(req), PacketType::StunRequest as u8).unwrap();
|
||||
if let Err(e) = send_to_sock(
|
||||
eee,
|
||||
&msg,
|
||||
&eee.config.super_nodes[eee.config.super_node_index.load(Ordering::Relaxed) as usize],
|
||||
)
|
||||
.await
|
||||
{
|
||||
error!("failed to send to sock: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
if size < 5 {
|
||||
pub async fn read_a_packet(
|
||||
reader: &mut RecvStream,
|
||||
) -> Result<SdlanTcp, std::io::Error> {
|
||||
// debug!("read a packet");
|
||||
let payload_size = reader.read_u16().await?;
|
||||
// debug!("payload size: {}", payload_size);
|
||||
let packet_type = reader.read_u8().await?;
|
||||
// debug!("packet type: 0x{:02x}", packet_type);
|
||||
|
||||
if payload_size < 1 {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"size less than five",
|
||||
));
|
||||
}
|
||||
|
||||
let bufsize = (size - 5) as usize;
|
||||
let bufsize = (payload_size - 1) as usize;
|
||||
let mut binary = vec![0; bufsize];
|
||||
|
||||
let mut to_read = bufsize;
|
||||
@ -118,6 +160,10 @@ pub async fn read_a_packet(
|
||||
break;
|
||||
}
|
||||
let size_got = reader.read(&mut binary[(bufsize - to_read)..]).await?;
|
||||
if size_got.is_none() {
|
||||
break;
|
||||
}
|
||||
let size_got = size_got.unwrap();
|
||||
|
||||
if size_got == 0 {
|
||||
return Err(std::io::Error::new(
|
||||
@ -130,11 +176,11 @@ pub async fn read_a_packet(
|
||||
let Ok(packet_type) = packet_type.try_into() else {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"packet type error",
|
||||
format!("packet type error: 0x{:02x}", packet_type),
|
||||
));
|
||||
};
|
||||
let result = SdlanTcp {
|
||||
_packet_id: packet_id,
|
||||
// _packet_id: packet_id,
|
||||
packet_type,
|
||||
current_packet: binary,
|
||||
};
|
||||
|
||||
332
src/tcp/tcp_conn.rs
Normal file → Executable file
332
src/tcp/tcp_conn.rs
Normal file → Executable file
@ -1,6 +1,10 @@
|
||||
use myactor::{ActorError, SupervisedActor};
|
||||
use once_cell::sync::OnceCell;
|
||||
use sdlan_sn_rs::utils::{get_current_timestamp, Result, SDLanError};
|
||||
use std::future::Future;
|
||||
use prost::Message;
|
||||
use sdlan_sn_rs::config::AF_INET;
|
||||
use sdlan_sn_rs::peer::{SdlanSock, V6Info};
|
||||
use sdlan_sn_rs::utils::{Result, SDLanError, get_current_timestamp, ip_to_string, rsa_decrypt};
|
||||
use std::net::IpAddr;
|
||||
use std::sync::atomic::AtomicU64;
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
@ -19,216 +23,76 @@ use tokio::{
|
||||
};
|
||||
use tracing::error;
|
||||
|
||||
use crate::config::TCP_PING_TIME;
|
||||
use crate::network::StartStopInfo;
|
||||
use crate::tcp::read_a_packet;
|
||||
use crate::ConnectionState;
|
||||
use crate::config::{NULL_MAC, TCP_PING_TIME};
|
||||
use crate::network::{Node, RegisterSuperFeedback, StartStopInfo, check_peer_registration_needed, handle_packet_peer_info};
|
||||
use crate::pb::{SdlRegisterSuper, SdlRegisterSuperAck, SdlRegisterSuperNak, SdlSendRegisterEvent, SdlStunRequest, Sdlv6Info, encode_to_tcp_message, encode_to_udp_message};
|
||||
use crate::tcp::{EventType, NakMsgCode, NatType, PacketType, read_a_packet};
|
||||
use crate::utils::send_to_sock;
|
||||
use crate::{ConnectionInfo, ConnectionState, get_edge};
|
||||
|
||||
use super::tcp_codec::SdlanTcp;
|
||||
|
||||
static GLOBAL_TCP_HANDLE: OnceCell<ReadWriterHandle> = OnceCell::new();
|
||||
|
||||
pub struct ReadWriteActor {
|
||||
// actor接收的发送给tcp的接收端,由handle存放发送端
|
||||
// to_tcp: Receiver<Vec<u8>>,
|
||||
remote: String,
|
||||
connected: Arc<AtomicBool>,
|
||||
pong_time: Arc<AtomicU64>,
|
||||
// actor收到数据之后,发送给上层的发送端口,接收端由handle保存
|
||||
from_tcp: Sender<SdlanTcp>,
|
||||
cancel: CancellationToken,
|
||||
connecting_chan: Option<Sender<ConnectionState>>,
|
||||
ipv6_network_restarter: Option<Sender<bool>>,
|
||||
|
||||
|
||||
|
||||
|
||||
async fn on_disconnected_callback() {
|
||||
let edge = get_edge();
|
||||
edge.set_authorized(false, vec![]);
|
||||
}
|
||||
|
||||
impl ReadWriteActor {
|
||||
pub fn new(
|
||||
cancel: CancellationToken,
|
||||
remote: &str,
|
||||
from_tcp: Sender<SdlanTcp>,
|
||||
connected: Arc<AtomicBool>,
|
||||
pong_time: Arc<AtomicU64>,
|
||||
connecting_chan: Option<Sender<ConnectionState>>,
|
||||
ipv6_network_restarter: Option<Sender<bool>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
// to_tcp,
|
||||
cancel,
|
||||
pong_time,
|
||||
connected,
|
||||
remote: remote.to_owned(),
|
||||
from_tcp,
|
||||
connecting_chan,
|
||||
ipv6_network_restarter
|
||||
}
|
||||
}
|
||||
async fn on_connected_callback<'a>(stream: &'a mut tokio::net::TcpStream, pkt_id: Option<u32>) {
|
||||
let edge = get_edge();
|
||||
// let installed_channel = install_channel.to_owned();
|
||||
|
||||
pub async fn run<'a, T, T2, F>(
|
||||
&self,
|
||||
keep_reconnect: bool,
|
||||
mut to_tcp: Receiver<Vec<u8>>,
|
||||
on_connected: T,
|
||||
on_disconnected: T2,
|
||||
mut start_stop_chan: Receiver<StartStopInfo>,
|
||||
// cancel: CancellationToken,
|
||||
) where
|
||||
T: for<'b> Fn(&'b mut TcpStream, Option<u32>) -> BoxFuture<'b, ()>,
|
||||
T2: Fn() -> F,
|
||||
F: Future<Output = ()>,
|
||||
{
|
||||
// let (tx, rx) = channel(20);
|
||||
let mut started = false;
|
||||
let mut start_pkt_id = None;
|
||||
loop {
|
||||
if let Some(ref connecting_chan) = self.connecting_chan {
|
||||
let _ = connecting_chan.send(ConnectionState::NotConnected).await;
|
||||
}
|
||||
self.connected.store(false, Ordering::Relaxed);
|
||||
if !started {
|
||||
// println!("waiting for start");
|
||||
loop {
|
||||
let start_or_stop = start_stop_chan.recv().await;
|
||||
if let Some(m) = start_or_stop {
|
||||
if m.is_start {
|
||||
started = true;
|
||||
start_pkt_id = m.pkt_id;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// None, just return
|
||||
return;
|
||||
}
|
||||
}
|
||||
/*
|
||||
while let Some(m) = start_stop_chan.recv().await {
|
||||
println!("4");
|
||||
if m.is_start {
|
||||
// println!("start received");
|
||||
started = true;
|
||||
start_pkt_id = m.pkt_id;
|
||||
break;
|
||||
} else {
|
||||
// println!("stop received");
|
||||
}
|
||||
}
|
||||
*/
|
||||
debug!("start stop chan recv none");
|
||||
continue;
|
||||
}
|
||||
// let token = edge._token.lock().unwrap().clone();
|
||||
// let code = edge.network_code.lock().unwrap().clone();
|
||||
|
||||
if let Some(ref connecting_chan) = self.connecting_chan {
|
||||
let _ = connecting_chan.send(ConnectionState::Connecting).await;
|
||||
// let edge = get_edge();
|
||||
// let edge = get_edge();
|
||||
// let token = args.token.clone();
|
||||
if let Ok(ipaddr) = stream.local_addr() {
|
||||
match ipaddr.ip() {
|
||||
IpAddr::V4(v4) => {
|
||||
let ip = v4.into();
|
||||
// println!("outer ip is {} => {}", v4, ip);
|
||||
edge.outer_ip_v4.store(ip, Ordering::Relaxed);
|
||||
}
|
||||
debug!("try connecting...");
|
||||
let Ok(mut stream) = TcpStream::connect(&self.remote).await else {
|
||||
self.connected.store(false, Ordering::Relaxed);
|
||||
if keep_reconnect {
|
||||
/*
|
||||
tokio::select! {
|
||||
_ = tokio::time::sleep(Duration::from_secs(3)) => {
|
||||
continue;
|
||||
_other => {}
|
||||
}
|
||||
}
|
||||
*/
|
||||
tokio::time::sleep(Duration::from_secs(3)).await;
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
let register_super = SdlRegisterSuper {
|
||||
mac: Vec::from(edge.device_config.get_mac()),
|
||||
pkt_id: edge.get_next_packet_id(),
|
||||
network_id: edge.network_id.load(Ordering::Relaxed),
|
||||
ip: edge.device_config.get_ip(),
|
||||
mask_len: edge.device_config.get_net_bit() as u32,
|
||||
access_token: edge.access_token.get(),
|
||||
|
||||
// installed_channel,
|
||||
client_id: edge.config.node_uuid.clone(),
|
||||
pub_key: edge.rsa_pubkey.clone(),
|
||||
hostname: edge.hostname.read().unwrap().clone(),
|
||||
};
|
||||
self.connected.store(true, Ordering::Relaxed);
|
||||
on_connected(&mut stream, start_pkt_id.take()).await;
|
||||
|
||||
if let Some(ref connecting_chan) = self.connecting_chan {
|
||||
let _ = connecting_chan.send(ConnectionState::Connected).await;
|
||||
}
|
||||
if let Some(ref ipv6_restarter) = self.ipv6_network_restarter {
|
||||
let _ = ipv6_restarter.send(true).await;
|
||||
}
|
||||
// stream.write("hello".as_bytes()).await;
|
||||
let (reader, mut write) = stream.into_split();
|
||||
|
||||
let read_from_tcp = async move {
|
||||
let mut buffed_reader = BufReader::new(reader);
|
||||
loop {
|
||||
match read_a_packet(&mut buffed_reader).await {
|
||||
Ok(packet) => {
|
||||
debug!("got packet: {:?}", packet);
|
||||
if let Err(_e) = self.from_tcp.send(packet).await {
|
||||
error!("failed to receive a packet: {:?}", _e);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("failed to read a packet: {}, reconnecting...", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// debug!("send register super: {:?}", register_super);
|
||||
let packet_id = match pkt_id {
|
||||
Some(id) => id,
|
||||
None => edge.get_next_packet_id(),
|
||||
};
|
||||
|
||||
let write_to_tcp = async {
|
||||
while let Some(data) = to_tcp.recv().await {
|
||||
match write.write(&data).await {
|
||||
Ok(size) => {
|
||||
debug!("{} bytes sent to tcp", size);
|
||||
}
|
||||
Err(e) => {
|
||||
// let packet_id = edge.get_next_packet_id();
|
||||
let data = encode_to_tcp_message(
|
||||
Some(register_super),
|
||||
packet_id,
|
||||
PacketType::RegisterSuper as u8,
|
||||
)
|
||||
.unwrap();
|
||||
if let Err(e) = stream.write(&data).await {
|
||||
error!("failed to write to tcp: {}", e.to_string());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
error!("to_tcp recv None");
|
||||
};
|
||||
|
||||
let check_pong = async {
|
||||
loop {
|
||||
tokio::time::sleep(Duration::from_secs(10)).await;
|
||||
|
||||
let connected = self.connected.load(Ordering::Relaxed);
|
||||
let now = get_current_timestamp();
|
||||
if connected && now - self.pong_time.load(Ordering::Relaxed) > TCP_PING_TIME * 2
|
||||
{
|
||||
// pong time expire, need to re-connect
|
||||
error!("pong check expired");
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let check_stop = async {
|
||||
loop {
|
||||
match start_stop_chan.recv().await {
|
||||
Some(v) => {
|
||||
if !v.is_start {
|
||||
started = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
other => {
|
||||
// send chan is closed;
|
||||
started = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pin_mut!(read_from_tcp, write_to_tcp);
|
||||
|
||||
tokio::select! {
|
||||
_ = read_from_tcp => {},
|
||||
_ = write_to_tcp => {},
|
||||
_ = check_pong => {},
|
||||
_ = check_stop => {},
|
||||
}
|
||||
on_disconnected().await;
|
||||
debug!("connect retrying");
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
debug!("disconnected");
|
||||
// future::select(read_from_tcp, write_to_tcp).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ReadWriterHandle {
|
||||
@ -238,10 +102,10 @@ pub struct ReadWriterHandle {
|
||||
}
|
||||
|
||||
impl ReadWriterHandle {
|
||||
pub async fn send(&self, data: &[u8]) -> Result<()> {
|
||||
pub async fn send(&self, data: Vec<u8>) -> Result<()> {
|
||||
if self.connected.load(Ordering::Relaxed) {
|
||||
// connected, send to it
|
||||
if let Err(e) = self.send_to_tcp.send(Vec::from(data)).await {
|
||||
if let Err(e) = self.send_to_tcp.send(data).await {
|
||||
error!("failed to send to send_to_tcp: {}", e.to_string());
|
||||
return Err(SDLanError::NormalError("failed to send"));
|
||||
};
|
||||
@ -253,41 +117,55 @@ impl ReadWriterHandle {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn new<'a, T, T3, T2, F, F2>(
|
||||
fn new<>(
|
||||
cancel: CancellationToken,
|
||||
addr: &str,
|
||||
on_connected: T,
|
||||
on_disconnected: T3,
|
||||
on_message: T2,
|
||||
// on_connected: OnConnectedCallback<'a>,
|
||||
// on_disconnected: T3,
|
||||
// on_message: T2,
|
||||
pong_time: Arc<AtomicU64>,
|
||||
start_stop_chan: Receiver<StartStopInfo>,
|
||||
// cancel: CancellationToken,
|
||||
connecting_chan: Option<Sender<ConnectionState>>,
|
||||
connecting_chan: Option<Sender<ConnectionInfo>>,
|
||||
ipv6_network_restarter: Option<Sender<bool>>,
|
||||
) -> Self
|
||||
where
|
||||
T: for<'b> Fn(&'b mut TcpStream, Option<u32>) -> BoxFuture<'b, ()> + Send + 'static,
|
||||
T3: Fn() -> F2 + Send + 'static,
|
||||
T2: Fn(SdlanTcp) -> F + Send + 'static,
|
||||
F: Future<Output = ()> + Send,
|
||||
F2: Future<Output = ()> + Send,
|
||||
// T3: Fn() -> F2 + Send + 'static,
|
||||
// T2: Fn(SdlanTcp) -> F + Send + 'static,
|
||||
// F: Future<Output = ()> + Send,
|
||||
// F2: Future<Output = ()> + Send,
|
||||
{
|
||||
let (send_to_tcp, to_tcp) = channel(20);
|
||||
let (from_tcp, mut data_from_tcp) = channel(20);
|
||||
|
||||
let connected: Arc<AtomicBool> = Arc::new(AtomicBool::new(false));
|
||||
let actor = ReadWriteActor::new(cancel, addr, from_tcp, connected.clone(), pong_time, connecting_chan, ipv6_network_restarter);
|
||||
let actor = ReadWriteActor::new(
|
||||
cancel,
|
||||
addr,
|
||||
from_tcp,
|
||||
connected.clone(),
|
||||
pong_time,
|
||||
connecting_chan,
|
||||
ipv6_network_restarter,
|
||||
);
|
||||
tokio::spawn(async move {
|
||||
actor
|
||||
.run(true, to_tcp, on_connected, on_disconnected, start_stop_chan)
|
||||
.run(
|
||||
true,
|
||||
to_tcp,
|
||||
// on_connected,
|
||||
// on_disconnected,
|
||||
start_stop_chan
|
||||
)
|
||||
.await
|
||||
});
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
if let Some(msg) = data_from_tcp.recv().await {
|
||||
on_message(msg).await;
|
||||
handle_tcp_message(msg).await;
|
||||
} else {
|
||||
eprintln!("data from tcp exited");
|
||||
error!("data from tcp exited");
|
||||
// eprintln!("data from tcp exited");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -301,40 +179,6 @@ impl ReadWriterHandle {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_tcp_conn<'a, T, T3, T2, F, F2>(
|
||||
cancel: CancellationToken,
|
||||
addr: &str,
|
||||
on_connected: T,
|
||||
on_disconnected: T3,
|
||||
on_message: T2,
|
||||
pong_time: Arc<AtomicU64>,
|
||||
// cancel: CancellationToken,
|
||||
start_stop_chan: Receiver<StartStopInfo>,
|
||||
connecting_chan: Option<Sender<ConnectionState>>,
|
||||
ipv6_network_restarter: Option<Sender<bool>>,
|
||||
) where
|
||||
T: for<'b> Fn(&'b mut TcpStream, Option<u32>) -> BoxFuture<'b, ()> + Send + 'static,
|
||||
T3: Fn() -> F2 + Send + 'static,
|
||||
T2: Fn(SdlanTcp) -> F + Send + 'static,
|
||||
F: Future<Output = ()> + Send,
|
||||
F2: Future<Output = ()> + Send,
|
||||
{
|
||||
let tcp_handle = ReadWriterHandle::new(
|
||||
cancel,
|
||||
addr,
|
||||
on_connected,
|
||||
on_disconnected,
|
||||
on_message,
|
||||
pong_time,
|
||||
start_stop_chan,
|
||||
connecting_chan,
|
||||
ipv6_network_restarter,
|
||||
);
|
||||
|
||||
GLOBAL_TCP_HANDLE
|
||||
.set(tcp_handle)
|
||||
.expect("failed to set global tcp handle");
|
||||
}
|
||||
|
||||
pub fn get_tcp_conn() -> &'static ReadWriterHandle {
|
||||
match GLOBAL_TCP_HANDLE.get() {
|
||||
|
||||
193
src/utils/acl_session.rs
Normal file
193
src/utils/acl_session.rs
Normal file
@ -0,0 +1,193 @@
|
||||
use std::{
|
||||
net::IpAddr,
|
||||
sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
Arc,
|
||||
},
|
||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use ahash::RandomState;
|
||||
use dashmap::{DashMap, DashSet};
|
||||
use tracing::{debug, error};
|
||||
|
||||
const RULE_VALID_TIME_IN_SECS: u64 = 60;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct FiveTuple {
|
||||
pub src_ip: IpAddr,
|
||||
pub dst_ip: IpAddr,
|
||||
pub src_port: u16,
|
||||
pub dst_port: u16,
|
||||
pub proto: u8,
|
||||
}
|
||||
|
||||
impl FiveTuple {
|
||||
pub fn reverse(&self) -> Self {
|
||||
Self {
|
||||
src_ip: self.dst_ip,
|
||||
dst_ip: self.src_ip,
|
||||
dst_port: self.src_port,
|
||||
src_port: self.dst_port,
|
||||
proto: self.proto,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SessionInfo {
|
||||
pub last_active: AtomicU64,
|
||||
}
|
||||
|
||||
pub struct SessionTable {
|
||||
table: Arc<DashMap<FiveTuple, SessionInfo, RandomState>>,
|
||||
timeout_secs: u64,
|
||||
}
|
||||
|
||||
fn now_secs() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or_default()
|
||||
.as_secs()
|
||||
}
|
||||
|
||||
impl SessionTable {
|
||||
pub fn new(timeout_secs: u64) -> Self {
|
||||
Self {
|
||||
table: Arc::new(DashMap::with_hasher(RandomState::new())),
|
||||
timeout_secs,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_session_info(&self, key: FiveTuple) {
|
||||
if let Some(info) = self.table.get(&key) {
|
||||
info.last_active.store(now_secs(), Ordering::Relaxed);
|
||||
return;
|
||||
}
|
||||
self.table.insert(
|
||||
key,
|
||||
SessionInfo {
|
||||
last_active: AtomicU64::new(now_secs()),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn process_packet(&self, key: &FiveTuple) -> bool {
|
||||
if let Some(session) = self.table.get_mut(&key) {
|
||||
session.last_active.store(now_secs(), Ordering::Relaxed);
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn retain(&self) {
|
||||
let now = now_secs();
|
||||
debug!("retain session");
|
||||
self.table.retain(|_, info| {
|
||||
let last = info.last_active.load(Ordering::Relaxed);
|
||||
now - last < self.timeout_secs
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ip's u32 representation
|
||||
type IdentityID = u32;
|
||||
// port representation
|
||||
type Port = u16;
|
||||
// proto, like icmp = 1, udp=16
|
||||
type Proto = u8;
|
||||
|
||||
type RuleInfo = (Port, Proto);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RuleFromServer {
|
||||
pub proto: Proto,
|
||||
pub port: Port,
|
||||
}
|
||||
|
||||
pub struct RuleCache {
|
||||
pub rule_info: DashMap<IdentityID, (AtomicU64, DashSet<RuleInfo, RandomState>), RandomState>,
|
||||
pub rule_valid_secs: u64,
|
||||
pub session_table: Arc<SessionTable>,
|
||||
}
|
||||
|
||||
type ShouldRenew = bool;
|
||||
|
||||
impl RuleCache {
|
||||
pub fn new() -> Self {
|
||||
let session_table = Arc::new(SessionTable::new(300));
|
||||
let table_cleaner = Arc::clone(&session_table);
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
tokio::time::sleep(Duration::from_secs(20)).await;
|
||||
table_cleaner.retain();
|
||||
}
|
||||
});
|
||||
Self {
|
||||
rule_info: DashMap::with_hasher(RandomState::new()),
|
||||
rule_valid_secs: RULE_VALID_TIME_IN_SECS,
|
||||
session_table,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_identity_cache(&self, identity: IdentityID, infos: Vec<RuleFromServer>) {
|
||||
debug!(
|
||||
"setting identity cache for identity={}, infos: {:?}",
|
||||
identity, infos
|
||||
);
|
||||
|
||||
let now = now_secs();
|
||||
|
||||
let now_sets = DashSet::with_hasher(RandomState::new());
|
||||
for info in &infos {
|
||||
// let mut protomap = HashSet::new();
|
||||
now_sets.insert((info.port, info.proto));
|
||||
}
|
||||
self.rule_info
|
||||
.insert(identity, (AtomicU64::new(now), now_sets));
|
||||
}
|
||||
|
||||
pub fn touch_packet(&self, info: FiveTuple) {
|
||||
// error!("touch a packet: {:?}", info);
|
||||
self.session_table.add_session_info(info);
|
||||
}
|
||||
|
||||
pub fn is_identity_ok(
|
||||
&self,
|
||||
allow_routing: bool,
|
||||
identity: IdentityID,
|
||||
info: FiveTuple,
|
||||
) -> (bool, ShouldRenew) {
|
||||
// return (true, false);
|
||||
if allow_routing {
|
||||
return (true, false);
|
||||
}
|
||||
|
||||
error!("is identity ok? {:?}", info);
|
||||
if self.session_table.process_packet(&info) {
|
||||
error!("identity is ok");
|
||||
return (true, false);
|
||||
}
|
||||
|
||||
let now = now_secs();
|
||||
if let Some(sets_info) = self.rule_info.get(&identity) {
|
||||
let last_set_time = sets_info.0.load(Ordering::Relaxed);
|
||||
if sets_info.1.contains(&(info.dst_port, info.proto)) {
|
||||
if (now - last_set_time) > self.rule_valid_secs {
|
||||
return (true, true);
|
||||
}
|
||||
return (true, false);
|
||||
}
|
||||
}
|
||||
|
||||
self.rule_info.insert(
|
||||
identity,
|
||||
(
|
||||
AtomicU64::new(now),
|
||||
DashSet::with_hasher(RandomState::new()),
|
||||
),
|
||||
);
|
||||
|
||||
(false, true)
|
||||
}
|
||||
}
|
||||
195
src/utils/command.rs
Normal file → Executable file
195
src/utils/command.rs
Normal file → Executable file
@ -1,9 +1,159 @@
|
||||
use structopt::StructOpt;
|
||||
use clap::{Parser, Subcommand, Args};
|
||||
|
||||
const APP_TOKEN_ENV_NAME: &str = "PUNCH_TOKEN";
|
||||
const APP_USER_ENV_NAME: &str = "PUNCH_USER";
|
||||
const APP_PASS_ENV_NAME: &str = "PUNCH_PASS";
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct CommandLineInput2 {
|
||||
#[command(subcommand)]
|
||||
pub cmd: Commands,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum Commands {
|
||||
Login(UserLogin),
|
||||
// TokenLogin(TokenLogin),
|
||||
|
||||
/// if logined in, just start,
|
||||
/// else, use the token to login, and start
|
||||
AutoRun(AutoRunArgument),
|
||||
|
||||
/// after login, we can use start to
|
||||
/// connect to the remote
|
||||
Start(StartArguments),
|
||||
|
||||
Info,
|
||||
|
||||
#[command(subcommand)]
|
||||
ExitNode(ExitNodeCmd),
|
||||
|
||||
RouteAdd(RouteCmdInfo),
|
||||
RouteDel(RouteCmdInfo),
|
||||
RouteList,
|
||||
|
||||
/// exits the
|
||||
Stop,
|
||||
|
||||
Version,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum ExitNodeCmd {
|
||||
// list current exit node, and the now used exit node,
|
||||
List,
|
||||
|
||||
/// just exit the node, but keep the process running
|
||||
Start(ExitNodeInfo),
|
||||
Stop,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct ExitNodeInfo {
|
||||
#[arg(short, long)]
|
||||
pub id: u32,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct StartArguments {
|
||||
#[arg(short, long, default_value="")]
|
||||
pub route: String,
|
||||
|
||||
#[arg(short, long, default_value="")]
|
||||
pub route_file: String,
|
||||
|
||||
#[arg(short, long, default_value_t=false)]
|
||||
pub allow_routing: bool,
|
||||
|
||||
// take over dns or not if change /etc/resolv.conf
|
||||
#[arg(long)]
|
||||
pub take_dns: bool,
|
||||
|
||||
#[arg(short)]
|
||||
pub foreground: bool,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct RouteCmdInfo {
|
||||
#[arg(short, long, default_value="")]
|
||||
pub route: String,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct UserLogin {
|
||||
#[arg(short='u', long, env = APP_USER_ENV_NAME, help="specify username")]
|
||||
pub username: Option<String>,
|
||||
|
||||
#[arg(short='p', long, env = APP_PASS_ENV_NAME, help="specify user's password")]
|
||||
pub password: Option<String>,
|
||||
|
||||
#[arg(short='t', long, env=APP_TOKEN_ENV_NAME, help="login with a token")]
|
||||
pub token: Option<String>,
|
||||
}
|
||||
|
||||
/*
|
||||
#[derive(Args, Debug)]
|
||||
pub struct AutoRunTokenLogin {
|
||||
#[arg(short='t', long, env=APP_TOKEN_ENV_NAME)]
|
||||
pub token: String,
|
||||
|
||||
#[arg(short='r', long, default_value="")]
|
||||
pub route: String,
|
||||
}
|
||||
*/
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct AutoRunArgument {
|
||||
#[arg(short='t', long, env=APP_TOKEN_ENV_NAME)]
|
||||
pub token: Option<String>,
|
||||
|
||||
#[arg(short='u', long, env = APP_USER_ENV_NAME, help="specify username")]
|
||||
pub username: Option<String>,
|
||||
|
||||
#[arg(short='p', long, env = APP_PASS_ENV_NAME, help="specify user's password")]
|
||||
pub password: Option<String>,
|
||||
|
||||
#[arg(short, long, default_value_t=false)]
|
||||
pub allow_routing: bool,
|
||||
|
||||
#[arg(short, long, default_value="")]
|
||||
pub route: String,
|
||||
|
||||
#[arg(short, long, default_value="")]
|
||||
pub route_file: String,
|
||||
|
||||
// take over dns or not if change /etc/resolv.conf
|
||||
#[arg(long)]
|
||||
pub take_dns: bool,
|
||||
|
||||
#[arg(short)]
|
||||
pub foreground: bool,
|
||||
}
|
||||
|
||||
#[derive(Args, Debug)]
|
||||
pub struct TokenLogin {
|
||||
#[arg(long, env=APP_TOKEN_ENV_NAME, required=false)]
|
||||
pub token: String,
|
||||
}
|
||||
|
||||
|
||||
#[derive(StructOpt, Debug)]
|
||||
pub struct CommandLineInput {
|
||||
#[structopt(long = "token", default_value = "")]
|
||||
#[structopt(short="u", long = "user", default_value = "", help="specify a token")]
|
||||
pub user: String,
|
||||
|
||||
#[structopt(short="P", long = "pass", default_value = "", help="specify a token")]
|
||||
pub pass: String,
|
||||
|
||||
#[structopt(short="t", long = "token", default_value = "", help="specify a token")]
|
||||
pub token: String,
|
||||
|
||||
#[structopt(short = "p", long = "port", default_value = "0", help="which port to use")]
|
||||
pub port: u16,
|
||||
|
||||
#[structopt(short= "h", long = "hostname", default_value="", help="specify the hostname")]
|
||||
pub hostname: String,
|
||||
}
|
||||
|
||||
#[derive(StructOpt, Debug)]
|
||||
@ -12,7 +162,9 @@ pub struct CommandLine {
|
||||
pub sn: String,
|
||||
|
||||
#[structopt(short = "t", long = "tcp", default_value = "127.0.0.1:7656")]
|
||||
pub tcp: String,
|
||||
pub quic: String,
|
||||
|
||||
pub quic_domain: String,
|
||||
|
||||
/// in the format of "localhost:1234"
|
||||
#[structopt(long = "nat1")]
|
||||
@ -23,7 +175,10 @@ pub struct CommandLine {
|
||||
pub nat_server2: String,
|
||||
|
||||
#[structopt(short = "r")]
|
||||
pub _allow_routing: bool,
|
||||
pub allow_routing: bool,
|
||||
|
||||
#[structopt(short = "dm")]
|
||||
pub _drop_multicast: bool,
|
||||
|
||||
#[structopt(
|
||||
help = "ttl of the register udp4 packet",
|
||||
@ -41,27 +196,51 @@ pub struct CommandLine {
|
||||
#[structopt(long = "tos", default_value = "0")]
|
||||
pub tos: u32,
|
||||
|
||||
#[structopt(long = "token", default_value = "0")]
|
||||
pub token: String,
|
||||
// #[structopt(long = "token", default_value = "")]
|
||||
// pub token: String,
|
||||
|
||||
// #[structopt(long = "code", default_value = "")]
|
||||
// pub network_code: String,
|
||||
|
||||
#[structopt(short = "p")]
|
||||
pub allow_p2p: bool,
|
||||
|
||||
#[structopt(short = "l")]
|
||||
pub local_port: u16,
|
||||
|
||||
#[structopt(long, default_value="")]
|
||||
pub route_str: String,
|
||||
|
||||
#[structopt(long, default_value="")]
|
||||
pub route_file: String,
|
||||
|
||||
// take over dns or not
|
||||
#[structopt(long)]
|
||||
pub take_dns: bool,
|
||||
|
||||
}
|
||||
|
||||
impl Clone for CommandLine {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
sn: self.sn.clone(),
|
||||
tcp: self.tcp.clone(),
|
||||
_allow_routing: self._allow_routing,
|
||||
quic: self.quic.clone(),
|
||||
quic_domain: self.quic_domain.clone(),
|
||||
allow_routing: self.allow_routing,
|
||||
_drop_multicast: self._drop_multicast,
|
||||
register_ttl: self.register_ttl,
|
||||
mtu: self.mtu,
|
||||
local_port: self.local_port,
|
||||
name: self.name.clone(),
|
||||
tos: self.tos,
|
||||
token: self.token.clone(),
|
||||
// token: self.token.clone(),
|
||||
// network_code: self.network_code.clone(),
|
||||
allow_p2p: self.allow_p2p,
|
||||
nat_server1: self.nat_server1.clone(),
|
||||
nat_server2: self.nat_server2.clone(),
|
||||
route_str: self.route_str.clone(),
|
||||
route_file: self.route_file.clone(),
|
||||
take_dns: self.take_dns,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
205
src/utils/dns.rs
Normal file
205
src/utils/dns.rs
Normal file
@ -0,0 +1,205 @@
|
||||
use std::sync::atomic::{AtomicU8, Ordering};
|
||||
use std::sync::{Arc, atomic::AtomicU16};
|
||||
use std::{net::{Ipv4Addr, SocketAddr}, time::Duration};
|
||||
|
||||
use arc_swap::ArcSwap;
|
||||
use sdlan_sn_rs::utils::Result;
|
||||
use simple_dns::{Name, QCLASS, QTYPE, Question, rdata::RData};
|
||||
use tokio::signal;
|
||||
use tokio::sync::mpsc::{channel, Sender, Receiver};
|
||||
use tokio::{net::UdpSocket, time};
|
||||
|
||||
use dashmap::DashMap;
|
||||
use sdlan_sn_rs::utils::get_current_timestamp;
|
||||
|
||||
pub struct DNSCacheInfo {
|
||||
pub src_ip: u32,
|
||||
pub src_port: u16,
|
||||
pub origin_transaction_id: u16,
|
||||
pub added: u64,
|
||||
}
|
||||
|
||||
pub struct DNSMatcher {
|
||||
current_transaction_id: AtomicU16,
|
||||
// match transaction_id to (client ip, client port)
|
||||
matcher: DashMap<u16, DNSCacheInfo>,
|
||||
}
|
||||
|
||||
impl DNSMatcher {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
current_transaction_id: AtomicU16::new(0),
|
||||
matcher: DashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn retain(&self) {
|
||||
let now = get_current_timestamp();
|
||||
self.matcher.retain(|_, v| (now - v.added) < 10);
|
||||
}
|
||||
|
||||
pub fn generate_transaction_id(&self, client_ip: u32, client_port: u16, origin_transaction_id: u16) -> u16 {
|
||||
let transaction_id = self.current_transaction_id.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
self.matcher.insert(transaction_id, DNSCacheInfo{
|
||||
src_ip: client_ip,
|
||||
src_port: client_port,
|
||||
origin_transaction_id,
|
||||
added: get_current_timestamp(),
|
||||
});
|
||||
transaction_id
|
||||
}
|
||||
|
||||
pub fn get_client_info(&self, transaction_id: u16) -> Option<(u32, u16, u16)> {
|
||||
let res = self.matcher.remove(&transaction_id)?;
|
||||
Some((res.1.src_ip, res.1.src_port, res.1.origin_transaction_id))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// usage
|
||||
/// ```rust
|
||||
/// let (tx, rx) = tokio::sync::mpsc::channel(10);
|
||||
/// let client = DynamicDNSClient::new("223.5.5.5:53".parse().unwrap(), tx);
|
||||
///
|
||||
/// ```
|
||||
pub struct DynamicDNSClient {
|
||||
current_socket: Arc<ArcSwap<UdpSocket>>,
|
||||
rotate_send_channel: Sender<bool>,
|
||||
query_count: AtomicU8,
|
||||
response_tx: Sender<(Vec<u8>, SocketAddr)>,
|
||||
dns_server: SocketAddr,
|
||||
}
|
||||
|
||||
impl DynamicDNSClient {
|
||||
pub async fn new(dns_server: SocketAddr, response_tx: Sender<(Vec<u8>, SocketAddr)>) -> Result<Self> {
|
||||
let initial_socket = Arc::new(UdpSocket::bind("0.0.0.0:0").await?);
|
||||
let current_socket = Arc::new(ArcSwap::from(initial_socket.clone()));
|
||||
|
||||
Self::spawn_receiver(initial_socket, response_tx.clone());
|
||||
|
||||
let (tx, rx) = channel(5);
|
||||
|
||||
let result = Self {
|
||||
current_socket,
|
||||
dns_server,
|
||||
response_tx,
|
||||
rotate_send_channel: tx,
|
||||
query_count: AtomicU8::new(0),
|
||||
};
|
||||
result.start_refresher(rx);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn send_query(&self, packet: &[u8]) -> Result<usize> {
|
||||
let sock_guard = self.current_socket.load();
|
||||
let size = sock_guard.send_to(packet, self.dns_server).await?;
|
||||
let count = self.query_count.fetch_add(1, Ordering::Relaxed);
|
||||
if count >= 100 {
|
||||
self.query_count.store(0, Ordering::Release);
|
||||
let _ = self.rotate_send_channel.send(true).await;
|
||||
}
|
||||
Ok(size)
|
||||
}
|
||||
|
||||
fn start_refresher(&self, mut rx: Receiver<bool>) {
|
||||
let socket_swap = self.current_socket.clone();
|
||||
let response_tx = self.response_tx.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = tokio::time::sleep(Duration::from_secs(60)) => {
|
||||
|
||||
}
|
||||
data = rx.recv() => {
|
||||
if data.is_none() {
|
||||
panic!("global dns rx None");
|
||||
}
|
||||
}
|
||||
};
|
||||
// tokio::time::sleep(Duration::from_secs(60)).await;
|
||||
match UdpSocket::bind("0.0.0.0:0").await {
|
||||
Ok(new_socket) => {
|
||||
let new_socket = Arc::new(new_socket);
|
||||
// let port = new_socket.local_addr().unwrap().port();
|
||||
Self::spawn_receiver(new_socket.clone(), response_tx.clone());
|
||||
socket_swap.store(new_socket);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("failed to refresh: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn spawn_receiver(socket: Arc<UdpSocket>, tx: Sender<(Vec<u8>, SocketAddr)>) {
|
||||
tokio::spawn(async move {
|
||||
let mut buf = vec![0u8; 1024];
|
||||
let port = socket.local_addr().unwrap().port();
|
||||
|
||||
loop {
|
||||
match time::timeout(Duration::from_secs(65), socket.recv_from(&mut buf)).await {
|
||||
Ok(Ok((len, from))) => {
|
||||
let data = buf[..len].to_vec();
|
||||
if tx.send((data, from)).await.is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(Err(_)) => break,
|
||||
Err(_) => {
|
||||
// timeout occured
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
eprintln!("port {} has been closed", socket.local_addr().unwrap().port());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
async fn test_simple_dns() {
|
||||
let mut id = 0;
|
||||
let (tx, mut rx) = tokio::sync::mpsc::channel(10);
|
||||
let client = DynamicDNSClient::new("223.5.5.5:53".parse().unwrap(), tx).await.unwrap();
|
||||
|
||||
tokio::spawn(async move {
|
||||
loop {
|
||||
let data = rx.recv().await;
|
||||
if let Some(data) = data {
|
||||
let packet = simple_dns::Packet::parse(&data.0).unwrap();
|
||||
println!("got response: id = {}", packet.id());
|
||||
for answer in packet.answers {
|
||||
println!(" domain: {}", answer.name);
|
||||
match answer.rdata {
|
||||
RData::A(a_record) => {
|
||||
println!(" {}", Ipv4Addr::from_bits(a_record.address).to_string())
|
||||
}
|
||||
_other => {
|
||||
println!(" other response type")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
loop {
|
||||
let mut packet = simple_dns::Packet::new_query(id);
|
||||
id += 1;
|
||||
let name = Name::new("www.baidu.com").unwrap();
|
||||
let question = Question {
|
||||
qname: name,
|
||||
qtype: QTYPE::TYPE(simple_dns::TYPE::A),
|
||||
qclass: QCLASS::CLASS(simple_dns::CLASS::IN),
|
||||
unicast_response: false,
|
||||
};
|
||||
packet.questions.push(question);
|
||||
let question = packet.build_bytes_vec().unwrap();
|
||||
client.send_query(&question).await;
|
||||
tokio::time::sleep(Duration::from_secs(5)).await;
|
||||
}
|
||||
}
|
||||
*/
|
||||
222
src/utils/encrypter.rs
Normal file
222
src/utils/encrypter.rs
Normal file
@ -0,0 +1,222 @@
|
||||
use std::{
|
||||
sync::atomic::{AtomicU32, Ordering},
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
use bytes::BytesMut;
|
||||
use chacha20poly1305::{aead::AeadInPlace, ChaCha20Poly1305, KeyInit};
|
||||
use sdlan_sn_rs::utils::{aes_decrypt, aes_encrypt, Result, SDLanError};
|
||||
|
||||
const COUNTER_MASK: u32 = (1 << 24) - 1;
|
||||
|
||||
pub trait Encryptor {
|
||||
fn is_setted(&self) -> bool;
|
||||
fn set_key(&mut self, region_id: u32, key: Vec<u8>);
|
||||
fn encrypt(&self, data: &mut BytesMut) -> Result<()>;
|
||||
fn decrypt(&self, data: &mut BytesMut) -> Result<()>;
|
||||
}
|
||||
|
||||
pub enum MyEncryptor {
|
||||
Invalid,
|
||||
ChaChao20(Chacha20Encryptor),
|
||||
Aes(AesEncryptor),
|
||||
}
|
||||
|
||||
impl MyEncryptor {
|
||||
pub fn new() -> Self {
|
||||
Self::Invalid
|
||||
}
|
||||
|
||||
pub fn is_setted(&self) -> bool {
|
||||
match self {
|
||||
Self::Invalid => false,
|
||||
Self::Aes(aes) => aes.is_setted(),
|
||||
Self::ChaChao20(cha) => cha.is_setted(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_key(&mut self, region_id: u32, key: Vec<u8>) {
|
||||
match self {
|
||||
Self::Invalid => {}
|
||||
Self::Aes(aes) => {
|
||||
aes.set_key(region_id, key);
|
||||
}
|
||||
Self::ChaChao20(cha) => {
|
||||
cha.set_key(region_id, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encrypt(&self, data: &mut BytesMut) -> Result<()> {
|
||||
match self {
|
||||
Self::Invalid => Err(SDLanError::EncryptError("invalid encryptor".to_owned())),
|
||||
Self::Aes(aes) => aes.encrypt(data),
|
||||
Self::ChaChao20(cha) => cha.encrypt(data),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decrypt(&self, data: &mut BytesMut) -> Result<()> {
|
||||
match self {
|
||||
Self::Invalid => Err(SDLanError::EncryptError("invalid encryptor".to_owned())),
|
||||
Self::Aes(aes) => aes.decrypt(data),
|
||||
Self::ChaChao20(cha) => cha.decrypt(data),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Chacha20Encryptor {
|
||||
cipher: ChaCha20Poly1305,
|
||||
key: Vec<u8>,
|
||||
is_setted: bool,
|
||||
next_counter: AtomicU32,
|
||||
region_id: u32,
|
||||
}
|
||||
|
||||
impl Chacha20Encryptor {
|
||||
pub fn new(key: Vec<u8>, region_id: u32) -> Self {
|
||||
Self {
|
||||
cipher: chacha20poly1305::ChaCha20Poly1305::new(key.as_slice().into()),
|
||||
key,
|
||||
is_setted: true,
|
||||
next_counter: AtomicU32::new(0),
|
||||
region_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encryptor for Chacha20Encryptor {
|
||||
fn set_key(&mut self, region_id: u32, key: Vec<u8>) {
|
||||
self.cipher = chacha20poly1305::ChaCha20Poly1305::new(key.as_slice().into());
|
||||
self.key = key;
|
||||
self.region_id = region_id;
|
||||
}
|
||||
|
||||
fn encrypt(&self, data: &mut BytesMut) -> Result<()> {
|
||||
let plaintext_len = data.len();
|
||||
|
||||
// Prepare nonce
|
||||
let now = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_millis() as u64;
|
||||
|
||||
let next_counter = self
|
||||
.next_counter
|
||||
.fetch_update(Ordering::Release, Ordering::Acquire, |current| {
|
||||
Some((current + 1) & COUNTER_MASK)
|
||||
})
|
||||
.unwrap() as u64;
|
||||
|
||||
let mut nonce_bytes = [0u8; 12];
|
||||
let region_id_bytes = self.region_id.to_be_bytes();
|
||||
nonce_bytes[0..4].copy_from_slice(®ion_id_bytes);
|
||||
let next_data = (now << 24) | next_counter;
|
||||
nonce_bytes[4..12].copy_from_slice(&next_data.to_be_bytes());
|
||||
let nonce = chacha20poly1305::Nonce::from_slice(&nonce_bytes);
|
||||
|
||||
// Make room for tag (16 bytes) at the end
|
||||
data.resize(plaintext_len + 16, 0);
|
||||
let (payload, tag_space) = data.split_at_mut(plaintext_len);
|
||||
|
||||
// Encrypt payload in place and get tag
|
||||
let tag = self
|
||||
.cipher
|
||||
.encrypt_in_place_detached(&nonce, &[], payload)
|
||||
.map_err(|e| SDLanError::EncryptError(e.to_string()))?;
|
||||
|
||||
tag_space[..16].copy_from_slice(&tag);
|
||||
|
||||
// Prepend nonce (12 bytes)
|
||||
let mut final_buf = BytesMut::with_capacity(12 + data.len());
|
||||
final_buf.extend_from_slice(&nonce_bytes);
|
||||
final_buf.unsplit(data.split_off(0));
|
||||
*data = final_buf;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decrypt(&self, data: &mut BytesMut) -> Result<()> {
|
||||
if data.len() < 28 {
|
||||
return Err(SDLanError::EncryptError(
|
||||
"ciphered text size error".to_owned(),
|
||||
));
|
||||
}
|
||||
|
||||
// Split off the 12-byte Nonce
|
||||
let mut payload = data.split_off(12);
|
||||
let mut nonce_bytes = [0u8; 12];
|
||||
nonce_bytes.copy_from_slice(&data[0..12]);
|
||||
let nonce = chacha20poly1305::Nonce::from_slice(&nonce_bytes);
|
||||
|
||||
// Split off the 16-byte Tag
|
||||
let ciphertext_len = payload.len() - 16;
|
||||
let (ciphertext, tag_space) = payload.split_at_mut(ciphertext_len);
|
||||
let tag = chacha20poly1305::Tag::from_slice(&tag_space[..16]);
|
||||
|
||||
// Decrypt in place
|
||||
self.cipher
|
||||
.decrypt_in_place_detached(&nonce, &[], ciphertext, tag)
|
||||
.map_err(|e| SDLanError::EncryptError(format!("failed to decrypt: {}", e.to_string())))?;
|
||||
|
||||
payload.truncate(ciphertext_len);
|
||||
*data = payload;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_setted(&self) -> bool {
|
||||
self.is_setted
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AesEncryptor {
|
||||
key: Vec<u8>,
|
||||
is_setted: bool,
|
||||
}
|
||||
|
||||
impl AesEncryptor {
|
||||
pub fn new(key: Vec<u8>) -> Self {
|
||||
Self {
|
||||
key,
|
||||
is_setted: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encryptor for AesEncryptor {
|
||||
fn decrypt(&self, data: &mut BytesMut) -> Result<()> {
|
||||
let res = aes_decrypt(&self.key, data)?;
|
||||
*data = BytesMut::from(res.as_slice());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn encrypt(&self, data: &mut BytesMut) -> Result<()> {
|
||||
let res = aes_encrypt(&self.key, data)?;
|
||||
*data = BytesMut::from(res.as_slice());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_setted(&self) -> bool {
|
||||
self.is_setted
|
||||
}
|
||||
|
||||
fn set_key(&mut self, _region_id: u32, key: Vec<u8>) {
|
||||
self.key = key;
|
||||
self.is_setted = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_chacha20_encrypt_decrypt() {
|
||||
let key = vec![0u8; 32];
|
||||
let encryptor = Chacha20Encryptor::new(key, 1);
|
||||
let mut data = BytesMut::from(&b"hello world"[..]);
|
||||
encryptor.encrypt(&mut data).unwrap();
|
||||
encryptor.decrypt(&mut data).unwrap();
|
||||
assert_eq!(&data[..], b"hello world");
|
||||
}
|
||||
}
|
||||
28
src/utils/error_report.rs
Normal file
28
src/utils/error_report.rs
Normal file
@ -0,0 +1,28 @@
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Debug, Serialize, Clone)]
|
||||
#[repr(u8)]
|
||||
#[serde(into = "u8")]
|
||||
pub enum ErrorSeverity {
|
||||
Panic,
|
||||
Error,
|
||||
Warning,
|
||||
Info,
|
||||
Log,
|
||||
}
|
||||
|
||||
impl From<ErrorSeverity> for u8 {
|
||||
fn from(value: ErrorSeverity) -> Self {
|
||||
value as u8
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Clone)]
|
||||
pub struct ErrorReport {
|
||||
pub severity: ErrorSeverity,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
mod test {
|
||||
|
||||
}
|
||||
68
src/utils/file_configuration.rs
Normal file
68
src/utils/file_configuration.rs
Normal file
@ -0,0 +1,68 @@
|
||||
use std::{collections::HashMap, path::Path, sync::atomic::AtomicBool};
|
||||
|
||||
use sdlan_sn_rs::utils::{Mac, Result};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{ExitNodeInfo, generate_mac_address, get_base_dir};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct ExitNodeConfiguration {
|
||||
pub in_use: bool,
|
||||
pub node_id: u32,
|
||||
pub node_name: String,
|
||||
pub gateway: String,
|
||||
pub target_network: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct LocalConfigInfo {
|
||||
pub allow_routing: Option<bool>,
|
||||
pub mac: Option<[u8; 6]>,
|
||||
pub exit_node: HashMap<u32, ExitNodeConfiguration>,
|
||||
}
|
||||
|
||||
pub fn store_configuration(config: &LocalConfigInfo) -> Result<()> {
|
||||
let path = format!("{}/.config", get_base_dir());
|
||||
let data = serde_json::to_string(config)?;
|
||||
std::fs::write(&path, data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_or_create_new_mac() -> Mac {
|
||||
let path = format!("{}/.mac", get_base_dir());
|
||||
let mut mac = None;
|
||||
if let Ok(content) = std::fs::read(&path) {
|
||||
if content.len() == 6 {
|
||||
let mut mac_slice = [0; 6];
|
||||
mac_slice.copy_from_slice(&content);
|
||||
mac = Some(mac_slice);
|
||||
}
|
||||
}
|
||||
let _ = std::fs::remove_file(&path);
|
||||
if let None = mac {
|
||||
mac = Some(generate_mac_address());
|
||||
}
|
||||
mac.unwrap()
|
||||
}
|
||||
|
||||
pub fn load_configuration() -> LocalConfigInfo {
|
||||
let path = format!("{}/.config", get_base_dir());
|
||||
if let Ok(content) = std::fs::read(&path) {
|
||||
if let Ok(mut config) = serde_json::from_slice::<LocalConfigInfo>(&content) {
|
||||
if config.mac.is_none() {
|
||||
config.mac = Some(load_or_create_new_mac());
|
||||
let _ = store_configuration(&config);
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
||||
|
||||
let mac = Some(load_or_create_new_mac());
|
||||
let config = LocalConfigInfo {
|
||||
allow_routing: Some(false),
|
||||
mac,
|
||||
exit_node: HashMap::new(),
|
||||
};
|
||||
let _ = store_configuration(&config);
|
||||
config
|
||||
}
|
||||
134
src/utils/mod.rs
Normal file → Executable file
134
src/utils/mod.rs
Normal file → Executable file
@ -1,8 +1,140 @@
|
||||
mod command;
|
||||
mod acl_session;
|
||||
mod encrypter;
|
||||
mod system_action;
|
||||
mod file_configuration;
|
||||
mod dns;
|
||||
mod error_report;
|
||||
|
||||
use std::{fs::OpenOptions, io::Write, net::Ipv4Addr, path::Path};
|
||||
use tracing::error;
|
||||
|
||||
pub use encrypter::*;
|
||||
pub use command::*;
|
||||
pub use acl_session::*;
|
||||
pub use system_action::*;
|
||||
pub use dns::*;
|
||||
pub use error_report::*;
|
||||
|
||||
mod socks;
|
||||
use rand::Rng;
|
||||
use sdlan_sn_rs::utils::{Mac, Result, SDLanError};
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use socks::*;
|
||||
pub use file_configuration::*;
|
||||
|
||||
use crate::get_base_dir;
|
||||
|
||||
mod pid_recorder;
|
||||
pub use pid_recorder::PidRecorder;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct CachedLoginInfo {
|
||||
pub access_token: String,
|
||||
pub username: String,
|
||||
pub user_type: String,
|
||||
pub audit: u32,
|
||||
pub network_id: u32,
|
||||
pub network_name: String,
|
||||
pub network_domain: String,
|
||||
}
|
||||
|
||||
// pub const CRC_HASH: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_XFER);
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn caculate_crc(data: &[u8]) -> u32 {
|
||||
let res = crc32fast::hash(data);
|
||||
res
|
||||
}
|
||||
|
||||
pub fn ip_string_to_u32(ip: &str) -> Result<u32> {
|
||||
let ip = ip.parse::<Ipv4Addr>()?;
|
||||
Ok(u32::from(ip))
|
||||
}
|
||||
|
||||
pub fn get_access_token() -> Option<CachedLoginInfo> {
|
||||
let path = format!("{}/.access_token", get_base_dir());
|
||||
if let Ok(content) = std::fs::read(&path) {
|
||||
let Ok(data) = serde_json::from_slice(&content) else {
|
||||
return None;
|
||||
};
|
||||
return Some(data);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get_hostname() -> String {
|
||||
match hostname::get() {
|
||||
Ok(os_string) => {
|
||||
match os_string.into_string() {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
error!("failed to convert hostname to string");
|
||||
"".to_owned()
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("failed to get hostname");
|
||||
"".to_owned()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_access_token(cache_info: &CachedLoginInfo) -> Result<()> {
|
||||
let path = format!("{}/.access_token", get_base_dir());
|
||||
let data = serde_json::to_string(cache_info).unwrap();
|
||||
std::fs::write(path, &data)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/*
|
||||
pub fn create_or_load_mac() -> Mac {
|
||||
let path = format!("{}/.mac", get_base_dir());
|
||||
if let Ok(content) = std::fs::read(&path) {
|
||||
if content.len() == 6 {
|
||||
let mut mac = [0; 6];
|
||||
mac.copy_from_slice(&content);
|
||||
return mac;
|
||||
}
|
||||
}
|
||||
let mac = generate_mac_address();
|
||||
let _ = save_to_file_binary(&path, &mac);
|
||||
mac
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
pub fn save_to_file_binary(idfile: &str, content: &[u8]) -> Result<()> {
|
||||
if idfile.len() == 0 {
|
||||
return Err(SDLanError::IOError("file is empty".to_owned()));
|
||||
}
|
||||
|
||||
let filepath = Path::new(idfile);
|
||||
OpenOptions::new()
|
||||
.create(true)
|
||||
.write(true)
|
||||
.truncate(true)
|
||||
.open(filepath)?
|
||||
.write_all(content)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mac_to_string(mac: &Mac) -> String {
|
||||
format!(
|
||||
"[{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}]",
|
||||
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
|
||||
)
|
||||
}
|
||||
|
||||
pub fn generate_mac_address() -> Mac {
|
||||
let mut rng = rand::thread_rng();
|
||||
let mut mac = [0; 6];
|
||||
for i in 0..6 {
|
||||
let number: u8 = rng.gen();
|
||||
mac[i] = number;
|
||||
}
|
||||
mac[0] &= !0x01;
|
||||
mac[0] |= 0x02;
|
||||
|
||||
mac
|
||||
}
|
||||
|
||||
9
src/utils/pid_recorder.rs
Normal file → Executable file
9
src/utils/pid_recorder.rs
Normal file → Executable file
@ -2,9 +2,12 @@ use std::{
|
||||
fs::{self, OpenOptions},
|
||||
io::Write,
|
||||
};
|
||||
use tracing::{error};
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct PidRecorder(String);
|
||||
|
||||
#[allow(unused)]
|
||||
impl PidRecorder {
|
||||
pub fn new(pidfile: &str) -> Self {
|
||||
let pid = std::process::id();
|
||||
@ -19,7 +22,8 @@ impl PidRecorder {
|
||||
.expect("failed to write");
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("failed to open pid file: {}", e);
|
||||
error!("failed to open pid file: {}", e);
|
||||
// eprintln!("failed to open pid file: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +34,8 @@ impl PidRecorder {
|
||||
impl Drop for PidRecorder {
|
||||
fn drop(&mut self) {
|
||||
if let Err(e) = fs::remove_file(&self.0) {
|
||||
eprintln!("failed to remove pid file: {}", e);
|
||||
error!("failed to remove pid file: {}", e);
|
||||
// eprintln!("failed to remove pid file: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
67
src/utils/socks.rs
Normal file → Executable file
67
src/utils/socks.rs
Normal file → Executable file
@ -1,8 +1,10 @@
|
||||
use default_net::Interface;
|
||||
use sdlan_sn_rs::{
|
||||
config::{AF_INET, AF_INET6},
|
||||
utils::{Result, SDLanError},
|
||||
};
|
||||
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
|
||||
use socket2::{Domain, Protocol, SockAddr};
|
||||
use std::{net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4}, num::NonZeroU32};
|
||||
use tokio::net::ToSocketAddrs;
|
||||
use tracing::{debug, error};
|
||||
|
||||
@ -11,11 +13,11 @@ use tokio::net::UdpSocket;
|
||||
|
||||
use crate::network::Node;
|
||||
|
||||
#[allow(unused)]
|
||||
pub struct SocketV6 {
|
||||
ipv6: Option<Ipv4Addr>,
|
||||
port: u16,
|
||||
has_v6: bool,
|
||||
|
||||
}
|
||||
|
||||
pub struct Socket {
|
||||
@ -58,18 +60,37 @@ impl Socket {
|
||||
|
||||
pub async fn build_v6(v6: Ipv6Addr, port: u16) -> Result<Self> {
|
||||
let udp = UdpSocket::bind(format!("[{}]:{}", v6, port)).await?;
|
||||
Ok(Self {
|
||||
udp
|
||||
})
|
||||
Ok(Self { udp })
|
||||
}
|
||||
|
||||
pub async fn build(port: u16, bind_any: bool, join_multicast: bool, tos: u32) -> Result<Self> {
|
||||
pub async fn build(
|
||||
port: u16,
|
||||
bind_any: bool,
|
||||
join_multicast: bool,
|
||||
tos: u32,
|
||||
default_interface: &Option<Interface>,
|
||||
) -> Result<Self> {
|
||||
let addr = match bind_any {
|
||||
true => "0.0.0.0",
|
||||
false => "127.0.0.1",
|
||||
};
|
||||
let addr = format!("{}:{}", addr, port);
|
||||
let udp = UdpSocket::bind(&addr).await?;
|
||||
|
||||
let socket2 =
|
||||
socket2::Socket::new(Domain::IPV4, socket2::Type::DGRAM, Some(Protocol::UDP))?;
|
||||
|
||||
socket2.bind(&addr.parse::<SocketAddr>()?.into());
|
||||
|
||||
if let Some(iface) = default_interface {
|
||||
set_unicast_if_v4(&socket2, iface);
|
||||
}
|
||||
|
||||
let std_socket: std::net::UdpSocket = socket2.into();
|
||||
std_socket.set_nonblocking(true);
|
||||
|
||||
let udp = UdpSocket::from_std(std_socket)?;
|
||||
|
||||
// let udp = UdpSocket::bind(&addr).await?;
|
||||
if join_multicast {
|
||||
if let Err(e) =
|
||||
udp.join_multicast_v4(Ipv4Addr::new(224, 0, 0, 69), Ipv4Addr::new(0, 0, 0, 0))
|
||||
@ -80,7 +101,7 @@ impl Socket {
|
||||
}
|
||||
}
|
||||
if tos != 0 {
|
||||
if let Err(e) = udp.set_tos(tos) {
|
||||
if let Err(e) = udp.set_tos_v4(tos) {
|
||||
error!("failed to set tos: {}", e.to_string());
|
||||
}
|
||||
}
|
||||
@ -88,6 +109,36 @@ impl Socket {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
pub fn set_unicast_if_v4(sock: &socket2::Socket, interface: &Interface) -> Result<()> {
|
||||
sock.bind_device_by_index_v4(NonZeroU32::new(interface.index))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
pub fn set_unicast_if_v4(sock: &socket2::Socket, interface: &Interface) -> Result<()> {
|
||||
use std::os::windows::io::AsRawSocket;
|
||||
use windows_sys::Win32::Networking::WinSock::{setsockopt, IPPROTO_IP, IP_UNICAST_IF, SOCKET};
|
||||
|
||||
let idx = interface.index.to_be();
|
||||
let ret = unsafe {
|
||||
setsockopt(
|
||||
sock.as_raw_socket() as SOCKET,
|
||||
IPPROTO_IP as i32,
|
||||
IP_UNICAST_IF as i32,
|
||||
&idx as *const _ as *const _,
|
||||
std::mem::size_of_val(&idx) as i32,
|
||||
)
|
||||
};
|
||||
if ret == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(SDLanError::IOError(
|
||||
std::io::Error::last_os_error().to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
pub async fn send_to_sock_v4_and_v6(
|
||||
// sk: &Socket,
|
||||
|
||||
147
src/utils/system_action.rs
Normal file
147
src/utils/system_action.rs
Normal file
@ -0,0 +1,147 @@
|
||||
use std::{net::Ipv4Addr, sync::Arc};
|
||||
|
||||
use arc_swap::ArcSwap;
|
||||
use default_net::Interface;
|
||||
use sdlan_sn_rs::utils::{Result, SDLanError};
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct TrieNode {
|
||||
child: [Option<Box<TrieNode>>; 2],
|
||||
prefix_len: u8,
|
||||
nexthop: Option<Ipv4Addr>,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
struct IpTrie {
|
||||
root: TrieNode,
|
||||
}
|
||||
|
||||
impl IpTrie {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
root: TrieNode::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn insert(&mut self, prefix: u32, prefix_len: u8, nexthop: Ipv4Addr) {
|
||||
if prefix_len > 32 {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut node = &mut self.root;
|
||||
for i in 0..prefix_len {
|
||||
let bit = ((prefix >> (31 - i)) & 1) as usize;
|
||||
node = node.child[bit].get_or_insert_with(|| Box::new(TrieNode::default()));
|
||||
}
|
||||
if prefix_len >= node.prefix_len {
|
||||
node.prefix_len = prefix_len;
|
||||
node.nexthop = Some(nexthop);
|
||||
}
|
||||
}
|
||||
|
||||
fn lookup(&self, ip: u32) -> Option<(u8, Ipv4Addr)> {
|
||||
let mut node = &self.root;
|
||||
let mut best = None;
|
||||
|
||||
for i in 0..32 {
|
||||
if node.nexthop.is_some() {
|
||||
best = Some((node.prefix_len, node.nexthop.unwrap()));
|
||||
}
|
||||
let bit = ((ip >> (31 - i)) & 1) as usize;
|
||||
// println!("to find bit {} => {}", i, bit);
|
||||
match &node.child[bit] {
|
||||
Some(child) => {
|
||||
node = child;
|
||||
}
|
||||
None => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if node.nexthop.is_some() {
|
||||
best = Some((node.prefix_len, node.nexthop.unwrap()));
|
||||
}
|
||||
best
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_default_interface() -> Result<Interface> {
|
||||
match default_net::get_default_interface() {
|
||||
Ok(interface) => Ok(interface),
|
||||
Err(e) => Err(SDLanError::IOError(e)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::{network::RouteTable2, TrieNode};
|
||||
|
||||
#[test]
|
||||
fn test_trie() {
|
||||
let trie = RouteTable2::new();
|
||||
|
||||
let mut ips: Vec<(Ipv4Addr, Ipv4Addr)> = vec![];
|
||||
for i in 1..254u32 {
|
||||
ips.push((
|
||||
format!("192.168.{}.0", i).parse::<Ipv4Addr>().unwrap(),
|
||||
format!("192.168.{}.1", i).parse::<Ipv4Addr>().unwrap(),
|
||||
));
|
||||
}
|
||||
for ip in &ips {
|
||||
trie.route_table
|
||||
.insert(u32::from_be_bytes(ip.0.octets()), 24, ip.1);
|
||||
}
|
||||
|
||||
for ip in &ips {
|
||||
let origin = ip.0;
|
||||
let mut query = origin.octets();
|
||||
query[3] = 123;
|
||||
let query = Ipv4Addr::from_octets(query);
|
||||
|
||||
println!("query for {}", query);
|
||||
let result = ip.1;
|
||||
let Some((prefix, target)) =
|
||||
trie.route_table.lookup(u32::from_be_bytes(query.octets()))
|
||||
else {
|
||||
panic!("failed to lookup: {} for {}", query, result);
|
||||
};
|
||||
if prefix != 24 {
|
||||
panic!("prefix is not 24: {}", prefix);
|
||||
}
|
||||
if target != result {
|
||||
panic!("gateway is not match, {} expected {}", target, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RouteTableTrie {
|
||||
trie: ArcSwap<IpTrie>,
|
||||
}
|
||||
|
||||
impl RouteTableTrie {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
trie: ArcSwap::new(Arc::new(IpTrie::default())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&self) {
|
||||
self.trie.store(Arc::new(IpTrie::default()));
|
||||
}
|
||||
|
||||
pub(crate) fn lookup(&self, ip: u32) -> Option<(u8, Ipv4Addr)> {
|
||||
let trie = self.trie.load();
|
||||
trie.lookup(ip)
|
||||
}
|
||||
|
||||
pub(crate) fn insert(&self, prefix: u32, prefix_len: u8, nexthop: Ipv4Addr) {
|
||||
let old = self.trie.load();
|
||||
let mut new_trie = (*(*old)).clone();
|
||||
|
||||
new_trie.insert(prefix, prefix_len, nexthop);
|
||||
self.trie.store(Arc::new(new_trie));
|
||||
}
|
||||
}
|
||||
2
start_docker.sh
Executable file
2
start_docker.sh
Executable file
@ -0,0 +1,2 @@
|
||||
## pull image: docker pull ubuntu
|
||||
docker run -v .:/root/punchnet -it --device=/dev/net/tun --cap-add=NET_ADMIN --cap-add=SYS_ADMIN -w /root/punchnet ubuntu
|
||||
BIN
wintun.dll
Executable file
BIN
wintun.dll
Executable file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user