1use crate::packet::{header::TransportLayer, icmp::IcmpType, icmp6::Icmp6Type, Packet};
2use std::fmt;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
13pub enum PacketDirection {
14 #[default]
15 Upwards,
16 Downwards,
17}
18
19impl fmt::Display for PacketDirection {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 match self {
22 PacketDirection::Upwards => write!(f, "Upwards"),
23 PacketDirection::Downwards => write!(f, "Downwards"),
24 }
25 }
26}
27
28impl PacketDirection {
29 pub fn infer(pkt: &Packet<'_>) -> PacketDirection {
38 let transport = pkt.transport();
39 match transport {
40 Some(TransportLayer::Tcp(tcp)) => {
41 if tcp.header.has_syn() {
43 if tcp.header.has_ack() {
44 return PacketDirection::Downwards; } else {
46 return PacketDirection::Upwards; }
48 }
49
50 return Self::infer_by_ports_and_payload_tcp(
51 tcp.src_port(),
52 tcp.dst_port(),
53 pkt.data(),
54 );
55 }
56 Some(TransportLayer::Udp(udp)) => {
57 Self::infer_by_ports_and_payload_udp(udp.src_port(), udp.dst_port(), pkt.data())
58 }
59
60 Some(TransportLayer::Icmp(icmp)) => match icmp.icmp_type() {
61 IcmpType::ECHO => PacketDirection::Upwards,
62 IcmpType::ECHO_REPLY => PacketDirection::Downwards,
63 IcmpType::TIMESTAMP => PacketDirection::Upwards,
64 IcmpType::TIMESTAMP_REPLY => PacketDirection::Downwards,
65 IcmpType::INFO_REQUEST => PacketDirection::Upwards,
66 IcmpType::INFO_REPLY => PacketDirection::Downwards,
67 IcmpType::ADDRESS => PacketDirection::Upwards,
68 IcmpType::ADDRESS_REPLY => PacketDirection::Downwards,
69 IcmpType::EX_ECHO => PacketDirection::Upwards,
70 IcmpType::EX_ECHO_REPLY => PacketDirection::Downwards,
71 IcmpType::DEST_UNREACH => PacketDirection::Upwards,
72 IcmpType::SOURCE_QUENCH => PacketDirection::Upwards,
73 IcmpType::REDIRECT => PacketDirection::Upwards,
74 IcmpType::ROUTER_ADV => PacketDirection::Downwards,
75 IcmpType::ROUTER_SOLICIT => PacketDirection::Upwards,
76 IcmpType::TIME_EXCEEDED => PacketDirection::Upwards,
77 IcmpType::PARAMETER_PROBLEM => PacketDirection::Upwards,
78 _ => PacketDirection::Upwards,
79 },
80 Some(TransportLayer::Icmp6(icmp6)) => match icmp6.icmp6_type() {
81 Icmp6Type::DST_UNREACH => PacketDirection::Upwards,
82 Icmp6Type::PACKET_TOO_BIG => PacketDirection::Upwards,
83 Icmp6Type::TIME_EXCEEDED => PacketDirection::Upwards,
84 Icmp6Type::PARAM_PROB => PacketDirection::Upwards,
85 Icmp6Type::ECHO_REQUEST => PacketDirection::Upwards,
86 Icmp6Type::ECHO_REPLY => PacketDirection::Downwards,
87 Icmp6Type::MLD_LISTENER_QUERY => PacketDirection::Downwards,
88 Icmp6Type::MLD_LISTENER_REPORT => PacketDirection::Upwards,
89 Icmp6Type::MLD_LISTENER_REDUCTION => PacketDirection::Upwards,
90 Icmp6Type::ROUTER_SOLICITATION => PacketDirection::Upwards,
91 Icmp6Type::ROUTER_ADVERTISEMENT => PacketDirection::Downwards,
92 Icmp6Type::NEIGHBOR_SOLICITATION => PacketDirection::Upwards,
93 Icmp6Type::NEIGHBOR_ADVERTISEMENT => PacketDirection::Downwards,
94 Icmp6Type::REDIRECT_MESSAGE => PacketDirection::Upwards,
95 Icmp6Type::ROUTER_RENUMBERING => PacketDirection::Downwards,
96 Icmp6Type::NODE_INFORMATION_QUERY => PacketDirection::Upwards,
97 Icmp6Type::NODE_INFORMATION_RESPONSE => PacketDirection::Downwards,
98 Icmp6Type::INVERSE_NEIGHBOR_DISCOVERY_SOLICITATION => PacketDirection::Upwards,
99 Icmp6Type::INVERSE_NEIGHBOR_DISCOVERY_ADVERTISEMENT => PacketDirection::Downwards,
100 Icmp6Type::MULTICAST_LISTENER_DISCOVERY_REPORTS => PacketDirection::Upwards,
101 Icmp6Type::HOME_AGENT_ADDRESS_DISCOVERY_REQUEST => PacketDirection::Upwards,
102 Icmp6Type::HOME_AGENT_ADDRESS_DISCOVERY_REPLY => PacketDirection::Downwards,
103 Icmp6Type::MOBILE_PREFIX_SOLICITATION => PacketDirection::Upwards,
104 Icmp6Type::MOBILE_PREFIX_ADVERTISEMENT => PacketDirection::Downwards,
105 Icmp6Type::MULTICAST_ROUTER_SOLICITATION => PacketDirection::Upwards,
106 Icmp6Type::MULTICAST_ROUTER_TERMINATION => PacketDirection::Upwards,
107 Icmp6Type::FMIPV6 => PacketDirection::Upwards,
108 Icmp6Type::RPL_CONTROL_MESSAGE => PacketDirection::Upwards,
109 Icmp6Type::ILNPV6_LOCATOR_UPDATE => PacketDirection::Upwards,
110 Icmp6Type::DUPLICATE_ADDRESS_REQUEST => PacketDirection::Upwards,
111 Icmp6Type::DUPLICATE_ADDRESS_CONFIRM => PacketDirection::Downwards,
112 Icmp6Type::MPL_CONTROL_MESSAGE => PacketDirection::Upwards,
113 Icmp6Type::EXTENDED_ECHO_REQUEST => PacketDirection::Upwards,
114 Icmp6Type::EXTENDED_ECHO_REPLY => PacketDirection::Downwards,
115 _ => PacketDirection::Upwards,
116 },
117 Some(TransportLayer::Sctp(_)) | None => PacketDirection::Upwards,
118 }
119 }
120
121 fn infer_by_ports_and_payload_udp(source: u16, dest: u16, data: &[u8]) -> PacketDirection {
129 if source == 68 && dest == 67 {
133 return PacketDirection::Upwards;
134 }
135 if source == 67 && dest == 68 {
136 return PacketDirection::Downwards;
137 }
138
139 if source == 546 && dest == 547 {
141 return PacketDirection::Upwards;
142 }
143 if source == 547 && dest == 546 {
144 return PacketDirection::Downwards;
145 }
146
147 if (source == 53 || dest == 53) && source != dest && data.len() >= 3 {
149 let is_response = (data[2] & 0x80) != 0;
150 return if is_response {
151 PacketDirection::Downwards
152 } else {
153 PacketDirection::Upwards
154 };
155 }
156
157 if (source == 123 || dest == 123) && !data.is_empty() {
159 let mode = data[0] & 0x07;
160 return match mode {
161 1 | 3 => PacketDirection::Upwards, 2 | 4 | 5 => PacketDirection::Downwards, _ => {
164 if dest == 123 {
166 PacketDirection::Upwards
167 } else {
168 PacketDirection::Downwards
169 }
170 }
171 };
172 }
173
174 if source == dest {
176 let threshold = match source {
177 53 => Some(64), 123 => Some(48), 137 => Some(60), 138 => Some(100), 161 => Some(80), 162 => Some(80), 389 => Some(150), 500 => Some(200), 514 => Some(200), 520 => Some(60), 1194 => Some(100), 1900 => Some(200), 4500 => Some(200), 5353 => Some(80), 5355 => Some(64), _ => None,
193 };
194
195 if let Some(t) = threshold {
196 return if data.len() <= t {
197 PacketDirection::Upwards
198 } else {
199 PacketDirection::Downwards
200 };
201 }
202 }
203
204 let get_port_rank = |p: u16| -> u8 {
207 if p <= 1024 {
208 0
209 } else if p <= 49151 {
210 1
211 } else {
212 2
213 }
214 };
215
216 let src_rank = get_port_rank(source);
217 let dst_rank = get_port_rank(dest);
218
219 if src_rank > dst_rank {
220 return PacketDirection::Upwards; } else if src_rank < dst_rank {
222 return PacketDirection::Downwards; }
224
225 PacketDirection::Upwards
227 }
228
229 fn infer_by_ports_and_payload_tcp(source: u16, dest: u16, data: &[u8]) -> PacketDirection {
235 if (source == 443 || dest == 443 || source == 8443 || dest == 8443) && data.len() >= 6 {
239 if data[0] == 0x16 {
240 let handshake_type = data[5];
242 return match handshake_type {
243 0x01 => PacketDirection::Upwards, 0x02 => PacketDirection::Downwards, _ => {
246 if dest == 443 || dest == 8443 {
247 PacketDirection::Upwards
248 } else {
249 PacketDirection::Downwards
250 }
251 }
252 };
253 }
254 }
255
256 if (source == 53 || dest == 53) && data.len() >= 5 {
258 let is_response = (data[4] & 0x80) != 0;
259 return if is_response {
260 PacketDirection::Downwards
261 } else {
262 PacketDirection::Upwards
263 };
264 }
265
266 let get_port_rank = |p: u16| -> u8 {
268 if p <= 1024 {
269 0
270 } else if p <= 49151 {
271 1
272 } else {
273 2
274 }
275 };
276
277 let src_rank = get_port_rank(source);
278 let dst_rank = get_port_rank(dest);
279
280 if src_rank > dst_rank {
281 return PacketDirection::Upwards; } else if src_rank < dst_rank {
283 return PacketDirection::Downwards; }
285
286 PacketDirection::Upwards
288 }
289}