feat: tested nl-sys, and it links

This commit is contained in:
Andrew Rioux
2025-01-20 14:40:29 -05:00
parent 6b33f1a5ba
commit c2b5ea37d0
25 changed files with 3615 additions and 32 deletions

8
packets/Cargo.toml Normal file
View 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
View 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
View 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);
}
}