1use crate::error::Result;
2use crate::{
3 Error, IcmpExtensionParseMode, MaxInflight, MaxRounds, MultipathStrategy, PacketSize,
4 PayloadPattern, PortDirection, PrivilegeMode, Protocol, Round, Sequence, State, TimeToLive,
5 TraceId, TypeOfService,
6};
7use std::fmt::Debug;
8use std::net::IpAddr;
9use std::sync::Arc;
10use std::thread;
11use std::thread::JoinHandle;
12use std::time::Duration;
13
14#[derive(Debug, Clone)]
20pub struct Tracer {
21 inner: Arc<inner::TracerInner>,
22}
23
24impl Tracer {
25 #[allow(clippy::too_many_arguments)]
29 #[must_use]
30 pub(crate) fn new(
31 interface: Option<String>,
32 source_addr: Option<IpAddr>,
33 target_addr: IpAddr,
34 privilege_mode: PrivilegeMode,
35 protocol: Protocol,
36 packet_size: PacketSize,
37 payload_pattern: PayloadPattern,
38 tos: TypeOfService,
39 icmp_extension_parse_mode: IcmpExtensionParseMode,
40 read_timeout: Duration,
41 tcp_connect_timeout: Duration,
42 trace_identifier: TraceId,
43 max_rounds: Option<MaxRounds>,
44 first_ttl: TimeToLive,
45 max_ttl: TimeToLive,
46 grace_duration: Duration,
47 max_inflight: MaxInflight,
48 initial_sequence: Sequence,
49 multipath_strategy: MultipathStrategy,
50 port_direction: PortDirection,
51 min_round_duration: Duration,
52 max_round_duration: Duration,
53 max_samples: usize,
54 max_flows: usize,
55 drop_privileges: bool,
56 ) -> Self {
57 Self {
58 inner: Arc::new(inner::TracerInner::new(
59 interface,
60 source_addr,
61 target_addr,
62 privilege_mode,
63 protocol,
64 packet_size,
65 payload_pattern,
66 tos,
67 icmp_extension_parse_mode,
68 read_timeout,
69 tcp_connect_timeout,
70 trace_identifier,
71 max_rounds,
72 first_ttl,
73 max_ttl,
74 grace_duration,
75 max_inflight,
76 initial_sequence,
77 multipath_strategy,
78 port_direction,
79 min_round_duration,
80 max_round_duration,
81 max_samples,
82 max_flows,
83 drop_privileges,
84 )),
85 }
86 }
87
88 pub fn run(&self) -> Result<()> {
127 self.inner.run()
128 }
129
130 pub fn run_with<F: Fn(&Round<'_>)>(&self, func: F) -> Result<()> {
166 self.inner.run_with(func)
167 }
168
169 pub fn spawn(self) -> Result<(Self, JoinHandle<Result<()>>)> {
208 let tracer = self.clone();
209 let handle = thread::Builder::new()
210 .name(format!("tracer-{}", self.trace_identifier().0))
211 .spawn(move || tracer.run())
212 .map_err(|err| Error::Other(err.to_string()))?;
213 Ok((self, handle))
214 }
215
216 pub fn spawn_with<F: Fn(&Round<'_>) + Send + 'static>(
256 self,
257 func: F,
258 ) -> Result<(Self, JoinHandle<Result<()>>)> {
259 let tracer = self.clone();
260 let handle = thread::Builder::new()
261 .name(format!("tracer-{}", self.trace_identifier().0))
262 .spawn(move || tracer.run_with(func))
263 .map_err(|err| Error::Other(err.to_string()))?;
264 Ok((self, handle))
265 }
266
267 #[must_use]
269 pub fn snapshot(&self) -> State {
270 self.inner.snapshot()
271 }
272
273 pub fn clear(&self) {
275 self.inner.clear();
276 }
277
278 #[must_use]
280 pub fn max_flows(&self) -> usize {
281 self.inner.max_flows()
282 }
283
284 #[must_use]
286 pub fn max_samples(&self) -> usize {
287 self.inner.max_samples()
288 }
289
290 #[must_use]
292 pub fn privilege_mode(&self) -> PrivilegeMode {
293 self.inner.privilege_mode()
294 }
295
296 #[must_use]
298 pub fn protocol(&self) -> Protocol {
299 self.inner.protocol()
300 }
301
302 #[must_use]
304 pub fn interface(&self) -> Option<&str> {
305 self.inner.interface()
306 }
307
308 #[must_use]
310 pub fn source_addr(&self) -> Option<IpAddr> {
311 self.inner.source_addr()
312 }
313
314 #[must_use]
316 pub fn target_addr(&self) -> IpAddr {
317 self.inner.target_addr()
318 }
319
320 #[must_use]
322 pub fn packet_size(&self) -> PacketSize {
323 self.inner.packet_size()
324 }
325
326 #[must_use]
328 pub fn payload_pattern(&self) -> PayloadPattern {
329 self.inner.payload_pattern()
330 }
331
332 #[must_use]
334 pub fn initial_sequence(&self) -> Sequence {
335 self.inner.initial_sequence()
336 }
337
338 #[must_use]
340 pub fn tos(&self) -> TypeOfService {
341 self.inner.tos()
342 }
343
344 #[must_use]
346 pub fn icmp_extension_parse_mode(&self) -> IcmpExtensionParseMode {
347 self.inner.icmp_extension_parse_mode()
348 }
349
350 #[must_use]
352 pub fn read_timeout(&self) -> Duration {
353 self.inner.read_timeout()
354 }
355
356 #[must_use]
358 pub fn tcp_connect_timeout(&self) -> Duration {
359 self.inner.tcp_connect_timeout()
360 }
361
362 #[must_use]
364 pub fn trace_identifier(&self) -> TraceId {
365 self.inner.trace_identifier()
366 }
367
368 #[must_use]
370 pub fn max_rounds(&self) -> Option<MaxRounds> {
371 self.inner.max_rounds()
372 }
373
374 #[must_use]
376 pub fn first_ttl(&self) -> TimeToLive {
377 self.inner.first_ttl()
378 }
379
380 #[must_use]
382 pub fn max_ttl(&self) -> TimeToLive {
383 self.inner.max_ttl()
384 }
385
386 #[must_use]
388 pub fn grace_duration(&self) -> Duration {
389 self.inner.grace_duration()
390 }
391
392 #[must_use]
394 pub fn max_inflight(&self) -> MaxInflight {
395 self.inner.max_inflight()
396 }
397
398 #[must_use]
400 pub fn multipath_strategy(&self) -> MultipathStrategy {
401 self.inner.multipath_strategy()
402 }
403
404 #[must_use]
406 pub fn port_direction(&self) -> PortDirection {
407 self.inner.port_direction()
408 }
409
410 #[must_use]
412 pub fn min_round_duration(&self) -> Duration {
413 self.inner.min_round_duration()
414 }
415
416 #[must_use]
418 pub fn max_round_duration(&self) -> Duration {
419 self.inner.max_round_duration()
420 }
421}
422
423mod inner {
424 use crate::config::{ChannelConfig, StateConfig, StrategyConfig};
425 use crate::error::Result;
426 use crate::net::{PlatformImpl, SocketImpl};
427 use crate::{
428 Channel, Error, IcmpExtensionParseMode, MaxInflight, MaxRounds, MultipathStrategy,
429 PacketSize, PayloadPattern, PortDirection, PrivilegeMode, Protocol, Round, Sequence,
430 SourceAddr, State, Strategy, TimeToLive, TraceId, TypeOfService,
431 };
432 use parking_lot::RwLock;
433 use std::fmt::Debug;
434 use std::net::IpAddr;
435 use std::sync::OnceLock;
436 use std::time::Duration;
437 use tracing::instrument;
438 use trippy_privilege::Privilege;
439
440 #[derive(Debug)]
441 pub(super) struct TracerInner {
442 source_addr: Option<IpAddr>,
443 interface: Option<String>,
444 target_addr: IpAddr,
445 privilege_mode: PrivilegeMode,
446 protocol: Protocol,
447 packet_size: PacketSize,
448 payload_pattern: PayloadPattern,
449 tos: TypeOfService,
450 icmp_extension_parse_mode: IcmpExtensionParseMode,
451 read_timeout: Duration,
452 tcp_connect_timeout: Duration,
453 trace_identifier: TraceId,
454 max_rounds: Option<MaxRounds>,
455 first_ttl: TimeToLive,
456 max_ttl: TimeToLive,
457 grace_duration: Duration,
458 max_inflight: MaxInflight,
459 initial_sequence: Sequence,
460 multipath_strategy: MultipathStrategy,
461 port_direction: PortDirection,
462 min_round_duration: Duration,
463 max_round_duration: Duration,
464 max_samples: usize,
465 max_flows: usize,
466 drop_privileges: bool,
467 state: RwLock<State>,
468 src: OnceLock<IpAddr>,
469 }
470
471 impl TracerInner {
472 #[allow(clippy::too_many_arguments)]
473 pub(super) fn new(
474 interface: Option<String>,
475 source_addr: Option<IpAddr>,
476 target_addr: IpAddr,
477 privilege_mode: PrivilegeMode,
478 protocol: Protocol,
479 packet_size: PacketSize,
480 payload_pattern: PayloadPattern,
481 tos: TypeOfService,
482 icmp_extension_parse_mode: IcmpExtensionParseMode,
483 read_timeout: Duration,
484 tcp_connect_timeout: Duration,
485 trace_identifier: TraceId,
486 max_rounds: Option<MaxRounds>,
487 first_ttl: TimeToLive,
488 max_ttl: TimeToLive,
489 grace_duration: Duration,
490 max_inflight: MaxInflight,
491 initial_sequence: Sequence,
492 multipath_strategy: MultipathStrategy,
493 port_direction: PortDirection,
494 min_round_duration: Duration,
495 max_round_duration: Duration,
496 max_samples: usize,
497 max_flows: usize,
498 drop_privileges: bool,
499 ) -> Self {
500 Self {
501 source_addr,
502 interface,
503 target_addr,
504 privilege_mode,
505 protocol,
506 packet_size,
507 payload_pattern,
508 tos,
509 icmp_extension_parse_mode,
510 read_timeout,
511 tcp_connect_timeout,
512 trace_identifier,
513 max_rounds,
514 first_ttl,
515 max_ttl,
516 grace_duration,
517 max_inflight,
518 initial_sequence,
519 multipath_strategy,
520 port_direction,
521 min_round_duration,
522 max_round_duration,
523 max_samples,
524 max_flows,
525 drop_privileges,
526 state: RwLock::new(State::new(Self::make_state_config(max_flows, max_samples))),
527 src: OnceLock::new(),
528 }
529 }
530
531 #[instrument(skip_all, level = "trace")]
532 pub(super) fn run(&self) -> Result<()> {
533 self.run_internal(|_| ())
534 .map_err(|err| self.handle_error(err))
535 }
536
537 #[instrument(skip_all, level = "trace")]
538 pub(super) fn run_with<F: Fn(&Round<'_>)>(&self, func: F) -> Result<()> {
539 self.run_internal(func)
540 .map_err(|err| self.handle_error(err))
541 }
542
543 pub(super) fn snapshot(&self) -> State {
544 self.state.read().clone()
545 }
546
547 pub(super) fn clear(&self) {
548 *self.state.write() =
549 State::new(Self::make_state_config(self.max_flows, self.max_samples));
550 }
551
552 pub(super) const fn max_flows(&self) -> usize {
553 self.max_flows
554 }
555
556 pub(super) const fn max_samples(&self) -> usize {
557 self.max_samples
558 }
559
560 pub(super) const fn privilege_mode(&self) -> PrivilegeMode {
561 self.privilege_mode
562 }
563
564 pub(super) const fn protocol(&self) -> Protocol {
565 self.protocol
566 }
567
568 pub(super) fn interface(&self) -> Option<&str> {
569 self.interface.as_deref()
570 }
571
572 pub(super) fn source_addr(&self) -> Option<IpAddr> {
573 self.src.get().copied()
574 }
575
576 pub(super) const fn target_addr(&self) -> IpAddr {
577 self.target_addr
578 }
579
580 pub(super) const fn packet_size(&self) -> PacketSize {
581 self.packet_size
582 }
583
584 pub(super) const fn payload_pattern(&self) -> PayloadPattern {
585 self.payload_pattern
586 }
587
588 pub(super) const fn initial_sequence(&self) -> Sequence {
589 self.initial_sequence
590 }
591
592 pub(super) const fn tos(&self) -> TypeOfService {
593 self.tos
594 }
595
596 pub(super) const fn icmp_extension_parse_mode(&self) -> IcmpExtensionParseMode {
597 self.icmp_extension_parse_mode
598 }
599
600 pub(super) const fn read_timeout(&self) -> Duration {
601 self.read_timeout
602 }
603
604 pub(super) const fn tcp_connect_timeout(&self) -> Duration {
605 self.tcp_connect_timeout
606 }
607
608 pub(super) const fn trace_identifier(&self) -> TraceId {
609 self.trace_identifier
610 }
611
612 pub(super) const fn max_rounds(&self) -> Option<MaxRounds> {
613 self.max_rounds
614 }
615
616 pub(super) const fn first_ttl(&self) -> TimeToLive {
617 self.first_ttl
618 }
619
620 pub(super) const fn max_ttl(&self) -> TimeToLive {
621 self.max_ttl
622 }
623
624 pub(super) const fn grace_duration(&self) -> Duration {
625 self.grace_duration
626 }
627
628 pub(super) const fn max_inflight(&self) -> MaxInflight {
629 self.max_inflight
630 }
631
632 pub(super) const fn multipath_strategy(&self) -> MultipathStrategy {
633 self.multipath_strategy
634 }
635
636 pub(super) const fn port_direction(&self) -> PortDirection {
637 self.port_direction
638 }
639
640 pub(super) const fn min_round_duration(&self) -> Duration {
641 self.min_round_duration
642 }
643
644 pub(super) const fn max_round_duration(&self) -> Duration {
645 self.max_round_duration
646 }
647
648 #[instrument(skip_all, level = "trace")]
649 fn run_internal<F: Fn(&Round<'_>)>(&self, func: F) -> Result<()> {
650 let source_addr = match self.source_addr {
653 None => SourceAddr::discover::<SocketImpl, PlatformImpl>(
654 self.target_addr,
655 self.port_direction,
656 self.interface.as_deref(),
657 )?,
658 Some(addr) => SourceAddr::validate::<SocketImpl>(addr)?,
659 };
660 self.src
661 .set(source_addr)
662 .map_err(|_| Error::Other(String::from("failed to set source_addr")))?;
663 let channel_config = self.make_channel_config(source_addr);
664 let channel = Channel::<SocketImpl>::connect(&channel_config)?;
665 if self.drop_privileges {
666 Privilege::drop_privileges()?;
667 }
668 let strategy_config = self.make_strategy_config();
669 let strategy = Strategy::new(&strategy_config, |round| {
670 self.handler(round);
671 func(round);
672 });
673 strategy.run(channel)?;
674 Ok(())
675 }
676
677 fn handler(&self, round: &Round<'_>) {
678 self.state.write().update_from_round(round);
679 }
680
681 fn handle_error(&self, err: Error) -> Error {
682 self.state.write().set_error(Some(err.to_string()));
683 err
684 }
685
686 const fn make_state_config(max_flows: usize, max_samples: usize) -> StateConfig {
687 StateConfig {
688 max_samples,
689 max_flows,
690 }
691 }
692
693 const fn make_channel_config(&self, source_addr: IpAddr) -> ChannelConfig {
694 ChannelConfig {
695 privilege_mode: self.privilege_mode,
696 protocol: self.protocol,
697 source_addr,
698 target_addr: self.target_addr,
699 packet_size: self.packet_size,
700 payload_pattern: self.payload_pattern,
701 initial_sequence: self.initial_sequence,
702 tos: self.tos,
703 icmp_extension_parse_mode: self.icmp_extension_parse_mode,
704 read_timeout: self.read_timeout,
705 tcp_connect_timeout: self.tcp_connect_timeout,
706 }
707 }
708
709 const fn make_strategy_config(&self) -> StrategyConfig {
710 StrategyConfig {
711 target_addr: self.target_addr,
712 protocol: self.protocol,
713 trace_identifier: self.trace_identifier,
714 max_rounds: self.max_rounds,
715 first_ttl: self.first_ttl,
716 max_ttl: self.max_ttl,
717 grace_duration: self.grace_duration,
718 max_inflight: self.max_inflight,
719 initial_sequence: self.initial_sequence,
720 multipath_strategy: self.multipath_strategy,
721 port_direction: self.port_direction,
722 min_round_duration: self.min_round_duration,
723 max_round_duration: self.max_round_duration,
724 }
725 }
726 }
727}