1use crate::packet::{header::TransportLayer, icmp::IcmpType, icmp6::Icmp6Type, Packet};
2use std::{cmp::Ordering, fmt};
3
4const PORT_DNS: u16 = 53;
6const PORT_DHCP_SERVER: u16 = 67;
7const PORT_DHCP_CLIENT: u16 = 68;
8const PORT_NTP: u16 = 123;
9const PORT_NETBIOS_NS: u16 = 137;
10const PORT_NETBIOS_DGM: u16 = 138;
11const PORT_SNMP: u16 = 161;
12const PORT_SNMP_TRAP: u16 = 162;
13const PORT_CLDAP: u16 = 389;
14const PORT_HTTPS: u16 = 443;
15const PORT_IKE: u16 = 500;
16const PORT_SYSLOG: u16 = 514;
17const PORT_RIP: u16 = 520;
18const PORT_DHCPV6_CLIENT: u16 = 546;
19const PORT_DHCPV6_SERVER: u16 = 547;
20const PORT_OPENVPN: u16 = 1194;
21const PORT_SSDP: u16 = 1900;
22const PORT_IPSEC_NATT: u16 = 4500;
23const PORT_MDNS: u16 = 5353;
24const PORT_LLMNR: u16 = 5355;
25const PORT_HTTPS_ALT: u16 = 8443;
26const PORT_HTTP: u16 = 80;
27const PORT_HTTP_ALT: u16 = 8080;
28const PORT_STUN: u16 = 3478;
29const PORT_STUN_TLS: u16 = 5349;
30
31const DNS_QR_BIT_MASK: u8 = 0x80;
33const NTP_MODE_MASK: u8 = 0x07;
34const TLS_HANDSHAKE_CONTENT_TYPE: u8 = 0x16;
35const TLS_CLIENT_HELLO: u8 = 0x01;
36const TLS_SERVER_HELLO: u8 = 0x02;
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
47pub enum PacketDirection {
48 #[default]
49 Upwards,
50 Downwards,
51}
52
53impl fmt::Display for PacketDirection {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 match self {
56 PacketDirection::Upwards => write!(f, "Upwards"),
57 PacketDirection::Downwards => write!(f, "Downwards"),
58 }
59 }
60}
61
62impl PacketDirection {
63 pub fn infer(pkt: &Packet<'_>) -> PacketDirection {
72 let transport = pkt.transport();
73 match transport {
74 Some(TransportLayer::Tcp(tcp)) => {
75 if tcp.header.has_syn() {
77 if tcp.header.has_ack() {
78 return PacketDirection::Downwards; } else {
80 return PacketDirection::Upwards; }
82 }
83
84 return Self::infer_direction_tcp(tcp.src_port(), tcp.dst_port(), pkt.data());
85 }
86 Some(TransportLayer::Udp(udp)) => {
87 Self::infer_direction_udp(udp.src_port(), udp.dst_port(), pkt.data())
88 }
89
90 Some(TransportLayer::Icmp(icmp)) => match icmp.icmp_type() {
91 IcmpType::ECHO => PacketDirection::Upwards,
92 IcmpType::ECHO_REPLY => PacketDirection::Downwards,
93 IcmpType::TIMESTAMP => PacketDirection::Upwards,
94 IcmpType::TIMESTAMP_REPLY => PacketDirection::Downwards,
95 IcmpType::INFO_REQUEST => PacketDirection::Upwards,
96 IcmpType::INFO_REPLY => PacketDirection::Downwards,
97 IcmpType::ADDRESS => PacketDirection::Upwards,
98 IcmpType::ADDRESS_REPLY => PacketDirection::Downwards,
99 IcmpType::EX_ECHO => PacketDirection::Upwards,
100 IcmpType::EX_ECHO_REPLY => PacketDirection::Downwards,
101 IcmpType::DEST_UNREACH => PacketDirection::Upwards,
102 IcmpType::SOURCE_QUENCH => PacketDirection::Upwards,
103 IcmpType::REDIRECT => PacketDirection::Upwards,
104 IcmpType::ROUTER_ADV => PacketDirection::Downwards,
105 IcmpType::ROUTER_SOLICIT => PacketDirection::Upwards,
106 IcmpType::TIME_EXCEEDED => PacketDirection::Upwards,
107 IcmpType::PARAMETER_PROBLEM => PacketDirection::Upwards,
108 _ => PacketDirection::Upwards,
109 },
110 Some(TransportLayer::Icmp6(icmp6)) => match icmp6.icmp6_type() {
111 Icmp6Type::DST_UNREACH => PacketDirection::Upwards,
112 Icmp6Type::PACKET_TOO_BIG => PacketDirection::Upwards,
113 Icmp6Type::TIME_EXCEEDED => PacketDirection::Upwards,
114 Icmp6Type::PARAM_PROB => PacketDirection::Upwards,
115 Icmp6Type::ECHO_REQUEST => PacketDirection::Upwards,
116 Icmp6Type::ECHO_REPLY => PacketDirection::Downwards,
117 Icmp6Type::MLD_LISTENER_QUERY => PacketDirection::Downwards,
118 Icmp6Type::MLD_LISTENER_REPORT => PacketDirection::Upwards,
119 Icmp6Type::MLD_LISTENER_REDUCTION => PacketDirection::Upwards,
120 Icmp6Type::ROUTER_SOLICITATION => PacketDirection::Upwards,
121 Icmp6Type::ROUTER_ADVERTISEMENT => PacketDirection::Downwards,
122 Icmp6Type::NEIGHBOR_SOLICITATION => PacketDirection::Upwards,
123 Icmp6Type::NEIGHBOR_ADVERTISEMENT => PacketDirection::Downwards,
124 Icmp6Type::REDIRECT_MESSAGE => PacketDirection::Upwards,
125 Icmp6Type::ROUTER_RENUMBERING => PacketDirection::Downwards,
126 Icmp6Type::NODE_INFORMATION_QUERY => PacketDirection::Upwards,
127 Icmp6Type::NODE_INFORMATION_RESPONSE => PacketDirection::Downwards,
128 Icmp6Type::INVERSE_NEIGHBOR_DISCOVERY_SOLICITATION => PacketDirection::Upwards,
129 Icmp6Type::INVERSE_NEIGHBOR_DISCOVERY_ADVERTISEMENT => PacketDirection::Downwards,
130 Icmp6Type::MULTICAST_LISTENER_DISCOVERY_REPORTS => PacketDirection::Upwards,
131 Icmp6Type::HOME_AGENT_ADDRESS_DISCOVERY_REQUEST => PacketDirection::Upwards,
132 Icmp6Type::HOME_AGENT_ADDRESS_DISCOVERY_REPLY => PacketDirection::Downwards,
133 Icmp6Type::MOBILE_PREFIX_SOLICITATION => PacketDirection::Upwards,
134 Icmp6Type::MOBILE_PREFIX_ADVERTISEMENT => PacketDirection::Downwards,
135 Icmp6Type::MULTICAST_ROUTER_SOLICITATION => PacketDirection::Upwards,
136 Icmp6Type::MULTICAST_ROUTER_TERMINATION => PacketDirection::Upwards,
137 Icmp6Type::FMIPV6 => PacketDirection::Upwards,
138 Icmp6Type::RPL_CONTROL_MESSAGE => PacketDirection::Upwards,
139 Icmp6Type::ILNPV6_LOCATOR_UPDATE => PacketDirection::Upwards,
140 Icmp6Type::DUPLICATE_ADDRESS_REQUEST => PacketDirection::Upwards,
141 Icmp6Type::DUPLICATE_ADDRESS_CONFIRM => PacketDirection::Downwards,
142 Icmp6Type::MPL_CONTROL_MESSAGE => PacketDirection::Upwards,
143 Icmp6Type::EXTENDED_ECHO_REQUEST => PacketDirection::Upwards,
144 Icmp6Type::EXTENDED_ECHO_REPLY => PacketDirection::Downwards,
145 _ => PacketDirection::Upwards,
146 },
147 Some(TransportLayer::Sctp(_)) | None => PacketDirection::Upwards,
148 }
149 }
150
151 fn infer_direction_udp(source: u16, dest: u16, data: &[u8]) -> PacketDirection {
159 match (source, dest) {
161 (PORT_DHCP_CLIENT, PORT_DHCP_SERVER) | (PORT_DHCPV6_CLIENT, PORT_DHCPV6_SERVER) => {
162 return PacketDirection::Upwards;
163 }
164 (PORT_DHCP_SERVER, PORT_DHCP_CLIENT) | (PORT_DHCPV6_SERVER, PORT_DHCPV6_CLIENT) => {
165 return PacketDirection::Downwards;
166 }
167 _ => {}
168 }
169
170 if (matches!(source, PORT_DNS | PORT_MDNS | PORT_LLMNR | PORT_NETBIOS_NS)
174 || matches!(dest, PORT_DNS | PORT_MDNS | PORT_LLMNR | PORT_NETBIOS_NS))
175 && source != dest
176 && data.len() >= 3
177 {
178 let is_response = (data[2] & DNS_QR_BIT_MASK) != 0;
179 return if is_response { PacketDirection::Downwards } else { PacketDirection::Upwards };
180 }
181
182 if (source == PORT_SSDP || dest == PORT_SSDP) && data.len() >= 8 {
184 if data.starts_with(b"HTTP/1.") {
185 return PacketDirection::Downwards;
186 }
187 if data.starts_with(b"M-SE") {
188 return PacketDirection::Upwards;
189 }
190 }
191
192 if (source == PORT_HTTPS || dest == PORT_HTTPS) && data.len() >= 1200 {
194 if (data[0] & 0xC0) == 0xC0 {
195 return PacketDirection::Upwards;
196 }
197 }
198
199 if (source == PORT_STUN || dest == PORT_STUN || source == PORT_STUN_TLS || dest == PORT_STUN_TLS) && data.len() >= 20 {
201 if (data[0] & 0xC0) == 0x00 {
202 let msg_type = ((data[0] as u16) << 8) | (data[1] as u16);
203 let is_response = (msg_type & 0x0110) != 0;
204 return if is_response { PacketDirection::Downwards } else { PacketDirection::Upwards };
205 }
206 }
207
208 if (source == PORT_NTP || dest == PORT_NTP) && !data.is_empty() {
210 match data[0] & NTP_MODE_MASK {
211 1 | 3 => return PacketDirection::Upwards, 2 | 4 | 5 => return PacketDirection::Downwards, _ => {
214 if source != dest {
215 return if dest == PORT_NTP { PacketDirection::Upwards } else { PacketDirection::Downwards };
216 }
217 }
218 }
219 }
220
221 if source == dest {
223 let threshold = match source {
224 PORT_DNS => Some(64), PORT_NTP => Some(48), PORT_NETBIOS_NS => Some(60), PORT_NETBIOS_DGM => Some(100), PORT_SNMP => Some(80), PORT_SNMP_TRAP => Some(80), PORT_CLDAP => Some(150), PORT_IKE => Some(200), PORT_SYSLOG => Some(200), PORT_RIP => Some(60), PORT_OPENVPN => Some(100), PORT_SSDP => Some(200), PORT_IPSEC_NATT => Some(200), PORT_MDNS => Some(80), PORT_LLMNR => Some(64), _ => None,
240 };
241
242 if let Some(t) = threshold {
243 return if data.len() <= t {
244 PacketDirection::Upwards
245 } else {
246 PacketDirection::Downwards
247 };
248 }
249 }
250
251 Self::infer_direction_from_ports(source, dest).unwrap_or(PacketDirection::Upwards)
253 }
254
255 fn infer_direction_tcp(source: u16, dest: u16, data: &[u8]) -> PacketDirection {
261 if (source == PORT_HTTP || dest == PORT_HTTP || source == PORT_HTTP_ALT || dest == PORT_HTTP_ALT) && data.len() >= 8 {
265 if data.starts_with(b"HTTP/1.") {
266 return PacketDirection::Downwards;
267 }
268 if data.starts_with(b"GET ") || data.starts_with(b"POST") || data.starts_with(b"PUT ") || data.starts_with(b"HEAD") {
269 return PacketDirection::Upwards;
270 }
271 }
272
273 if (source == PORT_HTTPS
275 || dest == PORT_HTTPS
276 || source == PORT_HTTPS_ALT
277 || dest == PORT_HTTPS_ALT)
278 && data.len() >= 6
279 {
280 if data[0] == TLS_HANDSHAKE_CONTENT_TYPE {
281 let handshake_type = data[5];
283 return match handshake_type {
284 TLS_CLIENT_HELLO => PacketDirection::Upwards, TLS_SERVER_HELLO => PacketDirection::Downwards, _ => {
287 if dest == PORT_HTTPS || dest == PORT_HTTPS_ALT {
288 PacketDirection::Upwards
289 } else {
290 PacketDirection::Downwards
291 }
292 }
293 };
294 }
295 }
296
297 if (source == PORT_DNS || dest == PORT_DNS) && data.len() >= 5 {
299 let is_response = (data[4] & DNS_QR_BIT_MASK) != 0;
300 return if is_response {
301 PacketDirection::Downwards
302 } else {
303 PacketDirection::Upwards
304 };
305 }
306
307 Self::infer_direction_from_ports(source, dest).unwrap_or(PacketDirection::Upwards)
309 }
310
311 fn infer_direction_from_ports(source: u16, dest: u16) -> Option<PacketDirection> {
314 if source == dest {
315 return None; }
317
318 let port_rank = |p: u16| -> u8 {
319 match p {
320 0..=1024 => 0,
321 1025..=49151 => 1,
322 49152..=u16::MAX => 2,
323 }
324 };
325
326 match port_rank(source).cmp(&port_rank(dest)) {
327 Ordering::Greater => Some(PacketDirection::Upwards), Ordering::Less => Some(PacketDirection::Downwards), Ordering::Equal => {
330 if source > dest {
331 Some(PacketDirection::Upwards)
332 } else {
333 Some(PacketDirection::Downwards)
334 }
335 }
336 }
337 }
338}