edge_raw/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2#![allow(async_fn_in_trait)]
3#![warn(clippy::large_futures)]
4#![allow(clippy::uninlined_format_args)]
5#![allow(unknown_lints)]
6
7use core::net::{Ipv4Addr, SocketAddrV4};
8
9use self::udp::UdpPacketHeader;
10
11// This mod MUST go first, so that the others see its macros.
12pub(crate) mod fmt;
13
14#[cfg(feature = "io")]
15pub mod io;
16
17pub mod bytes;
18pub mod ip;
19pub mod udp;
20
21use bytes::BytesIn;
22
23/// An error type for decoding and encoding IP and UDP oackets
24#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
25pub enum Error {
26    DataUnderflow,
27    BufferOverflow,
28    InvalidFormat,
29    InvalidChecksum,
30}
31
32impl From<bytes::Error> for Error {
33    fn from(value: bytes::Error) -> Self {
34        match value {
35            bytes::Error::BufferOverflow => Self::BufferOverflow,
36            bytes::Error::DataUnderflow => Self::DataUnderflow,
37            bytes::Error::InvalidFormat => Self::InvalidFormat,
38        }
39    }
40}
41
42impl core::fmt::Display for Error {
43    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
44        let str = match self {
45            Self::DataUnderflow => "Data underflow",
46            Self::BufferOverflow => "Buffer overflow",
47            Self::InvalidFormat => "Invalid format",
48            Self::InvalidChecksum => "Invalid checksum",
49        };
50
51        write!(f, "{}", str)
52    }
53}
54
55#[cfg(feature = "defmt")]
56impl defmt::Format for Error {
57    fn format(&self, f: defmt::Formatter<'_>) {
58        let str = match self {
59            Self::DataUnderflow => "Data underflow",
60            Self::BufferOverflow => "Buffer overflow",
61            Self::InvalidFormat => "Invalid format",
62            Self::InvalidChecksum => "Invalid checksum",
63        };
64
65        defmt::write!(f, "{}", str)
66    }
67}
68
69impl core::error::Error for Error {}
70
71/// Decodes an IP packet and its UDP payload
72#[allow(clippy::type_complexity)]
73pub fn ip_udp_decode(
74    packet: &[u8],
75    filter_src: Option<SocketAddrV4>,
76    filter_dst: Option<SocketAddrV4>,
77) -> Result<Option<(SocketAddrV4, SocketAddrV4, &[u8])>, Error> {
78    if let Some((src, dst, _proto, udp_packet)) = ip::decode(
79        packet,
80        filter_src.map(|a| *a.ip()).unwrap_or(Ipv4Addr::UNSPECIFIED),
81        filter_dst.map(|a| *a.ip()).unwrap_or(Ipv4Addr::UNSPECIFIED),
82        Some(UdpPacketHeader::PROTO),
83    )? {
84        udp::decode(
85            src,
86            dst,
87            udp_packet,
88            filter_src.map(|a| a.port()),
89            filter_dst.map(|a| a.port()),
90        )
91    } else {
92        Ok(None)
93    }
94}
95
96/// Encodes an IP packet and its UDP payload
97pub fn ip_udp_encode<F>(
98    buf: &mut [u8],
99    src: SocketAddrV4,
100    dst: SocketAddrV4,
101    encoder: F,
102) -> Result<&[u8], Error>
103where
104    F: FnOnce(&mut [u8]) -> Result<usize, Error>,
105{
106    ip::encode(buf, *src.ip(), *dst.ip(), UdpPacketHeader::PROTO, |buf| {
107        Ok(udp::encode(buf, src, dst, encoder)?.len())
108    })
109}
110
111pub fn checksum_accumulate(bytes: &[u8], checksum_word: usize) -> u32 {
112    let mut bytes = BytesIn::new(bytes);
113
114    let mut sum: u32 = 0;
115    while !bytes.is_empty() {
116        let skip = (bytes.offset() >> 1) == checksum_word;
117        let arr = bytes
118            .arr()
119            .ok()
120            .unwrap_or_else(|| [unwrap!(bytes.byte(), "Unreachable"), 0]);
121
122        let word = if skip { 0 } else { u16::from_be_bytes(arr) };
123
124        sum += word as u32;
125    }
126
127    sum
128}
129
130pub fn checksum_finish(mut sum: u32) -> u16 {
131    while sum >> 16 != 0 {
132        sum = (sum >> 16) + (sum & 0xffff);
133    }
134
135    !sum as u16
136}