zero_packet/network/
icmpv4.rs1use super::checksum::internet_checksum;
2use core::fmt;
3
4pub const ICMPV4_HEADER_LENGTH: usize = 8;
6
7pub const ICMPV4_MAX_VALID_CODE: u8 = 15;
9
10pub struct Icmpv4Writer<'a> {
12 pub bytes: &'a mut [u8],
13}
14
15impl<'a> Icmpv4Writer<'a> {
16 #[inline]
18 pub fn new(bytes: &'a mut [u8]) -> Result<Self, &'static str> {
19 if bytes.len() < ICMPV4_HEADER_LENGTH {
20 return Err("Slice is too short to contain an ICMP header.");
21 }
22
23 Ok(Self { bytes })
24 }
25
26 #[inline]
28 pub fn header_len(&self) -> usize {
29 ICMPV4_HEADER_LENGTH
30 }
31
32 #[inline]
34 pub fn packet_len(&self) -> usize {
35 self.bytes.len()
36 }
37
38 #[inline]
40 pub fn set_icmp_type(&mut self, icmp_type: u8) {
41 self.bytes[0] = icmp_type;
42 }
43
44 #[inline]
46 pub fn set_icmp_code(&mut self, code: u8) {
47 self.bytes[1] = code;
48 }
49
50 #[inline]
56 pub fn set_payload(&mut self, payload: &[u8]) -> Result<(), &'static str> {
57 let start = self.header_len();
58 let payload_len = payload.len();
59
60 if self.packet_len() - start < payload_len {
61 return Err("Payload is too large to fit in the ICMPv4 packet.");
62 }
63
64 let end = start + payload_len;
65 self.bytes[start..end].copy_from_slice(payload);
66
67 Ok(())
68 }
69
70 #[inline]
74 pub fn set_checksum(&mut self) {
75 self.bytes[2] = 0;
76 self.bytes[3] = 0;
77 let checksum = internet_checksum(self.bytes, 0);
78 self.bytes[2] = (checksum >> 8) as u8;
79 self.bytes[3] = (checksum & 0xff) as u8;
80 }
81}
82
83#[derive(PartialEq)]
85pub struct Icmpv4Reader<'a> {
86 pub bytes: &'a [u8],
87}
88
89impl<'a> Icmpv4Reader<'a> {
90 #[inline]
92 pub fn new(bytes: &'a [u8]) -> Result<Self, &'static str> {
93 if bytes.len() < ICMPV4_HEADER_LENGTH {
94 return Err("Slice is too short to contain an ICMP header.");
95 }
96
97 Ok(Self { bytes })
98 }
99
100 #[inline]
102 pub fn icmp_type(&self) -> u8 {
103 self.bytes[0]
104 }
105
106 #[inline]
108 pub fn icmp_code(&self) -> u8 {
109 self.bytes[1]
110 }
111
112 #[inline]
114 pub fn checksum(&self) -> u16 {
115 ((self.bytes[2] as u16) << 8) | (self.bytes[3] as u16)
116 }
117
118 #[inline]
120 pub fn header_len(&self) -> usize {
121 ICMPV4_HEADER_LENGTH
122 }
123
124 #[inline]
126 pub fn header(&self) -> &'a [u8] {
127 &self.bytes[..ICMPV4_HEADER_LENGTH]
128 }
129
130 #[inline]
132 pub fn payload(&self) -> &'a [u8] {
133 &self.bytes[ICMPV4_HEADER_LENGTH..]
134 }
135}
136
137impl fmt::Debug for Icmpv4Reader<'_> {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 f.debug_struct("Icmpv4Packet")
140 .field("type", &self.icmp_type())
141 .field("code", &self.icmp_code())
142 .field("checksum", &self.checksum())
143 .finish()
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150
151 #[test]
152 fn getters_and_setters() {
153 let mut bytes = [0u8; ICMPV4_HEADER_LENGTH];
155
156 let icmp_type = 8;
158 let code = 0;
159
160 let mut writer = Icmpv4Writer::new(&mut bytes).unwrap();
162
163 writer.set_icmp_type(icmp_type);
165 writer.set_icmp_code(code);
166 writer.set_checksum();
167
168 let reader = Icmpv4Reader::new(&bytes).unwrap();
170
171 assert_eq!(reader.icmp_type(), icmp_type);
173 assert_eq!(reader.icmp_code(), code);
174 }
175}