sniffnet_packet_parser/
packet.rs1use std::net::IpAddr;
2
3use etherparse::{ArpHardwareId, EtherType, LinkHeader, NetHeaders, TransportHeader};
4
5use crate::arp_type::ArpType;
6use crate::headers::{LinkInfo, NetInfo, TransportInfo, get_sniffable_headers};
7use crate::icmp_type::{IcmpTypeV4, IcmpTypeV6};
8use crate::link_type::LinkType;
9use crate::protocol::Protocol;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub struct ParsedPacket {
14 pub link_info: LinkInfo,
16 pub net_info: NetInfo,
18 pub transport_info: TransportInfo,
20}
21
22impl ParsedPacket {
23 #[must_use]
24 pub fn from_bytes(bytes: &[u8], link_type: LinkType) -> Option<ParsedPacket> {
26 let headers = get_sniffable_headers(bytes, link_type)?;
27
28 let link_info = analyze_link_header(headers.link);
29
30 let is_arp = matches!(&headers.net, Some(NetHeaders::Arp(_)));
31
32 let net_info = analyze_net_header(headers.net)?;
33
34 let transport_info = if is_arp {
35 Some(TransportInfo {
36 src_port: None,
37 dst_port: None,
38 protocol: Protocol::Arp,
39 icmp_type: None,
40 })
41 } else {
42 analyze_transport_header(headers.transport)
43 }?;
44
45 Some(ParsedPacket {
46 link_info,
47 net_info,
48 transport_info,
49 })
50 }
51
52 #[must_use]
53 pub fn bytes_count(&self) -> usize {
55 self.link_info.bytes.saturating_add(self.net_info.bytes)
56 }
57}
58
59fn analyze_link_header(link_header: Option<LinkHeader>) -> LinkInfo {
61 match link_header {
62 Some(LinkHeader::Ethernet2(header)) => {
63 let src_mac = Some(header.source);
64 let dst_mac = Some(header.destination);
65 let bytes = 14;
66 LinkInfo {
67 src_mac,
68 dst_mac,
69 bytes,
70 }
71 }
72 Some(LinkHeader::LinuxSll(header)) => {
73 let src_mac: Option<[u8; 6]> = if header.sender_address_valid_length == 6
74 && header.arp_hrd_type == ArpHardwareId::ETHERNET
75 && let Ok(sender) = header.sender_address[0..6].try_into()
76 {
77 Some(sender)
78 } else {
79 None
80 };
81 let dst_mac = None;
82 let bytes = 16;
83 LinkInfo {
84 src_mac,
85 dst_mac,
86 bytes,
87 }
88 }
89 None => LinkInfo {
90 src_mac: None,
91 dst_mac: None,
92 bytes: 0,
93 },
94 }
95}
96
97fn analyze_net_header(network_header: Option<NetHeaders>) -> Option<NetInfo> {
100 match network_header {
101 Some(NetHeaders::Ipv4(ipv4header, _)) => {
102 let src_ip = IpAddr::from(ipv4header.source);
103 let dst_ip = IpAddr::from(ipv4header.destination);
104 let bytes = usize::from(ipv4header.total_len);
105 Some(NetInfo {
106 src_ip,
107 dst_ip,
108 arp_type: None,
109 bytes,
110 })
111 }
112 Some(NetHeaders::Ipv6(ipv6header, _)) => {
113 let src_ip = IpAddr::from(ipv6header.source);
114 let dst_ip = IpAddr::from(ipv6header.destination);
115 let bytes = usize::from(ipv6header.payload_length) + 40;
116 Some(NetInfo {
117 src_ip,
118 dst_ip,
119 arp_type: None,
120 bytes,
121 })
122 }
123 Some(NetHeaders::Arp(arp_packet)) => {
124 let (src_ip, dst_ip) = match arp_packet.proto_addr_type {
125 EtherType::IPV4 => {
126 let src_ip =
127 match TryInto::<[u8; 4]>::try_into(arp_packet.sender_protocol_addr()) {
128 Ok(source) => IpAddr::from(source),
129 Err(_) => return None,
130 };
131 let dst_ip =
132 match TryInto::<[u8; 4]>::try_into(arp_packet.target_protocol_addr()) {
133 Ok(destination) => IpAddr::from(destination),
134 Err(_) => return None,
135 };
136 (src_ip, dst_ip)
137 }
138 EtherType::IPV6 => {
139 let src_ip =
140 match TryInto::<[u8; 16]>::try_into(arp_packet.sender_protocol_addr()) {
141 Ok(source) => IpAddr::from(source),
142 Err(_) => return None,
143 };
144 let dst_ip =
145 match TryInto::<[u8; 16]>::try_into(arp_packet.target_protocol_addr()) {
146 Ok(destination) => IpAddr::from(destination),
147 Err(_) => return None,
148 };
149 (src_ip, dst_ip)
150 }
151 _ => return None,
152 };
153 let bytes = arp_packet.packet_len();
154 let arp_type = ArpType::from_etherparse(arp_packet.operation);
155 Some(NetInfo {
156 src_ip,
157 dst_ip,
158 arp_type: Some(arp_type),
159 bytes,
160 })
161 }
162 None => None,
163 }
164}
165
166fn analyze_transport_header(transport_header: Option<TransportHeader>) -> Option<TransportInfo> {
169 match transport_header {
170 Some(TransportHeader::Udp(udp_header)) => {
171 let src_port = Some(udp_header.source_port);
172 let dst_port = Some(udp_header.destination_port);
173 let protocol = Protocol::Udp;
174 Some(TransportInfo {
175 src_port,
176 dst_port,
177 protocol,
178 icmp_type: None,
179 })
180 }
181 Some(TransportHeader::Tcp(tcp_header)) => {
182 let src_port = Some(tcp_header.source_port);
183 let dst_port = Some(tcp_header.destination_port);
184 let protocol = Protocol::Tcp;
185 Some(TransportInfo {
186 src_port,
187 dst_port,
188 protocol,
189 icmp_type: None,
190 })
191 }
192 Some(TransportHeader::Icmpv4(icmpv4_header)) => {
193 let src_port = None;
194 let dst_port = None;
195 let protocol = Protocol::Icmpv4;
196 let icmp_type = IcmpTypeV4::from_etherparse(&icmpv4_header.icmp_type);
197 Some(TransportInfo {
198 src_port,
199 dst_port,
200 protocol,
201 icmp_type: Some(icmp_type),
202 })
203 }
204 Some(TransportHeader::Icmpv6(icmpv6_header)) => {
205 let src_port = None;
206 let dst_port = None;
207 let protocol = Protocol::Icmpv6;
208 let icmp_type = IcmpTypeV6::from_etherparse(&icmpv6_header.icmp_type);
209 Some(TransportInfo {
210 src_port,
211 dst_port,
212 protocol,
213 icmp_type: Some(icmp_type),
214 })
215 }
216 Some(TransportHeader::Igmp(_)) => {
217 #[allow(clippy::match_same_arms)]
218 None
220 }
221 None => None,
222 }
223}