Skip to content

Commit

Permalink
Merge pull request #17 from narrowlink/UnknownPackets
Browse files Browse the repository at this point in the history
Implement UnknownTransport and UnknownNetwork
SajjadPourali authored Jan 21, 2024
2 parents 0d76b50 + 71c5533 commit 4819292
Showing 7 changed files with 228 additions and 39 deletions.
29 changes: 28 additions & 1 deletion examples/tun.rs
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@
//!
use clap::Parser;
use etherparse::{IcmpEchoHeader, Icmpv4Header};
use ipstack::stream::IpStackStream;
use std::net::{Ipv4Addr, SocketAddr};
use tokio::{join, net::TcpStream};
@@ -52,7 +53,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {

let mut config = tun2::Configuration::default();
config.address(ipv4).netmask(netmask).mtu(MTU as i32).up();
config.destination(gateway).name("utun3");
config.destination(gateway);

#[cfg(target_os = "linux")]
config.platform(|config| {
@@ -109,6 +110,32 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("==== end UDP connection ====");
});
}
IpStackStream::UnknownTransport(u) => {
if u.src_addr().is_ipv4() && u.ip_protocol() == 1 {
let (icmp_header, req_payload) = Icmpv4Header::from_slice(u.payload())?;
if let etherparse::Icmpv4Type::EchoRequest(req) = icmp_header.icmp_type {
println!("ICMPv4 echo");
let echo = IcmpEchoHeader {
id: req.id,
seq: req.seq,
};
let mut resp = Icmpv4Header::new(etherparse::Icmpv4Type::EchoReply(echo));
resp.update_checksum(req_payload);
let mut payload = resp.to_bytes().to_vec();
payload.extend_from_slice(req_payload);
u.send(payload).await?;
} else {
println!("ICMPv4");
}
continue;
}
println!("unknown transport - Ip Protocol {}", u.ip_protocol());
continue;
}
IpStackStream::UnknownNetwork(payload) => {
println!("unknown transport - {} bytes", payload.len());
continue;
}
};
}
}
35 changes: 22 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -18,7 +18,7 @@ use tracing::{error, trace};

use crate::{
packet::IpStackPacketProtocol,
stream::{IpStackTcpStream, IpStackUdpStream},
stream::{IpStackTcpStream, IpStackUdpStream, IpStackUnknownTransport},
};
mod error;
mod packet;
@@ -99,26 +99,32 @@ impl IpStack {
select! {
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 {
#[cfg(feature = "log")]
trace!("parse error");
accept_sender.send(IpStackStream::UnknownNetwork(buffer[offset..n].to_vec()))?;
continue;
};
if let IpStackPacketProtocol::Unknown = packet.transport_protocol() {
accept_sender.send(IpStackStream::UnknownTransport(IpStackUnknownTransport::new(packet.src_addr().ip(),packet.dst_addr().ip(),packet.payload,&packet.ip,config.mtu,pkt_sender.clone())))?;
continue;
}

match streams.entry(packet.network_tuple()){
Occupied(entry) =>{
let t = packet.transport_protocol();
// 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 t{
// IpStackPacketProtocol::Tcp(_t) => {
// // dbg!(t.flags());
// }
// IpStackPacketProtocol::Udp => {
// // dbg!("udp");
// }
// IpStackPacketProtocol::Unknown => {
// // dbg!("unknown");
// }
// }

}
}
@@ -141,6 +147,9 @@ impl IpStack {
entry.insert(stream.stream_sender());
accept_sender.send(IpStackStream::Udp(stream))?;
}
IpStackPacketProtocol::Unknown => {
unreachable!()
}
}
}
}
57 changes: 40 additions & 17 deletions src/packet.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};

use etherparse::{IpHeader, PacketHeaders, TcpHeader, TransportHeader};
use etherparse::{IpHeader, PacketHeaders, TcpHeader, UdpHeader, WriteError};

use crate::error::IpStackError;

@@ -23,25 +23,38 @@ pub mod tcp_flags {

pub(crate) enum IpStackPacketProtocol {
Tcp(TcpPacket),
Unknown,
Udp,
}

pub(crate) enum TransportHeader {
Tcp(TcpHeader),
Udp(UdpHeader),
Unknown,
}

pub struct NetworkPacket {
pub ip: IpHeader,
pub transport: TransportHeader,
pub payload: Vec<u8>,
pub(crate) ip: IpHeader,
pub(crate) transport: TransportHeader,
pub(crate) payload: Vec<u8>,
}

impl NetworkPacket {
pub fn parse(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(_)))
})
.ok_or(IpStackError::UnsupportedTransportProtocol)?;
let payload = p.payload.to_vec();
let transport = match p.transport {
Some(etherparse::TransportHeader::Tcp(h)) => TransportHeader::Tcp(h),
Some(etherparse::TransportHeader::Udp(u)) => TransportHeader::Udp(u),
_ => TransportHeader::Unknown,
};

let payload = if let TransportHeader::Unknown = transport {
buf[ip.header_len()..].to_vec()
} else {
p.payload.to_vec()
};

Ok(NetworkPacket {
ip,
transport,
@@ -52,14 +65,14 @@ impl NetworkPacket {
match self.transport {
TransportHeader::Udp(_) => IpStackPacketProtocol::Udp,
TransportHeader::Tcp(ref h) => IpStackPacketProtocol::Tcp(h.into()),
_ => unreachable!(),
_ => IpStackPacketProtocol::Unknown,
}
}
pub fn src_addr(&self) -> SocketAddr {
let port = match &self.transport {
TransportHeader::Udp(udp) => udp.source_port,
TransportHeader::Tcp(tcp) => tcp.source_port,
_ => unreachable!(),
_ => 0,
};
match &self.ip {
IpHeader::Version4(ip, _) => {
@@ -74,7 +87,7 @@ impl NetworkPacket {
let port = match &self.transport {
TransportHeader::Udp(udp) => udp.destination_port,
TransportHeader::Tcp(tcp) => tcp.destination_port,
_ => unreachable!(),
_ => 0,
};
match &self.ip {
IpHeader::Version4(ip, _) => {
@@ -104,9 +117,19 @@ impl NetworkPacket {
self.ip
.write(&mut buf)
.map_err(IpStackError::PacketWriteError)?;
self.transport
.write(&mut buf)
.map_err(IpStackError::PacketWriteError)?;
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)?
}
_ => {}
};
// self.transport
// .write(&mut buf)
// .map_err(IpStackError::PacketWriteError)?;
buf.extend_from_slice(&self.payload);
Ok(buf)
}
20 changes: 19 additions & 1 deletion src/stream/mod.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,46 @@
use std::net::SocketAddr;
use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};

pub use self::tcp::IpStackTcpStream;
pub use self::udp::IpStackUdpStream;
pub use self::unknown::IpStackUnknownTransport;

mod tcb;
mod tcp;
mod udp;
mod unknown;

pub enum IpStackStream {
Tcp(IpStackTcpStream),
Udp(IpStackUdpStream),
UnknownTransport(IpStackUnknownTransport),
UnknownNetwork(Vec<u8>),
}

impl IpStackStream {
pub fn local_addr(&self) -> SocketAddr {
match self {
IpStackStream::Tcp(tcp) => tcp.local_addr(),
IpStackStream::Udp(udp) => udp.local_addr(),
IpStackStream::UnknownNetwork(_) => {
SocketAddr::V4(SocketAddrV4::new(std::net::Ipv4Addr::new(0, 0, 0, 0), 0))
}
IpStackStream::UnknownTransport(unknown) => match unknown.src_addr() {
std::net::IpAddr::V4(addr) => SocketAddr::V4(SocketAddrV4::new(addr, 0)),
std::net::IpAddr::V6(addr) => SocketAddr::V6(SocketAddrV6::new(addr, 0, 0, 0)),
},
}
}
pub fn peer_addr(&self) -> SocketAddr {
match self {
IpStackStream::Tcp(tcp) => tcp.peer_addr(),
IpStackStream::Udp(udp) => udp.peer_addr(),
IpStackStream::UnknownNetwork(_) => {
SocketAddr::V4(SocketAddrV4::new(std::net::Ipv4Addr::new(0, 0, 0, 0), 0))
}
IpStackStream::UnknownTransport(unknown) => match unknown.dst_addr() {
std::net::IpAddr::V4(addr) => SocketAddr::V4(SocketAddrV4::new(addr, 0)),
std::net::IpAddr::V6(addr) => SocketAddr::V6(SocketAddrV6::new(addr, 0, 0, 0)),
},
}
}
}
4 changes: 2 additions & 2 deletions src/stream/tcp.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use crate::{
error::IpStackError,
packet::{tcp_flags, IpStackPacketProtocol, TcpPacket},
packet::{tcp_flags, IpStackPacketProtocol, TcpPacket, TransportHeader},
stream::tcb::{Tcb, TcpState},
DROP_TTL, TTL,
};
use etherparse::{Ipv4Extensions, Ipv4Header, Ipv6Extensions, TransportHeader};
use etherparse::{Ipv4Extensions, Ipv4Header, Ipv6Extensions};
use std::{
cmp,
future::Future,
11 changes: 6 additions & 5 deletions src/stream/udp.rs
Original file line number Diff line number Diff line change
@@ -8,16 +8,17 @@ use std::{
time::Duration,
};

use etherparse::{
Ipv4Extensions, Ipv4Header, Ipv6Extensions, Ipv6Header, TransportHeader, UdpHeader,
};
use etherparse::{Ipv4Extensions, Ipv4Header, Ipv6Extensions, Ipv6Header, UdpHeader};
use tokio::{
io::{AsyncRead, AsyncWrite},
sync::mpsc::{self, UnboundedReceiver, UnboundedSender},
time::Sleep,
};

use crate::{packet::NetworkPacket, TTL};
// use crate::packet::TransportHeader;
use crate::{
packet::{NetworkPacket, TransportHeader},
TTL,
};

pub struct IpStackUdpStream {
src_addr: SocketAddr,
111 changes: 111 additions & 0 deletions src/stream/unknown.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use std::{io::Error, mem, net::IpAddr};

use etherparse::{IpHeader, Ipv4Extensions, Ipv4Header, Ipv6Extensions, Ipv6Header};
use tokio::sync::mpsc::UnboundedSender;

use crate::{
packet::{NetworkPacket, TransportHeader},
TTL,
};

pub struct IpStackUnknownTransport {
src_addr: IpAddr,
dst_addr: IpAddr,
payload: Vec<u8>,
protocol: u8,
mtu: u16,
packet_sender: UnboundedSender<NetworkPacket>,
}

impl IpStackUnknownTransport {
pub fn new(
src_addr: IpAddr,
dst_addr: IpAddr,
payload: Vec<u8>,
ip: &IpHeader,
mtu: u16,
packet_sender: UnboundedSender<NetworkPacket>,
) -> Self {
let protocol = match ip {
IpHeader::Version4(ip, _) => ip.protocol,
IpHeader::Version6(ip, _) => ip.next_header,
};
IpStackUnknownTransport {
src_addr,
dst_addr,
payload,
protocol,
mtu,
packet_sender,
}
}
pub fn src_addr(&self) -> IpAddr {
self.src_addr
}
pub fn dst_addr(&self) -> IpAddr {
self.dst_addr
}
pub fn payload(&self) -> &[u8] {
&self.payload
}
pub fn ip_protocol(&self) -> u8 {
self.protocol
}
pub async fn send(&self, mut payload: Vec<u8>) -> Result<(), Error> {
loop {
let packet = self.create_rev_packet(&mut payload)?;
self.packet_sender
.send(packet)
.map_err(|_| Error::new(std::io::ErrorKind::Other, "send error"))?;
if payload.is_empty() {
return Ok(());
}
}
}

pub fn create_rev_packet(&self, payload: &mut Vec<u8>) -> Result<NetworkPacket, Error> {
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 line_buffer = self.mtu.saturating_sub(ip_h.header_len() as u16);

let p = if payload.len() > line_buffer as usize {
payload.drain(0..line_buffer as usize).collect::<Vec<u8>>()
} else {
mem::take(payload)
};
ip_h.payload_len = p.len() as u16;
Ok(NetworkPacket {
ip: etherparse::IpHeader::Version4(ip_h, Ipv4Extensions::default()),
transport: TransportHeader::Unknown,
payload: p,
})
}
(std::net::IpAddr::V6(dst), std::net::IpAddr::V6(src)) => {
let mut ip_h = Ipv6Header {
traffic_class: 0,
flow_label: 0,
payload_length: 0,
next_header: 17,
hop_limit: TTL,
source: dst.octets(),
destination: src.octets(),
};
let line_buffer = self.mtu.saturating_sub(ip_h.header_len() as u16);
payload.truncate(line_buffer as usize);
ip_h.payload_length = payload.len() as u16;
let p = if payload.len() > line_buffer as usize {
payload.drain(0..line_buffer as usize).collect::<Vec<u8>>()
} else {
mem::take(payload)
};
Ok(NetworkPacket {
ip: etherparse::IpHeader::Version6(ip_h, Ipv6Extensions::default()),
transport: TransportHeader::Unknown,
payload: p,
})
}
_ => unreachable!(),
}
}
}

0 comments on commit 4819292

Please sign in to comment.