#![no_std]
#![allow(dead_code)]
#![deny(missing_docs)]
#![feature(generic_const_exprs)]
#[cfg(feature = "panic_never")]
use panic_never as _;
pub use byte_struct::{ByteStruct, ByteStructLen};
pub use modular_bitfield;
pub use ufmt::{derive::uDebug, uDebug, uDisplay, uWrite};
pub mod enet; pub mod ip; pub mod udp;
pub mod arp; pub mod dhcp;
pub use arp::*;
pub use dhcp::*;
pub use enet::*;
pub use ip::*;
pub use udp::*;
pub type MacAddr = ByteArray<6>;
impl MacAddr {
pub fn new(v: [u8; 6]) -> Self {
ByteArray(v)
}
pub const BROADCAST: MacAddr = ByteArray([0xFF_u8; 6]);
pub const ANY: MacAddr = ByteArray([0x0_u8; 6]);
}
pub type IpV4Addr = ByteArray<4>;
impl IpV4Addr {
pub fn new(v: [u8; 4]) -> Self {
ByteArray(v)
}
pub const BROADCAST: IpV4Addr = ByteArray([0xFF_u8; 4]);
pub const BROADCAST_LOCAL: IpV4Addr = ByteArray([0x0, 0x0, 0x0, 0xFF]);
pub const ANY: IpV4Addr = ByteArray([0x0_u8; 4]);
}
#[derive(Clone, Copy, uDebug, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
pub enum Protocol {
Tcp = 0x06,
Udp = 0x11,
Unimplemented,
}
impl ByteStructLen for Protocol {
const BYTE_LEN: usize = 1;
}
impl ByteStruct for Protocol {
fn read_bytes(bytes: &[u8]) -> Self {
return match bytes[0] {
x if x == (Protocol::Tcp as u8) => Protocol::Tcp,
x if x == (Protocol::Udp as u8) => Protocol::Udp,
_ => Protocol::Unimplemented,
};
}
fn write_bytes(&self, bytes: &mut [u8]) {
bytes[0] = *self as u8;
}
}
impl Protocol {
fn to_be_bytes(&self) -> [u8; Self::BYTE_LEN] {
(*self as u8).to_be_bytes()
}
}
#[derive(Clone, Copy, uDebug, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(u8)]
pub enum DSCP {
Standard = 0,
Realtime = 32 << 2,
Unimplemented,
}
impl ByteStructLen for DSCP {
const BYTE_LEN: usize = 1;
}
impl ByteStruct for DSCP {
fn read_bytes(bytes: &[u8]) -> Self {
return match bytes[0] {
x if x == (DSCP::Standard as u8) => DSCP::Standard,
x if x == (DSCP::Realtime as u8) => DSCP::Realtime,
_ => DSCP::Unimplemented,
};
}
fn write_bytes(&self, bytes: &mut [u8]) {
bytes[0] = *self as u8;
}
}
impl DSCP {
fn to_be_bytes(&self) -> [u8; Self::BYTE_LEN] {
(*self as u8).to_be_bytes()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct ByteArray<const N: usize>(pub [u8; N]);
impl<const N: usize> ByteStructLen for ByteArray<N> {
const BYTE_LEN: usize = N;
}
impl<const N: usize> ByteStruct for ByteArray<N> {
fn read_bytes(bytes: &[u8]) -> Self {
let mut out = [0_u8; N];
out.copy_from_slice(&bytes[0..N]);
ByteArray(out)
}
fn write_bytes(&self, bytes: &mut [u8]) {
for i in 0..N {
bytes[i] = self.0[i];
}
}
}
impl<const N: usize> ByteArray<N> {
pub fn to_be_bytes(&self) -> [u8; N] {
self.0
}
}
impl uDebug for ByteArray<4> {
fn fmt<W>(&self, f: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error>
where
W: uWrite + ?Sized,
{
<[u8; 4] as uDebug>::fmt(&self.0, f)
}
}
impl uDebug for ByteArray<6> {
fn fmt<W>(&self, f: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error>
where
W: uWrite + ?Sized,
{
<[u8; 6] as uDebug>::fmt(&self.0, f)
}
}
#[macro_export]
macro_rules! enum_with_unknown {
(
$( #[$enum_attr:meta] )*
pub enum $name:ident($ty:ty) {
$(
$( #[$variant_attr:meta] )*
$variant:ident = $value:expr
),+ $(,)?
}
) => {
#[derive(Debug, uDebug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
$( #[$enum_attr] )*
pub enum $name {
$(
$( #[$variant_attr] )*
$variant
),*,
Unknown($ty)
}
impl ::core::convert::From<$ty> for $name {
fn from(value: $ty) -> Self {
match value {
$( $value => $name::$variant ),*,
other => $name::Unknown(other)
}
}
}
impl ::core::convert::From<$name> for $ty {
fn from(value: $name) -> Self {
match value {
$( $name::$variant => $value ),*,
$name::Unknown(other) => other
}
}
}
}
}
pub fn calc_ip_checksum(data: &[u8]) -> u16 {
let sum = calc_ip_checksum_incomplete(data);
let checksum = calc_ip_checksum_finalize(sum);
checksum
}
pub fn calc_ip_checksum_finalize(sum: u32) -> u16 {
let mut sum = sum;
sum = (sum & 0xffff).wrapping_add(sum >> 16);
sum = (sum & 0xffff).wrapping_add(sum >> 16);
sum = (sum & 0xffff).wrapping_add(sum >> 16);
let checksum = !(sum as u16);
checksum
}
pub fn calc_ip_checksum_incomplete(data: &[u8]) -> u32 {
let mut sum: u32 = 0;
let mut i: usize = 0;
for x in data {
if i % 2 == 0 {
sum += (*x as u32) << 8;
} else {
sum += *x as u32;
};
i += 1;
}
sum
}
#[cfg(test)]
mod test {
use crate::*;
extern crate std;
use std::*;
#[test]
fn test_calc_ip_checksum() -> () {
let src_ipaddr: IpV4Addr = IpV4Addr::new([10, 0, 0, 1]);
let dst_ipaddr: IpV4Addr = IpV4Addr::new([10, 0, 0, 2]);
let mut sample_ipv4_header = IpV4Header {
version_and_header_length: VersionAndHeaderLength::new()
.with_version(4)
.with_header_length((IpV4Header::BYTE_LEN / 4) as u8),
dscp: DSCP::Standard,
total_length: IpV4Frame::<UdpFrame<ByteArray<8>>>::BYTE_LEN as u16,
identification: 0,
fragmentation: Fragmentation::default(),
time_to_live: 10,
protocol: Protocol::Udp,
checksum: 0,
src_ipaddr: src_ipaddr,
dst_ipaddr: dst_ipaddr,
};
let checksum_pre = calc_ip_checksum(&sample_ipv4_header.to_be_bytes());
sample_ipv4_header.checksum = checksum_pre;
let checksum_post = calc_ip_checksum(&sample_ipv4_header.to_be_bytes());
assert!(checksum_post == 0)
}
}