TProxy 与 Raw Socket
相比于新建 udp socket 然后 bind source,raw socket 可以反复利用,节省资源,提高性能。同时需要 root 权限也不是 raw socket 的缺点,因为 bind source 没有 root 权限的话无法绑定 1024 以下的端口。
看到这里基本上都是写代码的老兄了,我就献丑把用 Rust 写的代码粘贴出来了。recvmsg 的代码可以看 shadowsocks-rust
fn udp_sendto(
&self,
saddr: String,
daddr: String,
buf: Vec<u8>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let saddr = saddr.to_socket_addrs()?.next().unwrap();
let daddr = daddr.to_socket_addrs()?.next().unwrap();
match (saddr, daddr) {
(SocketAddr::V4(saddr), SocketAddr::V4(daddr)) => self.clone().udp_sendto4(
*saddr.ip(),
saddr.port(),
*daddr.ip(),
daddr.port(),
buf,
)?,
(SocketAddr::V6(saddr), SocketAddr::V6(daddr)) => self.clone().udp_sendto6(
*saddr.ip(),
saddr.port(),
*daddr.ip(),
daddr.port(),
buf,
)?,
_ => unreachable!(),
}
Ok(())
}
fn udp_sendto4(
&self,
saddr: Ipv4Addr,
sport: u16,
daddr: Ipv4Addr,
dport: u16,
data_buf: Vec<u8>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut udp_buf = vec![0u8; UDPLEN];
let mut ip_header = ipv4::MutableIpv4Packet::new(&mut udp_buf).unwrap();
ip_header.set_version(4);
ip_header.set_header_length(5);
ip_header.set_total_length(20 + 8 + data_buf.len() as u16);
ip_header.set_ttl(64);
ip_header.set_next_level_protocol(IpNextHeaderProtocols::Udp);
ip_header.set_source(saddr);
ip_header.set_destination(daddr);
let checksum = ipv4::checksum(&ip_header.to_immutable());
ip_header.set_checksum(checksum);
let payload = ip_header.payload_mut();
let mut udp_header = MutableUdpPacket::new(payload).unwrap();
udp_header.set_source(sport);
udp_header.set_destination(dport);
udp_header.set_length((8 + data_buf.len()) as u16);
udp_header.set_payload(&data_buf);
let checksum = udp::ipv4_checksum(&udp_header.to_immutable(), &saddr, &daddr);
udp_header.set_checksum(checksum);
let daddr_socketaddr = SocketAddrV4::new(daddr, dport);
if unsafe {
libc::sendto(
self.raw4,
udp_buf.as_mut_ptr() as *mut _,
20 + 8 + data_buf.len(),
0,
&daddr_socketaddr as *const _ as *const _,
std::mem::size_of_val(&daddr_socketaddr) as _,
) == -1
} {
Err(io::Error::last_os_error())?;
}
Ok(())
}
fn udp_sendto6(
&self,
saddr: Ipv6Addr,
sport: u16,
daddr: Ipv6Addr,
dport: u16,
data_buf: Vec<u8>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let mut udp_buf = vec![0u8; UDPLEN];
let mut ip_header = ipv6::MutableIpv6Packet::new(&mut udp_buf).unwrap();
ip_header.set_version(6);
ip_header.set_payload_length(8 + data_buf.len() as u16);
ip_header.set_hop_limit(64);
ip_header.set_next_header(IpNextHeaderProtocols::Udp);
ip_header.set_source(saddr);
ip_header.set_destination(daddr);
let payload = ip_header.payload_mut();
let mut udp_header = MutableUdpPacket::new(payload).unwrap();
udp_header.set_source(sport);
udp_header.set_destination(dport);
udp_header.set_length(8 + data_buf.len() as u16);
udp_header.set_payload(&data_buf);
let checksum = udp::ipv6_checksum(&udp_header.to_immutable(), &saddr, &daddr);
udp_header.set_checksum(checksum);
// dport must 0, it's fuck enough, https://stackoverflow.com/a/47779888/12651220
let daddr_socketaddr = SocketAddrV6::new(daddr, 0, 0, 0);
if unsafe {
libc::sendto(
self.raw6,
udp_buf.as_mut_ptr() as *mut _,
40 + 8 + data_buf.len(),
0,
&daddr_socketaddr as *const _ as *const _,
std::mem::size_of_val(&daddr_socketaddr) as _,
) == -1
} {
Err(io::Error::last_os_error())?;
}
Ok(())
}