microsandbox_network/packet/
frame.rs1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
4
5use etherparse::{IpNumber, SlicedPacket};
6
7pub const DNS_PORT: u16 = 53;
13
14pub struct ParsedFrame<'a> {
24 raw: &'a [u8],
26
27 sliced: SlicedPacket<'a>,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum IpProtocol {
34 Tcp,
36
37 Udp,
39
40 Icmpv4,
42
43 Icmpv6,
45
46 Other(u8),
48}
49
50impl<'a> ParsedFrame<'a> {
55 pub fn parse(raw: &'a [u8]) -> Option<Self> {
57 let sliced = SlicedPacket::from_ethernet(raw).ok()?;
58 Some(Self { raw, sliced })
59 }
60
61 pub fn raw(&self) -> &'a [u8] {
63 self.raw
64 }
65
66 pub fn sliced(&self) -> &SlicedPacket<'a> {
68 &self.sliced
69 }
70
71 pub fn ethertype(&self) -> Option<u16> {
73 match &self.sliced.link {
74 Some(etherparse::LinkSlice::Ethernet2(header)) => Some(header.ether_type().0),
75 _ => None,
76 }
77 }
78
79 pub fn is_arp(&self) -> bool {
81 matches!(&self.sliced.net, Some(etherparse::NetSlice::Arp(_)))
82 }
83
84 pub fn is_ndp(&self) -> bool {
89 match &self.sliced.transport {
90 Some(etherparse::TransportSlice::Icmpv6(icmpv6)) => {
91 let icmp_type = icmpv6.type_u8();
92 (133..=137).contains(&icmp_type)
93 }
94 _ => false,
95 }
96 }
97
98 pub fn src_mac(&self) -> Option<[u8; 6]> {
100 match &self.sliced.link {
101 Some(etherparse::LinkSlice::Ethernet2(header)) => Some(header.source()),
102 _ => None,
103 }
104 }
105
106 pub fn dst_mac(&self) -> Option<[u8; 6]> {
108 match &self.sliced.link {
109 Some(etherparse::LinkSlice::Ethernet2(header)) => Some(header.destination()),
110 _ => None,
111 }
112 }
113
114 pub fn src_ip(&self) -> Option<IpAddr> {
116 match &self.sliced.net {
117 Some(etherparse::NetSlice::Ipv4(h)) => {
118 Some(IpAddr::V4(Ipv4Addr::from(h.header().source())))
119 }
120 Some(etherparse::NetSlice::Ipv6(h)) => {
121 Some(IpAddr::V6(Ipv6Addr::from(h.header().source())))
122 }
123 _ => None,
124 }
125 }
126
127 pub fn dst_ip(&self) -> Option<IpAddr> {
129 match &self.sliced.net {
130 Some(etherparse::NetSlice::Ipv4(h)) => {
131 Some(IpAddr::V4(Ipv4Addr::from(h.header().destination())))
132 }
133 Some(etherparse::NetSlice::Ipv6(h)) => {
134 Some(IpAddr::V6(Ipv6Addr::from(h.header().destination())))
135 }
136 _ => None,
137 }
138 }
139
140 pub fn protocol(&self) -> Option<IpProtocol> {
142 match &self.sliced.net {
143 Some(etherparse::NetSlice::Ipv4(h)) => {
144 Some(ip_number_to_protocol(h.header().protocol()))
145 }
146 Some(etherparse::NetSlice::Ipv6(h)) => {
147 Some(ip_number_to_protocol(h.payload().ip_number))
150 }
151 _ => None,
152 }
153 }
154
155 pub fn src_port(&self) -> Option<u16> {
157 match &self.sliced.transport {
158 Some(etherparse::TransportSlice::Tcp(h)) => Some(h.source_port()),
159 Some(etherparse::TransportSlice::Udp(h)) => Some(h.source_port()),
160 _ => None,
161 }
162 }
163
164 pub fn dst_port(&self) -> Option<u16> {
166 match &self.sliced.transport {
167 Some(etherparse::TransportSlice::Tcp(h)) => Some(h.destination_port()),
168 Some(etherparse::TransportSlice::Udp(h)) => Some(h.destination_port()),
169 _ => None,
170 }
171 }
172
173 pub fn is_dns(&self) -> bool {
175 self.dst_port() == Some(DNS_PORT) || self.src_port() == Some(DNS_PORT)
176 }
177
178 pub fn payload(&self) -> &'a [u8] {
183 match &self.sliced.transport {
184 Some(etherparse::TransportSlice::Tcp(h)) => h.payload(),
185 Some(etherparse::TransportSlice::Udp(h)) => h.payload(),
186 _ => {
187 match &self.sliced.net {
189 Some(etherparse::NetSlice::Ipv4(h)) => h.payload().payload,
190 Some(etherparse::NetSlice::Ipv6(h)) => h.payload().payload,
191 _ => &[],
192 }
193 }
194 }
195 }
196}
197
198fn ip_number_to_protocol(n: IpNumber) -> IpProtocol {
203 match n {
204 IpNumber::TCP => IpProtocol::Tcp,
205 IpNumber::UDP => IpProtocol::Udp,
206 IpNumber::ICMP => IpProtocol::Icmpv4,
207 IpNumber::IPV6_ICMP => IpProtocol::Icmpv6,
208 other => IpProtocol::Other(other.0),
209 }
210}
211
212#[cfg(test)]
217mod tests {
218 use super::*;
219
220 fn build_udp_frame(
222 src_ip: [u8; 4],
223 dst_ip: [u8; 4],
224 src_port: u16,
225 dst_port: u16,
226 payload: &[u8],
227 ) -> Vec<u8> {
228 use etherparse::PacketBuilder;
229
230 let builder = PacketBuilder::ethernet2(
231 [0x02, 0x00, 0x00, 0x00, 0x00, 0x01], [0x02, 0x00, 0x00, 0x00, 0x00, 0x02], )
234 .ipv4(src_ip, dst_ip, 64)
235 .udp(src_port, dst_port);
236
237 let mut buf = Vec::new();
238 builder.write(&mut buf, payload).unwrap();
239 buf
240 }
241
242 fn build_tcp_frame(src_ip: [u8; 4], dst_ip: [u8; 4], src_port: u16, dst_port: u16) -> Vec<u8> {
244 use etherparse::PacketBuilder;
245
246 let builder = PacketBuilder::ethernet2(
247 [0x02, 0x00, 0x00, 0x00, 0x00, 0x01],
248 [0x02, 0x00, 0x00, 0x00, 0x00, 0x02],
249 )
250 .ipv4(src_ip, dst_ip, 64)
251 .tcp(src_port, dst_port, 0, 65535);
252
253 let mut buf = Vec::new();
254 builder.write(&mut buf, &[]).unwrap();
255 buf
256 }
257
258 #[test]
259 fn test_parse_udp_frame() {
260 let frame = build_udp_frame([10, 0, 0, 1], [10, 0, 0, 2], 12345, 80, b"hello");
261 let parsed = ParsedFrame::parse(&frame).unwrap();
262
263 assert_eq!(
264 parsed.src_ip(),
265 Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 1)))
266 );
267 assert_eq!(
268 parsed.dst_ip(),
269 Some(IpAddr::V4(Ipv4Addr::new(10, 0, 0, 2)))
270 );
271 assert_eq!(parsed.src_port(), Some(12345));
272 assert_eq!(parsed.dst_port(), Some(80));
273 assert_eq!(parsed.protocol(), Some(IpProtocol::Udp));
274 assert!(!parsed.is_dns());
275 assert!(!parsed.is_arp());
276 assert_eq!(parsed.payload(), b"hello");
277 }
278
279 #[test]
280 fn test_parse_dns_frame() {
281 let frame = build_udp_frame([10, 0, 0, 1], [10, 0, 0, 2], 50000, DNS_PORT, &[0; 12]);
282 let parsed = ParsedFrame::parse(&frame).unwrap();
283
284 assert!(parsed.is_dns());
285 assert_eq!(parsed.dst_port(), Some(DNS_PORT));
286 }
287
288 #[test]
289 fn test_parse_tcp_frame() {
290 let frame = build_tcp_frame([192, 168, 1, 1], [93, 184, 216, 34], 45000, 443);
291 let parsed = ParsedFrame::parse(&frame).unwrap();
292
293 assert_eq!(parsed.protocol(), Some(IpProtocol::Tcp));
294 assert_eq!(parsed.src_port(), Some(45000));
295 assert_eq!(parsed.dst_port(), Some(443));
296 }
297
298 #[test]
299 fn test_parse_mac_addresses() {
300 let frame = build_udp_frame([10, 0, 0, 1], [10, 0, 0, 2], 1234, 5678, &[]);
301 let parsed = ParsedFrame::parse(&frame).unwrap();
302
303 assert_eq!(parsed.src_mac(), Some([0x02, 0x00, 0x00, 0x00, 0x00, 0x01]));
304 assert_eq!(parsed.dst_mac(), Some([0x02, 0x00, 0x00, 0x00, 0x00, 0x02]));
305 }
306
307 #[test]
308 fn test_parse_garbage_returns_none() {
309 let garbage = [0xff; 5];
310 assert!(ParsedFrame::parse(&garbage).is_none());
311 }
312}