1use crate::custom_module::manycastr::{Address, Origin};
2use crate::net::{ICMPPacket, TCPPacket, UDPPacket};
3use crate::{A_ID, CHAOS_ID};
4use mac_address::mac_address_by_name;
5use pnet::ipnetwork::IpNetwork;
6use std::fs::File;
7use std::io::{BufRead, BufReader};
8use std::net::IpAddr;
9use std::process::Command;
10use std::time::{SystemTime, UNIX_EPOCH};
11
12fn get_default_gateway_ip_linux() -> Result<String, String> {
13 let file = File::open("/proc/net/route")
14 .map_err(|e| format!("Failed to open /proc/net/route: {e}"))?;
15 let reader = BufReader::new(file);
16
17 for line in reader.lines().skip(1) {
18 let line = line.map_err(|e| format!("Failed to read line: {e}"))?;
19 let fields: Vec<&str> = line.split_whitespace().collect();
20 if fields.len() >= 3 && fields[1] == "00000000" {
21 let hex = fields[2];
23 if hex.len() != 8 {
24 return Err(format!("Invalid gateway hex: {hex}"));
25 }
26
27 let bytes: Vec<u8> = (0..4)
28 .map(|i| u8::from_str_radix(&hex[2 * i..2 * i + 2], 16).unwrap())
29 .collect();
30
31 return Ok(format!(
33 "{}.{}.{}.{}",
34 bytes[3], bytes[2], bytes[1], bytes[0]
35 ));
36 }
37 }
38
39 Err("Could not find default gateway in /proc/net/route".to_string())
40}
41fn get_default_gateway_ip_freebsd() -> Result<String, String> {
42 let output = Command::new("route")
43 .args(["-n", "get", "default"])
44 .output()
45 .map_err(|e| format!("Failed to execute 'route -n get default': {e}"))?;
46
47 if !output.status.success() {
48 return Err(format!(
49 "Failed to get default route (FreeBSD): {}",
50 String::from_utf8_lossy(&output.stderr)
51 ));
52 }
53
54 let stdout = String::from_utf8_lossy(&output.stdout);
55 for line in stdout.lines() {
56 let trimmed_line = line.trim();
57 if trimmed_line.starts_with("gateway:") {
58 let parts: Vec<&str> = trimmed_line.split_whitespace().collect();
59 if parts.len() >= 2 {
60 return Ok(parts[1].to_string());
61 }
62 }
63 }
64 Err("Could not parse default gateway IP from 'route -n get default' output".to_string())
65}
66
67pub fn get_ethernet_header(is_ipv6: bool, if_name: String) -> Vec<u8> {
75 let mac_src = mac_address_by_name(&if_name)
77 .unwrap_or_else(|_| panic!("No MAC address found for interface: {if_name}"))
78 .unwrap()
79 .bytes()
80 .to_vec();
81
82 let gateway_ip = if cfg!(target_os = "freebsd") {
84 get_default_gateway_ip_freebsd().expect("Could not get default gateway IP")
85 } else if cfg!(target_os = "linux") {
86 get_default_gateway_ip_linux().expect("Could not get default gateway IP")
87 } else {
88 panic!("Unsupported OS");
89 };
90
91 let lines: Vec<String> = if cfg!(target_os = "freebsd") {
92 let output = std::process::Command::new("arp")
93 .arg("-an")
94 .output()
95 .map_err(|e| format!("Failed to run arp command on FreeBSD: {e}"))
96 .unwrap();
97
98 if !output.status.success() {
99 panic!("arp command failed on FreeBSD");
100 }
101
102 let stdout = String::from_utf8_lossy(&output.stdout);
103 stdout.lines().map(|s| s.to_string()).collect()
104 } else {
105 let file = File::open("/proc/net/arp")
106 .map_err(|e| format!("Failed to open /proc/net/arp: {e}"))
107 .unwrap();
108 let reader = BufReader::new(file);
109
110 reader
111 .lines()
112 .map(|line| line.expect("Failed to read line from /proc/net/arp"))
113 .collect()
114 };
115
116 let mut mac_dst: Option<[u8; 6]> = None;
118
119 for (i, line) in lines.iter().enumerate() {
120 if cfg!(target_os = "linux") && i == 0 {
121 continue;
123 }
124 let parts: Vec<&str> = line.split_whitespace().collect();
125
126 if parts.len() > 5 {
127 let addr = parts[0]; if addr != gateway_ip {
130 continue;
132 }
133
134 let mac_address = if cfg!(target_os = "freebsd") {
135 parts[1]
137 } else {
138 parts[3]
140 };
141
142 if (mac_address == "00:00:00:00:00:00") || (mac_address == "ff:ff:ff:ff:ff:ff") {
144 continue;
145 }
146
147 if parts[5] == if_name {
149 let mac_bytes: [u8; 6] = mac_address
150 .split(':')
151 .map(|s| u8::from_str_radix(s, 16).unwrap())
152 .collect::<Vec<u8>>()
153 .try_into()
154 .expect("MAC address should have 6 bytes");
155
156 mac_dst = Some(mac_bytes);
157 break;
158 }
159 }
160 }
161
162 if mac_dst.is_none() {
164 panic!("No destination MAC address found for interface: {if_name}");
165 }
166
167 let ether_type = if is_ipv6 { 0x86DDu16 } else { 0x0800u16 };
169 let mut ethernet_header: Vec<u8> = Vec::with_capacity(14);
170 ethernet_header.extend_from_slice(&mac_dst.unwrap());
171 ethernet_header.extend_from_slice(&mac_src);
172 ethernet_header.extend_from_slice(ðer_type.to_be_bytes());
173
174 ethernet_header
175}
176
177pub fn create_icmp(
195 origin: &Origin,
196 dst: &Address,
197 worker_id: u32,
198 measurement_id: u32,
199 info_url: &str,
200) -> Vec<u8> {
201 let tx_time = SystemTime::now()
202 .duration_since(UNIX_EPOCH)
203 .unwrap()
204 .as_micros() as u64;
205 let src = origin.src.expect("None IP address");
206
207 let mut payload_bytes: Vec<u8> = Vec::new();
209 payload_bytes.extend_from_slice(&measurement_id.to_be_bytes()); payload_bytes.extend_from_slice(&tx_time.to_be_bytes()); payload_bytes.extend_from_slice(&worker_id.to_be_bytes()); if src.is_v6() {
215 payload_bytes.extend_from_slice(&src.get_v6().to_be_bytes()); payload_bytes.extend_from_slice(&dst.get_v6().to_be_bytes()); ICMPPacket::echo_request_v6(
219 origin.dport as u16,
220 2,
221 payload_bytes,
222 src.get_v6(),
223 dst.get_v6(),
224 255,
225 info_url,
226 )
227 } else {
228 payload_bytes.extend_from_slice(&src.get_v4().to_be_bytes()); payload_bytes.extend_from_slice(&dst.get_v4().to_be_bytes()); ICMPPacket::echo_request(
232 origin.dport as u16,
233 2,
234 payload_bytes,
235 src.get_v4(),
236 dst.get_v4(),
237 255,
238 info_url,
239 )
240 }
241}
242
243pub fn create_dns(
267 origin: &Origin,
268 dst: &Address,
269 worker_id: u32,
270 measurement_type: u8,
271 qname: &str,
272) -> Vec<u8> {
273 let tx_time = SystemTime::now()
274 .duration_since(UNIX_EPOCH)
275 .unwrap()
276 .as_micros() as u64;
277 let src = &origin.src.expect("None IP address");
278 let sport = origin.sport as u16;
279
280 if measurement_type == A_ID {
281 UDPPacket::dns_request(src, dst, sport, qname, tx_time, worker_id, 255)
282 } else if measurement_type == CHAOS_ID {
283 UDPPacket::chaos_request(src, dst, sport, worker_id, qname)
284 } else {
285 panic!("Invalid measurement type")
286 }
287}
288
289pub fn create_tcp(
309 origin: &Origin,
310 dst: &Address,
311 worker_id: u32,
312 is_symmetric: bool,
313 info_url: &str,
314) -> Vec<u8> {
315 let ack = if !is_symmetric || worker_id > u16::MAX as u32 {
316 worker_id
318 } else {
319 SystemTime::now()
321 .duration_since(UNIX_EPOCH)
322 .unwrap()
323 .as_millis() as u32
324 };
325
326 TCPPacket::tcp_syn_ack(
327 &origin.src.unwrap(),
328 dst,
329 origin.sport as u16,
330 origin.dport as u16,
331 ack,
332 255,
333 info_url,
334 )
335}
336
337pub fn is_in_prefix(address: &str, prefix: &IpNetwork) -> bool {
355 let address = address
357 .parse::<IpAddr>()
358 .expect("Invalid IP address format");
359
360 match address {
361 IpAddr::V4(ipv4) => {
362 if let IpNetwork::V4(network_ip) = prefix {
363 network_ip.contains(ipv4)
365 } else {
366 false
367 }
368 }
369 IpAddr::V6(ipv6) => {
370 if let IpNetwork::V6(network_ip) = prefix {
371 network_ip.contains(ipv6)
373 } else {
374 false
375 }
376 }
377 }
378}