fix handle_tun_packet unwrap bug

This commit is contained in:
alex 2026-04-10 20:37:44 +08:00
parent 2774431d49
commit 03b8da17dc
7 changed files with 146 additions and 49 deletions

View File

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

View File

@ -2,17 +2,30 @@ use std::{net::SocketAddr, sync::atomic::Ordering, time::Duration};
use num_enum::TryFromPrimitive; use num_enum::TryFromPrimitive;
use punchnet::get_edge; use punchnet::get_edge;
use sdlan_sn_rs::utils::Mac; use sdlan_sn_rs::utils::{Mac, Result, SDLanError};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::{net::UdpSocket, time::sleep}; use tokio::{net::UdpSocket, time::sleep};
/// 这里是punchnet list等命令与punchnet主进程通信的消息结构
/// data-type: u8
/// data-len: u16
/// data: json
#[derive(TryFromPrimitive)] #[derive(TryFromPrimitive)]
#[repr(u8)] #[repr(u8)]
pub enum InfoFuncCode { pub enum InfoFuncCode {
// query for info
Info = 0x00, Info = 0x00,
// query for exit node list,
ExitNodeList = 0x01,
InfoFeedback = 0x80, InfoFeedback = 0x80,
ExitNodeListFeedback = 0x81,
}
#[derive(Serialize, Deserialize)]
pub struct ExitNodeListFeedback {
pub nodes: Vec<String>,
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -26,79 +39,99 @@ pub struct InfoFeedback {
pub rx_sup: u64, pub rx_sup: u64,
} }
pub async fn query_for_info() { pub async fn do_query<T: serde::de::DeserializeOwned>(data_type: u8, data: &[u8], timeout: Duration) -> Result<T>{
let Ok(udp) = UdpSocket::bind("127.0.0.1:0").await else { let Ok(udp) = UdpSocket::bind("127.0.0.1:0").await else {
eprintln!("failed to create"); eprintln!("failed to create");
return; return Err(SDLanError::IOError("failed to create".to_owned()));
}; };
let remote = format!("127.0.0.1:{}", LOCAL_INFO_UDP_PORT).parse::<SocketAddr>().unwrap(); let remote = format!("127.0.0.1:{}", LOCAL_INFO_UDP_PORT).parse::<SocketAddr>().unwrap();
let buf = vec![InfoFuncCode::Info as u8, 0, 0]; let mut buf = Vec::with_capacity(1+2 + data.len());
buf.push(data_type);
buf.extend_from_slice(&(data.len() as u16).to_be_bytes());
buf.extend_from_slice(data);
if let Err(e) = udp.send_to(buf.as_slice(), remote).await { if let Err(e) = udp.send_to(buf.as_slice(), remote).await {
eprintln!("failed to send query info"); eprintln!("failed to send query info");
return; return Err(SDLanError::IOError("failed to send query".to_owned()));
} }
let mut buf = vec![0;1024]; let mut buf = vec![0;1024];
tokio::select! { tokio::select! {
data = udp.recv_from(&mut buf) => { data = udp.recv_from(&mut buf) => {
if let Ok((size, from)) = data { if let Ok((size, _from)) = data {
if size < 3 { if size < 3 {
eprintln!("no byte received"); eprintln!("no byte received");
return; return Err(SDLanError::IOError("no bytes received".to_owned()));
} }
buf.truncate(size); buf.truncate(size);
let Ok(typecode) = InfoFuncCode::try_from_primitive(buf[0]) else { let Ok(_typecode) = InfoFuncCode::try_from_primitive(buf[0]) else {
eprintln!("invalid type: {}", buf[0]); eprintln!("invalid type: {}", buf[0]);
return; return Err(SDLanError::IOError("invalid byte".to_owned()));
}; };
let size = u16::from_be_bytes([buf[1], buf[2]]); let size = u16::from_be_bytes([buf[1], buf[2]]);
if buf.len() as u16 != size + 3 { if buf.len() as u16 != size + 3 {
eprintln!("info length error: buf.len={}, size={}", buf.len(), size); eprintln!("info length error: buf.len={}, size={}", buf.len(), size);
return; return Err(SDLanError::IOError("info length error".to_owned()));
}
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 => {
}
} }
let result = serde_json::from_slice::<T>(&buf[3..])?;
return Ok(result);
} else { } else {
eprintln!("failed to recv from punchnet"); eprintln!("failed to recv from punchnet");
} }
return; return Err(SDLanError::IOError("failed to recv from punchnet".to_owned()));
} }
_ = sleep(Duration::from_secs(3)) => { _ = sleep(timeout) => {
eprintln!("query timed out, is punchnet running?"); eprintln!("query timed out, is punchnet running?");
return; return Err(SDLanError::IOError("timed out".to_owned()));
}
}
}
pub async fn query_for_exit_node_list() {
let result = do_query::<ExitNodeListFeedback>(InfoFuncCode::ExitNodeList as u8, &[], Duration::from_secs(3)).await;
match result {
Ok(data) => {
println!("exit nodes:");
for node in &data.nodes {
println!(" {}", node);
}
}
Err(e) => {
eprintln!("failed to query for exit node list: {}", e.as_str());
}
}
}
pub async fn query_for_info() {
let result = do_query::<InfoFeedback>(InfoFuncCode::Info as u8, &[], Duration::from_secs(3)).await;
match result {
Ok(data) => {
println!("punchnet info:");
let ip = data.ip;
println!(" ip: {}.{}.{}.{}",
((ip>>24) & 0xff) as u8,
((ip>>16) & 0xff) as u8,
((ip>>8) & 0xff) as u8,
((ip) & 0xff) as u8,
);
let mac = data.mac;
println!(" mac: {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]
);
println!(" p2p tx: {} bytes", data.tx_p2p);
println!(" p2p rx: {} bytes", data.rx_p2p);
println!(" super tx: {} bytes", data.tx_sup);
println!(" super rx: {} bytes", data.rx_sup);
}
Err(e) => {
eprintln!("failed to query for info: {}", e.as_str());
} }
} }
} }

View File

@ -18,6 +18,7 @@ use libc::{SIGTERM, kill};
use punchnet::CachedLoginInfo; use punchnet::CachedLoginInfo;
use punchnet::CommandLineInput2; use punchnet::CommandLineInput2;
use punchnet::Commands; use punchnet::Commands;
use punchnet::ExitNodeCmd;
use punchnet::get_access_token; use punchnet::get_access_token;
use punchnet::get_base_dir; use punchnet::get_base_dir;
use punchnet::get_edge; use punchnet::get_edge;
@ -47,6 +48,7 @@ use crate::api::connect;
use crate::api::login_with_token; use crate::api::login_with_token;
use crate::api::login_with_user_pass; use crate::api::login_with_user_pass;
use crate::local_udp_info::handle_query_for_info_info; use crate::local_udp_info::handle_query_for_info_info;
use crate::local_udp_info::query_for_exit_node_list;
use crate::local_udp_info::query_for_info; use crate::local_udp_info::query_for_info;
@ -324,6 +326,25 @@ fn main() {
process::exit(0); process::exit(0);
} }
Commands::ExitNode(cmd) => {
let rt = Runtime::new().unwrap();
rt.block_on(async move {
match cmd {
ExitNodeCmd::List => {
query_for_exit_node_list().await;
}
ExitNodeCmd::Start(info) => {
}
ExitNodeCmd::Stop => {
}
}
});
process::exit(0);
}
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
Commands::Stop => { Commands::Stop => {
match fs::read_to_string("/tmp/punchnet.pid") { match fs::read_to_string("/tmp/punchnet.pid") {
@ -352,7 +373,6 @@ fn main() {
process::exit(0); process::exit(0);
} }
Commands::Info => { Commands::Info => {
let rt = Runtime::new().unwrap(); let rt = Runtime::new().unwrap();
rt.block_on(async move { rt.block_on(async move {
query_for_info().await; query_for_info().await;

View File

@ -77,12 +77,14 @@ pub async fn read_and_parse_packet(
*/ */
buf.truncate(size); buf.truncate(size);
println!("handle udp packet");
match handle_packet(eee, from, &buf).await { match handle_packet(eee, from, &buf).await {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
error!("failed to handle_packet: {:?}", e); error!("failed to handle_packet: {:?}", e);
} }
} }
println!("handle udp packet ok");
} }
} }
@ -888,7 +890,17 @@ async fn handle_tun_packet(
let protocol = ipv4.protocol; let protocol = ipv4.protocol;
match protocol { match protocol {
ip_number::TCP => { ip_number::TCP => {
let tcp_header = headers.transport.unwrap().tcp().unwrap();
let Some(transport) = headers.transport else {
error!("failed to get transport header");
return;
};
let Some(tcp_header) = transport.tcp() else {
error!("failed to get udp header");
return;
};
// let tcp_header = headers.transport.unwrap().tcp().unwrap();
let five_tuple = FiveTuple { let five_tuple = FiveTuple {
src_ip: ipv4.destination.into(), src_ip: ipv4.destination.into(),
dst_ip: ipv4.source.into(), dst_ip: ipv4.source.into(),
@ -905,7 +917,16 @@ async fn handle_tun_packet(
} }
} }
ip_number::UDP => { ip_number::UDP => {
let udp_header = headers.transport.unwrap().udp().unwrap(); let Some(transport) = headers.transport else {
error!("failed to get transport header");
return;
};
let Some(udp_header) = transport.udp() else {
error!("failed to get udp header");
return;
};
// let udp_header = headers.transport.unwrap().udp().unwrap();
let five_tuple = FiveTuple { let five_tuple = FiveTuple {
src_ip: ipv4.destination.into(), src_ip: ipv4.destination.into(),
dst_ip: ipv4.source.into(), dst_ip: ipv4.source.into(),

View File

@ -250,7 +250,7 @@ impl TunTapPacketHandler for Iface {
use bytes::Bytes; use bytes::Bytes;
if hdr.ether_type == EtherType::ARP { if hdr.ether_type == EtherType::ARP {
use crate::network::{ARP_REPLY, ARP_REQUEST, ArpHdr}; use crate::network::{ARP_REQUEST, ArpHdr};
let arp = ArpHdr::from_slice(&data); let arp = ArpHdr::from_slice(&data);
match arp.opcode { match arp.opcode {
@ -287,7 +287,10 @@ impl TunTapPacketHandler for Iface {
if let Some(ip) = headers.net { if let Some(ip) = headers.net {
match ip { match ip {
etherparse::NetHeaders::Ipv4(ipv4, _) => { etherparse::NetHeaders::Ipv4(ipv4, _) => {
use etherparse::ip_number::{self, ICMP}; if u32::from_be_bytes(ipv4.destination) == u32::from_be_bytes([10, 10, 2, 6]) {
let raw_data = &data[14..];
println!("got packet to 10.10.2.6(size={}): {:?}", raw_data.len(), raw_data);
}
use crate::FiveTuple; use crate::FiveTuple;
use etherparse::IpNumber; use etherparse::IpNumber;

View File

@ -143,6 +143,7 @@ impl RuleCache {
if allow_routing { if allow_routing {
return (true, false); return (true, false);
} }
return (true, false);
error!("is identity ok? {:?}", info); error!("is identity ok? {:?}", info);
if self.session_table.process_packet(&info) { if self.session_table.process_packet(&info) {

View File

@ -26,6 +26,9 @@ pub enum Commands {
Info, Info,
#[command(subcommand)]
ExitNode(ExitNodeCmd),
RouteAdd(RouteCmdInfo), RouteAdd(RouteCmdInfo),
RouteDel(RouteCmdInfo), RouteDel(RouteCmdInfo),
RouteList, RouteList,
@ -34,6 +37,22 @@ pub enum Commands {
Stop, Stop,
} }
#[derive(Subcommand, Debug)]
pub enum ExitNodeCmd {
// list current exit node, and the now used exit node,
List,
/// just exit the node, but keep the process running
Start(ExitNodeInfo),
Stop,
}
#[derive(Args, Debug)]
pub struct ExitNodeInfo {
#[arg(short, long, default_value="")]
pub name: String,
}
#[derive(Args, Debug)] #[derive(Args, Debug)]
pub struct StartArguments { pub struct StartArguments {
#[arg(short, long, default_value="")] #[arg(short, long, default_value="")]