diff --git a/Cargo.toml b/Cargo.toml index 3e8e61b..d84c725 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,28 +14,28 @@ default = [] log = ["tracing/log"] [dependencies] -tokio = { version = "1.35", features = [ +tokio = { version = "1.36", features = [ "sync", "rt", "time", "io-util", "macros", ], default-features = false } -etherparse = { version = "0.13", default-features = false } +etherparse = { version = "0.14", default-features = false, features = ["std"] } thiserror = { version = "1.0", default-features = false } tracing = { version = "0.1", default-features = false, features = [ "log", ], optional = true } [dev-dependencies] -clap = { version = "4.4", features = ["derive"] } +clap = { version = "4.5", features = ["derive"] } udp-stream = { version = "0.0", default-features = false } -tokio = { version = "1.35", features = [ +tokio = { version = "1.36", features = [ "rt-multi-thread", ], default-features = false } #tun2.rs example -tun2 = { version = "1.0", features = ["async"] } +tun2 = { version = "1.2", features = ["async"] } #tun_wintun.rs example [target.'cfg(any(target_os = "linux", target_os = "macos"))'.dev-dependencies] diff --git a/README.md b/README.md index 1504db9..a34c589 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,15 @@ +IpStack +======= + An asynchronous lightweight implementation of TCP/IP stack for Tun device. Unstable, under development. +[![Crates.io](https://img.shields.io/crates/v/ipstack.svg)](https://crates.io/crates/ipstack) +![ipstack](https://docs.rs/ipstack/badge.svg) +[![Documentation](https://img.shields.io/badge/docs-release-brightgreen.svg?style=flat)](https://docs.rs/ipstack) +[![Download](https://img.shields.io/crates/d/ipstack.svg)](https://crates.io/crates/ipstack) +[![License](https://img.shields.io/crates/l/ipstack.svg?style=flat)](https://github.com/narrowlink/ipstack/blob/main/LICENSE) + ### Usage ```rust diff --git a/examples/tun2.rs b/examples/tun2.rs index 7eed409..91b03b5 100644 --- a/examples/tun2.rs +++ b/examples/tun2.rs @@ -52,7 +52,7 @@ async fn main() -> Result<(), Box> { let gateway = Ipv4Addr::new(10, 0, 0, 1); let mut config = tun2::Configuration::default(); - config.address(ipv4).netmask(netmask).mtu(MTU as usize).up(); + config.address(ipv4).netmask(netmask).mtu(MTU).up(); config.destination(gateway); #[cfg(target_os = "linux")] @@ -103,7 +103,7 @@ async fn main() -> Result<(), Box> { }); } IpStackStream::UnknownTransport(u) => { - if u.src_addr().is_ipv4() && u.ip_protocol() == 1 { + if u.src_addr().is_ipv4() && u.ip_protocol() == 1.into() { let (icmp_header, req_payload) = Icmpv4Header::from_slice(u.payload())?; if let etherparse::Icmpv4Type::EchoRequest(req) = icmp_header.icmp_type { println!("ICMPv4 echo"); @@ -121,7 +121,7 @@ async fn main() -> Result<(), Box> { } continue; } - println!("unknown transport - Ip Protocol {}", u.ip_protocol()); + println!("unknown transport - Ip Protocol {:?}", u.ip_protocol()); continue; } IpStackStream::UnknownNetwork(pkt) => { diff --git a/examples/tun_wintun.rs b/examples/tun_wintun.rs index a5e3844..76f4616 100644 --- a/examples/tun_wintun.rs +++ b/examples/tun_wintun.rs @@ -82,7 +82,7 @@ async fn main() -> Result<(), Box> { }); } IpStackStream::UnknownTransport(u) => { - if u.src_addr().is_ipv4() && u.ip_protocol() == 1 { + if u.src_addr().is_ipv4() && u.ip_protocol() == 1.into() { let (icmp_header, req_payload) = Icmpv4Header::from_slice(u.payload())?; if let etherparse::Icmpv4Type::EchoRequest(req) = icmp_header.icmp_type { println!("ICMPv4 echo"); @@ -100,7 +100,7 @@ async fn main() -> Result<(), Box> { } continue; } - println!("unknown transport - Ip Protocol {}", u.ip_protocol()); + println!("unknown transport - Ip Protocol {:?}", u.ip_protocol()); continue; } IpStackStream::UnknownNetwork(pkt) => { diff --git a/src/error.rs b/src/error.rs index 2b7f242..4d15246 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,14 +3,25 @@ pub enum IpStackError { #[error("The transport protocol is not supported")] UnsupportedTransportProtocol, + #[error("The packet is invalid")] InvalidPacket, - #[error("Write error: {0}")] - PacketWriteError(etherparse::WriteError), + + #[error("ValueTooBigError {0}")] + ValueTooBigErrorU16(#[from] etherparse::err::ValueTooBigError), + + #[error("From> {0}")] + ValueTooBigErrorU32(#[from] etherparse::err::ValueTooBigError), + + #[error("ValueTooBigError {0}")] + ValueTooBigErrorUsize(#[from] etherparse::err::ValueTooBigError), + #[error("Invalid Tcp packet")] InvalidTcpPacket, + #[error("IO error: {0}")] IoError(#[from] std::io::Error), + #[error("Accept Error")] AcceptError, diff --git a/src/lib.rs b/src/lib.rs index 9139097..02b0e44 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,6 @@ use std::{ }, time::Duration, }; -use stream::IpStackStream; use tokio::{ io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}, select, @@ -18,7 +17,7 @@ use tracing::{error, trace}; use crate::{ packet::IpStackPacketProtocol, - stream::{IpStackTcpStream, IpStackUdpStream, IpStackUnknownTransport}, + stream::{IpStackStream, IpStackTcpStream, IpStackUdpStream, IpStackUnknownTransport}, }; mod error; mod packet; diff --git a/src/packet.rs b/src/packet.rs index a89ca78..9685594 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -1,6 +1,6 @@ use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; -use etherparse::{IpHeader, PacketHeaders, TcpHeader, UdpHeader, WriteError}; +use etherparse::{NetHeaders, PacketHeaders, TcpHeader, UdpHeader}; use crate::error::IpStackError; @@ -34,7 +34,7 @@ pub(crate) enum TransportHeader { } pub struct NetworkPacket { - pub(crate) ip: IpHeader, + pub(crate) ip: NetHeaders, pub(crate) transport: TransportHeader, pub(crate) payload: Vec, } @@ -42,7 +42,7 @@ pub struct NetworkPacket { impl NetworkPacket { pub fn parse(buf: &[u8]) -> Result { let p = PacketHeaders::from_ip_slice(buf).map_err(|_| IpStackError::InvalidPacket)?; - let ip = p.ip.ok_or(IpStackError::InvalidPacket)?; + let ip = p.net.ok_or(IpStackError::InvalidPacket)?; let transport = match p.transport { Some(etherparse::TransportHeader::Tcp(h)) => TransportHeader::Tcp(h), Some(etherparse::TransportHeader::Udp(u)) => TransportHeader::Udp(u), @@ -52,7 +52,7 @@ impl NetworkPacket { let payload = if let TransportHeader::Unknown = transport { buf[ip.header_len()..].to_vec() } else { - p.payload.to_vec() + p.payload.slice().to_vec() }; Ok(NetworkPacket { @@ -75,12 +75,8 @@ impl NetworkPacket { _ => 0, }; match &self.ip { - IpHeader::Version4(ip, _) => { - SocketAddr::new(IpAddr::V4(Ipv4Addr::from(ip.source)), port) - } - IpHeader::Version6(ip, _) => { - SocketAddr::new(IpAddr::V6(Ipv6Addr::from(ip.source)), port) - } + NetHeaders::Ipv4(ip, _) => SocketAddr::new(IpAddr::V4(Ipv4Addr::from(ip.source)), port), + NetHeaders::Ipv6(ip, _) => SocketAddr::new(IpAddr::V6(Ipv6Addr::from(ip.source)), port), } } pub fn dst_addr(&self) -> SocketAddr { @@ -90,10 +86,10 @@ impl NetworkPacket { _ => 0, }; match &self.ip { - IpHeader::Version4(ip, _) => { + NetHeaders::Ipv4(ip, _) => { SocketAddr::new(IpAddr::V4(Ipv4Addr::from(ip.destination)), port) } - IpHeader::Version6(ip, _) => { + NetHeaders::Ipv6(ip, _) => { SocketAddr::new(IpAddr::V6(Ipv6Addr::from(ip.destination)), port) } } @@ -114,29 +110,22 @@ impl NetworkPacket { } pub fn to_bytes(&self) -> Result, IpStackError> { let mut buf = Vec::new(); - self.ip - .write(&mut buf) - .map_err(IpStackError::PacketWriteError)?; + match self.ip { + NetHeaders::Ipv4(ref ip, _) => ip.write(&mut buf)?, + NetHeaders::Ipv6(ref ip, _) => ip.write(&mut buf)?, + } match self.transport { - TransportHeader::Tcp(ref h) => h - .write(&mut buf) - .map_err(WriteError::from) - .map_err(IpStackError::PacketWriteError)?, - TransportHeader::Udp(ref h) => { - h.write(&mut buf).map_err(IpStackError::PacketWriteError)? - } + TransportHeader::Tcp(ref h) => h.write(&mut buf)?, + TransportHeader::Udp(ref h) => h.write(&mut buf)?, _ => {} }; - // self.transport - // .write(&mut buf) - // .map_err(IpStackError::PacketWriteError)?; buf.extend_from_slice(&self.payload); Ok(buf) } pub fn ttl(&self) -> u8 { match &self.ip { - IpHeader::Version4(ip, _) => ip.time_to_live, - IpHeader::Version6(ip, _) => ip.hop_limit, + NetHeaders::Ipv4(ip, _) => ip.time_to_live, + NetHeaders::Ipv6(ip, _) => ip.hop_limit, } } } diff --git a/src/stream/tcp.rs b/src/stream/tcp.rs index 9c739ca..714f5b9 100644 --- a/src/stream/tcp.rs +++ b/src/stream/tcp.rs @@ -119,41 +119,47 @@ impl IpStackTcpStream { let ip_header = match (self.dst_addr.ip(), self.src_addr.ip()) { (std::net::IpAddr::V4(dst), std::net::IpAddr::V4(src)) => { - let mut ip_h = Ipv4Header::new(0, ttl, 6, dst.octets(), src.octets()); - let payload_len = - self.calculate_payload_len(ip_h.header_len() as u16, tcp_header.header_len()); + let mut ip_h = Ipv4Header::new(0, ttl, 6.into(), dst.octets(), src.octets()) + .map_err(IpStackError::from)?; + let payload_len = self.calculate_payload_len( + ip_h.header_len() as u16, + tcp_header.header_len() as u16, + ); payload.truncate(payload_len as usize); - ip_h.payload_len = payload.len() as u16 + tcp_header.header_len(); + ip_h.set_payload_len(payload.len() + tcp_header.header_len()) + .map_err(IpStackError::from)?; ip_h.dont_fragment = true; - etherparse::IpHeader::Version4(ip_h, Ipv4Extensions::default()) + etherparse::NetHeaders::Ipv4(ip_h, Ipv4Extensions::default()) } (std::net::IpAddr::V6(dst), std::net::IpAddr::V6(src)) => { let mut ip_h = etherparse::Ipv6Header { traffic_class: 0, - flow_label: 0, + flow_label: 0.try_into().map_err(IpStackError::from)?, payload_length: 0, - next_header: 6, + next_header: 6.into(), hop_limit: ttl, source: dst.octets(), destination: src.octets(), }; - let payload_len = - self.calculate_payload_len(ip_h.header_len() as u16, tcp_header.header_len()); + let payload_len = self.calculate_payload_len( + ip_h.header_len() as u16, + tcp_header.header_len() as u16, + ); payload.truncate(payload_len as usize); - ip_h.payload_length = payload.len() as u16 + tcp_header.header_len(); + ip_h.payload_length = (payload.len() + tcp_header.header_len()) as u16; - etherparse::IpHeader::Version6(ip_h, Ipv6Extensions::default()) + etherparse::NetHeaders::Ipv6(ip_h, Ipv6Extensions::default()) } _ => unreachable!(), }; match ip_header { - etherparse::IpHeader::Version4(ref ip_header, _) => { + etherparse::NetHeaders::Ipv4(ref ip_header, _) => { tcp_header.checksum = tcp_header .calc_checksum_ipv4(ip_header, &payload) .map_err(|_e| Error::from(ErrorKind::InvalidInput))?; } - etherparse::IpHeader::Version6(ref ip_header, _) => { + etherparse::NetHeaders::Ipv6(ref ip_header, _) => { tcp_header.checksum = tcp_header .calc_checksum_ipv6(ip_header, &payload) .map_err(|_e| Error::from(ErrorKind::InvalidInput))?; diff --git a/src/stream/udp.rs b/src/stream/udp.rs index bf7a14d..7c144dd 100644 --- a/src/stream/udp.rs +++ b/src/stream/udp.rs @@ -17,7 +17,7 @@ use tokio::{ // use crate::packet::TransportHeader; use crate::{ packet::{NetworkPacket, TransportHeader}, - TTL, + IpStackError, TTL, }; pub struct IpStackUdpStream { @@ -62,10 +62,12 @@ impl IpStackUdpStream { fn create_rev_packet(&self, ttl: u8, mut payload: Vec) -> Result { match (self.dst_addr.ip(), self.src_addr.ip()) { (std::net::IpAddr::V4(dst), std::net::IpAddr::V4(src)) => { - let mut ip_h = Ipv4Header::new(0, ttl, 17, dst.octets(), src.octets()); + let mut ip_h = Ipv4Header::new(0, ttl, 17.into(), dst.octets(), src.octets()) + .map_err(IpStackError::from)?; let line_buffer = self.mtu.saturating_sub(ip_h.header_len() as u16 + 8); // 8 is udp header size payload.truncate(line_buffer as usize); - ip_h.payload_len = payload.len() as u16 + 8; // 8 is udp header size + ip_h.set_payload_len(payload.len() + 8) + .map_err(IpStackError::from)?; // 8 is udp header size let udp_header = UdpHeader::with_ipv4_checksum( self.dst_addr.port(), self.src_addr.port(), @@ -74,7 +76,7 @@ impl IpStackUdpStream { ) .map_err(|_e| Error::from(ErrorKind::InvalidInput))?; Ok(NetworkPacket { - ip: etherparse::IpHeader::Version4(ip_h, Ipv4Extensions::default()), + ip: etherparse::NetHeaders::Ipv4(ip_h, Ipv4Extensions::default()), transport: TransportHeader::Udp(udp_header), payload, }) @@ -82,9 +84,9 @@ impl IpStackUdpStream { (std::net::IpAddr::V6(dst), std::net::IpAddr::V6(src)) => { let mut ip_h = Ipv6Header { traffic_class: 0, - flow_label: 0, + flow_label: 0.try_into().map_err(IpStackError::from)?, payload_length: 0, - next_header: 17, + next_header: 17.into(), hop_limit: ttl, source: dst.octets(), destination: src.octets(), @@ -102,7 +104,7 @@ impl IpStackUdpStream { ) .map_err(|_e| Error::from(ErrorKind::InvalidInput))?; Ok(NetworkPacket { - ip: etherparse::IpHeader::Version6(ip_h, Ipv6Extensions::default()), + ip: etherparse::NetHeaders::Ipv6(ip_h, Ipv6Extensions::default()), transport: TransportHeader::Udp(udp_header), payload, }) diff --git a/src/stream/unknown.rs b/src/stream/unknown.rs index 4f5fae3..72adc3e 100644 --- a/src/stream/unknown.rs +++ b/src/stream/unknown.rs @@ -1,6 +1,6 @@ use std::{io::Error, mem, net::IpAddr}; -use etherparse::{IpHeader, Ipv4Extensions, Ipv4Header, Ipv6Extensions, Ipv6Header}; +use etherparse::{IpNumber, Ipv4Extensions, Ipv4Header, Ipv6Extensions, Ipv6Header, NetHeaders}; use tokio::sync::mpsc::UnboundedSender; use crate::{ @@ -12,7 +12,7 @@ pub struct IpStackUnknownTransport { src_addr: IpAddr, dst_addr: IpAddr, payload: Vec, - protocol: u8, + protocol: IpNumber, mtu: u16, packet_sender: UnboundedSender, } @@ -22,13 +22,13 @@ impl IpStackUnknownTransport { src_addr: IpAddr, dst_addr: IpAddr, payload: Vec, - ip: &IpHeader, + ip: &NetHeaders, mtu: u16, packet_sender: UnboundedSender, ) -> Self { let protocol = match ip { - IpHeader::Version4(ip, _) => ip.protocol, - IpHeader::Version6(ip, _) => ip.next_header, + NetHeaders::Ipv4(ip, _) => ip.protocol, + NetHeaders::Ipv6(ip, _) => ip.next_header, }; IpStackUnknownTransport { src_addr, @@ -48,7 +48,7 @@ impl IpStackUnknownTransport { pub fn payload(&self) -> &[u8] { &self.payload } - pub fn ip_protocol(&self) -> u8 { + pub fn ip_protocol(&self) -> IpNumber { self.protocol } pub async fn send(&self, mut payload: Vec) -> Result<(), Error> { @@ -66,7 +66,8 @@ impl IpStackUnknownTransport { pub fn create_rev_packet(&self, payload: &mut Vec) -> Result { match (self.dst_addr, self.src_addr) { (std::net::IpAddr::V4(dst), std::net::IpAddr::V4(src)) => { - let mut ip_h = Ipv4Header::new(0, TTL, self.protocol, dst.octets(), src.octets()); + let mut ip_h = Ipv4Header::new(0, TTL, self.protocol, dst.octets(), src.octets()) + .map_err(crate::IpStackError::from)?; let line_buffer = self.mtu.saturating_sub(ip_h.header_len() as u16); let p = if payload.len() > line_buffer as usize { @@ -74,9 +75,10 @@ impl IpStackUnknownTransport { } else { mem::take(payload) }; - ip_h.payload_len = p.len() as u16; + ip_h.set_payload_len(p.len()) + .map_err(crate::IpStackError::from)?; Ok(NetworkPacket { - ip: etherparse::IpHeader::Version4(ip_h, Ipv4Extensions::default()), + ip: etherparse::NetHeaders::Ipv4(ip_h, Ipv4Extensions::default()), transport: TransportHeader::Unknown, payload: p, }) @@ -84,9 +86,9 @@ impl IpStackUnknownTransport { (std::net::IpAddr::V6(dst), std::net::IpAddr::V6(src)) => { let mut ip_h = Ipv6Header { traffic_class: 0, - flow_label: 0, + flow_label: 0.try_into().map_err(crate::IpStackError::from)?, payload_length: 0, - next_header: 17, + next_header: 17.into(), hop_limit: TTL, source: dst.octets(), destination: src.octets(), @@ -100,7 +102,7 @@ impl IpStackUnknownTransport { mem::take(payload) }; Ok(NetworkPacket { - ip: etherparse::IpHeader::Version6(ip_h, Ipv6Extensions::default()), + ip: etherparse::NetHeaders::Ipv6(ip_h, Ipv6Extensions::default()), transport: TransportHeader::Unknown, payload: p, })