Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ tun2 = { version = "0.6", features = ["async"] }
tokio = { version = "1.35", features = [
"rt-multi-thread",
], default-features = false }
packet = "0.1.4"

@SajjadPourali SajjadPourali Jan 19, 2024 •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am hesitant to use this dependency even in the examples since it is left unmaintained.


[profile.release]
opt-level = 'z' # Optimize for size.
Expand Down
44 changes: 43 additions & 1 deletion examples/tun.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
//!

use clap::Parser;
use ipstack::stream::IpStackStream;
use ipstack::stream::{IpStackStream, RawPacket};
use std::net::{Ipv4Addr, SocketAddr};
use tokio::{join, net::TcpStream};
use udp_stream::UdpStream;
Expand Down Expand Up @@ -70,6 +70,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

let mut ip_stack = ipstack::IpStack::new(ipstack_config, tun2::create_as_async(&config)?);

#[cfg(target_os = "macos")]
std::process::Command::new("sh")
.arg("-c")
.arg("sudo route -n add -net 10.0.0.0/24 10.0.0.33")
.output()
.unwrap();

let server_addr = args.server_addr;

loop {
Expand Down Expand Up @@ -108,6 +115,41 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("==== end UDP connection ====");
});
}
IpStackStream::RawPacket(_pkt) => {
//println!("{:?}", _pkt);
icmp_reply(_pkt).await;
}
};
}
}

async fn icmp_reply(raw_pkt: RawPacket) {
use ::packet::{builder::Builder, Packet};
let pkt = ::packet::ip::v4::Packet::new(raw_pkt.bytes()).unwrap();
let icmp = ::packet::icmp::Packet::new(pkt.payload()).unwrap();
let icmp = icmp.echo().unwrap();
let reply = ::packet::ip::v4::Builder::default()
.id(0x42)
.unwrap()
.ttl(64)
.unwrap()
.source(pkt.destination())
.unwrap()
.destination(pkt.source())
.unwrap()
.icmp()
.unwrap()
.echo()
.unwrap()
.reply()
.unwrap()
.identifier(icmp.identifier())
.unwrap()
.sequence(icmp.sequence())
.unwrap()
.payload(icmp.payload())
.unwrap()
.build()
.unwrap();
raw_pkt.send(&reply).await.unwrap();
}
78 changes: 44 additions & 34 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pub use error::{IpStackError, Result};
use etherparse::TransportHeader;
use packet::{NetworkPacket, NetworkTuple};
use std::{
collections::{
Expand All @@ -7,7 +8,7 @@ use std::{
},
time::Duration,
};
use stream::IpStackStream;
use stream::{IpStackStream, RawPacket};
use tokio::{
io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt},
select,
Expand Down Expand Up @@ -100,53 +101,62 @@ impl IpStack {
Ok(n) = device.read(&mut buffer) => {
let offset = if config.packet_information && cfg!(unix) {4} else {0};
// dbg!(&buffer[offset..n]);
let Ok(packet) = NetworkPacket::parse(&buffer[offset..n]) else {
let Ok(packet) = packet::parse_packet(&buffer[offset..n]) else {
#[cfg(feature = "log")]
trace!("parse error");
continue;
};
match streams.entry(packet.network_tuple()){
Occupied(entry) =>{
let t = packet.transport_protocol();
if let Err(_x) = entry.get().send(packet){
#[cfg(feature = "log")]
trace!("{}", _x);
match t{
IpStackPacketProtocol::Tcp(_t) => {
// dbg!(t.flags());
}
IpStackPacketProtocol::Udp => {
// dbg!("udp");
match packet{
packet::TunPacket::NetworkPacket(packet)=>{
let packet = *packet;
match streams.entry(packet.network_tuple()){
Occupied(entry) =>{
let t = packet.transport_protocol();
if let Err(_x) = entry.get().send(packet){
#[cfg(feature = "log")]
trace!("{}", _x);
match t{
IpStackPacketProtocol::Tcp(_t) => {
// dbg!(t.flags());
}
IpStackPacketProtocol::Udp => {
// dbg!("udp");
}
}

}
}

}
}
Vacant(entry) => {
match packet.transport_protocol(){
IpStackPacketProtocol::Tcp(h) => {
match IpStackTcpStream::new(packet.src_addr(),packet.dst_addr(),h, pkt_sender.clone(),config.mtu,config.tcp_timeout).await{
Ok(stream) => {
entry.insert(stream.stream_sender());
accept_sender.send(IpStackStream::Tcp(stream))?;
Vacant(entry) => {
match packet.transport_protocol(){
IpStackPacketProtocol::Tcp(h) => {
match IpStackTcpStream::new(packet.src_addr(),packet.dst_addr(),h, pkt_sender.clone(),config.mtu,config.tcp_timeout).await{
Ok(stream) => {
entry.insert(stream.stream_sender());
accept_sender.send(IpStackStream::Tcp(stream))?;
}
Err(_e) => {
#[cfg(feature = "log")]
error!("{}", _e);
}
}
}
Err(_e) => {
#[cfg(feature = "log")]
error!("{}", _e);
IpStackPacketProtocol::Udp => {
let stream = IpStackUdpStream::new(packet.src_addr(),packet.dst_addr(),packet.payload, pkt_sender.clone(),config.mtu,config.udp_timeout);
entry.insert(stream.stream_sender());
accept_sender.send(IpStackStream::Udp(stream))?;
}
}
}
IpStackPacketProtocol::Udp => {
let stream = IpStackUdpStream::new(packet.src_addr(),packet.dst_addr(),packet.payload, pkt_sender.clone(),config.mtu,config.udp_timeout);
entry.insert(stream.stream_sender());
accept_sender.send(IpStackStream::Udp(stream))?;
}
}
}
packet::TunPacket::RawPacket=>{
accept_sender.send(IpStackStream::RawPacket(RawPacket::new(buffer[offset..n].to_vec(), pkt_sender.clone(),config.mtu)))?;
}
}
}
Some(packet) = pkt_receiver.recv() => {
if packet.ttl() == 0{
let t = packet.transport.clone();
if (matches!(t, TransportHeader::Tcp(_)) || matches!(t, TransportHeader::Udp(_))) && packet.ttl() == 0{
streams.remove(&packet.reverse_network_tuple());
continue;
}
Expand All @@ -158,7 +168,7 @@ impl IpStack {
};
#[cfg(unix)]
if config.packet_information {
if packet.src_addr().is_ipv4(){
if packet.src_ip().is_ipv4(){
packet_byte.splice(0..0, [TUN_FLAGS, TUN_PROTO_IP4].concat());
} else{
packet_byte.splice(0..0, [TUN_FLAGS, TUN_PROTO_IP6].concat());
Expand Down
53 changes: 39 additions & 14 deletions src/packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,47 @@ pub(crate) enum IpStackPacketProtocol {
Tcp(TcpPacket),
Udp,
}
#[derive(Debug)]
pub struct NetworkPacket {
pub ip: IpHeader,
pub transport: TransportHeader,
pub payload: Vec<u8>,
}

pub enum TunPacket {
NetworkPacket(Box<NetworkPacket>),
RawPacket,
}

pub(crate) fn parse_packet(buf: &[u8]) -> Result<TunPacket, IpStackError> {
let p = PacketHeaders::from_ip_slice(buf).map_err(|_| IpStackError::InvalidPacket)?;
let ip = p.ip.ok_or(IpStackError::InvalidPacket)?;
let transport = p
.transport
.ok_or(IpStackError::UnsupportedTransportProtocol)?;
match transport {
TransportHeader::Tcp(_) | TransportHeader::Udp(_) => {
Ok(TunPacket::NetworkPacket(Box::new(NetworkPacket {
ip,
transport,
payload: p.payload.to_vec(),
})))
}
TransportHeader::Icmpv4(_) | TransportHeader::Icmpv6(_) => Ok(TunPacket::RawPacket),
}
}

impl NetworkPacket {
pub fn parse(buf: &[u8]) -> Result<Self, IpStackError> {
pub fn parse_from(buf: &[u8]) -> Result<Self, IpStackError> {
let p = PacketHeaders::from_ip_slice(buf).map_err(|_| IpStackError::InvalidPacket)?;
let ip = p.ip.ok_or(IpStackError::InvalidPacket)?;
let transport = p
.transport
.filter(|t| {
(matches!(t, TransportHeader::Tcp(_)) || matches!(t, TransportHeader::Udp(_)))
(matches!(t, TransportHeader::Tcp(_))
|| matches!(t, TransportHeader::Udp(_))
|| matches!(t, TransportHeader::Icmpv4(_))
|| matches!(t, TransportHeader::Icmpv6(_)))
})
.ok_or(IpStackError::UnsupportedTransportProtocol)?;
let payload = p.payload.to_vec();
Expand All @@ -61,13 +88,12 @@ impl NetworkPacket {
TransportHeader::Tcp(tcp) => tcp.source_port,
_ => unreachable!(),
};
SocketAddr::new(self.src_ip(), port)
}
pub fn src_ip(&self) -> IpAddr {
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)
}
IpHeader::Version4(ip, _) => IpAddr::V4(Ipv4Addr::from(ip.source)),
IpHeader::Version6(ip, _) => IpAddr::V6(Ipv6Addr::from(ip.source)),
}
}
pub fn dst_addr(&self) -> SocketAddr {
Expand All @@ -76,13 +102,12 @@ impl NetworkPacket {
TransportHeader::Tcp(tcp) => tcp.destination_port,
_ => unreachable!(),
};
SocketAddr::new(self.dst_ip(), port)
}
pub fn dst_ip(&self) -> IpAddr {
match &self.ip {
IpHeader::Version4(ip, _) => {
SocketAddr::new(IpAddr::V4(Ipv4Addr::from(ip.destination)), port)
}
IpHeader::Version6(ip, _) => {
SocketAddr::new(IpAddr::V6(Ipv6Addr::from(ip.destination)), port)
}
IpHeader::Version4(ip, _) => IpAddr::V4(Ipv4Addr::from(ip.destination)),
IpHeader::Version6(ip, _) => IpAddr::V6(Ipv6Addr::from(ip.destination)),
}
}
pub fn network_tuple(&self) -> NetworkTuple {
Expand Down
21 changes: 20 additions & 1 deletion src/stream/mod.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,47 @@
use std::net::SocketAddr;
use std::net::{IpAddr, SocketAddr};

pub use self::raw::RawPacket;
pub use self::tcp::IpStackTcpStream;
pub use self::udp::IpStackUdpStream;

mod raw;
mod tcb;
mod tcp;
mod udp;

pub enum IpStackStream {
Tcp(IpStackTcpStream),
Udp(IpStackUdpStream),
RawPacket(RawPacket),
}

impl IpStackStream {
pub fn local_addr(&self) -> SocketAddr {
match self {
IpStackStream::Tcp(tcp) => tcp.local_addr(),
IpStackStream::Udp(udp) => udp.local_addr(),
IpStackStream::RawPacket(_) => unreachable!(),
}
}
pub fn local_ip(&self) -> IpAddr {
match self {
IpStackStream::Tcp(tcp) => tcp.local_addr().ip(),
IpStackStream::Udp(udp) => udp.local_addr().ip(),
IpStackStream::RawPacket(_) => unreachable!(),
}
}
pub fn peer_addr(&self) -> SocketAddr {
match self {
IpStackStream::Tcp(tcp) => tcp.peer_addr(),
IpStackStream::Udp(udp) => udp.peer_addr(),
IpStackStream::RawPacket(_) => unreachable!(),
}
}
pub fn peer_ip(&self) -> IpAddr {
match self {
IpStackStream::Tcp(tcp) => tcp.peer_addr().ip(),
IpStackStream::Udp(udp) => udp.peer_addr().ip(),
IpStackStream::RawPacket(_) => unreachable!(),
}
}
}
38 changes: 38 additions & 0 deletions src/stream/raw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
use crate::IpStackError;
use etherparse::WriteError;
use tokio::sync::mpsc::UnboundedSender;

use crate::packet::NetworkPacket;

#[derive(Debug)]
pub struct RawPacket {
packet: Vec<u8>,
pkt_sender: UnboundedSender<NetworkPacket>,
mtu: u16,
}
impl RawPacket {
pub fn new(packet: Vec<u8>, pkt_sender: UnboundedSender<NetworkPacket>, mtu: u16) -> Self {
Self {
packet,
pkt_sender,
mtu,
}
}
pub fn bytes(&self) -> &[u8] {
&self.packet
}
pub fn mtu(&self) -> u16 {
self.mtu
}
pub async fn send(self, pkt: &[u8]) -> Result<(), IpStackError> {
self.pkt_sender
.send(NetworkPacket::parse_from(pkt)?)
.map_err(|_| {
IpStackError::PacketWriteError(WriteError::IoError(std::io::Error::new(
std::io::ErrorKind::Other,
"",
)))
})?;
Ok(())
}
}