238 lines
6.1 KiB
Rust
238 lines
6.1 KiB
Rust
// TODO don't really want to document this now
|
|
#![allow(missing_docs)]
|
|
|
|
use core::{fmt, mem::size_of};
|
|
|
|
use crate::bitflags;
|
|
|
|
use super::{
|
|
types::{net_value::WrappedValue, NetValue, NetValueImpl},
|
|
MacAddress,
|
|
};
|
|
|
|
/////////// Layer 2 protocols ////////////
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
|
|
#[repr(C, packed)]
|
|
pub struct EthernetFrame {
|
|
pub destination_mac: MacAddress,
|
|
pub source_mac: MacAddress,
|
|
pub ethertype: NetValue<EtherType>,
|
|
}
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
|
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
|
|
#[repr(transparent)]
|
|
pub struct EtherType(pub u16);
|
|
|
|
impl EtherType {
|
|
pub const ARP: Self = Self(0x0806);
|
|
pub const IPV4: Self = Self(0x0800);
|
|
}
|
|
|
|
impl WrappedValue for EtherType {
|
|
type Inner = u16;
|
|
|
|
fn into_inner(self) -> u16 {
|
|
self.0
|
|
}
|
|
fn from_inner(value: u16) -> Self {
|
|
Self(value)
|
|
}
|
|
}
|
|
|
|
/////////// Layer 3 protocols ////////////
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
|
|
#[repr(C, packed)]
|
|
pub struct ArpFrame {
|
|
pub hardware_type: NetValue<u16>,
|
|
pub protocol: NetValue<EtherType>,
|
|
pub hardware_size: u8,
|
|
pub protocol_size: u8,
|
|
pub opcode: NetValue<u16>,
|
|
pub sender_mac: MacAddress,
|
|
pub sender_ip: NetValue<u32>,
|
|
pub target_mac: MacAddress,
|
|
// TODO handle IPv6
|
|
pub target_ip: NetValue<u32>,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
|
|
#[repr(C, packed)]
|
|
pub struct Ipv4Frame {
|
|
pub version_length: u8,
|
|
pub dscp_flags: u8,
|
|
pub total_length: NetValue<u16>,
|
|
pub id: NetValue<u16>,
|
|
pub flags_frag: NetValue<u16>,
|
|
pub ttl: u8,
|
|
pub protocol: IpProtocol,
|
|
pub header_checksum: NetValue<u16>,
|
|
pub source_address: NetValue<u32>,
|
|
pub destination_address: NetValue<u32>,
|
|
}
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
|
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
|
|
#[repr(transparent)]
|
|
pub struct IpProtocol(pub u8);
|
|
|
|
impl IpProtocol {
|
|
pub const ICMP: Self = Self(1);
|
|
pub const TCP: Self = Self(6);
|
|
pub const UDP: Self = Self(17);
|
|
}
|
|
|
|
/////////// Layer 4 protocols ////////////
|
|
|
|
#[derive(Clone, Copy)]
|
|
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
|
|
#[repr(C, packed)]
|
|
pub struct UdpFrame {
|
|
pub source_port: NetValue<u16>,
|
|
pub destination_port: NetValue<u16>,
|
|
pub length: NetValue<u16>,
|
|
pub checksum: NetValue<u16>,
|
|
}
|
|
|
|
bitflags! {
|
|
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
|
|
pub struct TcpFlags: u8 {
|
|
const FIN: bit 0;
|
|
const SYN: bit 1;
|
|
const RST: bit 2;
|
|
const PSH: bit 3;
|
|
const ACK: bit 4;
|
|
const URG: bit 5;
|
|
const ECE: bit 6;
|
|
const CWR: bit 7;
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
|
|
#[repr(C, packed)]
|
|
pub struct TcpFrame {
|
|
pub source_port: NetValue<u16>,
|
|
pub destination_port: NetValue<u16>,
|
|
pub sequence_number: NetValue<u32>,
|
|
pub acknowledge_number: NetValue<u32>,
|
|
pub data_offset: u8,
|
|
pub flags: TcpFlags,
|
|
pub window_size: NetValue<u16>,
|
|
pub checksum: NetValue<u16>,
|
|
pub urgent_pointer: NetValue<u16>,
|
|
}
|
|
|
|
#[derive(Clone, Copy)]
|
|
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
|
|
#[repr(C, packed)]
|
|
pub struct TcpV4PseudoHeader {
|
|
pub source_address: NetValue<u32>,
|
|
pub destination_address: NetValue<u32>,
|
|
pub _zero: u8,
|
|
pub protocol: IpProtocol,
|
|
pub tcp_length: NetValue<u16>,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
|
|
#[repr(C, packed)]
|
|
pub struct IcmpV4Frame {
|
|
pub ty: u8,
|
|
pub code: u8,
|
|
pub checksum: NetValue<u16>,
|
|
pub rest: NetValue<u32>,
|
|
}
|
|
|
|
/////////// Helper utilities /////////////
|
|
|
|
pub struct InetChecksum {
|
|
state: u32,
|
|
}
|
|
|
|
///////////// Implementations ////////////
|
|
|
|
/////////// Layer 3 protocols ////////////
|
|
|
|
impl fmt::Display for IpProtocol {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match *self {
|
|
Self::ICMP => f.write_str("ICMP"),
|
|
Self::UDP => f.write_str("UDP"),
|
|
Self::TCP => f.write_str("TCP"),
|
|
_ => f.write_str("<unknown-l4-proto>"),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Ipv4Frame {
|
|
pub fn header_length(&self) -> usize {
|
|
core::cmp::max(
|
|
(self.version_length & 0xF) << 2,
|
|
size_of::<Ipv4Frame>() as u8,
|
|
) as usize
|
|
}
|
|
|
|
pub fn total_length(&self) -> usize {
|
|
u16::from_network_order(self.total_length) as usize
|
|
}
|
|
}
|
|
|
|
/////////// Layer 4 protocols ////////////
|
|
|
|
impl UdpFrame {
|
|
pub fn data_length(&self) -> usize {
|
|
(u16::from_network_order(self.length) as usize).saturating_sub(size_of::<UdpFrame>())
|
|
}
|
|
}
|
|
|
|
impl TcpFrame {
|
|
pub fn data_offset(&self) -> usize {
|
|
((self.data_offset >> 4) as usize * size_of::<u32>()).max(size_of::<TcpFrame>())
|
|
}
|
|
}
|
|
|
|
/////////// Helper utilities /////////////
|
|
|
|
impl InetChecksum {
|
|
pub fn new() -> Self {
|
|
Self { state: 0 }
|
|
}
|
|
|
|
#[cfg(feature = "bytemuck")]
|
|
pub fn add_value<T: bytemuck::Pod>(&mut self, value: &T, reorder: bool) {
|
|
self.add_bytes(bytemuck::bytes_of(value), reorder)
|
|
}
|
|
|
|
pub fn add_bytes(&mut self, bytes: &[u8], reorder: bool) {
|
|
let len = bytes.len();
|
|
for i in 0..len / 2 {
|
|
let word = if reorder {
|
|
((bytes[i * 2] as u16) << 8) | (bytes[i * 2 + 1] as u16)
|
|
} else {
|
|
(bytes[i * 2] as u16) | ((bytes[i * 2 + 1] as u16) << 8)
|
|
};
|
|
self.state = self.state.wrapping_add(word as u32);
|
|
}
|
|
if len % 2 != 0 {
|
|
let word = if reorder {
|
|
(bytes[len - 1] as u16) << 8
|
|
} else {
|
|
todo!()
|
|
};
|
|
self.state = self.state.wrapping_add(word as u32);
|
|
}
|
|
}
|
|
|
|
pub fn finish(self) -> u16 {
|
|
let sum = (self.state >> 16) + (self.state & 0xFFFF);
|
|
|
|
(!(sum & 0xFFFF)) as u16
|
|
}
|
|
}
|