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
11pub(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#[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#[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
96pub 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}