1use pnet::datalink::MacAddr;
2use pnet::datalink::NetworkInterface;
3use pnet::datalink::interfaces;
4use pnet::ipnetwork::IpNetwork;
5use pnet::packet::ethernet::EtherTypes;
6use pnet::packet::ethernet::EthernetPacket;
7use pnet::packet::icmpv6;
8use pnet::packet::icmpv6::Icmpv6Code;
9use pnet::packet::icmpv6::Icmpv6Type;
10use pnet::packet::icmpv6::Icmpv6Types;
11use pnet::packet::icmpv6::MutableIcmpv6Packet;
12use pnet::packet::icmpv6::ndp::MutableRouterSolicitPacket;
13use pnet::packet::icmpv6::ndp::NdpOption;
14use pnet::packet::icmpv6::ndp::NdpOptionTypes;
15use pnet::packet::ip::IpNextHeaderProtocols;
16use pnet::packet::ipv6::MutableIpv6Packet;
17use regex::Regex;
18use std::collections::HashMap;
19use std::fmt;
20use std::net::IpAddr;
21use std::net::Ipv4Addr;
22use std::net::Ipv6Addr;
23use std::panic::Location;
24use std::process::Command;
25use std::str::FromStr;
26use std::sync::Arc;
27use std::sync::Mutex;
28use std::time::Duration;
29use tracing::debug;
30use tracing::warn;
31
32use crate::DST_CACHE;
33use crate::LayerMatch;
34use crate::NEIGHBOR_SCAN_STATUS;
35use crate::SYSTEM_NET_CACHE;
36use crate::error::PistolError;
37use crate::layer::ICMPV6_RS_HEADER_SIZE;
38use crate::layer::IPV6_HEADER_SIZE;
39use crate::layer::Layer3Match;
40use crate::layer::Layer4MatchIcmpv6;
41use crate::layer::PayloadMatch;
42use crate::layer::PayloadMatchIcmpv6;
43use crate::layer::PayloadMatchIp;
44use crate::layer::find_interface_by_index;
45use crate::layer::find_interface_by_src;
46use crate::layer::layer2_work;
47use crate::scan::arp::send_arp_scan_packet;
48use crate::scan::ndp_ns::send_ndp_ns_scan_packet;
49use crate::utils::neigh_cache_update;
50
51#[cfg(any(
52 target_os = "linux",
53 target_os = "freebsd",
54 target_os = "openbsd",
55 target_os = "netbsd",
56 target_os = "macos"
57))]
58fn find_interface_by_name(name: &str) -> Option<NetworkInterface> {
59 for interface in interfaces() {
60 if interface.name == name {
61 return Some(interface);
62 }
63 }
64 None
65}
66
67fn ipv6_addr_bsd_fix(dst_str: &str) -> Result<String, PistolError> {
68 if dst_str.contains("%") {
72 let bsd_fix_re = Regex::new(r"(?P<subnet>[^\s^%^/]+)(%(?P<dev>\w+))?(/(?P<mask>\d+))?")?;
73 match bsd_fix_re.captures(dst_str) {
74 Some(caps) => {
75 let addr = caps.name("subnet").map_or("", |m| m.as_str());
76 let mask = caps.name("mask").map_or("", |m| m.as_str());
77 if dst_str.contains("/") {
78 let output = addr.to_string() + "/" + mask;
79 return Ok(output);
80 } else {
81 return Ok(addr.to_string());
82 }
83 }
84 None => {
85 warn!("line: [{}] bsd_fix_re no match", dst_str);
86 Ok(String::new())
87 }
88 }
89 } else {
90 Ok(dst_str.to_string())
91 }
92}
93
94#[derive(Debug, Clone)]
95pub struct DefaultRoute {
96 pub via: IpAddr, pub dev: NetworkInterface, }
99
100impl fmt::Display for DefaultRoute {
101 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102 let default_routes_string = format!(
103 "default dst: {}, default interface: {}",
104 self.via, self.dev.name
105 );
106 write!(f, "{}", default_routes_string)
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq, Hash)]
111pub enum RouteAddr {
112 IpNetwork(IpNetwork),
113 IpAddr(IpAddr),
114}
115
116impl fmt::Display for RouteAddr {
117 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118 let output_str = match self {
119 RouteAddr::IpNetwork(ipn) => format!("{}", ipn),
120 RouteAddr::IpAddr(ip) => format!("{}", ip),
121 };
122 write!(f, "{}", output_str)
123 }
124}
125
126impl RouteAddr {
127 pub fn contains(&self, ip: IpAddr) -> bool {
128 match self {
129 RouteAddr::IpNetwork(ip_network) => ip_network.contains(ip),
130 RouteAddr::IpAddr(ip_addr) => {
131 if *ip_addr == ip {
132 true
133 } else {
134 false
135 }
136 }
137 }
138 }
139 pub fn is_unspecified(&self) -> bool {
140 match self {
141 RouteAddr::IpNetwork(ip_network) => ip_network.ip().is_unspecified(),
142 RouteAddr::IpAddr(ip_addr) => ip_addr.is_unspecified(),
143 }
144 }
145}
146
147#[derive(Debug, Clone)]
148struct InnerDefaultRoute {
149 via: IpAddr,
150 #[cfg(any(
152 target_os = "linux",
153 target_os = "freebsd",
154 target_os = "openbsd",
155 target_os = "netbsd",
156 target_os = "macos"
157 ))]
158 dev: String,
159 #[cfg(target_os = "windows")]
160 if_index: u32,
161}
162
163impl fmt::Display for InnerDefaultRoute {
164 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
165 #[cfg(any(
166 target_os = "linux",
167 target_os = "freebsd",
168 target_os = "openbsd",
169 target_os = "netbsd",
170 target_os = "macos"
171 ))]
172 let default_routes_string =
173 format!("default via: {}, default interface: {}", self.via, self.dev);
174 #[cfg(target_os = "windows")]
175 let default_routes_string = format!(
176 "default via: {}, default interface index: {}",
177 self.via, self.if_index
178 );
179 write!(f, "{}", default_routes_string)
180 }
181}
182
183#[derive(Debug, Clone)]
184struct InnerRouteInfo {
185 #[cfg(any(
187 target_os = "linux",
188 target_os = "freebsd",
189 target_os = "openbsd",
190 target_os = "netbsd",
191 target_os = "macos"
192 ))]
193 dev: String,
194 #[cfg(target_os = "windows")]
196 dev: u32,
197 via: Option<String>,
198}
199
200#[derive(Debug, Clone)]
202struct InnerRouteTable {
203 default_route: Option<InnerDefaultRoute>,
204 default_route6: Option<InnerDefaultRoute>,
205 routes: HashMap<RouteAddr, InnerRouteInfo>,
208}
209
210impl fmt::Display for InnerRouteTable {
211 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
212 let mut new_routes = HashMap::new();
213 for (r, n) in &self.routes {
214 let addr = match r {
215 RouteAddr::IpAddr(i) => i.to_string(),
216 RouteAddr::IpNetwork(i) => i.to_string(),
217 };
218 new_routes.insert(addr, n);
219 }
220 let mut output = String::new();
221 match &self.default_route {
222 Some(r) => {
223 output += &format!("ipv4: {}, ", r);
224 }
225 None => (),
226 }
227 match &self.default_route6 {
228 Some(r) => {
229 output += &format!("ipv6: {}, ", r);
230 }
231 None => (),
232 }
233 let routes_string = format!("routes: {:?}", new_routes);
234 output += &routes_string;
235 match output.strip_suffix(", ") {
236 Some(o) => write!(f, "{}", o),
237 None => write!(f, "{}", output),
238 }
239 }
240}
241
242impl InnerRouteTable {
243 #[cfg(target_os = "linux")]
244 fn parser(system_route_lines: &[String]) -> Result<InnerRouteTable, PistolError> {
245 let mut default_route = None;
246 let mut default_route6 = None;
247 let mut routes = HashMap::new();
248
249 for line in system_route_lines {
250 let line = line.trim();
251 if line.len() == 0 {
252 continue;
253 }
254 let default_route_judge = |line: &str| -> bool { line.starts_with("default") };
257 if default_route_judge(&line) {
258 let default_route_re =
259 Regex::new(r"^default\s+via\s+(?P<via>[^\s]+)\s+dev\s+(?P<dev>[^\s]+)(.+)?")?;
260 match default_route_re.captures(&line) {
261 Some(caps) => {
262 let via_str = caps.name("via").map_or("", |m| m.as_str());
263 let via: IpAddr = match via_str.parse() {
264 Ok(v) => v,
265 Err(e) => {
266 warn!(
267 "parse route table 'via' [{}] into IpAddr error: {}",
268 via_str, e
269 );
270 continue;
271 }
272 };
273 let dev = caps.name("dev").map_or("", |m| m.as_str()).to_string();
274 let inner_default_route = InnerDefaultRoute { via, dev };
275
276 let mut is_ipv4 = true;
277 if via_str.contains(":") {
278 is_ipv4 = false;
279 }
280
281 if is_ipv4 {
282 default_route = Some(inner_default_route);
283 } else {
284 default_route6 = Some(inner_default_route);
285 }
286 }
287 None => warn!("line: [{}] default_route_re no match", line),
288 }
289 } else {
290 let route_re_1 = Regex::new(r"^(?P<subnet>[^\s]+)\s+dev\s+(?P<dev>[^\s]+)(.+)?")?;
296 let route_re_2 = Regex::new(
297 r"^(?P<subnet>[^\s]+)\s+via\s+(?P<via>[^\s]+)\s+dev\s+(?P<dev>[^\s]+)(.+)?",
298 )?;
299 let caps = if let Some(caps) = route_re_1.captures(&line) {
301 Some(caps)
302 } else if let Some(caps) = route_re_2.captures(&line) {
303 Some(caps)
304 } else {
305 None
306 };
307
308 if let Some(caps) = caps {
309 let dst_str = caps.name("subnet").map_or("", |m| m.as_str());
310 let dst = if dst_str.contains("/") {
311 let dst = match IpNetwork::from_str(dst_str) {
312 Ok(d) => d,
313 Err(e) => {
314 warn!("parse route table 'dst' [{}] error: {}", dst_str, e);
315 continue;
316 }
317 };
318 let dst = RouteAddr::IpNetwork(dst);
319 dst
320 } else {
321 let dst: IpAddr = match dst_str.parse() {
322 Ok(d) => d,
323 Err(e) => {
324 warn!("parse route table 'dst' [{}] error: {}", dst_str, e);
325 continue;
326 }
327 };
328 let dst = RouteAddr::IpAddr(dst);
329 dst
330 };
331 let dev = caps.name("dev").map_or("", |m| m.as_str()).to_string();
332 let via = caps.name("via").map_or(None, |m| Some(m.as_str().into()));
333 let inner_route_info = InnerRouteInfo { dev, via };
334 routes.insert(dst, inner_route_info);
335 } else {
336 warn!("line: [{}] route_re_1 and route_re_2 both no match", line);
337 }
338 }
339 }
340
341 Ok(InnerRouteTable {
342 default_route,
343 default_route6,
344 routes,
345 })
346 }
347 #[cfg(any(
348 target_os = "freebsd",
349 target_os = "openbsd",
350 target_os = "netbsd",
351 target_os = "macos"
352 ))]
353 fn parser(system_route_lines: &[String]) -> Result<InnerRouteTable, PistolError> {
354 let mut default_route = None;
355 let mut default_route6 = None;
356 let mut routes = HashMap::new();
357
358 for line in system_route_lines {
359 if line.len() == 0
360 || line.starts_with("R")
361 || line.starts_with("I")
362 || line.starts_with("D")
363 {
364 continue;
365 }
366 let default_route_judge = |line: &str| -> bool { line.starts_with("default") };
367 if default_route_judge(&line) {
368 let line = line.trim();
369 let line_split: Vec<&str> = line
372 .split(" ")
373 .map(|x| x.trim())
374 .filter(|x| x.len() > 0)
375 .collect();
376 if line_split.len() >= 2 {
377 let via_str = line_split[1];
378 let via_str = ipv6_addr_bsd_fix(via_str)?;
379 let via: IpAddr = match via_str.parse() {
380 Ok(v) => v,
381 Err(e) => {
382 warn!(
383 "parse route table 'via' [{}] into IpAddr error: {}",
384 via_str, e
385 );
386 continue;
387 }
388 };
389 let dev = line_split[line_split.len() - 1].to_string();
390
391 let mut is_ipv4 = true;
392 if via_str.contains(":") {
393 is_ipv4 = false;
394 }
395
396 let inner_default_route = InnerDefaultRoute { via, dev };
397
398 if is_ipv4 {
399 default_route = Some(inner_default_route);
400 } else {
401 default_route6 = Some(inner_default_route);
402 }
403 } else {
404 warn!("line: [{}] default route split no match", line);
405 }
406 } else {
407 let line_split: Vec<&str> = line
409 .split(" ")
410 .map(|x| x.trim())
411 .filter(|x| x.len() > 0)
412 .collect();
413 if line_split.len() >= 3 {
414 let dst_str = line_split[0];
415 let dst_str = ipv6_addr_bsd_fix(dst_str)?;
416 let dst = if dst_str.contains("/") {
417 let dst = match IpNetwork::from_str(&dst_str) {
418 Ok(d) => d,
419 Err(e) => {
420 warn!("parse route table 'dst' [{}] error: {}", dst_str, e);
421 continue;
422 }
423 };
424 let dst = RouteAddr::IpNetwork(dst);
425 dst
426 } else {
427 let dst: IpAddr = match dst_str.parse() {
428 Ok(d) => d,
429 Err(e) => {
430 warn!("parse route table 'dst' [{}] error: {}", dst_str, e);
431 continue;
432 }
433 };
434 let dst = RouteAddr::IpAddr(dst);
435 dst
436 };
437 let dev = line_split[line_split.len() - 1].to_string();
438 let via = Some(line_split[1].to_string());
439 let inner_route_info = InnerRouteInfo { dev, via };
440 routes.insert(dst, inner_route_info);
441 } else {
442 warn!("line: [{}] route split no match", line);
443 }
444 }
445 }
446 Ok(InnerRouteTable {
447 default_route,
448 default_route6,
449 routes,
450 })
451 }
452 #[cfg(target_os = "windows")]
453 fn parser(system_route_lines: &[String]) -> Result<InnerRouteTable, PistolError> {
454 let mut default_route = None;
455 let mut default_route6 = None;
456 let mut routes = HashMap::new();
457
458 for line in system_route_lines {
459 let line = line.trim();
460 if line.len() == 0 || line.starts_with("-") || line.starts_with("i") {
461 continue;
462 }
463 let default_route_judge =
464 |line: &str| -> bool { line.contains("0.0.0.0/0") || line.contains("::/0") };
465 if default_route_judge(&line) {
466 let default_route_re =
468 Regex::new(r"^(?P<index>[^\s]+)\s+[^\s]+\s+(?P<via>[^\s]+)(.+)?")?;
469 match default_route_re.captures(&line) {
470 Some(caps) => {
471 let if_index = caps.name("index").map_or("", |m| m.as_str());
472 let if_index: u32 = match if_index.parse() {
473 Ok(i) => i,
474 Err(e) => {
475 warn!("parse route table 'if_index' [{}] error: {}", if_index, e);
476 continue;
477 }
478 };
479
480 let via_str = caps.name("via").map_or("", |m| m.as_str());
481 let via: IpAddr = match via_str.parse() {
482 Ok(v) => v,
483 Err(e) => {
484 warn!(
485 "parse route table 'via' [{}] into IpAddr error: {}",
486 via_str, e
487 );
488 continue;
489 }
490 };
491
492 let mut is_ipv4 = true;
493 if via_str.contains(":") {
494 is_ipv4 = false;
495 }
496
497 let inner_default_route = InnerDefaultRoute { via, if_index };
498
499 if is_ipv4 {
500 default_route = Some(inner_default_route);
501 } else {
502 default_route6 = Some(inner_default_route);
503 }
504 }
505 None => warn!("line: [{}] default_route_re no match", line),
506 }
507 } else {
508 let route_re =
511 Regex::new(r"^(?P<index>[^\s]+)\s+(?P<dst>[^\s]+)\s+(?P<via>[^\s]+)(.+)?")?;
512 match route_re.captures(&line) {
513 Some(caps) => {
514 let if_index = caps.name("index").map_or("", |m| m.as_str());
515 let if_index: u32 = match if_index.parse() {
516 Ok(i) => i,
517 Err(e) => {
518 warn!("parse route table 'if_index' [{}] error: {}", if_index, e);
519 continue;
520 }
521 };
522
523 let dst_str = caps.name("dst").map_or("", |m| m.as_str());
524 let dst = match IpNetwork::from_str(dst_str) {
525 Ok(d) => d,
526 Err(e) => {
527 warn!("parse route table 'dst' [{}] error: {}", dst_str, e);
528 continue;
529 }
530 };
531 let dst = RouteAddr::IpNetwork(dst);
532 let via = caps.name("via").map_or(None, |m| Some(m.as_str().into()));
533 let inner_route_info = InnerRouteInfo { dev: if_index, via };
534 routes.insert(dst, inner_route_info);
535 }
536 None => warn!("line: [{}] default_route_re no match", line),
537 }
538 }
539 }
540
541 Ok(InnerRouteTable {
542 default_route,
543 default_route6,
544 routes,
545 })
546 }
547}
548
549pub struct DstCache {
551 pub dst_addr: IpAddr,
552 pub src_addr: IpAddr,
553 pub mac: MacAddr,
554 pub interface: NetworkInterface,
555}
556
557impl DstCache {
558 pub fn update(
560 dst_addr: IpAddr,
561 src_addr: IpAddr,
562 mac: MacAddr,
563 interface: NetworkInterface,
564 ) -> Result<(), PistolError> {
565 match DST_CACHE.lock() {
566 Ok(mut dst_cache) => {
567 if !dst_cache.contains_key(&dst_addr) {
568 let dc = DstCache {
569 dst_addr,
570 src_addr,
571 mac,
572 interface,
573 };
574 let _ = dst_cache.insert(dst_addr, dc);
575 }
576 Ok(())
577 }
578 Err(e) => Err(PistolError::TryLockGlobalVarFailed {
579 var_name: String::from("DST_CACHE"),
580 e: e.to_string(),
581 }),
582 }
583 }
584 pub fn get(dst_addr: IpAddr) -> Result<Option<(MacAddr, NetworkInterface)>, PistolError> {
585 match DST_CACHE.lock() {
586 Ok(dst_cache) => {
587 let ret = dst_cache.get(&dst_addr);
588 if let Some(dc) = ret {
589 debug!("dst {} found in DST_CACHE", dst_addr);
590 Ok(Some((dc.mac, dc.interface.clone())))
591 } else {
592 debug!("dst {} not found in DST_CACHE", dst_addr);
593 Ok(None)
594 }
595 }
596 Err(e) => Err(PistolError::TryLockGlobalVarFailed {
597 var_name: String::from("DST_CACHE"),
598 e: e.to_string(),
599 }),
600 }
601 }
602}
603
604fn find_loopback_interface() -> Option<NetworkInterface> {
605 for interface in interfaces() {
606 if interface.is_loopback() {
607 return Some(interface);
608 }
609 }
610 None
611}
612
613fn addr_is_my_ip(ip: IpAddr) -> bool {
615 for interface in interfaces() {
616 for ipn in interface.ips {
617 if ipn.ip() == ip {
618 return true;
619 }
620 }
621 }
622 debug!("dst {} is not in host", ip);
623 false
624}
625
626fn addr_in_local_net(ip: IpAddr) -> bool {
628 for interface in interfaces() {
629 for ipn in interface.ips {
630 if ipn.contains(ip) {
631 return true;
632 }
633 }
634 }
635 debug!("dst {} is not in local net", ip);
637 false
638}
639
640pub fn get_default_route() -> Result<(Option<DefaultRoute>, Option<DefaultRoute>), PistolError> {
642 let snc = match SYSTEM_NET_CACHE.lock() {
644 Ok(snc) => snc.clone(),
645 Err(e) => {
646 return Err(PistolError::TryLockGlobalVarFailed {
647 var_name: String::from("SYSTEM_NET_CACHE"),
648 e: e.to_string(),
649 });
650 }
651 };
652 Ok((snc.default_route, snc.default_route6))
653}
654
655pub fn search_route_table(dst_addr: IpAddr) -> Result<Option<RouteInfo>, PistolError> {
657 let snc = match SYSTEM_NET_CACHE.lock() {
658 Ok(snc) => snc,
659 Err(e) => {
660 return Err(PistolError::TryLockGlobalVarFailed {
661 var_name: String::from("SYSTEM_NET_CACHE"),
662 e: e.to_string(),
663 });
664 }
665 };
666 Ok(snc.search_route_table(dst_addr))
667}
668
669pub fn search_mac(dst_addr: IpAddr) -> Result<Option<MacAddr>, PistolError> {
671 let snc = match SYSTEM_NET_CACHE.lock() {
672 Ok(snc) => snc,
673 Err(e) => {
674 return Err(PistolError::TryLockGlobalVarFailed {
675 var_name: String::from("SYSTEM_NET_CACHE"),
676 e: e.to_string(),
677 });
678 }
679 };
680 Ok(snc.search_mac(dst_addr))
681}
682
683fn get_mac_from_ndp_rs(buff: &[u8]) -> Option<MacAddr> {
684 match EthernetPacket::new(buff) {
686 Some(ethernet_packet) => {
687 let mac = ethernet_packet.get_source();
688 Some(mac)
689 }
690 None => None,
691 }
692}
693
694fn send_ndp_rs_packet(
695 src_ipv6: Ipv6Addr,
696 timeout: Option<Duration>,
697) -> Result<(Option<MacAddr>, Duration), PistolError> {
698 let route_addr_2 = Ipv6Addr::new(0xFF02, 0, 0, 0, 0, 0, 0, 0x0002);
700 let interface = match find_interface_by_src(src_ipv6.into()) {
702 Some(i) => i,
703 None => return Err(PistolError::CanNotFoundInterface),
704 };
705 let src_mac = match interface.mac {
706 Some(m) => m,
707 None => return Err(PistolError::CanNotFoundMacAddress),
708 };
709
710 let mut ipv6_buff = [0u8; IPV6_HEADER_SIZE + ICMPV6_RS_HEADER_SIZE];
712 let mut ipv6_header = match MutableIpv6Packet::new(&mut ipv6_buff) {
713 Some(p) => p,
714 None => {
715 return Err(PistolError::BuildPacketError {
716 location: format!("{}", Location::caller()),
717 });
718 }
719 };
720 ipv6_header.set_version(6);
721 ipv6_header.set_traffic_class(0);
722 ipv6_header.set_flow_label(0);
723 ipv6_header.set_payload_length(ICMPV6_RS_HEADER_SIZE as u16);
724 ipv6_header.set_next_header(IpNextHeaderProtocols::Icmpv6);
725 ipv6_header.set_hop_limit(255);
726 ipv6_header.set_source(src_ipv6);
727 ipv6_header.set_destination(route_addr_2);
728
729 let mut icmpv6_header =
731 match MutableRouterSolicitPacket::new(&mut ipv6_buff[IPV6_HEADER_SIZE..]) {
732 Some(p) => p,
733 None => {
734 return Err(PistolError::BuildPacketError {
735 location: format!("{}", Location::caller()),
736 });
737 }
738 };
739 icmpv6_header.set_icmpv6_type(Icmpv6Type(133));
741 icmpv6_header.set_icmpv6_code(Icmpv6Code(0));
742 icmpv6_header.set_reserved(0);
743 let ndp_option = NdpOption {
744 option_type: NdpOptionTypes::SourceLLAddr,
745 length: 1,
746 data: src_mac.octets().to_vec(),
747 };
748 icmpv6_header.set_options(&vec![ndp_option]);
749
750 let mut icmpv6_header = match MutableIcmpv6Packet::new(&mut ipv6_buff[IPV6_HEADER_SIZE..]) {
751 Some(p) => p,
752 None => {
753 return Err(PistolError::BuildPacketError {
754 location: format!("{}", Location::caller()),
755 });
756 }
757 };
758 let checksum = icmpv6::checksum(&icmpv6_header.to_immutable(), &src_ipv6, &route_addr_2);
759 icmpv6_header.set_checksum(checksum);
760
761 let layer3 = Layer3Match {
767 name: "ndp_ns layer3",
768 layer2: None,
769 src_addr: None,
770 dst_addr: None,
771 };
772 let payload_ip = PayloadMatchIp {
774 src_addr: None,
775 dst_addr: None,
776 };
777 let payload_icmpv6 = PayloadMatchIcmpv6 {
778 layer3: Some(payload_ip),
779 icmpv6_type: Some(Icmpv6Types::RouterSolicit),
780 icmpv6_code: None,
781 };
782 let payload = PayloadMatch::PayloadMatchIcmpv6(payload_icmpv6);
783 let layer4_icmpv6 = Layer4MatchIcmpv6 {
784 name: "ndp_ns icmpv6",
785 layer3: Some(layer3),
786 icmpv6_type: Some(Icmpv6Types::RouterAdvert), icmpv6_code: None,
788 payload: Some(payload),
789 };
790 let layer_match = LayerMatch::Layer4MatchIcmpv6(layer4_icmpv6);
791
792 let dst_mac = MacAddr(33, 33, 00, 00, 00, 02);
793 let ethernet_type = EtherTypes::Ipv6;
794 let (r, rtt) = layer2_work(
795 dst_mac,
796 interface.clone(),
797 &ipv6_buff,
798 IPV6_HEADER_SIZE + ICMPV6_RS_HEADER_SIZE,
799 ethernet_type,
800 vec![layer_match],
801 timeout,
802 true,
803 )?;
804
805 let mac = get_mac_from_ndp_rs(&r);
806 Ok((mac, rtt))
807}
808
809#[derive(Debug, Clone, Copy)]
810pub enum RouteVia {
811 IpAddr(IpAddr),
813 IfIndex(u32),
815 MacAddr(MacAddr),
817}
818
819impl fmt::Display for RouteVia {
820 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
821 let output = match self {
822 RouteVia::IpAddr(ip_addr) => format!("via ip_addr {}", ip_addr),
823 RouteVia::IfIndex(if_index) => format!("via if_index {}", if_index),
824 RouteVia::MacAddr(mac_addr) => format!("via mac_addr {}", mac_addr),
825 };
826 write!(f, "{}", output)
827 }
828}
829
830impl RouteVia {
831 fn standard_process(
832 dst_addr: IpAddr,
833 src_addr: IpAddr,
834 src_interface: NetworkInterface,
835 timeout: Option<Duration>,
836 ) -> Result<MacAddr, PistolError> {
837 if dst_addr.is_loopback() || addr_is_my_ip(dst_addr) {
838 match src_interface.mac {
840 Some(dst_mac) => Ok(dst_mac),
841 None => Err(PistolError::CanNotFoundMacAddress),
842 }
843 } else if addr_in_local_net(dst_addr) {
844 match search_mac(dst_addr)? {
845 Some(dst_mac) => {
846 debug!("found {} in arp cache", dst_addr);
847 Ok(dst_mac)
848 }
849 None => {
850 let src_mac = match src_interface.mac {
853 Some(m) => m,
854 None => return Err(PistolError::CanNotFoundMacAddress),
855 };
856
857 match dst_addr {
858 IpAddr::V4(via_ipv4) => {
859 if let IpAddr::V4(src_ipv4) = src_addr {
860 let dst_mac = match send_arp_scan_packet(
861 via_ipv4,
862 MacAddr::broadcast(),
863 src_ipv4,
864 src_mac,
865 src_interface,
866 timeout,
867 )? {
868 (Some(m), _rtt) => m,
869 (None, _rtt) => {
870 return Err(PistolError::CanNotFoundMacAddress);
871 }
872 };
873 neigh_cache_update(via_ipv4.into(), dst_mac)?;
874 Ok(dst_mac)
875 } else {
876 Err(PistolError::CanNotFoundMacAddress)
877 }
878 }
879 IpAddr::V6(via_ipv6) => {
880 if let IpAddr::V6(src_ipv6) = src_addr {
881 let dst_mac = match send_ndp_ns_scan_packet(
882 via_ipv6,
883 src_ipv6,
884 src_mac,
885 src_interface,
886 timeout,
887 )? {
888 (Some(m), _rtt) => m,
889 (None, _rtt) => {
890 return Err(PistolError::CanNotFoundMacAddress);
891 }
892 };
893 neigh_cache_update(via_ipv6.into(), dst_mac)?;
894 Ok(dst_mac)
895 } else {
896 Err(PistolError::CanNotFoundMacAddress)
897 }
898 }
899 }
900 }
901 }
902 } else {
903 let (default_route, default_route6) = get_default_route()?;
905 let dr = if dst_addr.is_ipv4() {
906 if let Some(dr_ipv4) = default_route {
907 dr_ipv4
908 } else {
909 return Err(PistolError::CanNotFoundRouterAddress);
910 }
911 } else {
912 if let Some(dr_ipv6) = default_route6 {
913 dr_ipv6
914 } else {
915 return Err(PistolError::CanNotFoundRouterAddress);
916 }
917 };
918
919 let dst_mac = match search_mac(dr.via)? {
920 Some(m) => m,
921 None => {
922 let dst_mac = MacAddr::broadcast();
923 let src_mac = match src_interface.mac {
924 Some(m) => m,
925 None => return Err(PistolError::CanNotFoundMacAddress),
926 };
927 match dr.via {
928 IpAddr::V4(dr_ipv4) => {
929 debug!("try to found the dst mac by arp scan");
930 if let IpAddr::V4(src_ipv4) = src_addr {
931 match send_arp_scan_packet(
932 dr_ipv4,
933 dst_mac,
934 src_ipv4,
935 src_mac,
936 src_interface,
937 timeout,
938 )? {
939 (Some(m), _rtt) => {
940 debug!("update the neigh cache: {} - {}", dr_ipv4, m);
941 neigh_cache_update(dr_ipv4.into(), m)?;
942 m
943 }
944 (None, _rtt) => {
945 return Err(PistolError::CanNotFoundRouteMacAddress);
946 }
947 }
948 } else {
949 return Err(PistolError::CanNotFoundRouteMacAddress);
950 }
951 }
952 IpAddr::V6(dr_ipv6) => {
953 debug!("try to found the dst mac by ndp_ns scan");
954 if let IpAddr::V6(src_ipv6) = src_addr {
955 match send_ndp_rs_packet(src_ipv6, timeout)? {
957 (Some(m), _rtt) => {
958 debug!("update the neigh cache: {} - {}", dr_ipv6, m);
959 neigh_cache_update(dr_ipv6.into(), m)?;
960 m
961 }
962 (None, _rtt) => {
963 return Err(PistolError::CanNotFoundRouteMacAddress);
964 }
965 }
966 } else {
967 return Err(PistolError::CanNotFoundRouteMacAddress);
968 }
969 }
970 }
971 }
972 };
973 Ok(dst_mac)
974 }
975 }
976 pub fn get_dst_mac_and_src_if_with_lock(
977 dst_addr: IpAddr,
978 src_addr: IpAddr,
979 timeout: Option<Duration>,
980 ) -> Result<(MacAddr, NetworkInterface), PistolError> {
981 if let Some((dst_mac, src_interface)) = DstCache::get(dst_addr)? {
983 return Ok((dst_mac, src_interface));
984 }
985
986 let (src_interface, route_via) = if src_addr == dst_addr {
987 let dev = match find_loopback_interface() {
988 Some(i) => i,
989 None => return Err(PistolError::CanNotFoundInterface),
990 };
991 if dst_addr.is_ipv4() {
992 let via = Some(RouteVia::IpAddr(IpAddr::V4(Ipv4Addr::LOCALHOST)));
993 (dev, via)
994 } else {
995 let via = Some(RouteVia::IpAddr(IpAddr::V6(Ipv6Addr::LOCALHOST)));
996 (dev, via)
997 }
998 } else {
999 let route_info = match search_route_table(dst_addr)? {
1000 Some(route_info) => route_info,
1001 None => {
1002 let (default_route, default_route6) = get_default_route()?;
1004 let dr = if dst_addr.is_ipv4() {
1005 if let Some(dr_ipv4) = default_route {
1006 dr_ipv4
1007 } else {
1008 return Err(PistolError::CanNotFoundRouterAddress);
1009 }
1010 } else {
1011 if let Some(dr_ipv6) = default_route6 {
1012 dr_ipv6
1013 } else {
1014 return Err(PistolError::CanNotFoundRouterAddress);
1015 }
1016 };
1017 match search_route_table(dr.via)? {
1018 Some(route_info) => route_info,
1019 None => return Err(PistolError::CanNotFoundInterface),
1020 }
1021 }
1022 };
1023 (route_info.dev, route_info.via)
1028 };
1029 debug!(
1030 "src interface: {}, via addr: {:?}",
1031 src_interface.name, route_via
1032 );
1033
1034 match route_via {
1035 Some(route_via) => {
1036 match route_via {
1037 RouteVia::IfIndex(if_index) => {
1038 let src_interface = match find_interface_by_index(if_index) {
1039 Some(i) => i,
1040 None => return Err(PistolError::CanNotFoundInterface),
1041 };
1042 let dst_mac = match search_mac(dst_addr)? {
1044 Some(dst_mac) => dst_mac,
1045 None => Self::standard_process(
1046 dst_addr,
1047 src_addr,
1048 src_interface.clone(),
1049 timeout,
1050 )?,
1051 };
1052 DstCache::update(dst_addr, src_addr, dst_mac, src_interface.clone())?;
1053 Ok((dst_mac, src_interface))
1054 }
1055 RouteVia::MacAddr(dst_mac) => {
1056 DstCache::update(dst_addr, src_addr, dst_mac, src_interface.clone())?;
1057 Ok((dst_mac, src_interface))
1058 }
1059 RouteVia::IpAddr(via_addr) => {
1060 let via_addr = if via_addr.is_unspecified() {
1062 dst_addr
1063 } else {
1064 via_addr
1065 };
1066 let dst_mac = match search_mac(via_addr)? {
1067 Some(m) => m,
1068 None => Self::standard_process(
1069 via_addr,
1070 src_addr,
1071 src_interface.clone(),
1072 timeout,
1073 )?,
1074 };
1075 DstCache::update(dst_addr, src_addr, dst_mac, src_interface.clone())?;
1076 Ok((dst_mac, src_interface))
1077 }
1078 }
1079 }
1080 None => {
1081 debug!("route_via is none");
1082 let dst_mac =
1083 Self::standard_process(dst_addr, src_addr, src_interface.clone(), timeout)?;
1084 DstCache::update(dst_addr, src_addr, dst_mac, src_interface.clone())?;
1085 Ok((dst_mac, src_interface))
1086 }
1087 }
1088 }
1089 pub fn get_dst_mac_and_src_if(
1090 dst_addr: IpAddr,
1091 src_addr: IpAddr,
1092 timeout: Option<Duration>,
1093 ) -> Result<(MacAddr, NetworkInterface), PistolError> {
1094 let mut neighbor_scan_status = match NEIGHBOR_SCAN_STATUS.lock() {
1095 Ok(n) => n,
1096 Err(e) => {
1097 return Err(PistolError::TryLockGlobalVarFailed {
1098 var_name: String::from("NEIGHBOR_SCAN_STATUS"),
1099 e: e.to_string(),
1100 });
1101 }
1102 };
1103 let mutex = match neighbor_scan_status.get(&dst_addr) {
1104 Some(x) => (*x).clone(),
1105 None => {
1106 let x = Arc::new(Mutex::new(()));
1107 neighbor_scan_status.insert(dst_addr, x.clone());
1108 x
1109 }
1110 };
1111
1112 match mutex.lock() {
1113 Ok(_) => Self::get_dst_mac_and_src_if_with_lock(dst_addr, src_addr, timeout),
1115 Err(e) => {
1116 return Err(PistolError::TryLockGlobalVarFailed {
1117 var_name: format!("{}", dst_addr),
1118 e: e.to_string(),
1119 });
1120 }
1121 }
1122 }
1123 pub fn parser(via_str: &str) -> Result<Option<RouteVia>, PistolError> {
1124 if !via_str.contains("link") {
1125 let ipv6_re = Regex::new(r"^[\w\d:]+(\/\d+)?")?;
1126 let ipv4_re = Regex::new(r"^[\d\.]+(\/\d+)?")?;
1127 let mac_re = Regex::new(
1128 r"^[\w\d]{1,2}:[\w\d]{1,2}:[\w\d]{1,2}:[\w\d]{1,2}:[\w\d]{1,2}:[\w\d]{1,2}",
1129 )?;
1130 let windows_index_re = Regex::new(r"^\d+")?;
1131 if ipv6_re.is_match(via_str) {
1132 let ipv6_addr = ipv6_addr_bsd_fix(via_str)?;
1134 let via: IpAddr = match ipv6_addr.parse() {
1135 Ok(d) => d,
1136 Err(e) => {
1137 warn!("parse 'via' [{}] into IpAddr(V6) error: {}", via_str, e);
1138 return Ok(None);
1139 }
1140 };
1141 Ok(Some(RouteVia::IpAddr(via.into())))
1142 } else if ipv4_re.is_match(via_str) {
1143 let via: IpAddr = match via_str.parse() {
1145 Ok(d) => d,
1146 Err(e) => {
1147 warn!("parse 'via' [{}] into IpAddr(V4) error: {}", via_str, e);
1148 return Ok(None);
1149 }
1150 };
1151 if !via.is_unspecified() {
1152 Ok(Some(RouteVia::IpAddr(via.into())))
1153 } else {
1154 Ok(None)
1155 }
1156 } else if mac_re.is_match(via_str) {
1157 let mac: MacAddr = match via_str.parse() {
1159 Ok(m) => m,
1160 Err(e) => {
1161 warn!("parse 'via' [{}] into MacAddr error: {}", via_str, e);
1162 return Ok(None);
1163 }
1164 };
1165 Ok(Some(RouteVia::MacAddr(mac)))
1166 } else if windows_index_re.is_match(via_str) {
1167 let if_index: u32 = match via_str.parse() {
1168 Ok(i) => i,
1169 Err(e) => {
1170 warn!("parse route table 'if_index' [{}] error: {}", via_str, e);
1171 return Ok(None);
1172 }
1173 };
1174 Ok(Some(RouteVia::IfIndex(if_index)))
1175 } else {
1176 debug!("via string [{}] no match any regex rules", via_str);
1177 Ok(None)
1178 }
1179 } else {
1180 let unix_index_re = Regex::new(r"^link#\d+")?;
1182 if unix_index_re.is_match(via_str) {
1183 let via_str_split: Vec<&str> = via_str
1185 .split("#")
1186 .map(|x| x.trim())
1187 .filter(|x| x.len() > 0)
1188 .collect();
1189 if via_str_split.len() > 1 {
1190 let if_index = via_str_split[1];
1191 let if_index: u32 = match if_index.parse() {
1192 Ok(i) => i,
1193 Err(e) => {
1194 warn!("parse route table 'if_index' [{}] error: {}", if_index, e);
1195 return Ok(None);
1196 }
1197 };
1198 Ok(Some(RouteVia::IfIndex(if_index)))
1199 } else {
1200 Ok(None)
1201 }
1202 } else {
1203 debug!("via string [{}] not match unix_index_re", via_str);
1204 Ok(None)
1205 }
1206 }
1207 }
1208}
1209
1210#[derive(Debug, Clone)]
1211pub struct RouteInfo {
1212 pub dev: NetworkInterface,
1213 pub via: Option<RouteVia>,
1214}
1215
1216#[derive(Debug, Clone)]
1217pub struct RouteTable {
1218 pub default_route: Option<DefaultRoute>,
1219 pub default_route6: Option<DefaultRoute>,
1220 pub routes: HashMap<RouteAddr, RouteInfo>,
1221}
1222
1223impl fmt::Display for RouteTable {
1224 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1225 let mut new_routes = HashMap::new();
1226 for (r, n) in &self.routes {
1227 let addr = match r {
1228 RouteAddr::IpAddr(i) => i.to_string(),
1229 RouteAddr::IpNetwork(i) => i.to_string(),
1230 };
1231 let interface_name = n.dev.name.clone();
1232 new_routes.insert(addr, interface_name);
1233 }
1234 let mut output = String::new();
1235 match &self.default_route {
1236 Some(r) => {
1237 output += &format!("ipv4 {}, ", r);
1238 }
1239 None => (),
1240 }
1241 match &self.default_route6 {
1242 Some(r) => {
1243 output += &format!("ipv6 {}, ", r);
1244 }
1245 None => (),
1246 }
1247 let routes_string = format!("routes: {:?}", new_routes);
1248 output += &routes_string;
1249 match output.strip_suffix(", ") {
1250 Some(o) => write!(f, "{}", o),
1251 None => write!(f, "{}", output),
1252 }
1253 }
1254}
1255
1256impl RouteTable {
1257 fn exec_system_command() -> Result<Vec<String>, PistolError> {
1258 #[cfg(target_os = "linux")]
1259 let c = Command::new("sh").args(["-c", "ip -4 route"]).output()?;
1260 #[cfg(target_os = "linux")]
1261 let ipv4_output = String::from_utf8_lossy(&c.stdout);
1262 #[cfg(target_os = "linux")]
1263 let c = Command::new("sh").args(["-c", "ip -6 route"]).output()?;
1264 #[cfg(target_os = "linux")]
1265 let ipv6_output = String::from_utf8_lossy(&c.stdout);
1266 #[cfg(target_os = "linux")]
1267 let output = ipv4_output.to_string() + &ipv6_output;
1268
1269 #[cfg(any(
1270 target_os = "freebsd",
1271 target_os = "openbsd",
1272 target_os = "netbsd",
1273 target_os = "macos"
1274 ))]
1275 let c = Command::new("sh").args(["-c", "netstat -rn"]).output()?;
1276 #[cfg(any(
1277 target_os = "freebsd",
1278 target_os = "openbsd",
1279 target_os = "netbsd",
1280 target_os = "macos"
1281 ))]
1282 let output = String::from_utf8_lossy(&c.stdout);
1283
1284 #[cfg(target_os = "windows")]
1287 let c = Command::new("powershell").args(["Get-NetRoute"]).output()?;
1288 #[cfg(target_os = "windows")]
1289 let output = String::from_utf8_lossy(&c.stdout);
1290
1291 let system_route_lines: Vec<String> = output
1292 .lines()
1293 .map(|x| x.trim().to_string())
1294 .filter(|v| v.len() > 0)
1295 .collect();
1296 Ok(system_route_lines)
1297 }
1298 pub fn init() -> Result<RouteTable, PistolError> {
1299 let system_route_lines = Self::exec_system_command()?;
1300 let inner_route_table = InnerRouteTable::parser(&system_route_lines)?;
1301 debug!("inner route table: {}", inner_route_table);
1302 let default_route = match inner_route_table.default_route {
1303 Some(inner_default_route) => {
1304 #[cfg(any(
1305 target_os = "linux",
1306 target_os = "freebsd",
1307 target_os = "openbsd",
1308 target_os = "netbsd",
1309 target_os = "macos"
1310 ))]
1311 let dev_name = inner_default_route.dev;
1312 #[cfg(any(
1313 target_os = "linux",
1314 target_os = "freebsd",
1315 target_os = "openbsd",
1316 target_os = "netbsd",
1317 target_os = "macos"
1318 ))]
1319 match find_interface_by_name(&dev_name) {
1320 Some(dev) => Some(DefaultRoute {
1321 via: inner_default_route.via,
1322 dev,
1323 }),
1324 None => {
1325 warn!("can not found interface by name [{}]", dev_name);
1326 None
1327 }
1328 }
1329 #[cfg(target_os = "windows")]
1330 let if_index = inner_default_route.if_index;
1331 #[cfg(target_os = "windows")]
1332 match find_interface_by_index(if_index) {
1333 Some(dev) => Some(DefaultRoute {
1334 via: inner_default_route.via,
1335 dev,
1336 }),
1337 None => {
1338 warn!("can not found interface by if_index [{}]", if_index);
1339 None
1340 }
1341 }
1342 }
1343 None => None,
1344 };
1345 let default_route6 = match inner_route_table.default_route6 {
1346 Some(inner_default_route6) => {
1347 #[cfg(any(
1348 target_os = "linux",
1349 target_os = "freebsd",
1350 target_os = "openbsd",
1351 target_os = "netbsd",
1352 target_os = "macos"
1353 ))]
1354 let dev_name = inner_default_route6.dev;
1355 #[cfg(any(
1356 target_os = "linux",
1357 target_os = "freebsd",
1358 target_os = "openbsd",
1359 target_os = "netbsd",
1360 target_os = "macos"
1361 ))]
1362 match find_interface_by_name(&dev_name) {
1363 Some(dev) => Some(DefaultRoute {
1364 via: inner_default_route6.via,
1365 dev,
1366 }),
1367 None => {
1368 warn!("can not found interface by name [{}]", dev_name);
1369 None
1370 }
1371 }
1372 #[cfg(target_os = "windows")]
1373 let if_index = inner_default_route6.if_index;
1374 #[cfg(target_os = "windows")]
1375 match find_interface_by_index(if_index) {
1376 Some(dev) => Some(DefaultRoute {
1377 via: inner_default_route6.via,
1378 dev,
1379 }),
1380 None => {
1381 warn!("can not found interface by if_index [{}]", if_index);
1382 None
1383 }
1384 }
1385 }
1386 None => None,
1387 };
1388
1389 let mut routes = HashMap::new();
1390 for (r, inner_route_info) in inner_route_table.routes {
1391 let dev_str = inner_route_info.dev;
1392 let via_str = inner_route_info.via;
1393
1394 #[cfg(any(
1395 target_os = "linux",
1396 target_os = "freebsd",
1397 target_os = "openbsd",
1398 target_os = "netbsd",
1399 target_os = "macos"
1400 ))]
1401 let dev = match find_interface_by_name(&dev_str) {
1402 Some(dev) => dev,
1403 None => {
1404 warn!("can not found interface by name [{}]", dev_str);
1405 continue;
1406 }
1407 };
1408
1409 #[cfg(target_os = "windows")]
1410 let dev = match find_interface_by_index(dev_str) {
1411 Some(dev) => dev,
1412 None => {
1413 warn!("can not found interface by if_index [{}]", dev_str);
1414 continue;
1415 }
1416 };
1417
1418 match via_str {
1419 Some(via_str) => match RouteVia::parser(&via_str)? {
1420 Some(via) => {
1421 let route_info = RouteInfo {
1422 dev,
1423 via: Some(via),
1424 };
1425 routes.insert(r, route_info);
1426 }
1427 None => {
1428 warn!("parse route table 'via' [{}] into RouteVia failed", via_str);
1429 continue;
1430 }
1431 },
1432 None => {
1433 let route_info = RouteInfo { dev, via: None };
1434 routes.insert(r, route_info);
1435 }
1436 }
1437 }
1438
1439 Ok(RouteTable {
1440 default_route,
1441 default_route6,
1442 routes,
1443 })
1444 }
1445}
1446
1447#[derive(Debug, Clone)]
1448pub struct NeighborCache {}
1449
1450impl NeighborCache {
1451 #[cfg(target_os = "linux")]
1452 pub fn init() -> Result<HashMap<IpAddr, MacAddr>, PistolError> {
1453 let c = Command::new("sh").args(["-c", "ip neigh show"]).output()?;
1464 let output = String::from_utf8_lossy(&c.stdout);
1465 let lines: Vec<&str> = output
1466 .lines()
1467 .map(|x| x.trim())
1468 .filter(|v| v.len() > 0)
1469 .collect();
1470
1471 let neighbor_re =
1473 Regex::new(r"(?P<addr>[\d\w\.:]+)\s+dev[\w\s]+lladdr\s+(?P<mac>[\d\w:]+).+")?;
1474
1475 let mut ret = HashMap::new();
1476 for line in lines {
1477 match neighbor_re.captures(line) {
1478 Some(caps) => {
1479 let addr = caps.name("addr").map_or("", |m| m.as_str());
1480 let addr: IpAddr = match addr.parse() {
1481 Ok(a) => a,
1482 Err(e) => {
1483 warn!("parse neighbor 'addr' error: {e}");
1484 continue;
1485 }
1486 };
1487 let mac = caps.name("mac").map_or("", |m| m.as_str());
1488 let mac: MacAddr = match mac.parse() {
1489 Ok(m) => m,
1490 Err(e) => {
1491 warn!("parse neighbor 'mac' error: {e}");
1492 continue;
1493 }
1494 };
1495 if !mac.is_zero() {
1496 ret.insert(addr, mac);
1497 }
1498 }
1499 None => warn!("line: [{}] neighbor_re no match", line),
1500 }
1501 }
1502 Ok(ret)
1503 }
1504 #[cfg(any(
1505 target_os = "freebsd",
1506 target_os = "openbsd",
1507 target_os = "netbsd",
1508 target_os = "macos"
1509 ))]
1510 pub fn init() -> Result<HashMap<IpAddr, MacAddr>, PistolError> {
1511 let c = Command::new("sh").args(["-c", "arp -a"]).output()?;
1523 let arp_output = String::from_utf8_lossy(&c.stdout);
1524 let c = Command::new("sh").args(["-c", "ndp -a"]).output()?;
1525 let ndp_output = String::from_utf8_lossy(&c.stdout);
1526 let output = arp_output.to_string() + &ndp_output;
1527 let lines: Vec<&str> = output
1528 .lines()
1529 .map(|x| x.trim())
1530 .filter(|v| v.len() > 0 && !v.contains("Neighbor"))
1531 .collect();
1532
1533 let neighbor_re = Regex::new(r"\?\s+\((?P<addr>[^\s]+)\)\s+at\s+(?P<mac>[\w\d:]+).+")?;
1535 let neighbor_re6 = Regex::new(r"(?P<addr>[^\s]+)\s+(?P<mac>[^\s]+).+")?;
1536
1537 let mut ret = HashMap::new();
1538 for line in lines {
1539 match neighbor_re.captures(line) {
1540 Some(caps) => {
1541 let addr_str = caps.name("addr").map_or("", |m| m.as_str());
1542 let addr_str = ipv6_addr_bsd_fix(addr_str)?;
1543 let addr: IpAddr = match addr_str.parse() {
1544 Ok(a) => a,
1545 Err(e) => {
1546 warn!("parse neighbor 'addr' error: {e}");
1547 continue;
1548 }
1549 };
1550 let mac = caps.name("mac").map_or("", |m| m.as_str());
1551 let mac: MacAddr = match mac.parse() {
1552 Ok(m) => m,
1553 Err(e) => {
1554 warn!("parse neighbor 'mac' error: {e}");
1555 continue;
1556 }
1557 };
1558 ret.insert(addr, mac);
1559 }
1560 None => match neighbor_re6.captures(line) {
1562 Some(caps) => {
1563 let addr_str = caps.name("addr").map_or("", |m| m.as_str());
1564 let addr_str = ipv6_addr_bsd_fix(addr_str)?;
1565 let addr: IpAddr = match addr_str.parse() {
1566 Ok(a) => a,
1567 Err(e) => {
1568 warn!("parse neighbor 'addr' error: {e}");
1569 continue;
1570 }
1571 };
1572 let mac = caps.name("mac").map_or("", |m| m.as_str());
1573 let mac: MacAddr = match mac.parse() {
1574 Ok(m) => m,
1575 Err(e) => {
1576 warn!("parse neighbor 'mac' error: {e}");
1577 continue;
1578 }
1579 };
1580 if !mac.is_zero() {
1581 ret.insert(addr, mac);
1582 }
1583 }
1584 None => warn!(
1585 "line: [{}] neighbor_re and neighbor_re6 both no match",
1586 line
1587 ),
1588 },
1589 }
1590 }
1591 Ok(ret)
1592 }
1593 #[cfg(target_os = "windows")]
1594 pub fn init() -> Result<HashMap<IpAddr, MacAddr>, PistolError> {
1595 let c = Command::new("powershell")
1600 .args(["Get-NetNeighbor"])
1601 .output()?;
1602 let output = String::from_utf8_lossy(&c.stdout);
1603 let lines: Vec<&str> = output
1604 .lines()
1605 .map(|x| x.trim())
1606 .filter(|v| v.len() > 0 && !v.contains("ifIndex") && !v.contains("--"))
1607 .collect();
1608
1609 let neighbor_re =
1611 Regex::new(r"^\d+\s+(?P<addr>[\w\d\.:]+)\s+((?P<mac>[\w\d-]+)\s+)?\w+\s+\w+")?;
1612
1613 let mut ret = HashMap::new();
1614 for line in lines {
1615 match neighbor_re.captures(line) {
1616 Some(caps) => {
1617 let addr = caps.name("addr").map_or("", |m| m.as_str());
1618 let addr: IpAddr = match addr.parse() {
1619 Ok(a) => a,
1620 Err(e) => {
1621 warn!("parse neighbor 'addr' error: {e}");
1622 continue;
1623 }
1624 };
1625 let mac: Option<String> =
1626 caps.name("mac").map_or(None, |m| Some(m.as_str().into()));
1627 match mac {
1629 Some(mac) => {
1630 let mac = mac.replace("-", ":");
1631 let mac: MacAddr = match mac.parse() {
1632 Ok(m) => m,
1633 Err(e) => {
1634 warn!("parse neighbor 'mac' error: {e}");
1635 continue;
1636 }
1637 };
1638 if !mac.is_zero() {
1639 ret.insert(addr, mac);
1641 }
1642 }
1643 None => (),
1644 }
1645 }
1646 None => warn!("line: [{}] neighbor_re no match", line),
1647 }
1648 }
1649 Ok(ret)
1650 }
1651}
1652
1653#[derive(Debug, Clone)]
1654pub struct SystemNetCache {
1655 pub default_route: Option<DefaultRoute>,
1656 pub default_route6: Option<DefaultRoute>,
1657 pub routes: HashMap<RouteAddr, RouteInfo>,
1658 pub neighbor: HashMap<IpAddr, MacAddr>,
1659}
1660
1661impl SystemNetCache {
1662 pub fn init() -> Result<SystemNetCache, PistolError> {
1663 let route_table = RouteTable::init()?;
1664 debug!("route table: {}", route_table);
1665 let neighbor_cache = NeighborCache::init()?;
1666 debug!("neighbor cache: {:?}", neighbor_cache);
1667 let snc = SystemNetCache {
1668 default_route: route_table.default_route,
1669 default_route6: route_table.default_route6,
1670 routes: route_table.routes,
1671 neighbor: neighbor_cache,
1672 };
1673 Ok(snc)
1674 }
1675 pub fn search_mac(&self, ip: IpAddr) -> Option<MacAddr> {
1676 match self.neighbor.get(&ip) {
1677 Some(&m) => Some(m),
1678 None => None,
1679 }
1680 }
1681 pub fn update_neighbor_cache(&mut self, ip: IpAddr, mac: MacAddr) {
1682 self.neighbor.insert(ip, mac);
1683 debug!("update the neigh cache done: {:?}", self.neighbor);
1684 }
1685 pub fn search_route_table(&self, ip: IpAddr) -> Option<RouteInfo> {
1686 for (dst, route_info) in &self.routes {
1687 if !dst.is_unspecified() && dst.contains(ip) {
1688 debug!(
1689 "route table {} contains target ip, route info dev: {}, via: {:?}",
1690 dst, route_info.dev.name, route_info.via
1691 );
1692 return Some(route_info.clone());
1693 }
1694 }
1695 None
1696 }
1697}
1698
1699#[cfg(test)]
1700mod tests {
1701 use super::*;
1702 use crate::PistolLogger;
1703 use crate::PistolRunner;
1704 use pnet::datalink::interfaces;
1705 use std::fs::read_to_string;
1706 #[test]
1707 fn test_network_cache() {
1708 let _pr = PistolRunner::init(
1709 PistolLogger::Debug,
1710 None,
1711 None, )
1713 .unwrap();
1714
1715 let snc = SystemNetCache::init().unwrap();
1716 for (route_addr, route_info) in snc.routes {
1717 println!(
1718 "route_addr: {:?}, route_info.dev: {}, route_info.via: {:?}",
1719 route_addr, route_info.dev.name, route_info.via
1720 );
1721 }
1722 }
1723 #[test]
1724 fn test_details() {
1725 let n = NeighborCache::init().unwrap();
1726 println!("{:?}", n);
1727
1728 let r = RouteTable::init().unwrap();
1729 println!("{:?}", r);
1730 }
1731 #[test]
1732 fn test_mac() {
1733 let mac = MacAddr::new(0, 0, 0, 0, 0, 0);
1734 println!("{}", mac);
1735 assert_eq!(mac.is_zero(), true)
1736 }
1737 #[test]
1738 fn test_windows_interface() {
1739 println!("TEST!!!!");
1740 for interface in interfaces() {
1741 println!("{}", interface);
1743 println!("{}", interface.index);
1744 }
1745 }
1746 #[test]
1747 fn test_unix() {
1748 let input = "fe80::%em0/64";
1749 let input_split: Vec<&str> = input
1750 .split("%")
1751 .map(|x| x.trim())
1752 .filter(|v| v.len() > 0)
1753 .collect();
1754 let _ip: IpAddr = input_split[0].parse().unwrap();
1755 let ipnetwork = IpNetwork::from_str("fe80::").unwrap();
1756 let test_ipv6: IpAddr = "fe80::20c:29ff:feb6:8d99".parse().unwrap();
1757 println!("{}", ipnetwork.contains(test_ipv6));
1758 let ipnetwork = IpNetwork::from_str("fe80::/64").unwrap();
1759 let test_ipv6: IpAddr = "fe80::20c:29ff:feb6:8d99".parse().unwrap();
1760 println!("{}", ipnetwork.contains(test_ipv6));
1761 let ipnetwork = IpNetwork::from_str("::/96").unwrap();
1762 let test_ipv6: IpAddr = "fe80::20c:29ff:feb6:8d99".parse().unwrap();
1763 println!("{}", ipnetwork.contains(test_ipv6));
1764 }
1765 #[test]
1766 fn test_all() {
1767 let _pr = PistolRunner::init(
1768 PistolLogger::Debug,
1769 None,
1770 None, )
1772 .unwrap();
1773
1774 #[cfg(target_os = "linux")]
1775 let routetable_str = read_to_string("./tests/linux_routetable.txt").unwrap();
1776
1777 #[cfg(any(
1778 target_os = "freebsd",
1779 target_os = "openbsd",
1780 target_os = "netbsd",
1781 target_os = "macos"
1782 ))]
1783 let routetable_str = read_to_string("./tests/unix_routetable.txt").unwrap();
1784
1785 #[cfg(target_os = "windows")]
1786 let routetable_str = read_to_string("./tests/windows_routetable.txt").unwrap();
1787
1788 let routetable_fix: Vec<String> = routetable_str
1789 .lines()
1790 .map(|x| x.trim().to_string())
1791 .collect();
1792
1793 let mut routetables = Vec::new();
1794 let mut tmp = Vec::new();
1795 for r in &routetable_fix {
1796 if r != "+++" {
1797 tmp.push(r.clone());
1799 } else {
1800 routetables.push(tmp.clone());
1801 tmp.clear();
1802 }
1803 }
1804
1805 for t in routetables {
1806 let ret = InnerRouteTable::parser(&t).unwrap();
1807 println!("{}", ret);
1808 println!("-------------------------------------------------------------------------");
1809 }
1810 }
1811}