huginn_net/
packet_parser.rs1use pnet::packet::ethernet::{EtherTypes, EthernetPacket};
10use pnet::packet::ipv4::Ipv4Packet;
11use pnet::packet::ipv6::Ipv6Packet;
12use tracing::debug;
13
14#[derive(Debug)]
16pub enum IpPacket<'a> {
17 Ipv4(&'a [u8]),
19 Ipv6(&'a [u8]),
21 None,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq)]
27pub enum DatalinkFormat {
28 Ethernet,
30 RawIp,
32 Null,
34}
35
36pub fn parse_packet(packet: &[u8]) -> IpPacket<'_> {
51 if let Some(parsed) = try_ethernet_format(packet) {
53 return parsed;
54 }
55
56 if let Some(parsed) = try_raw_ip_format(packet) {
58 return parsed;
59 }
60
61 if let Some(parsed) = try_null_datalink_format(packet) {
63 return parsed;
64 }
65
66 IpPacket::None
67}
68
69fn try_ethernet_format(packet: &[u8]) -> Option<IpPacket<'_>> {
71 if packet.len() < 14 {
73 return None;
74 }
75
76 let ethernet = EthernetPacket::new(packet)?;
77 let ip_data = &packet[14..]; match ethernet.get_ethertype() {
80 EtherTypes::Ipv4 => {
81 if Ipv4Packet::new(ip_data).is_some() {
82 debug!("Parsed Ethernet IPv4 packet");
83 return Some(IpPacket::Ipv4(ip_data));
84 }
85 }
86 EtherTypes::Ipv6 => {
87 if Ipv6Packet::new(ip_data).is_some() {
88 debug!("Parsed Ethernet IPv6 packet");
89 return Some(IpPacket::Ipv6(ip_data));
90 }
91 }
92 _ => {}
93 }
94
95 None
96}
97
98fn try_raw_ip_format(packet: &[u8]) -> Option<IpPacket<'_>> {
100 if packet.len() < 20 {
101 return None;
102 }
103
104 let version = (packet[0] & 0xF0) >> 4;
106 match version {
107 4 => {
108 if Ipv4Packet::new(packet).is_some() {
109 debug!("Parsed Raw IPv4 packet");
110 return Some(IpPacket::Ipv4(packet));
111 }
112 }
113 6 => {
114 if Ipv6Packet::new(packet).is_some() {
115 debug!("Parsed Raw IPv6 packet");
116 return Some(IpPacket::Ipv6(packet));
117 }
118 }
119 _ => {}
120 }
121
122 None
123}
124
125fn try_null_datalink_format(packet: &[u8]) -> Option<IpPacket<'_>> {
127 if packet.len() < 24 || packet[0] != 0x1e || packet[1] != 0x00 {
129 return None;
130 }
131
132 let ip_data = &packet[4..]; let version = (ip_data[0] & 0xF0) >> 4;
134
135 match version {
136 4 => {
137 if Ipv4Packet::new(ip_data).is_some() {
138 debug!("Parsed NULL datalink IPv4 packet");
139 return Some(IpPacket::Ipv4(ip_data));
140 }
141 }
142 6 => {
143 if Ipv6Packet::new(ip_data).is_some() {
144 debug!("Parsed NULL datalink IPv6 packet");
145 return Some(IpPacket::Ipv6(ip_data));
146 }
147 }
148 _ => {}
149 }
150
151 None
152}
153
154pub fn detect_datalink_format(packet: &[u8]) -> Option<DatalinkFormat> {
158 if packet.len() >= 24 && packet[0] == 0x1e && packet[1] == 0x00 {
160 let ip_data = &packet[4..];
161 let version = (ip_data[0] & 0xF0) >> 4;
162 if version == 4 || version == 6 {
163 return Some(DatalinkFormat::Null);
164 }
165 }
166
167 if packet.len() >= 20 {
169 let version = (packet[0] & 0xF0) >> 4;
170 if version == 4 || version == 6 {
171 if version == 4 {
173 let ihl = (packet[0] & 0x0F).saturating_mul(4);
174 if ihl >= 20 && packet.len() >= usize::from(ihl) {
175 return Some(DatalinkFormat::RawIp);
176 }
177 }
178 else if version == 6 && packet.len() >= 40 {
180 return Some(DatalinkFormat::RawIp);
181 }
182 }
183 }
184
185 if packet.len() >= 14 {
187 if let Some(ethernet) = EthernetPacket::new(packet) {
188 let ethertype = ethernet.get_ethertype();
189 if ethertype == EtherTypes::Ipv4 || ethertype == EtherTypes::Ipv6 {
191 let ip_data = &packet[14..];
192 if !ip_data.is_empty() {
193 let version = (ip_data[0] & 0xF0) >> 4;
194 if (ethertype == EtherTypes::Ipv4 && version == 4)
195 || (ethertype == EtherTypes::Ipv6 && version == 6)
196 {
197 return Some(DatalinkFormat::Ethernet);
198 }
199 }
200 }
201 }
202 }
203
204 None
205}