feat: tested nl-sys, and it links
This commit is contained in:
8
packets/Cargo.toml
Normal file
8
packets/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "packets"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
40
packets/src/error.rs
Normal file
40
packets/src/error.rs
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (C) 2023 Andrew Rioux
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{error, fmt::Display};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
StringParse,
|
||||
UnknownPacketType(u16),
|
||||
PacketLengthInvalid,
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Error::StringParse => write!(f, "unable to parse a string from pcap"),
|
||||
Error::UnknownPacketType(ptype) => write!(f, "unknown packet type ({ptype})"),
|
||||
Error::PacketLengthInvalid => write!(
|
||||
f,
|
||||
"received a packet with a length that mismatched the header"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {}
|
||||
806
packets/src/lib.rs
Normal file
806
packets/src/lib.rs
Normal file
@@ -0,0 +1,806 @@
|
||||
// Copyright (C) 2023 Andrew Rioux
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use std::{
|
||||
net::Ipv4Addr,
|
||||
sync::atomic::{AtomicU16, Ordering},
|
||||
};
|
||||
|
||||
pub mod error;
|
||||
|
||||
pub struct EthernetPkt<'a> {
|
||||
pub data: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> EthernetPkt<'a> {
|
||||
pub fn destination_address(&self) -> &[u8; 6] {
|
||||
self.data[0..6].try_into().unwrap()
|
||||
}
|
||||
|
||||
pub fn source_address(&self) -> &[u8; 6] {
|
||||
self.data[6..12].try_into().unwrap()
|
||||
}
|
||||
|
||||
pub fn ether_type(&self) -> u16 {
|
||||
u16::from_be_bytes(self.data[12..14].try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn get_layer3_pkt(&'_ self) -> error::Result<Layer3Pkt<'_>> {
|
||||
if self.data.len() < 14 {
|
||||
return Err(error::Error::PacketLengthInvalid);
|
||||
}
|
||||
|
||||
match self.ether_type() {
|
||||
0x0800 => {
|
||||
if self.data.len() < 34 {
|
||||
return Err(error::Error::PacketLengthInvalid);
|
||||
}
|
||||
|
||||
let pkt = IPv4Pkt {
|
||||
data: &self.data[14..],
|
||||
};
|
||||
|
||||
if self.data.len() < (14 + pkt.total_length()).into() {
|
||||
return Err(error::Error::PacketLengthInvalid);
|
||||
}
|
||||
|
||||
Ok(Layer3Pkt::IPv4Pkt(pkt))
|
||||
}
|
||||
0x0806 => {
|
||||
if self.data.len() < 42 {
|
||||
return Err(error::Error::PacketLengthInvalid);
|
||||
}
|
||||
|
||||
let pkt = ARPPkt {
|
||||
data: &self.data[14..],
|
||||
};
|
||||
|
||||
Ok(Layer3Pkt::ARP(pkt))
|
||||
}
|
||||
p => Err(error::Error::UnknownPacketType(p)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_owned(&self) -> EthernetPacket {
|
||||
EthernetPacket {
|
||||
data: self.data.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Layer3Pkt<'a> {
|
||||
IPv4Pkt(IPv4Pkt<'a>),
|
||||
ARP(ARPPkt<'a>),
|
||||
}
|
||||
|
||||
pub struct IPv4Pkt<'a> {
|
||||
data: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> IPv4Pkt<'a> {
|
||||
pub fn version(&self) -> u8 {
|
||||
4
|
||||
}
|
||||
|
||||
pub fn header_len(&self) -> u8 {
|
||||
4 * (0x0F & self.data[0])
|
||||
}
|
||||
|
||||
pub fn type_of_service(&self) -> u8 {
|
||||
self.data[1] >> 2
|
||||
}
|
||||
|
||||
pub fn ecn(&self) -> u8 {
|
||||
self.data[1] & 0x03
|
||||
}
|
||||
|
||||
pub fn total_length(&self) -> u16 {
|
||||
u16::from_be_bytes(self.data[2..4].try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn id(&self) -> u16 {
|
||||
u16::from_be_bytes(self.data[4..6].try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn dont_fragment(&self) -> bool {
|
||||
self.data[6] & 0x40 != 0
|
||||
}
|
||||
|
||||
pub fn more_fragments(&self) -> bool {
|
||||
self.data[6] & 0x80 != 0
|
||||
}
|
||||
|
||||
pub fn fragment_offset(&self) -> u16 {
|
||||
u16::from_be_bytes([self.data[6] & 0x1F, self.data[7]]) * 8
|
||||
}
|
||||
|
||||
pub fn ttl(&self) -> u8 {
|
||||
self.data[8]
|
||||
}
|
||||
|
||||
pub fn protocol(&self) -> u8 {
|
||||
self.data[9]
|
||||
}
|
||||
|
||||
pub fn header_checksum(&self) -> u16 {
|
||||
u16::from_be_bytes(self.data[10..12].try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn source_ip(&self) -> Ipv4Addr {
|
||||
<&[u8] as TryInto<[u8; 4]>>::try_into(&self.data[12..16])
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn dest_ip(&self) -> Ipv4Addr {
|
||||
<&[u8] as TryInto<[u8; 4]>>::try_into(&self.data[16..20])
|
||||
.unwrap()
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn to_owned(&self) -> IPv4Packet {
|
||||
IPv4Packet {
|
||||
data: self.data.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_layer4_packet(&self) -> error::Result<Layer4Pkt<'a>> {
|
||||
match self.protocol() {
|
||||
// ICMP
|
||||
0x1 => Err(error::Error::UnknownPacketType(0x1)),
|
||||
// TCP
|
||||
0x6 => Ok(Layer4Pkt::TCP(TCPPkt {
|
||||
data: &self.data[(self.header_len() as usize)..],
|
||||
})),
|
||||
0x11 => Ok(Layer4Pkt::UDP(UDPPkt {
|
||||
data: &self.data[(self.header_len() as usize)..],
|
||||
})),
|
||||
p => Err(error::Error::UnknownPacketType(p.into())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ARPPkt<'a> {
|
||||
data: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> ARPPkt<'a> {
|
||||
pub fn htype(&self) -> u16 {
|
||||
u16::from_be_bytes(self.data[0..2].try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn ptype(&self) -> u16 {
|
||||
u16::from_be_bytes(self.data[2..4].try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn hwlen(&self) -> u8 {
|
||||
self.data[4]
|
||||
}
|
||||
|
||||
pub fn plen(&self) -> u8 {
|
||||
self.data[5]
|
||||
}
|
||||
|
||||
pub fn opcode(&self) -> u16 {
|
||||
u16::from_be_bytes(self.data[6..8].try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn srchwaddr(&self) -> &[u8] {
|
||||
&self.data[8usize..8usize + self.hwlen() as usize]
|
||||
}
|
||||
|
||||
pub fn srcprotoaddr(&self) -> &[u8] {
|
||||
let start = self.hwlen() as usize + 8;
|
||||
&self.data[start..start + self.plen() as usize]
|
||||
}
|
||||
|
||||
pub fn targethwaddr(&self) -> &[u8] {
|
||||
let start = self.hwlen() as usize + self.plen() as usize + 8;
|
||||
&self.data[start..start + self.hwlen() as usize]
|
||||
}
|
||||
|
||||
pub fn targetprotoaddr(&self) -> &[u8] {
|
||||
let start = self.hwlen() as usize + self.plen() as usize + self.hwlen() as usize + 8;
|
||||
&self.data[start..start + self.plen() as usize]
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Layer4Pkt<'a> {
|
||||
UDP(UDPPkt<'a>),
|
||||
TCP(TCPPkt<'a>),
|
||||
}
|
||||
|
||||
impl<'a> Layer4Pkt<'a> {
|
||||
pub fn len(&self) -> u16 {
|
||||
match self {
|
||||
Layer4Pkt::UDP(pkt) => pkt.len(),
|
||||
Layer4Pkt::TCP(pkt) => pkt.data.len() as u16,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct UDPPkt<'a> {
|
||||
data: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> UDPPkt<'a> {
|
||||
pub fn srcport(&self) -> u16 {
|
||||
u16::from_be_bytes(self.data[0..2].try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn dstport(&self) -> u16 {
|
||||
u16::from_be_bytes(self.data[2..4].try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn len(&self) -> u16 {
|
||||
u16::from_be_bytes(self.data[4..6].try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
pub fn to_owned(&self) -> UDPPacket {
|
||||
UDPPacket {
|
||||
data: self.data.to_vec(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_data(&'_ self) -> &'_ [u8] {
|
||||
&self.data[8..(self.len() as usize)]
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TCPPkt<'a> {
|
||||
data: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> TCPPkt<'a> {
|
||||
pub fn len(&self) -> u8 {
|
||||
(self.data[12] >> 4) * 4
|
||||
}
|
||||
|
||||
pub fn srcport(&self) -> u16 {
|
||||
u16::from_be_bytes(self.data[0..2].try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn dstport(&self) -> u16 {
|
||||
u16::from_be_bytes(self.data[2..4].try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn seqnumber(&self) -> u32 {
|
||||
u32::from_be_bytes(self.data[4..8].try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn acknumber(&self) -> u32 {
|
||||
u32::from_be_bytes(self.data[8..12].try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn flags(&self) -> u8 {
|
||||
self.data[13]
|
||||
}
|
||||
|
||||
pub fn cwr(&self) -> bool {
|
||||
self.flags() & 0x80 != 0
|
||||
}
|
||||
|
||||
pub fn ece(&self) -> bool {
|
||||
self.flags() & 0x40 != 0
|
||||
}
|
||||
|
||||
pub fn urg(&self) -> bool {
|
||||
self.flags() & 0x20 != 0
|
||||
}
|
||||
|
||||
pub fn ack(&self) -> bool {
|
||||
self.flags() & 0x10 != 0
|
||||
}
|
||||
|
||||
pub fn psh(&self) -> bool {
|
||||
self.flags() & 0x08 != 0
|
||||
}
|
||||
|
||||
pub fn rst(&self) -> bool {
|
||||
self.flags() & 0x04 != 0
|
||||
}
|
||||
|
||||
pub fn syn(&self) -> bool {
|
||||
self.flags() & 0x02 != 0
|
||||
}
|
||||
|
||||
pub fn fin(&self) -> bool {
|
||||
self.flags() & 0x01 != 0
|
||||
}
|
||||
|
||||
pub fn window(&self) -> u16 {
|
||||
u16::from_be_bytes(self.data[14..16].try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn checksum(&self) -> u16 {
|
||||
u16::from_be_bytes(self.data[16..18].try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn urgent_ptr(&self) -> u16 {
|
||||
u16::from_be_bytes(self.data[18..20].try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn options(&self) -> &[u8] {
|
||||
&self.data[20..(self.len() as usize)]
|
||||
}
|
||||
|
||||
pub fn data(&self) -> &[u8] {
|
||||
&self.data[(self.len() as usize)..]
|
||||
}
|
||||
|
||||
pub fn verify_checksum<I1, I2>(&self, srcip: I1, dstip: I2) -> bool
|
||||
where
|
||||
I1: Into<Ipv4Addr>,
|
||||
I2: Into<Ipv4Addr>,
|
||||
{
|
||||
let source = srcip.into();
|
||||
let dest = dstip.into();
|
||||
|
||||
let protocol = &[0x00u8, 0x06u8];
|
||||
|
||||
let tcp_length = (self.data.len()) as u16;
|
||||
|
||||
let len: u8 = (self.len()).try_into().unwrap();
|
||||
let len = len << 4;
|
||||
|
||||
let bytes = [
|
||||
&source.octets()[..],
|
||||
&dest.octets(),
|
||||
protocol,
|
||||
&tcp_length.to_be_bytes(),
|
||||
&self.srcport().to_be_bytes()[..],
|
||||
&self.dstport().to_be_bytes(),
|
||||
&self.seqnumber().to_be_bytes(),
|
||||
&self.acknumber().to_be_bytes(),
|
||||
&[len],
|
||||
&[self.flags()],
|
||||
&self.window().to_be_bytes(),
|
||||
&[0, 0],
|
||||
&self.urgent_ptr().to_be_bytes(),
|
||||
&self.options(),
|
||||
&self.data(),
|
||||
]
|
||||
.concat();
|
||||
|
||||
let checksum = bytes
|
||||
.chunks(2)
|
||||
.map(|pair| {
|
||||
if pair.len() == 1 {
|
||||
(pair[0] as u32) << 8
|
||||
} else {
|
||||
(pair[0] as u32) << 8 | (pair[1] as u32)
|
||||
}
|
||||
})
|
||||
.fold(0u32, |acc, e| acc + e)
|
||||
+ 30;
|
||||
// + 30 to intentionally deviate from
|
||||
// the RFC, to make it so that I can identify the packets as sparse
|
||||
// packets
|
||||
|
||||
let checksum = (checksum >> 16) + (checksum & 0xffff);
|
||||
let checksum = ((checksum >> 16) as u16) + (checksum as u16);
|
||||
|
||||
checksum == self.checksum()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EthernetPacket {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl EthernetPacket {
|
||||
pub fn construct(src: [u8; 6], dst: [u8; 6], packet: &Layer3Packet) -> EthernetPacket {
|
||||
match packet {
|
||||
Layer3Packet::IPv4(pkt) => EthernetPacket {
|
||||
data: [&dst[..], &src[..], &[0x08_u8, 0x00_u8][..], &*pkt.data].concat(),
|
||||
},
|
||||
Layer3Packet::ARP(pkt) => EthernetPacket {
|
||||
data: [&dst[..], &src[..], &[0x08_u8, 0x06_u8][..], &*pkt.data].concat(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pkt(&'_ self) -> EthernetPkt<'_> {
|
||||
EthernetPkt { data: &self.data }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Layer3Packet {
|
||||
IPv4(IPv4Packet),
|
||||
ARP(ARPPacket),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ARPPacket {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
pub enum ARPMode {
|
||||
Request,
|
||||
Reply,
|
||||
}
|
||||
|
||||
impl ARPMode {
|
||||
fn opcode(&self) -> u16 {
|
||||
match self {
|
||||
Self::Request => 1,
|
||||
Self::Reply => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ARPProto {
|
||||
IPv4,
|
||||
}
|
||||
|
||||
impl ARPProto {
|
||||
fn opcode(&self) -> u16 {
|
||||
match self {
|
||||
Self::IPv4 => 0x0800,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ARPPacket {
|
||||
pub fn construct(
|
||||
op: ARPMode,
|
||||
proto: ARPProto,
|
||||
srchwaddr: &[u8],
|
||||
targethwaddr: &[u8],
|
||||
srcprotoaddr: &[u8],
|
||||
targetprotoaddr: &[u8],
|
||||
) -> ARPPacket {
|
||||
assert!(srchwaddr.len() == targethwaddr.len());
|
||||
assert!(srcprotoaddr.len() == targetprotoaddr.len());
|
||||
|
||||
let data = [
|
||||
&0x0001u16.to_be_bytes()[..],
|
||||
&proto.opcode().to_be_bytes()[..],
|
||||
&[srchwaddr.len() as u8],
|
||||
&[srcprotoaddr.len() as u8],
|
||||
&op.opcode().to_be_bytes(),
|
||||
srchwaddr,
|
||||
srcprotoaddr,
|
||||
targethwaddr,
|
||||
targetprotoaddr,
|
||||
]
|
||||
.concat();
|
||||
|
||||
ARPPacket { data }
|
||||
}
|
||||
}
|
||||
|
||||
static IPV4_ID: AtomicU16 = AtomicU16::new(0xabcd);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct IPv4Packet {
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl IPv4Packet {
|
||||
pub fn construct<I1, I2>(source: I1, dest: I2, packet: &Layer4Packet) -> IPv4Packet
|
||||
where
|
||||
I1: Into<Ipv4Addr>,
|
||||
I2: Into<Ipv4Addr>,
|
||||
{
|
||||
let source = source.into();
|
||||
let dest = dest.into();
|
||||
|
||||
let dscpen: u8 = 0;
|
||||
|
||||
let totallen: u16 = 20 + packet.pkt().len();
|
||||
let id: u16 = IPV4_ID.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
let fragmentation: u16 = 0x4000;
|
||||
|
||||
let ttl: u8 = 64;
|
||||
let protocol: u8 = match packet {
|
||||
Layer4Packet::UDP(_) => 0x11,
|
||||
Layer4Packet::TCP(_) => 0x6,
|
||||
};
|
||||
|
||||
let source_upper = u16::from_be_bytes(source.octets()[0..2].try_into().unwrap());
|
||||
let source_lower = u16::from_be_bytes(source.octets()[2..4].try_into().unwrap());
|
||||
let dest_upper = u16::from_be_bytes(dest.octets()[0..2].try_into().unwrap());
|
||||
let dest_lower = u16::from_be_bytes(dest.octets()[2..4].try_into().unwrap());
|
||||
|
||||
let versihldscpen = 0x4500 | dscpen as u16;
|
||||
let ttlprotocol = ((ttl as u32) << 8_u8) | protocol as u32;
|
||||
|
||||
let checksum_part: u32 = versihldscpen as u32
|
||||
+ totallen as u32
|
||||
+ id as u32
|
||||
+ ttlprotocol
|
||||
+ fragmentation as u32
|
||||
+ source_upper as u32
|
||||
+ source_lower as u32
|
||||
+ dest_upper as u32
|
||||
+ dest_lower as u32;
|
||||
|
||||
let checksum: u16 = (((checksum_part & 0xFFFF) + (checksum_part >> 16)) & 0xFFFF)
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
let checksum = 0xFFFF - checksum;
|
||||
|
||||
let data = [
|
||||
&versihldscpen.to_be_bytes()[..],
|
||||
&totallen.to_be_bytes(),
|
||||
&id.to_be_bytes(),
|
||||
&fragmentation.to_be_bytes(),
|
||||
&[ttl, protocol],
|
||||
&checksum.to_be_bytes(),
|
||||
&source.octets(),
|
||||
&dest.octets(),
|
||||
match packet {
|
||||
Layer4Packet::UDP(pkt) => &*pkt.data,
|
||||
Layer4Packet::TCP(pkt) => &*pkt.data,
|
||||
},
|
||||
]
|
||||
.concat();
|
||||
|
||||
IPv4Packet { data }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pkt(&'_ self) -> IPv4Pkt<'_> {
|
||||
IPv4Pkt { data: &self.data }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Layer4Packet {
|
||||
UDP(UDPPacket),
|
||||
TCP(TCPPacket),
|
||||
}
|
||||
|
||||
impl Layer4Packet {
|
||||
pub fn pkt(&'_ self) -> Layer4Pkt<'_> {
|
||||
match self {
|
||||
Layer4Packet::UDP(pkt) => Layer4Pkt::UDP(pkt.pkt()),
|
||||
Layer4Packet::TCP(pkt) => Layer4Pkt::TCP(pkt.pkt()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UDPPacket {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl UDPPacket {
|
||||
pub fn construct<T>(source: u16, dest: u16, data: T) -> UDPPacket
|
||||
where
|
||||
T: Into<Vec<u8>>,
|
||||
{
|
||||
let data = data.into();
|
||||
|
||||
UDPPacket {
|
||||
data: [
|
||||
&source.to_be_bytes(),
|
||||
&dest.to_be_bytes(),
|
||||
&(8_u16 + TryInto::<u16>::try_into(data.len()).unwrap()).to_be_bytes(),
|
||||
&[0_u8, 0_u8],
|
||||
&*data,
|
||||
]
|
||||
.concat(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pkt(&'_ self) -> UDPPkt<'_> {
|
||||
UDPPkt { data: &self.data }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct TCPPacket {
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl TCPPacket {
|
||||
#[inline]
|
||||
pub fn pkt(&'_ self) -> TCPPkt<'_> {
|
||||
TCPPkt { data: &self.data }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
pub struct TCPPacketBuilder {
|
||||
srcport: Option<u16>,
|
||||
dstport: Option<u16>,
|
||||
seqnumber: Option<u32>,
|
||||
acknumber: u32,
|
||||
flags: u8,
|
||||
window: Option<u16>,
|
||||
urgent_ptr: u16,
|
||||
options: Vec<u8>,
|
||||
}
|
||||
|
||||
macro_rules! declare_flag {
|
||||
($name:ident, $offset:expr) => {
|
||||
pub fn $name(mut self, enable: bool) -> Self {
|
||||
if enable {
|
||||
self.flags |= $offset
|
||||
} else {
|
||||
self.flags &= (0xFF ^ $offset)
|
||||
}
|
||||
self
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl TCPPacketBuilder {
|
||||
pub fn srcport(mut self, port: u16) -> Self {
|
||||
self.srcport = Some(port);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn dstport(mut self, port: u16) -> Self {
|
||||
self.dstport = Some(port);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn seqnumber(mut self, num: u32) -> Self {
|
||||
self.seqnumber = Some(num);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn acknumber(mut self, num: u32) -> Self {
|
||||
self.acknumber = num;
|
||||
self
|
||||
}
|
||||
|
||||
declare_flag!(cwr, 0x80);
|
||||
declare_flag!(ece, 0x40);
|
||||
declare_flag!(urg, 0x20);
|
||||
declare_flag!(ack, 0x10);
|
||||
declare_flag!(psh, 0x08);
|
||||
declare_flag!(rst, 0x04);
|
||||
declare_flag!(syn, 0x02);
|
||||
declare_flag!(fin, 0x01);
|
||||
|
||||
pub fn window(mut self, window: u16) -> Self {
|
||||
self.window = Some(window);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn urgent_ptr(mut self, ptr: u16) -> Self {
|
||||
self.urgent_ptr = ptr;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn options(mut self, opts: Vec<u8>) -> Self {
|
||||
self.options = opts;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build<I1, I2>(
|
||||
self,
|
||||
srcip: I1,
|
||||
dstip: I2,
|
||||
data: Vec<u8>,
|
||||
checksum_offset: Option<u16>,
|
||||
) -> TCPPacket
|
||||
where
|
||||
I1: Into<Ipv4Addr>,
|
||||
I2: Into<Ipv4Addr>,
|
||||
{
|
||||
let source = srcip.into();
|
||||
let dest = dstip.into();
|
||||
|
||||
let protocol = &[0x00u8, 0x06u8];
|
||||
|
||||
let tcp_length = (data.len() + self.options.len() + 20) as u16;
|
||||
|
||||
let srcport = self.srcport.unwrap();
|
||||
let dstport = self.dstport.unwrap();
|
||||
let seqnumber = self.seqnumber.unwrap();
|
||||
let window = self.window.unwrap();
|
||||
|
||||
let mut options = self.options;
|
||||
while options.len() % 4 != 0 {
|
||||
options.push(0);
|
||||
}
|
||||
|
||||
let len: u8 = (options.len() / 4 + 5).try_into().unwrap();
|
||||
let len = len << 4;
|
||||
|
||||
let mut bytes = [
|
||||
&source.octets()[..],
|
||||
&dest.octets(),
|
||||
protocol,
|
||||
&tcp_length.to_be_bytes(),
|
||||
&srcport.to_be_bytes()[..],
|
||||
&dstport.to_be_bytes(),
|
||||
&seqnumber.to_be_bytes(),
|
||||
&self.acknumber.to_be_bytes(),
|
||||
&[len],
|
||||
&[self.flags],
|
||||
&window.to_be_bytes(),
|
||||
&[0, 0],
|
||||
&self.urgent_ptr.to_be_bytes(),
|
||||
&options,
|
||||
&data,
|
||||
]
|
||||
.concat();
|
||||
|
||||
let checksum = bytes
|
||||
.chunks(2)
|
||||
.map(|pair| {
|
||||
if pair.len() == 1 {
|
||||
(pair[0] as u32) << 8
|
||||
} else {
|
||||
(pair[0] as u32) << 8 | (pair[1] as u32)
|
||||
}
|
||||
})
|
||||
.fold(0u32, |acc, e| acc + e)
|
||||
+ (checksum_offset.unwrap_or(0) as u32);
|
||||
|
||||
let checksum = (checksum >> 16) + (checksum & 0xffff);
|
||||
let checksum = ((checksum >> 16) as u16) + (checksum as u16);
|
||||
let checksum = (!checksum).to_be_bytes();
|
||||
|
||||
//bytes[16] = checksum[0];
|
||||
//bytes[17] = checksum[1];
|
||||
bytes[28] = checksum[0];
|
||||
bytes[29] = checksum[1];
|
||||
|
||||
TCPPacket {
|
||||
data: bytes[12..].to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::TCPPacketBuilder;
|
||||
|
||||
#[test]
|
||||
fn test_checksum() {
|
||||
let tcp_packet = TCPPacketBuilder::default()
|
||||
.srcport(57116)
|
||||
.dstport(54248)
|
||||
.seqnumber(0xaed77262)
|
||||
.acknumber(0)
|
||||
.syn(true)
|
||||
.window(64240)
|
||||
.options(vec![
|
||||
0x02, 0x04, 0x05, 0xb4, 0x04, 0x02, 0x08, 0x0a, 0xd4, 0x06, 0xdf, 0x18, 0x00, 0x00,
|
||||
0x00, 0x00, 0x01, 0x03, 0x03, 0x07,
|
||||
])
|
||||
.build(
|
||||
Ipv4Addr::new(10, 104, 18, 67),
|
||||
Ipv4Addr::new(10, 104, 21, 16),
|
||||
vec![],
|
||||
None,
|
||||
);
|
||||
|
||||
assert_eq!(tcp_packet.pkt().checksum(), 0x898d);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user