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