Compare commits

...

100 Commits

Author SHA1 Message Date
4aa6406428 changed to new message.proto, which eliminates the pkt_id 2026-03-30 17:17:50 +08:00
2cff146f8c message downcast ok 2026-03-26 20:40:42 +08:00
e2d5b465f7 remove some println and daemonize 2026-03-26 20:01:21 +08:00
caf62fe079 merge cookie_match and packet_id_match to one 2026-03-26 19:58:47 +08:00
be401afc7b packet id match is merged to cookie_match 2026-03-26 19:50:15 +08:00
b10c721179 changed encryptor to ArcSwap 2026-03-26 10:52:16 +08:00
9cba4a976d added route table 2026-03-25 16:35:11 +08:00
1f46a15b11 acl check 2026-03-24 15:55:21 +08:00
2261d83e18 added acl in utils 2026-03-24 14:39:30 +08:00
78351e0493 musl version 2026-03-20 16:19:54 +08:00
f8c213c8c7 api header X-sign is tested, and ok 2026-03-18 22:42:23 +08:00
372afd470b token login 2026-03-18 22:36:51 +08:00
c6df26ac85 fix some warnings 2026-03-18 16:37:40 +08:00
fbe2aa12fb windows, and tun, and tap are supported 2026-03-18 16:32:21 +08:00
33a995e7dd fix key is not set bug 2026-03-18 16:09:59 +08:00
321146f6ac chacha20 is supported, need test aes 2026-03-18 15:58:28 +08:00
fa8a60a737 chacha20 and aes is supported, need further test 2026-03-18 11:48:41 +08:00
5249a271c5 punchnet 1.0.1 is ok, no permission is checked 2026-03-18 10:59:23 +08:00
814ab9181a added query for info 2026-03-12 11:40:56 +08:00
de8bf7cd31 daemonize is added to unix 2026-03-10 19:14:41 +08:00
46536fc587 ignore quic server ca.crt 2026-03-10 15:47:16 +08:00
a52df35198 cmdline's tcp name is changed to quic 2026-03-10 15:39:42 +08:00
50db315c79 no identity id is checked 2026-03-10 15:13:38 +08:00
c8c618015a changed the daemonize 2026-03-07 16:17:05 +08:00
651b9ba5cd construct the arp response while SdlArpResponse arrived 2026-03-07 15:42:35 +08:00
46170feb5b encode_to_tcp_message, changed the packet_id to the struct, not the parameter of the function 2026-03-06 16:21:39 +08:00
31845c6707 added policy request and response, need test tomorrow 2026-03-05 22:53:42 +08:00
e8e8655100 cached login info, and connect info is accessed while connect 2026-03-02 16:12:19 +08:00
00c5c67307 login with user 2026-03-02 11:07:42 +08:00
8902c27b1d client id and mac are passed as values 2026-02-28 10:32:44 +08:00
8645b65534 changed tcp to quic, can remove tcp_conn.rs safely 2026-02-27 16:30:46 +08:00
9d52223f84 added api request with reqwest 2026-02-26 10:58:15 +08:00
be71e8404a fix windows and tun's data 2026-02-25 16:06:44 +08:00
13c287e089 session token is received from RegisterSuperACK 2026-02-25 15:33:30 +08:00
6f895c3404 changed to new message.proto, and use Bytes for SDLData's data 2026-02-25 15:30:26 +08:00
c5b04eb843 fix some warning 2026-01-16 16:33:25 +08:00
214e73a0b9 fix on_connected_callback, on_disconnected_callback, and on_message callback 2026-01-16 15:53:00 +08:00
6ef685420c remove redudency myactor 2026-01-15 16:28:14 +08:00
f9c33b564f fix warning and added myactor library 2026-01-15 16:27:18 +08:00
d8c908a0b2 fix merge and add myactor 2026-01-15 16:26:17 +08:00
2fc246a1fe changed reqwest's tls to rustls-tls, avoiding the openssl-sys 2025-12-26 23:13:09 +08:00
89f7db657d added start_with(out)_feedback's hostname 2025-12-25 22:19:25 +08:00
43c2b01fca win's tun, linux's tap and tun, are all ok 2025-12-24 23:49:39 +08:00
b36f526cfc win is ok 2025-12-24 23:45:00 +08:00
2096c3faac linux is ok for tun and tap 2025-12-24 22:15:52 +08:00
0300eb9ade dns resolvectl or /etc/resolv.conf is tested in linux 2025-12-23 22:43:19 +08:00
6672e78c42 check for resolvectl 2025-12-23 22:25:48 +08:00
b343124821 in linux, use resolvectl or /etc/resolv.conf for dns resolving 2025-12-23 22:23:53 +08:00
fad345becb 15353's parse is ok, should restore /etc/resolv.conf while exitting 2025-12-23 10:30:33 +08:00
41a21188bf added hostname for dns, first step 2025-12-22 14:25:00 +08:00
9861c4e850 connecting_chan, includes the IPInfo 2025-10-30 15:33:09 +08:00
e003584463 added Serialize 2025-10-27 16:12:36 +08:00
6ab4f7042b aded get_my_networks api 2025-10-27 15:00:56 +08:00
1e429ad874 added get_my_network interface in lib.rs 2025-10-25 16:27:27 +08:00
c2a3048427 fix some warning, and tested with mac 2025-10-23 17:35:41 +08:00
8db61a7bbb added set_base_dir for recording log, rsa keys 2025-10-22 22:14:14 +08:00
6f996d020a fix error 2025-10-22 21:21:06 +08:00
df06803004 added start_with_feedback and start_without_feedback's network_code 2025-10-22 16:35:18 +08:00
d9b1329e6a changed dependencies to gitea.s5s8, and added network_code in register super 2025-10-22 16:27:45 +08:00
ec68a54957 changed the .a to .so, for cross compile fails in release mode 2025-10-10 15:36:48 +08:00
9a72c29870 punchnet renamed 2025-09-30 11:31:05 +08:00
7fa2ffa04d changed mode 2025-05-12 10:37:16 +08:00
6e8f4583d6 fix warning 2025-02-06 10:50:49 +08:00
3f6da7c65e changed back to log 2025-02-06 10:16:37 +08:00
7e5792e190 updated win's mtu 2025-02-06 10:13:43 +08:00
4aa33a4e26 tap-test 2025-02-05 16:19:37 +08:00
4fdcfa5bee is mtu's bug 2025-01-28 15:47:24 +08:00
c070d345ad fix nat probe bug 2024-12-30 10:15:44 +08:00
51c323008a nat-simulator.sh is still on work, now, it simulates the fullcone(nat1), ip restricted cone(nat2) 2024-12-27 00:25:13 +08:00
63805d9a47 fix nat probe's bug 2024-12-27 00:23:30 +08:00
240a3d263e ipv6 p2p is ok 2024-11-28 16:09:49 +08:00
c098a3c421 changed the message's proto, added nat_type for send_register event 2024-11-25 15:50:55 +08:00
b51c78bdfb guess port at -10 to 10 2024-11-13 19:24:33 +08:00
966f054a63 BROADCAST ip to BROADCAST_MAC 2024-10-30 10:30:58 +08:00
f3cbc1def2 register with local peers, broadcast mac 2024-10-26 11:33:15 +08:00
7a75c1d171 stdout's info 2024-10-26 11:22:02 +08:00
93f3dbdcf9 self mac print 2024-10-26 11:17:46 +08:00
c28c9e2911 fix features typo 2024-10-26 10:56:11 +08:00
2b8a536151 skip REGISTER SUPER and REGISTER from self 2024-10-26 10:53:58 +08:00
e32c640466 init wait list 2024-10-26 09:48:16 +08:00
07c6536d5a linux add to arp-list 2024-10-25 16:47:29 +08:00
d1e30370fc win arp wait list 2024-10-25 16:43:05 +08:00
69255d7907 add arp to waitlist 2024-10-25 16:41:02 +08:00
e7352c7b67 fix some warning 2024-10-25 11:24:50 +08:00
c4c1f109b1 tun win fix warning 2024-10-25 11:19:46 +08:00
cb71c53102 fix warning 2024-10-25 11:01:02 +08:00
06d7819fd1 linux 2024-10-24 18:46:21 +08:00
220cee4a89 win tun 2024-10-24 18:38:17 +08:00
9b1710e590 message 2024-10-23 20:36:51 +08:00
7c4bb64bd4 changed to handle_packet 2024-10-23 16:22:52 +08:00
6dd3d8694c added crc after the ether packet 2024-10-23 11:29:41 +08:00
1a5e1d8be3 tap is ok 2024-10-22 21:05:46 +08:00
c130dcc83e srcmac reply 2024-10-22 16:14:34 +08:00
53b0be35a7 src changed to dst 2024-10-22 16:09:31 +08:00
893eef5871 packet 2024-10-22 15:48:10 +08:00
32bbdc4f78 arp reply encrypt 2024-10-22 15:14:03 +08:00
af56edd331 dst mac 2024-10-22 11:01:52 +08:00
3e4fe82a59 changed arp 2024-10-22 11:00:44 +08:00
302300294a tap mac 2024-10-22 10:12:08 +08:00
56d6a35fea fix all error 2024-10-21 09:23:29 +08:00
52 changed files with 9451 additions and 1948 deletions

14
.cargo/config.toml Executable file
View File

@ -0,0 +1,14 @@
[target.x86_64-unknown-linux-musl]
linker = "musl-gcc"
[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
ar = "aarch64-linux-gnu-gcc"
strip = { path = "aarch64-linux-gnu-strip" }
# ranlib = "aarch64-linux-gnu-ranlib"
[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"
# [build]
# target = "x86_64-unknown-linux-gnu"

9
.gitignore vendored Normal file → Executable file
View File

@ -4,3 +4,12 @@
.client
.output
sdlan
sdlan.exe
*.bak
*.a
*.o
*.tar
*.tar.gz
*.tgz
/punchnet
/ca

4
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,4 @@
{
// "rust-analyzer.cargo.target": "x86_64-pc-windows-gnu",
// "rust-analyzer.cargo.features": ["tun"]
}

3012
Cargo.lock generated

File diff suppressed because it is too large Load Diff

59
Cargo.toml Normal file → Executable file
View File

@ -1,9 +1,11 @@
[package]
name = "sdlan-rs"
version = "0.1.0"
name = "punchnet"
version = "1.0.3"
edition = "2021"
[dependencies]
crc = "3.2.1"
crc32fast = "1.4.2"
dashmap = "6.0.1"
dns-lookup = "2.0.4"
etherparse = "0.15.0"
@ -13,13 +15,62 @@ 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"
# 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"
[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"],
# lib
["libtuntap.so", "usr/lib/", "755"],
]
maintainer-scripts = "debian"

42
Makefile Normal file → Executable file
View File

@ -1,9 +1,45 @@
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-so-aarch64:
rm libtuntap.* && cd src/network && aarch64-linux-gnu-gcc -fPIC -shared -o libtuntap.so tuntap.c && cp libtuntap.so ../.. && 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: libtun-musl
RUSTFLAGS="-L ." cargo deb --target x86_64-unknown-linux-musl --deb-revision="1-static"
deb: libtun-so
RUSTFLAGS="-L ." cargo deb --deb-revision="1-dynamic"
deb-aarch64: libtun-so-aarch64
RUSTFLAGS="-L ." cargo deb --target aarch64-unknown-linux-gnu

9
debian/postinst vendored Normal file
View 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
View File

@ -0,0 +1,9 @@
#!/bin/sh
set -e
#DEBHELPER#
rm -rf /usr/lib/libswscale.so.5
exit 0;

13
install.sh Normal file
View 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

View File

@ -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
View 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
View File

@ -0,0 +1,6 @@
syntax = "proto3";
package message;
message SDLEmpty {
}

185
proto/message.proto Normal file
View File

@ -0,0 +1,185 @@
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>> ; allowdeny规则的服务器端已经屏蔽
bytes rules = 4;
}
//
message SDLNatChangedEvent {
bytes mac = 1;
uint32 ip = 2;
}
message SDLSendRegisterEvent {
bytes dst_mac = 1;
uint32 nat_ip = 2;
uint32 nat_port = 3;
uint32 nat_type = 4;
optional SDLV6Info v6_info = 5;
}
message SDLNetworkShutdownEvent {
string message = 1;
}
// 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
View File

@ -0,0 +1,12 @@
[Unit]
Description=punchnet client
Before=network.target network.service
Wants=network.target
[Service]
Type=simple
WorkingDirectory=/usr/local/punchnet
ExecStart=/usr/local/punchnet/punchnet
[Install]
WantedBy=multi-user.target

4
remote.record Normal file
View 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)

1
run.sh Executable file
View File

@ -0,0 +1 @@
LD_LIBRARY_PATH=. ./sdlan

3
src/bin/build_pb/main.rs Normal file → Executable file
View 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();
}

318
src/bin/punchnet/api/mod.rs Normal file
View File

@ -0,0 +1,318 @@
use hmac::{Hmac, Mac as HamcMac};
use punchnet::TokenLogin;
use reqwest::Client;
use sdlan_sn_rs::utils::{Mac, Result, SDLanError};
use serde::{Deserialize, Serialize};
use md5::Md5;
pub const TEST_PREFIX: &'static str = "https://punchnet.s5s8.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,
}
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={}&mac={}&system={}&token={}&version={}",
self.client_id,
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,
}
impl HMacCalculator for UserPassLoginData<'_> {
fn calculate_hmac(&self) -> Result<String> {
let data = format!("client_id={}&mac={}&password={}&system={}&username={}&version={}",
self.client_id,
self.mac,
self.password,
self.system,
self.username,
self.version,
);
do_calculate(data.as_bytes())
}
}
#[derive(Debug, Deserialize)]
pub struct LoginResponse {
pub code: i32,
pub message: String,
pub data: Option<LoginData>,
}
#[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 exit_node: Vec<ExitNode>,
}
#[derive(Debug, Deserialize)]
pub struct ExitNode {
pub nnid: u32,
pub node_name: 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 Ok(response) = client
.post(url)
.header("X-sign", hmac)
.json(&data)
.send()
.await else {
return Err(SDLanError::IOError("failed to do request".to_owned()));
};
// println!("status: {}", response.status());
let text = response.text().await.unwrap();
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 post_data = UserPassLoginData {
client_id,
username,
password,
mac: &mac,
system,
version,
};
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 post_data = TokenLoginData {
client_id,
token,
mac: &mac,
system,
version,
};
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>,
}
#[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
}

View File

@ -0,0 +1,167 @@
use std::{net::SocketAddr, sync::atomic::Ordering, time::Duration};
use num_enum::TryFromPrimitive;
use punchnet::get_edge;
use sdlan_sn_rs::utils::Mac;
use serde::{Deserialize, Serialize};
use tokio::{net::UdpSocket, time::sleep};
#[derive(TryFromPrimitive)]
#[repr(u8)]
pub enum InfoFuncCode {
Info = 0x00,
InfoFeedback = 0x80,
}
#[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 query_for_info() {
let Ok(udp) = UdpSocket::bind("127.0.0.1:0").await else {
eprintln!("failed to create");
return;
};
let remote = format!("127.0.0.1:{}", LOCAL_INFO_UDP_PORT).parse::<SocketAddr>().unwrap();
let buf = vec![InfoFuncCode::Info as u8, 0, 0];
if let Err(e) = udp.send_to(buf.as_slice(), remote).await {
eprintln!("failed to send query info");
return;
}
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;
}
buf.truncate(size);
let Ok(typecode) = InfoFuncCode::try_from_primitive(buf[0]) else {
eprintln!("invalid type: {}", buf[0]);
return;
};
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;
}
match typecode {
InfoFuncCode::InfoFeedback => {
let Ok(data) = serde_json::from_slice::<InfoFeedback>(&buf[3..]) else {
eprintln!("failed to marshal to json");
return;
};
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);
}
_other => {
}
}
} else {
eprintln!("failed to recv from punchnet");
}
return;
}
_ = sleep(Duration::from_secs(3)) => {
eprintln!("query timed out, is punchnet running?");
return;
}
}
}
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() < 1 {
return;
}
let tp = buf[0];
let Ok(typecode) = InfoFuncCode::try_from_primitive(tp) else {
eprintln!("invalid type: {}", tp);
return;
};
match typecode {
InfoFuncCode::Info => {
send_info_back(udp, from).await;
}
_other => {
}
}
}
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;
}

459
src/bin/punchnet/main.rs Executable file
View File

@ -0,0 +1,459 @@
mod api;
mod local_udp_info;
use std::fs;
use std::fs::OpenOptions;
use std::process;
use std::env;
use clap::Parser;
#[cfg(not(target_os = "windows"))]
use daemonize::Daemonize;
use futures_util::io;
#[cfg(not(target_os = "windows"))]
use libc::{SIGTERM, kill};
use punchnet::CachedLoginInfo;
use punchnet::CommandLineInput2;
use punchnet::Commands;
use punchnet::create_or_load_mac;
use punchnet::get_access_token;
use punchnet::get_base_dir;
use punchnet::get_edge;
use punchnet::ip_string_to_u32;
use punchnet::restore_dns;
use punchnet::run_sdlan;
use punchnet::set_access_token;
use punchnet::set_base_dir;
use punchnet::CommandLine;
use sdlan_sn_rs::log;
use sdlan_sn_rs::utils::Mac;
use sdlan_sn_rs::utils::Result;
use sdlan_sn_rs::utils::create_or_load_uuid;
use tokio::runtime::Runtime;
use tracing::error;
use std::net::ToSocketAddrs;
use crate::api::ConnectData;
use crate::api::ConnectResponse;
use crate::api::LoginData;
use crate::api::LoginResponse;
use crate::api::TEST_PREFIX;
use crate::api::connect;
use crate::api::login_with_token;
use crate::api::login_with_user_pass;
use crate::local_udp_info::handle_query_for_info_info;
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";
fn parse_connect_result(res: Result<ConnectResponse>) -> ConnectData {
match res {
Err(e) => {
eprintln!("failed to connect");
process::exit(-3);
}
Ok(data) => {
if data.code != 0 {
eprintln!("failed to connect: {}", data.message);
process::exit(-3);
}
if data.data.is_none() {
eprintln!("connect empty response");
process::exit(-3);
}
data.data.unwrap()
}
}
}
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(),
}) {
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,
) {
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 = "root.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);
*/
let _ = run_sdlan(
client_id,
mac,
CommandLine {
sn: server.clone()+":1365",
quic: server.clone()+":443",
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,
},
tx,
&punchnet::get_install_channel(),
server,
Some(self_host_name),
None,
)
.await;
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,
&"".to_owned(),
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;
*/
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();
}
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";
fn main() {
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 mac = create_or_load_mac();
let system = SYSTEM;
let version = env!("CARGO_PKG_VERSION");
// let cmd = CommandLineInput::from_args();
let cmd = CommandLineInput2::parse();
// println!("port is {}", cmd.port);
match &cmd.cmd {
Commands::Login(user) => {
let rt = Runtime::new().unwrap();
rt.block_on( async move {
let _ = parse_login_result(
login_with_user_pass(TEST_PREFIX, &client_id, &user.username, &user.password, mac, system, version).await
);
});
process::exit(0);
// TODO: do login with user
}
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
);
});
process::exit(0);
}
#[cfg(not(target_os = "windows"))]
Commands::Stop => {
match fs::read_to_string("/tmp/punchnet.pid") {
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 {
kill(pid, SIGTERM)
};
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);
}
_other => {
// just fall through to next code
}
}
let should_daemonize = true;
#[cfg(not(target_os = "windows"))]
if should_daemonize {
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("/tmp/punchnet.pid")
.chown_pid_file(true)
.working_directory(get_base_dir())
.stdout(out)
.stderr(err)
.privileged_action(|| {
});
match daemonize.start() {
Ok(_) => {
run_it(cmd, client_id, mac, system, version);
}
Err(e) => {
eprintln!("failed to daemonize");
}
}
} else {
run_it(cmd, client_id, mac, system, version);
}
#[cfg(target_os = "windows")]
run_it(cmd, client_id, mac, system, version);
}
fn run_it(cmd: CommandLineInput2, client_id: String, 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
);
daemonize_me(rtinfo.allow_routing, connect_info, remembered, client_id, rtinfo.route_file.clone(), rtinfo.route.clone(), mac).await;
})
}
Commands::AutoRun(tk) => {
rt.block_on(async move {
let mut remembered_token = get_access_token();
if remembered_token.is_none() {
let data = parse_login_result(
login_with_token(TEST_PREFIX, &client_id, &tk.token, mac, system, version).await
);
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,
});
}
let remembered = remembered_token.unwrap();
let connect_info = parse_connect_result(
connect(TEST_PREFIX, &client_id, &remembered.access_token).await
);
daemonize_me(tk.allow_routing, connect_info, remembered, client_id, tk.route_file.clone(), tk.route.clone(), mac).await;
})
}
other => {
eprintln!("should not comes here");
process::exit(-1);
}
}
}
pub fn delete_pid_file() {
fs::remove_file("/tmp/punchnet.pid");
}

View File

@ -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
View 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");
}
}

128
src/lib.rs Normal file → Executable file
View File

@ -3,24 +3,38 @@ mod network;
mod pb;
mod tcp;
mod utils;
mod quic;
use std::sync::Arc;
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};
use sdlan_sn_rs::utils::{Mac, save_to_file};
use serde::{Deserialize, Serialize};
use tokio::net::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},
};
#[derive(Clone)]
pub enum ConnectionInfo {
ConnState(ConnectionState),
IPInfo(String),
}
#[derive(Clone)]
#[repr(u8)]
pub enum ConnectionState {
@ -30,28 +44,60 @@ 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>,
server_ip: String,
hostname: Option<String>,
connecting_chan: Option<Sender<ConnectionInfo>>, // start_stop_sender: Sender<String>,
// start_stop_receiver: Receiver<String>,
) -> 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 {
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 = Arc::new(UdpSocket::bind("0.0.0.0:0").await?);
if let Err(e) = init_edge(
// &args.token,
// &args.network_code,
&args,
mac,
node_conf,
// args.tos,
start_stop_sender,
// args.mtu,
connecting_chan.clone(),
sock,
hostname,
server_ip,
install_channel.to_owned(),
)
.await
{
panic!("failed to init edge: {:?}", e);
}
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).await
{
error!("failed to run async main: {}", e.as_str());
}
});
@ -108,14 +154,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: args.allow_routing,
_drop_multicast: true,
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 +206,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;

570
src/network/arp.rs Executable file
View File

@ -0,0 +1,570 @@
#![allow(unused)]
use std::{
collections::HashMap,
sync::{Arc, atomic::{AtomicU8, Ordering}},
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::{BROADCAST_MAC, MULTICAST_MAC, Mac, get_current_timestamp, ip_to_string, mac_to_string, net_bit_len_to_mask}};
use tokio::sync::{
mpsc::{channel, Receiver, Sender},
oneshot,
};
use crate::{network::{form_ethernet_packet, send_packet_to_net}, pb::{SdlData, encode_to_udp_message}, 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(60),
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(30)).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;
}
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 packet = form_ethernet_packet(src_mac, mac, item.origin_data);
let pkt_size = packet.len();
let Ok(encrypted) = edge.encryptor.load().encrypt(&packet) else {
// let Ok(encrypted) = edge.encryptor.read().unwrap().encrypt(&packet) else {
// let Ok(encrypted) = aes_encrypt(&encrypt_key, &packet) else {
error!("failed to encrypt packet request");
return;
};
let data_bytes = Bytes::from(encrypted);
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;
}
}
}

545
src/network/async_main.rs Normal file → Executable file
View File

@ -1,321 +1,46 @@
use std::net::IpAddr;
use std::sync::atomic::Ordering;
use std::sync::atomic::{Ordering};
use std::sync::Arc;
use std::time::Duration;
use crate::config::TCP_PING_TIME;
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::network::{
get_edge, ping_to_sn, read_and_parse_packet, TunTapPacketHandler,
};
use crate::tcp::{init_tcp_conn, EventType, NakMsgCode, PacketType, SdlanTcp};
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 sdlan_sn_rs::peer::{SdlanSock};
use sdlan_sn_rs::utils::{get_current_timestamp, 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};
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>>,
) -> 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,
// |msg| handle_tcp_message(msg),
edge.tcp_pong.clone(),
// tcp_pong,
start_stop_chan,
@ -323,11 +48,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...");
/*
@ -404,9 +124,9 @@ 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;
}
}
{
@ -416,36 +136,7 @@ async fn run_edge_loop(eee: &'static Node, cancel: CancellationToken) {
}
}
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,40 +164,52 @@ 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 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, cancel: CancellationToken) {
debug!("loop tap");
let (tx, mut rx) = channel(10);
@ -520,6 +223,26 @@ async fn loop_tap(eee: &'static Node, cancel: CancellationToken) {
drop(rx);
break;
}
reply = receive_dns_reply(&eee.udp_sock_for_dns) => {
if reply.is_none() {
drop(rx);
break;
}
let reply = reply.unwrap();
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,18 +251,42 @@ 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];
#[cfg(feature = "tun")]
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) else {
return BytesMut::new();
};
data_buf.truncate(size);
temp.unsplit(data_buf);
temp
}
#[cfg(not(feature = "tun"))]
fn get_data_from_tap_with_layer2(eee: &Node) -> BytesMut {
let mut buf = BytesMut::zeroed(1514);
let Ok(size) = eee.device.recv(&mut buf) else {
return vec![];
return BytesMut::new();
};
buf.truncate(size);
buf
}
async fn get_tun_flow(eee: &'static Node, tx: Sender<BytesMut>) {
loop {
let buf = tokio::task::spawn_blocking(|| {
#[cfg(feature = "tun")]
let data = get_data_from_tun_with_layer2_zeroed(eee);
#[cfg(not(feature = "tun"))]
let data = get_data_from_tap_with_layer2(eee);
data
})
.await
.unwrap();
@ -554,7 +301,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 +316,35 @@ 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 +354,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 +364,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
View 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.

28
src/network/ipv6.rs Normal file → Executable file
View File

@ -1,13 +1,15 @@
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;
@ -15,11 +17,16 @@ pub async fn run_ipv6(edge: &'static Node, mut v6_may_change: Receiver<bool>) {
v6_may_change.recv().await;
loop {
tokio::time::sleep(Duration::from_secs(1)).await;
let ipv6 = get_current_ipv6();
let mut ipv6 = get_current_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();
/*
@ -37,11 +44,10 @@ pub async fn run_ipv6(edge: &'static Node, mut v6_may_change: Receiver<bool>) {
let socket_clone = socket.clone();
let cancel = CancellationToken::new();
*edge.ipv6.write().unwrap() = Some(SdlanSock {
family: AF_INET6,
port,
v4: [0;4],
v4: [0; 4],
v6: ipv6.octets(),
});
*edge.udp_sock_v6.write().unwrap() = Some(socket);
@ -68,9 +74,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);
}
}
}

14
src/network/mod.rs Normal file → Executable file
View File

@ -9,10 +9,20 @@ 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};
mod device;

541
src/network/node.rs Normal file → Executable file
View File

@ -1,23 +1,30 @@
use arc_swap::ArcSwap;
use dashmap::DashMap;
use prost::Message;
use quinn::Endpoint;
use rsa::RsaPrivateKey;
use sdlan_sn_rs::config::{AF_INET, AF_INET6};
use std::collections::HashMap;
use tokio::net::UdpSocket;
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::sync::mpsc::Sender;
use tokio::sync::oneshot;
use tracing::error;
use tracing::{debug, error, warn};
use crate::network::{ArpTable, RouteTable2};
use crate::quic::quic_init;
use crate::{CommandLine, ConnectionInfo, MyEncryptor, RuleCache, get_base_dir};
use crate::pb::{
encode_to_tcp_message, encode_to_udp_message, SdlEmpty, SdlStunProbe, SdlStunProbeReply,
SdlArpRequest, SdlEmpty, SdlStunProbe, SdlStunProbeReply, encode_to_tcp_message, encode_to_udp_message
};
use crate::tcp::{get_tcp_conn, NatType, PacketType, StunProbeAttr};
use crate::utils::{PidRecorder, Socket};
use crate::tcp::{NatType, PacketType, StunProbeAttr, get_quic_write_conn};
use crate::utils::{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,51 +32,76 @@ 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::{Mac, gen_rsa_keys, load_private_key_file, save_to_file};
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>,
hostname: String,
server_ip: String,
install_channel: String
) -> 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 Ok(sock_v4) = Socket::build(node_conf._local_port, true, false, args.tos).await else {
panic!("failed to build sock for sock v4");
};
let sock_multicast = Socket::build(MULTICAST_PORT, true, true, 0).await?;
let mut sock_multicast = None;
if !node_conf._drop_multicast {
sock_multicast = Some(Socket::build(MULTICAST_PORT, true, true, 0).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 edge = Node::new(
mac,
pubkey,
node_conf,
sock_v4,
sock_multicast,
token,
// token,
// network_code,
privatekey,
tcp_pong.clone(),
start_stop,
args.mtu,
connecting_chan,
hostname,
udpsock_for_dns,
server_ip,
install_channel,
);
edge.route_table.parse_and_add_route(&args.route_file, &args.route_str);
do_init_edge(edge)?;
Ok(())
@ -102,17 +134,72 @@ 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: RwLock<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 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,
@ -120,7 +207,7 @@ pub struct Node {
// 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 +218,14 @@ pub struct Node {
// store pending, and known peers
pub pending_peers: PeerMap,
#[cfg(feature = "tun")]
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 +240,60 @@ 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>>,
//cookie_match: DashMap<u32, oneshot::Sender<SdlStunProbeReply>>,
pub cookie_match: Queryer,
// packet_id_match: DashMap<u32, oneshot::Sender<RegisterSuperFeedback>>,
}
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.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 +306,42 @@ 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.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 +349,21 @@ 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 +374,71 @@ impl Node {
}
pub fn new(
mac: Mac,
pubkey: String,
config: NodeConfig,
sock: Socket,
multicast_sock: Socket,
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>,
server_ip: String,
install_channel: String,
) -> Self {
let mode = if cfg!(not(feature = "tun")) {
Mode::Tap
} else {
Mode::Tun
};
Self {
#[cfg(feature = "tun")]
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),
_token: Mutex::new(token.to_owned()),
hostname: RwLock::new(hostname),
rule_cache: RuleCache::new(),
route_table: RouteTable2::new(),
network_domain: RwLock::new(String::new()),
udp_sock_for_dns: udpsock_for_dns,
quic_endpoint: quic_init(),
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: new_iface("dev", mode),
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 +466,11 @@ 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(),
server_ip,
install_channel,
}
}
@ -299,9 +483,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 +494,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 +549,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 +581,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 +627,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 +644,21 @@ 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 +669,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,7 +734,7 @@ 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,
@ -588,12 +774,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 +802,7 @@ pub struct EdgePeer {
impl EdgePeer {
pub fn new(
// mac: Mac,
net_addr: u32,
net_bit_len: u8,
sock: &SdlanSock,
@ -629,14 +819,123 @@ 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()))
}
}
}
}

755
src/network/packet.rs Normal file → Executable file

File diff suppressed because it is too large Load Diff

116
src/network/route.rs Executable file
View File

@ -0,0 +1,116 @@
use std::{collections::HashMap, fs, io::{BufRead, BufReader}, net::Ipv4Addr, sync::atomic::{AtomicBool, Ordering}, time::Duration};
use ahash::RandomState;
use dashmap::{DashMap};
use ipnet::Ipv4Net;
use sdlan_sn_rs::utils::{Result, SDLanError};
use tokio::{io::ReadBuf, sync::oneshot::{Receiver, Sender, channel}};
use tracing::{debug, error};
use crate::{RouteTableTrie, network::tun::{add_route, del_route}, pb::{SdlArpResponse, SdlStunReply}};
pub struct RouteTable2 {
pub cache_table: DashMap<(Ipv4Net, Ipv4Addr), AtomicBool, RandomState>,
pub route_table: RouteTableTrie,
}
impl RouteTable2 {
pub fn new() -> Self {
Self {
cache_table: DashMap::with_hasher(RandomState::new()),
route_table: RouteTableTrie::new(),
}
}
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 routes.keys() {
self.cache_table.insert(*route, AtomicBool::new(false));
self.route_table.insert(route.0.addr().into(), route.0.prefix_len(), route.1);
}
Ok(())
}
pub fn apply_system(&self) {
for route in &self.cache_table {
let (net, gw) = route.key();
if let Err(e) = del_route(net, gw) {
error!("failed to del route: {}", e.as_str());
}
// should add to system
if let Err(e) = add_route(net, gw) {
error!("failed to add route: {}", e.as_str());
}
}
}
}
/// 路由文件,应该是以下格式,一行一条
/// 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)
}

898
src/network/tun_linux.rs Normal file → Executable file
View File

@ -1,18 +1,51 @@
use sdlan_sn_rs::utils::{ip_to_string, net_bit_len_to_mask, SDLanError};
#[cfg(not(feature = "tun"))]
use bytes::BytesMut;
#[cfg(feature = "tun")]
use bytes::{Bytes, BytesMut};
use etherparse::{Ethernet2Header};
use ipnet::Ipv4Net;
use sdlan_sn_rs::config::SDLAN_DEFAULT_TTL;
use sdlan_sn_rs::utils::{
ip_to_string, is_ipv6_multicast, net_bit_len_to_mask,
SDLanError,
};
use std::ffi::CStr;
use std::ffi::{c_char, c_int};
use std::fs::OpenOptions;
use std::fs::{self, OpenOptions};
use std::net::Ipv4Addr;
use std::os::unix::fs::{MetadataExt, PermissionsExt};
use std::path::Path;
use std::ptr::null_mut;
use std::sync::atomic::Ordering;
use sdlan_sn_rs::utils::Result;
use std::io::{Read, Write};
use std::io::{BufRead, BufReader, Read, Write};
use std::os::fd::AsRawFd;
use std::process::Command;
use tracing::{debug, error};
use tracing::{debug, error, info};
#[cfg(feature = "tun")]
use crate::caculate_crc;
use crate::get_edge;
#[cfg(not(feature = "tun"))]
use crate::network::{ARP_REPLY, ArpHdr, EthHdr};
use crate::network::{Node, send_packet_to_net};
#[cfg(not(feature = "tun"))]
use crate::pb::SdlArpResponse;
#[cfg(feature = "tun")]
use crate::pb::SdlArpResponse;
use crate::pb::{encode_to_udp_message, SdlData};
use crate::tcp::PacketType;
use super::device::{DeviceConfig, Mode};
use super::TunTapPacketHandler;
const RESOLV_FILE: &'static str = "/etc/resolv.conf";
const RESOLV_FILE_BACKUP: &'static str = "/etc/resolv.conf.punchnet.bak";
use crate::network::DNS_IP;
// #[link(name = "tuntap", kind="static")]
#[link(name = "tuntap")]
extern "C" {
fn tuntap_setup(fd: c_int, name: *mut u8, mode: c_int, packet_info: c_int) -> c_int;
@ -23,6 +56,7 @@ pub struct Iface {
fd: std::fs::File,
mode: Mode,
name: String,
has_resolvectl: bool,
}
pub fn new_iface(tunname: &str, mode: Mode) -> Iface {
@ -57,7 +91,7 @@ impl Iface {
let mut success = false;
let mut _name = Vec::new();
for i in 0..16 {
_name = Vec::new();
_name.clear();
_name.extend_from_slice(ifname.as_bytes());
_name.extend_from_slice(i.to_string().as_bytes());
_name.extend_from_slice(&[0; 33]);
@ -83,27 +117,46 @@ impl Iface {
.to_string_lossy()
.into_owned()
};
Ok(Iface { fd: fs, mode, name })
let has_resolvectl = check_has_resolvectl();
Ok(Iface { fd: fs, mode, name, has_resolvectl })
} else {
Err(SDLanError::NormalError("failed to setup tun"))
}
}
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 mut default_gw = (ip & mask) + 1;
if default_gw == ip {
default_gw += 1;
}
let ip = ip_to_string(&ip);
let netbit = ip_to_string(&net_bit_len_to_mask(netbit));
if cfg!(not(feature = "tun")) {
info!("set tap device");
let mac = device_config.get_mac();
let res = Command::new("ifconfig")
.arg(&self.name)
.arg(ip)
.arg("netmask")
.arg(&netbit)
.arg("hw")
.arg("ether")
.arg(format!(
"{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
))
.arg("mtu")
.arg(format!("{}", device_config.mtu))
.arg("up")
.output();
match res {
@ -114,6 +167,43 @@ impl Iface {
error!("failed to run ifconfig: {}", e.to_string());
}
}
node.route_table.apply_system();
if node.config.allow_routing {
set_allow_routing();
}
// TODO: set dns should be opened
/*
if let Err(e) = set_dns(self, &self.name, network_domain, &ip_to_string(&default_gw)) {
error!("failed to set dns: {}", e.as_str());
}
*/
} else {
info!("set tun device");
let res = Command::new("ifconfig")
.arg(&self.name)
.arg(ip)
.arg("netmask")
.arg(&netbit)
.arg("mtu")
.arg(format!("{}", device_config.mtu))
.arg("up")
.output();
match res {
Ok(_) => {
debug!("ifconfig ok");
}
Err(e) => {
error!("failed to run ifconfig: {}", e.to_string());
}
}
if let Err(e) = set_dns(self, &self.name, network_domain, &ip_to_string(&default_gw)) {
error!("failed to set dns: {}", e.as_str());
}
}
}
pub fn recv(&self, buf: &mut [u8]) -> std::io::Result<usize> {
@ -125,6 +215,800 @@ impl Iface {
}
}
#[cfg(not(feature = "tun"))]
impl TunTapPacketHandler for Iface {
async fn handle_packet_from_net(&self, data: &[u8]) -> std::io::Result<()> {
// debug!("in tap mode, got data: {:?}", data);
match self.send(data) {
Err(e) => {
error!("failed to write to tap: {}", e.to_string());
return Err(e);
}
Ok(_) => return Ok(()),
}
}
async fn handle_packet_from_device(
&self,
data: BytesMut,
// encrypt_key: &[u8],
) -> std::io::Result<()> {
use etherparse::PacketHeaders;
debug!("in tap mode2");
let edge = get_edge();
let Ok(headers) = PacketHeaders::from_ethernet_slice(&data) else {
error!("failed to parse packet");
return Ok(());
};
if let Some(eth) = headers.link {
use etherparse::EtherType;
if let Some(hdr) = eth.ethernet2() {
use bytes::Bytes;
if hdr.ether_type == EtherType::ARP {
use crate::network::{ARP_REPLY, ARP_REQUEST, ArpHdr};
let arp = ArpHdr::from_slice(&data);
match arp.opcode {
ARP_REQUEST => {
let dest_ip = ((arp.dipaddr[0] as u32) << 16) + arp.dipaddr[1] as u32;
if edge.device_config.contains(&Ipv4Addr::from_bits(dest_ip)) {
let _ = edge.send_arp_request(dest_ip, dest_ip).await;
} else {
if let Some((_, real_ip)) = edge.route_table.route_table.lookup(dest_ip) {
let real_ip = u32::from_be_bytes(real_ip.octets());
let _ = edge.send_arp_request(dest_ip, real_ip).await;
}
}
/*
let request = SdlArpRequest {
pkt_id: edge.get_next_packet_id(),
target_ip: dest_ip,
};
let req = encode_to_tcp_message(Some(request), PacketType::ArpRequest as u8).unwrap();
let conn = get_quic_write_conn();
debug!("sending arp request");
let _ = conn.send(req).await;
*/
return Ok(());
}
_other => {
// just do the following logic
}
}
}
if let Some(ip) = headers.net {
match ip {
etherparse::NetHeaders::Ipv4(ipv4, _) => {
use etherparse::ip_number::{self, ICMP};
use crate::FiveTuple;
use etherparse::IpNumber;
if let Some(transport) = headers.transport {
match ipv4.protocol {
IpNumber::TCP => {
let tcp = transport.tcp().unwrap();
let out_five_tuple = FiveTuple {
src_ip: ipv4.source.into(),
dst_ip: ipv4.destination.into(),
src_port: tcp.source_port,
dst_port: tcp.destination_port,
proto: IpNumber::TCP.0,
};
edge.rule_cache.touch_packet(out_five_tuple);
// is tcp
}
IpNumber::UDP => {
let udp = transport.udp().unwrap();
let out_five_tuple = FiveTuple {
src_ip: ipv4.source.into(),
dst_ip: ipv4.destination.into(),
src_port: udp.source_port,
dst_port: udp.destination_port,
proto: IpNumber::UDP.0,
};
edge.rule_cache.touch_packet(out_five_tuple);
}
_other => {
}
}
}
if u32::from_be_bytes(ipv4.destination) == DNS_IP {
// should send to dns
if let Err(e) = edge.udp_sock_for_dns.send_to(&data[14..], format!("{}:15353", edge.server_ip)).await {
error!("failed to send request to 15353: {}", e);
}
// edge.udp_sock_for_dns.send_to()
return Ok(())
}
}
_other => {
// just ignore
}
}
}
let target = hdr.destination;
if is_ipv6_multicast(&target) {
return Ok(());
}
let size = data.len();
let Ok(encrypted) = edge.encryptor.load().encrypt(&data) else {
// let Ok(encrypted) = edge.encryptor.read().unwrap().encrypt(&data) else {
// let Ok(encrypted) = aes_encrypt(encrypt_key, &data) else {
error!("failed to encrypt packet request");
return Ok(());
};
let data_bytes = Bytes::from(encrypted);
let data = SdlData {
is_p2p: true,
network_id: edge.network_id.load(Ordering::Relaxed),
ttl: SDLAN_DEFAULT_TTL as u32,
src_mac: Vec::from(edge.device_config.get_mac()),
dst_mac: Vec::from(target),
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, target, &msg, size as u64).await;
} else {
error!("erro 2");
}
} else {
error!("erro 1");
}
Ok(())
}
}
#[cfg(feature = "tun")]
impl TunTapPacketHandler for Iface {
async fn handle_packet_from_net(&self, data: &[u8]) -> std::io::Result<()> {
debug!("in tun mode");
// got layer 2 frame
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 data = arp.marshal_to_bytes();
// let Ok(encrypted) = aes_encrypt(key, &data) else {
let Ok(encrypted) = edge.encryptor.load().encrypt(&data) else {
error!("failed to encrypt arp reply");
return Ok(());
};
let data_bytes = Bytes::from(encrypted);
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) {
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<()> {
use etherparse::IpHeaders;
let eee = get_edge();
let src_mac = eee.device_config.get_mac();
let data = header.split_off(14);
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) && (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(&etherheader.to_bytes()[..]);
let crc = caculate_crc(&data);
header.unsplit(data);
// packet.extend_from_slice(&etherheader.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.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(())
}
}
pub fn get_install_channel() -> String {
"linux".to_owned()
}
fn check_has_resolvectl() -> bool {
let res = Command::new("resolvectl")
.arg("status")
.output();
if let Ok(_) = res {
true
} else {
false
}
}
fn add_dns_route(gw: &str) -> Result<()>{
Command::new("route")
.arg("add")
.arg("-host")
.arg("100.100.100.100")
.arg("gw")
.arg(gw)
.output()?;
Ok(())
}
fn add_resolvectl(
name: &str,
network_domain: &str,
) -> Result<()>{
Command::new("resolvectl")
.arg("dns")
.arg(name)
.arg("100.100.100.100")
.output()?;
Command::new("resolvectl")
.arg("domain")
.arg(name)
.arg(format!("~{}", network_domain))
.output()?;
Ok(())
}
fn set_dns(
iface: &Iface,
name: &str,
network_domain: &str,
gw: &str
) -> Result<()> {
if iface.has_resolvectl {
add_resolvectl(name, network_domain)?;
} else {
backup_resolv_conf()?;
modify_resolv_conf(&vec!["100.100.100.100".to_owned()], network_domain, true)?;
}
add_dns_route(gw)?;
Ok(())
}
pub fn restore_dns() -> Result<()> {
let eee = get_edge();
if !eee.device.has_resolvectl {
// should restore /etc/resolv.conf
restore_resolv_conf()?;
}
Ok(())
}
/// 修改 /etc/resolv.conf 中的 nameserver 条目
///
/// - `new_nameservers`: 新的 nameserver 列表IPv4/IPv6 字符串)
/// - `keep_other_ns`: 是否保留原有的 nameservertrue = 追加到新列表后false = 完全替换)
pub fn modify_resolv_conf(new_nameservers: &[String], search_domain: &str, keep_other_ns: bool) -> Result<()> {
let path = Path::new(RESOLV_FILE);
if !path.exists() {
return Err(SDLanError::IOError(format!("{} does not exists", RESOLV_FILE)));
}
// 读取原文件权限和元数据
let metadata = fs::metadata(path)?;
let perms = fs::Permissions::from_mode(metadata.mode());
let uid = metadata.uid();
let gid = metadata.gid();
// 读取所有行
let file = fs::File::open(path)?;
let reader = BufReader::new(file);
let mut lines = Vec::new();
let mut inserted = false;
let mut encounted_nameserver = false;
let mut search_added = false;
for line in reader.lines() {
let line = line?;
let trimmed = line.trim();
if trimmed.starts_with("nameserver ") {
// 提取 IP
encounted_nameserver = true;
if keep_other_ns {
lines.push(line);
}
// 如果不保留原有 nameserver就不加入 lines
} else {
if encounted_nameserver {
if !inserted {
inserted = true;
for new in new_nameservers {
lines.push(format!("nameserver {}", new.clone()));
}
}
}
// 保留非 nameserver 行注释、search、options 等)
if trimmed.starts_with("search ") {
lines.push(format!("{} {}", trimmed, search_domain));
search_added = true;
} else {
lines.push(line);
}
}
}
if !search_added {
lines.push(format!("search {}", search_domain));
}
// 原子写入:先写临时文件
let tmp_dir = Path::new("/etc");
let tmp_path = tmp_dir.join("resolv.conf.tmp");
let mut tmp_file = fs::File::create(&tmp_path)?;
for l in &lines {
writeln!(tmp_file, "{}", l)?;
}
tmp_file.flush()?;
// 设置权限
fs::set_permissions(&tmp_path, perms)?;
#[cfg(unix)]
{
use std::os::unix::fs::chown;
chown(&tmp_path, Some(uid), Some(gid))?;
}
// 原子替换
fs::rename(&tmp_path, path)?;
Ok(())
}
/// 备份 /etc/resolv.conf 到指定路径
fn backup_resolv_conf() -> Result<()> {
let src = Path::new(RESOLV_FILE);
let dst = Path::new(RESOLV_FILE_BACKUP);
if !src.exists() {
return Err(SDLanError::IOError(format!("{} does not exists", RESOLV_FILE)));
// anyhow::bail!("Source /etc/resolv.conf does not exist");
}
// 如果目标已存在,可选择覆盖或跳过(这里覆盖)
if dst.exists() {
fs::remove_file(dst)?;
}
fs::copy(src, dst)?;
// 保留原权限
let metadata = fs::metadata(src)?;
let permissions = fs::Permissions::from_mode(metadata.mode());
fs::set_permissions(dst, permissions)?;
// 保留所有者(需要 root 权限,否则会失败)
#[cfg(unix)]
{
use std::os::unix::fs::chown;
let uid = metadata.uid();
let gid = metadata.gid();
chown(dst, Some(uid), Some(gid))?;
}
Ok(())
}
/// 从备份路径恢复 /etc/resolv.conf
fn restore_resolv_conf() -> Result<()> {
let src = Path::new(RESOLV_FILE_BACKUP);
let dst = Path::new(RESOLV_FILE);
if !src.exists() {
return Err(SDLanError::IOError(format!("{} does not exists", RESOLV_FILE_BACKUP)));
}
// 如果目标是符号链接,先删除链接再复制(避免写入到链接指向位置)
if dst.is_symlink() {
fs::remove_file(dst)?;
} else if dst.exists() {
fs::remove_file(dst)?;
}
fs::copy(src, dst)?;
// 保留备份文件的权限
let metadata = fs::metadata(src)?;
let permissions = fs::Permissions::from_mode(metadata.mode());
fs::set_permissions(dst, permissions)?;
#[cfg(unix)]
{
use std::os::unix::fs::chown;
let uid = metadata.uid();
let gid = metadata.gid();
chown(dst, Some(uid), Some(gid))?;
}
Ok(())
}
pub fn del_route(net: &Ipv4Net, gw: &Ipv4Addr) -> Result<()> {
let res = Command::new("route")
.arg("del")
.arg("-net")
.arg(net.to_string())
.arg("gw")
.arg(gw.to_string())
.output()?;
Ok(())
}
pub fn add_route(net: &Ipv4Net, gw: &Ipv4Addr) -> Result<()> {
let res = Command::new("route")
.arg("add")
.arg("-net")
.arg(net.to_string())
.arg("gw")
.arg(gw.to_string())
.output()?;
Ok(())
}
fn set_allow_routing() {
let _ = Command::new("sysctl")
.arg("-w")
.arg("net.ipv4.ip_forward=1")
.output();
let _ = Command::new("iptables")
.arg("-t")
.arg("nat")
.arg("-D")
.arg("POSTROUTING")
.arg("-j")
.arg("MASQUERADE")
.output();
let _ = Command::new("iptables")
.arg("-t")
.arg("nat")
.arg("-A")
.arg("POSTROUTING")
.arg("-j")
.arg("MASQUERADE")
.output();
}
#[cfg(feature = "tun")]
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.target_ip;
let mac = data.target_mac.try_into().unwrap();
edge.arp_table.arp_arrived(ip, mac).await;
}
#[cfg(not(feature = "tun"))]
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;
}
// TODO: construct the arp reply, and write to tun;
let src_mac = data.target_mac.try_into().unwrap();
let dst_mac = edge.device_config.get_mac();
let dst_ip = edge.device_config.get_ip();
let src_ip = data.origin_ip;
let hdr = ArpHdr{
ethhdr: EthHdr {
dest: dst_mac,
src: src_mac,
eth_type: 0x0806,
},
hwtype: 0x0001,
protocol: 0x0800,
hwlen: 6,
protolen: 4,
opcode: ARP_REPLY,
shwaddr: src_mac,
sipaddr: [((src_ip >> 16) as u16) & 0xffff, (src_ip as u16) & 0xffff],
dhwaddr: dst_mac,
dipaddr: [((dst_ip >> 16) & 0x0000ffff) as u16, (dst_ip & 0x0000ffff) as u16]
};
let data = hdr.marshal_to_bytes();
if let Err(_e) = edge.device.send(&data) {
error!("failed to write arp response to device");
}
}

424
src/network/tun_win.rs Normal file → Executable file
View File

@ -1,14 +1,33 @@
use sdlan_sn_rs::utils::{ip_to_string, net_bit_len_to_mask};
use bytes::Bytes;
use etherparse::ether_type::ARP;
use etherparse::{Ethernet2Header, IpHeaders};
use ipnet::Ipv4Net;
use sdlan_sn_rs::config::SDLAN_DEFAULT_TTL;
use sdlan_sn_rs::utils::{
BROADCAST_MAC, Result, aes_encrypt, ip_to_string, is_multi_broadcast, net_bit_len_to_mask
};
use std::io::{Error, ErrorKind};
use std::net::Ipv4Addr;
use std::os::windows::process::CommandExt;
use std::process::Command;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use tracing::{debug, error, info};
use wintun;
use tracing::{error, debug};
use super::device::{Mode, DeviceConfig};
use crate::get_edge;
use crate::network::{
ARP_REPLY, ARP_REQUEST, ArpHdr, ArpRequestInfo, ArpResponse, DNS_IP, Node, add_to_arp_wait_list, arp_arrived, form_ethernet_packet, generate_arp_request, send_arp_request, send_packet_to_net
};
use crate::pb::{encode_to_udp_message, SdlData};
use crate::tcp::PacketType;
use crate::utils::mac_to_string;
use super::device::{DeviceConfig, Mode};
use super::TunTapPacketHandler;
pub struct Iface {
if_idx: u32,
name: String,
_adapter: Arc<wintun::Adapter>,
session: Arc<wintun::Session>,
@ -41,14 +60,21 @@ impl Iface {
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 mut default_gw = (ip & mask) + 1;
if default_gw == ip {
default_gw += 1;
}
let ip = ip_to_string(&ip);
let netbit = ip_to_string(&net_bit_len_to_mask(netbit));
let mut cmd = Command::new("netsh");
@ -74,19 +100,342 @@ impl Iface {
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.output();
match res {
Ok(r) => {
debug!("netsh2 ok: [{:?}]", String::from_utf8_lossy(&r.stdout));
}
Err(e) => {
error!("failed to run netsh2: {}", e.to_string());
}
}
let gw = ip_to_string(&default_gw);
debug!("gw = {}", gw);
if let Err(e) = set_dns(&self.name, network_domain, &gw, self.if_idx) {
error!("failed to set dns: {:?}", e);
} else {
error!("set dns ok");
}
}
}
fn create_wintun(path: &str,name: &str) -> Iface {
impl TunTapPacketHandler for Iface {
async fn handle_packet_from_net(&self, data: &[u8]) -> std::io::Result<()> {
// got layer 2 frame
match Ethernet2Header::from_slice(&data) {
Ok((hdr, rest)) => {
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 = CRC_HASH.checksum(&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 && hdr.destination != BROADCAST_MAC {
error!(
"packet to [{:?}] not direct to us",
mac_to_string(&hdr.destination)
);
return Ok(());
}
if hdr.ether_type == ARP {
let mut arp = ArpHdr::from_slice(&data);
let self_ip = edge.device_config.get_ip();
debug!("self_ip: {:?}", self_ip.to_be_bytes());
let from_ip = ((arp.sipaddr[0] as u32) << 16) + arp.sipaddr[1] as u32;
debug!("from_ip: {:?}", from_ip.to_be_bytes());
let dest_ip = ((arp.dipaddr[0] as u32) << 16) + arp.dipaddr[1] as u32;
debug!("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 {
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 data = arp.marshal_to_bytes();
// let Ok(encrypted) = aes_encrypt(key, &data) else {
let Ok(encrypted) = edge.encryptor.load().encrypt(&data) else {
error!("failed to encrypt arp reply");
return Ok(());
};
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: Bytes::from(encrypted),
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 {
send_arp_request(ArpRequestInfo::Set {
ip: from_ip,
mac: arp.shwaddr,
})
.await;
arp_arrived(from_ip, arp.shwaddr).await;
}
}
_other => {
error!("unknown arp type info");
}
}
} else {
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) {
send_arp_request(ArpRequestInfo::Set { ip, mac }).await;
}
}
Err(_) => {
error!("failed to parse ip header, dropping");
return Ok(());
}
}
match edge.device.send(rest) {
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 tap packet: {}", e);
return Ok(());
}
}
Ok(())
}
async fn handle_packet_from_device(
&self,
data: Vec<u8>,
// encrypt_key: &[u8],
) -> std::io::Result<()> {
let eee = get_edge();
let src_mac = eee.device_config.get_mac();
match IpHeaders::from_slice(&data) {
Ok((iphdr, _payload)) => {
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) && (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 {
// 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 send_arp_request(ArpRequestInfo::Lookup { ip: dstip }).await {
ArpResponse::LookupResp {
mac,
ip,
do_arp_request,
} => {
if do_arp_request {
add_to_arp_wait_list(dstip, data);
info!(
"find ip: {:?} => {:?}",
src.to_be_bytes(),
dstip.to_be_bytes()
);
let arp_msg =
generate_arp_request(src_mac, ip, eee.device_config.get_ip());
let Ok(encrypted) = eee.encryptor.load().encrypt(&arp_msg) else {
// let Ok(encrypted) = aes_encrypt(&encrypt_key, &arp_msg) else {
error!("failed to encrypt arp request");
return Ok(());
};
// println!("arp_msg: {:?}", arp_msg);
let data = SdlData {
network_id: eee.network_id.load(Ordering::Relaxed),
src_mac: Vec::from(src_mac),
dst_mac: Vec::from([0xff; 6]),
is_p2p: true,
ttl: SDLAN_DEFAULT_TTL as u32,
data: Bytes::from(encrypted),
session_token: eee.session_token.get(),
identity_id: eee.identity_id.load(),
};
let data =
encode_to_udp_message(Some(data), PacketType::Data as u8).unwrap();
debug!("sending arp");
// let data = marshal_message(&data);
send_packet_to_net(eee, BROADCAST_MAC, &data, arp_msg.len() as u64)
.await;
// edge.sock.send(data).await;
// println!("should send arp");
return Ok(());
}
let packet = form_ethernet_packet(src_mac, mac, &data);
// prepend the ether header
/*
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);
packet.extend_from_slice(&etherheader.to_bytes()[..]);
packet.extend_from_slice(&data);
*/
// let crc = CRC_HASH.checksum(&packet);
// 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(&packet) 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;
// let dstip = u32::from_be_bytes(ipv4hdr.0.destination);
}
_ => {}
}
}
Err(e) => {
error!("failed to parse ip packet: {}", e.to_string());
}
}
Ok(())
}
}
fn create_wintun(path: &str, name: &str) -> Iface {
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"),
.expect("failed to create tun adapter"),
};
let idx = adapter.get_adapter_index().expect("failed to get adapter index");
// println!("idx = {}", idx);
let session = Arc::new(adapter.start_session(wintun::MAX_RING_CAPACITY).unwrap());
Iface { _adapter: adapter, session, name: name.to_owned()}
Iface {
if_idx: idx,
_adapter: adapter,
session,
name: name.to_owned(),
}
}
pub fn new_iface(name: &str, _mode: Mode) -> Iface {
@ -97,3 +446,64 @@ pub fn new_iface(name: &str, _mode: Mode) -> Iface {
pub fn get_install_channel() -> String {
"windows".to_owned()
}
pub fn set_dns(name: &str, _network_domain: &str, gw: &str, ifidx: u32) -> std::io::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)
.output()?;
//println!("res1: {}", res.status.success());
println!("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)
.output()?;
// println!("res2: {}", res.status.success());
println!("netsh set ok");
Ok(())
}
pub fn restore_dns() {
}
pub fn del_route(net: &Ipv4Net, gw: &Ipv4Addr) -> Result<()> {
let mask = net.netmask().to_string();
let network = net.network().to_string();
let res = Command::new("route")
.arg("delete")
.arg(network)
.arg(mask)
.arg(gw.to_string())
.output()?;
Ok(())
}
pub fn add_route(net: &Ipv4Net, gw: &Ipv4Addr) -> Result<()> {
let mask = net.netmask().to_string();
let network = net.network().to_string();
let res = Command::new("route")
.arg("add")
.arg(network)
.arg(mask)
.arg(gw.to_string())
.output()?;
Ok(())
}

10
src/network/tuntap.c Normal file → Executable file
View 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>
/**

131
src/network/tuntap.rs Executable file
View File

@ -0,0 +1,131 @@
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},
};
use tracing::debug;
use tracing::error;
use crate::{
network::{form_ethernet_packet, send_packet_to_net},
pb::{encode_to_udp_message, SdlData},
tcp::PacketType,
utils::mac_to_string,
};
use super::get_edge;
pub const MAX_WAIT_PACKETS: usize = 100;
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<()>;
}
/*
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 packet = form_ethernet_packet(src_mac, mac, item.origin_data);
let pkt_size = packet.len();
let Ok(encrypted) = edge.encryptor.load().encrypt(&packet) else {
// let Ok(encrypted) = edge.encryptor.read().unwrap().encrypt(&packet) else {
// let Ok(encrypted) = aes_encrypt(&encrypt_key, &packet) else {
error!("failed to encrypt packet request");
return;
};
let data_bytes = Bytes::from(encrypted);
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;
}
*/

View File

@ -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,50 +81,94 @@ 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>> 这个格式序列号所有的规则信息; 下发的数据默认都是allowdeny规则的服务器端已经屏蔽
#[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")]
#[prost(bytes = "vec", tag = "1")]
pub mac: ::prost::alloc::vec::Vec<u8>,
#[prost(uint32, tag = "2")]
pub ip: u32,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SdlSendRegisterEvent {
#[prost(uint32, tag = "1")]
pub dst_ip: u32,
#[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")]
#[prost(uint32, tag = "4")]
pub nat_type: u32,
#[prost(message, optional, tag = "5")]
pub v6_info: ::core::option::Option<Sdlv6Info>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
@ -124,95 +177,49 @@ pub struct SdlNetworkShutdownEvent {
#[prost(string, tag = "1")]
pub message: ::prost::alloc::string::String,
}
#[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>,
}
#[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,
}
/// 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 +228,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
View 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)

78
src/quic/mod.rs Normal file
View File

@ -0,0 +1,78 @@
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use std::sync::Arc;
use quinn::Endpoint;
use quinn::crypto::rustls::QuicClientConfig;
use rustls::crypto::CryptoProvider;
use rustls::crypto::ring;
use rustls::pki_types::CertificateDer;
use rustls::pki_types::PrivateKeyDer;
use rustls::pki_types::ServerName;
use rustls_pemfile::{certs, private_key};
pub fn quic_init() -> 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 rustls_config = rustls::ClientConfig::builder()
.dangerous()
.with_custom_certificate_verifier(Arc::new(SkipServerVerification{}))
.with_no_client_auth();
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 mut endpoint = Endpoint::client("0.0.0.0:0".parse().unwrap()).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);
let key = private_key(&mut reader).unwrap();
key
}

74
src/tcp/identity_cache.rs Normal file
View File

@ -0,0 +1,74 @@
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 init_identity_cache() {
RULE_CACHE.set(DashMap::new()).unwrap();
}
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
View 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::*;

719
src/tcp/quic.rs Normal file
View File

@ -0,0 +1,719 @@
use std::{net::IpAddr, sync::{Arc, OnceLock, atomic::{AtomicBool, AtomicU64, Ordering}}, time::Duration};
use futures_util::pin_mut;
use prost::Message;
use quinn::SendStream;
use sdlan_sn_rs::{config::AF_INET, peer::{SdlanSock, V6Info}, utils::{Result, SDLanError, get_current_timestamp, ip_to_string, rsa_decrypt}};
use tokio::{sync::mpsc::{Receiver, Sender, channel}, time::sleep};
use tokio_util::sync::CancellationToken;
use tracing::{debug, error, warn};
use crate::{AesEncryptor, Chacha20Encryptor, ConnectionInfo, ConnectionState, MyEncryptor, RuleFromServer, config::{NULL_MAC, TCP_PING_TIME}, get_edge, network::{ARP_REPLY, ArpHdr, EthHdr, Node, RegisterSuperFeedback, StartStopInfo, arp_reply_arrived, check_peer_registration_needed, handle_packet_peer_info}, pb::{SdlArpResponse, SdlPolicyResponse, SdlRegisterSuper, SdlRegisterSuperAck, SdlRegisterSuperNak, SdlSendRegisterEvent, encode_to_tcp_message}, tcp::{EventType, NakMsgCode, NatType, PacketType, SdlanTcp, read_a_packet, send_stun_request}};
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,
// 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,
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!("got 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: {:?}", 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);
// 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.read().unwrap().clone());
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[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 _ = 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: &'static 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,
});
}
}
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,
// 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,
// 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: 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,
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,
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
println!("start or stop is None");
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 received: {}", started);
continue;
}
if let Some(ref connecting_chan) = self.connecting_chan {
let state = ConnectionInfo::ConnState(ConnectionState::Connecting);
let _ = connecting_chan.send(state).await;
}
let host = self.remote.split(":").next().unwrap();
debug!("try connecting to {}, host = {}", self.remote, host);
let conn = match edge.quic_endpoint.connect(self.remote.parse().unwrap(), host) {
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 {
println!("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 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);
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 {
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_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());
}
}

70
src/tcp/tcp_codec.rs Normal file → Executable file
View 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 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);
}
}
pub async fn read_a_packet(
reader: &mut BufReader<OwnedReadHalf>,
reader: &mut RecvStream,
) -> Result<SdlanTcp, std::io::Error> {
debug!("read a packet");
let size = reader.read_u16().await?;
let payload_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");
if size < 5 {
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,
};

334
src/tcp/tcp_conn.rs Normal file → Executable file
View 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,217 +23,77 @@ 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 {
connected: Arc<AtomicBool>,
@ -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() {

163
src/utils/acl_session.rs Normal file
View File

@ -0,0 +1,163 @@
use std::{net::IpAddr, sync::{Arc, atomic::{AtomicU64, Ordering}}, 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, identity: IdentityID, info: FiveTuple) -> (bool, ShouldRenew) {
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)
}
}

140
src/utils/command.rs Normal file → Executable file
View File

@ -1,9 +1,113 @@
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,
RouteAdd(RouteCmdInfo),
RouteDel(RouteCmdInfo),
RouteList,
/// exits the
Stop,
}
#[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,
}
#[derive(Args, Debug)]
pub struct RouteCmdInfo {
#[arg(short, long, default_value="")]
pub route: String,
}
#[derive(Args, Debug)]
pub struct UserLogin {
#[arg(short, long, env = APP_USER_ENV_NAME)]
pub username: String,
#[arg(short, long, env = APP_PASS_ENV_NAME, required=false)]
pub password: String,
}
#[derive(Args, Debug)]
pub struct AutoRunTokenLogin {
#[arg(long, env=APP_TOKEN_ENV_NAME, required=false)]
pub token: String,
#[arg(short, long, default_value="")]
pub route: String,
}
#[derive(Args, Debug)]
pub struct AutoRunArgument {
#[arg(long, env=APP_TOKEN_ENV_NAME, required=false)]
pub token: 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,
}
#[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 +116,7 @@ pub struct CommandLine {
pub sn: String,
#[structopt(short = "t", long = "tcp", default_value = "127.0.0.1:7656")]
pub tcp: String,
pub quic: String,
/// in the format of "localhost:1234"
#[structopt(long = "nat1")]
@ -23,7 +127,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 +148,44 @@ 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,
}
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(),
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(),
}
}
}

179
src/utils/encrypter.rs Normal file
View File

@ -0,0 +1,179 @@
use std::{sync::atomic::{AtomicU32, Ordering}, time::{SystemTime, UNIX_EPOCH}};
use chacha20poly1305::{KeyInit, aead::Aead};
use sdlan_sn_rs::utils::{Result, SDLanError, aes_decrypt, aes_encrypt};
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: &[u8]) -> Result<Vec<u8>>;
fn decrypt(&self, ciphered: &[u8]) -> Result<Vec<u8>>;
}
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: &[u8]) -> Result<Vec<u8>> {
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, ciphered: &[u8]) -> Result<Vec<u8>> {
match self {
Self::Invalid => {
Err(SDLanError::EncryptError("invalid encryptor".to_owned()))
}
Self::Aes(aes) => {
aes.decrypt(ciphered)
}
Self::ChaChao20(cha) => {
cha.decrypt(ciphered)
}
}
}
}
pub struct Chacha20Encryptor {
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 {
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.key = key;
self.region_id = region_id;
}
fn encrypt(&self, data: &[u8]) -> Result<Vec<u8>> {
let cipher = chacha20poly1305::ChaCha20Poly1305::new(self.key.as_slice().into());
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 = Vec::new();
let region_id = self.region_id.to_be_bytes();
nonce.extend_from_slice(&region_id);
let next_data = (now<<24) | next_counter;
nonce.extend_from_slice(&next_data.to_be_bytes());
match cipher.encrypt(nonce.as_slice().into(), data) {
Ok(data) => {
nonce.extend_from_slice(&data);
Ok(nonce)
},
Err(e) => {
Err(SDLanError::EncryptError(e.to_string()))
}
}
}
fn decrypt(&self, ciphered: &[u8]) -> Result<Vec<u8>> {
if ciphered.len() < 12 {
return Err(SDLanError::EncryptError("ciphered text size error".to_owned()))
}
let cipher = chacha20poly1305::ChaCha20Poly1305::new(self.key.as_slice().into());
let nonce = &ciphered[0..12];
match cipher.decrypt(nonce.into(), &ciphered[12..]) {
Ok(data) => Ok(data),
Err(e) => {
Err(SDLanError::EncryptError(format!("failed to decyrpt: {}", e.to_string())))
}
}
}
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, ciphered: &[u8]) -> Result<Vec<u8>> {
aes_decrypt(&self.key, ciphered)
}
fn encrypt(&self, data: &[u8]) -> Result<Vec<u8>> {
aes_encrypt(&self.key, data)
}
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;
}
}

151
src/utils/mod.rs Normal file → Executable file
View File

@ -1,8 +1,157 @@
mod command;
mod acl_session;
mod encrypter;
mod system_action;
use std::{fs::OpenOptions, io::Write, net::Ipv4Addr, path::Path};
pub use encrypter::*;
pub use command::*;
pub use acl_session::*;
pub use system_action::*;
mod socks;
use rand::Rng;
use sdlan_sn_rs::utils::{Mac, Result, SDLanError};
use serde::{Deserialize, Serialize};
pub use socks::*;
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 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 data = serde_json::from_slice(&content).unwrap();
return Some(data);
}
None
}
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
}
#[cfg(windows)]
pub mod mod_hostname {
use std::{ffi::OsString, os::windows::ffi::OsStringExt};
use winapi::um::winbase::GetComputerNameW;
pub fn get_hostname() -> Option<String> {
unsafe {
let mut buffer: Vec<u16> = vec![0; 64];
let mut buffer_size = 64;
if GetComputerNameW(buffer.as_mut_ptr(), &mut buffer_size) != 0 {
let hostname = OsString::from_wide(&buffer[..buffer_size as usize]);
return hostname.into_string().ok();
}
None
}
}
}
#[cfg(unix)]
pub mod mod_hostname {
use libc::{size_t, c_int, c_char};
extern "C" {
fn gethostname(name: *mut c_char, size: size_t) -> c_int;
}
pub fn get_hostname() -> Option<String> {
let mut buffer = vec![0u8; 255];
unsafe {
if gethostname(buffer.as_mut_ptr() as *mut c_char, 255) == 0 {
let len = buffer.iter().position(|&b| b == 0).unwrap_or(255);
buffer.truncate(len);
return String::from_utf8(buffer).ok();
}
None
}
}
}
#[cfg(windows)]
pub mod hostname {
}

9
src/utils/pid_recorder.rs Normal file → Executable file
View 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);
}
}
}

8
src/utils/socks.rs Normal file → Executable file
View File

@ -11,11 +11,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,9 +58,7 @@ 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> {
@ -80,7 +78,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());
}
}

View File

@ -0,0 +1,89 @@
use std::{net::Ipv4Addr, sync::Arc};
use arc_swap::ArcSwap;
#[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;
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 struct RouteTableTrie {
trie: ArcSwap<IpTrie>,
}
impl RouteTableTrie {
pub fn new() -> Self {
Self {
trie: ArcSwap::new(Arc::new(IpTrie::default()))
}
}
pub fn lookup(&self, ip: u32) -> Option<(u8, Ipv4Addr)> {
let trie = self.trie.load();
trie.lookup(ip)
}
pub 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
View 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

Binary file not shown.