trippy_core/builder.rs
1use crate::config::{ChannelConfig, StateConfig, StrategyConfig};
2use crate::constants::MAX_INITIAL_SEQUENCE;
3use crate::error::Result;
4use crate::{
5 Error, IcmpExtensionParseMode, MaxInflight, MaxRounds, MultipathStrategy, PacketSize,
6 PayloadPattern, PortDirection, PrivilegeMode, Protocol, Sequence, TimeToLive, TraceId, Tracer,
7 TypeOfService, MAX_TTL,
8};
9use std::net::IpAddr;
10use std::num::NonZeroUsize;
11use std::time::Duration;
12
13/// Build a tracer.
14///
15/// This is a convenience builder to simplify the creation of execution of a
16/// tracer.
17///
18/// # Examples
19///
20/// ```no_run
21/// # fn main() -> anyhow::Result<()> {
22/// use trippy_core::{Builder, MultipathStrategy, Port, PortDirection, PrivilegeMode, Protocol};
23///
24/// let addr = std::net::IpAddr::from([1, 2, 3, 4]);
25/// let tracer = Builder::new(addr)
26/// .privilege_mode(PrivilegeMode::Unprivileged)
27/// .protocol(Protocol::Udp)
28/// .multipath_strategy(MultipathStrategy::Dublin)
29/// .port_direction(PortDirection::FixedBoth(Port(33434), Port(3500)))
30/// .build()?;
31/// # Ok(())
32/// # }
33/// ```
34///
35/// # See Also
36///
37/// - [`Tracer`] - A traceroute implementation.
38#[derive(Debug)]
39pub struct Builder {
40 interface: Option<String>,
41 source_addr: Option<IpAddr>,
42 target_addr: IpAddr,
43 privilege_mode: PrivilegeMode,
44 protocol: Protocol,
45 packet_size: PacketSize,
46 payload_pattern: PayloadPattern,
47 tos: TypeOfService,
48 icmp_extension_parse_mode: IcmpExtensionParseMode,
49 read_timeout: Duration,
50 tcp_connect_timeout: Duration,
51 trace_identifier: TraceId,
52 max_rounds: Option<MaxRounds>,
53 first_ttl: TimeToLive,
54 max_ttl: TimeToLive,
55 grace_duration: Duration,
56 max_inflight: MaxInflight,
57 initial_sequence: Sequence,
58 multipath_strategy: MultipathStrategy,
59 port_direction: PortDirection,
60 min_round_duration: Duration,
61 max_round_duration: Duration,
62 max_samples: usize,
63 max_flows: usize,
64 drop_privileges: bool,
65}
66
67impl Default for Builder {
68 fn default() -> Self {
69 Self {
70 interface: None,
71 source_addr: None,
72 target_addr: ChannelConfig::default().target_addr,
73 privilege_mode: ChannelConfig::default().privilege_mode,
74 protocol: ChannelConfig::default().protocol,
75 packet_size: ChannelConfig::default().packet_size,
76 payload_pattern: ChannelConfig::default().payload_pattern,
77 tos: ChannelConfig::default().tos,
78 icmp_extension_parse_mode: ChannelConfig::default().icmp_extension_parse_mode,
79 read_timeout: ChannelConfig::default().read_timeout,
80 tcp_connect_timeout: ChannelConfig::default().tcp_connect_timeout,
81 trace_identifier: StrategyConfig::default().trace_identifier,
82 max_rounds: StrategyConfig::default().max_rounds,
83 first_ttl: StrategyConfig::default().first_ttl,
84 max_ttl: StrategyConfig::default().max_ttl,
85 grace_duration: StrategyConfig::default().grace_duration,
86 max_inflight: StrategyConfig::default().max_inflight,
87 initial_sequence: StrategyConfig::default().initial_sequence,
88 multipath_strategy: StrategyConfig::default().multipath_strategy,
89 port_direction: StrategyConfig::default().port_direction,
90 min_round_duration: StrategyConfig::default().min_round_duration,
91 max_round_duration: StrategyConfig::default().max_round_duration,
92 max_samples: StateConfig::default().max_samples,
93 max_flows: StateConfig::default().max_flows,
94 drop_privileges: false,
95 }
96 }
97}
98
99impl Builder {
100 /// Build a tracer builder for a given target.
101 ///
102 /// # Examples
103 ///
104 /// Basic usage:
105 ///
106 /// ```no_run
107 /// # fn main() -> anyhow::Result<()> {
108 /// use trippy_core::Builder;
109 ///
110 /// let addr = std::net::IpAddr::from([1, 1, 1, 1]);
111 /// let tracer = Builder::new(addr).build()?;
112 /// # Ok(())
113 /// # }
114 /// ```
115 #[must_use]
116 pub fn new(target_addr: IpAddr) -> Self {
117 Self {
118 target_addr,
119 ..Default::default()
120 }
121 }
122
123 /// Set the source address.
124 ///
125 /// If not set then the source address will be discovered based on the
126 /// target address and the interface.
127 ///
128 /// # Examples
129 ///
130 /// ```no_run
131 /// # fn main() -> anyhow::Result<()> {
132 /// use std::net::IpAddr;
133 /// use trippy_core::Builder;
134 ///
135 /// let addr = IpAddr::from([1, 1, 1, 1]);
136 /// let source_addr = IpAddr::from([192, 168, 1, 1]);
137 /// let tracer = Builder::new(addr).source_addr(Some(source_addr)).build()?;
138 /// # Ok(())
139 /// # }
140 /// ```
141 #[must_use]
142 pub fn source_addr(self, source_addr: Option<IpAddr>) -> Self {
143 Self {
144 source_addr,
145 ..self
146 }
147 }
148
149 /// Set the source interface.
150 ///
151 /// If the source interface is provided it will be used to look up the IPv4
152 /// or IPv6 source address.
153 ///
154 /// If not provided the source address will be determined by OS based on
155 /// the target IPv4 or IPv6 address.
156 ///
157 /// # Examples
158 ///
159 /// ```no_run
160 /// # fn main() -> anyhow::Result<()> {
161 /// use std::net::IpAddr;
162 /// use trippy_core::Builder;
163 ///
164 /// let addr = IpAddr::from([1, 1, 1, 1]);
165 /// let tracer = Builder::new(addr).interface(Some("eth0")).build()?;
166 /// # Ok(())
167 /// # }
168 /// ```
169 #[must_use]
170 pub fn interface<S: Into<String>>(self, interface: Option<S>) -> Self {
171 Self {
172 interface: interface.map(Into::into),
173 ..self
174 }
175 }
176
177 /// Set the protocol.
178 ///
179 /// # Examples
180 ///
181 /// ```no_run
182 /// # fn main() -> anyhow::Result<()> {
183 /// use std::net::IpAddr;
184 /// use trippy_core::{Builder, Protocol};
185 ///
186 /// let addr = IpAddr::from([1, 1, 1, 1]);
187 /// let tracer = Builder::new(addr).protocol(Protocol::Udp).build()?;
188 /// # Ok(())
189 /// # }
190 /// ```
191 #[must_use]
192 pub fn protocol(self, protocol: Protocol) -> Self {
193 Self { protocol, ..self }
194 }
195
196 /// Set the trace identifier.
197 ///
198 /// If not set then 0 will be used as the trace identifier.
199 ///
200 /// ```no_run
201 /// # fn main() -> anyhow::Result<()> {
202 /// use std::net::IpAddr;
203 /// use trippy_core::Builder;
204 ///
205 /// let addr = IpAddr::from([1, 1, 1, 1]);
206 /// let tracer = Builder::new(addr).trace_identifier(12345).build()?;
207 /// # Ok(())
208 /// # }
209 /// ```
210 #[must_use]
211 pub fn trace_identifier(self, trace_id: u16) -> Self {
212 Self {
213 trace_identifier: TraceId(trace_id),
214 ..self
215 }
216 }
217
218 /// Set the privilege mode.
219 ///
220 /// # Examples
221 ///
222 /// ```no_run
223 /// # fn main() -> anyhow::Result<()> {
224 /// use std::net::IpAddr;
225 /// use trippy_core::{Builder, PrivilegeMode};
226 ///
227 /// let addr = IpAddr::from([1, 1, 1, 1]);
228 /// let tracer = Builder::new(addr)
229 /// .privilege_mode(PrivilegeMode::Unprivileged)
230 /// .build()?;
231 /// # Ok(())
232 /// # }
233 /// ```
234 #[must_use]
235 pub fn privilege_mode(self, privilege_mode: PrivilegeMode) -> Self {
236 Self {
237 privilege_mode,
238 ..self
239 }
240 }
241
242 /// Set the multipath strategy.
243 ///
244 /// # Examples
245 ///
246 /// ```no_run
247 /// # fn main() -> anyhow::Result<()> {
248 /// use std::net::IpAddr;
249 /// use trippy_core::{Builder, MultipathStrategy};
250 ///
251 /// let addr = IpAddr::from([1, 1, 1, 1]);
252 /// let tracer = Builder::new(addr)
253 /// .multipath_strategy(MultipathStrategy::Paris)
254 /// .build()?;
255 /// # Ok(())
256 /// # }
257 /// ```
258 #[must_use]
259 pub fn multipath_strategy(self, multipath_strategy: MultipathStrategy) -> Self {
260 Self {
261 multipath_strategy,
262 ..self
263 }
264 }
265
266 /// Set the packet size.
267 ///
268 /// # Examples
269 ///
270 /// ```no_run
271 /// # fn main() -> anyhow::Result<()> {
272 /// use std::net::IpAddr;
273 /// use trippy_core::Builder;
274 ///
275 /// let addr = IpAddr::from([1, 1, 1, 1]);
276 /// let tracer = Builder::new(addr).packet_size(128).build()?;
277 /// # Ok(())
278 /// # }
279 /// ```
280 #[must_use]
281 pub fn packet_size(self, packet_size: u16) -> Self {
282 Self {
283 packet_size: PacketSize(packet_size),
284 ..self
285 }
286 }
287
288 /// Set the payload pattern.
289 ///
290 /// # Examples
291 ///
292 /// ```no_run
293 /// # fn main() -> anyhow::Result<()> {
294 /// use std::net::IpAddr;
295 /// use trippy_core::Builder;
296 ///
297 /// let addr = IpAddr::from([1, 1, 1, 1]);
298 /// let tracer = Builder::new(addr).payload_pattern(0xff).build()?;
299 /// # Ok(())
300 /// # }
301 /// ```
302 #[must_use]
303 pub fn payload_pattern(self, payload_pattern: u8) -> Self {
304 Self {
305 payload_pattern: PayloadPattern(payload_pattern),
306 ..self
307 }
308 }
309
310 /// Set the type of service.
311 ///
312 /// # Examples
313 ///
314 /// ```no_run
315 /// # fn main() -> anyhow::Result<()> {
316 /// use std::net::IpAddr;
317 /// use trippy_core::Builder;
318 ///
319 /// let addr = IpAddr::from([1, 1, 1, 1]);
320 /// let tracer = Builder::new(addr).tos(0x1a).build()?;
321 /// # Ok(())
322 /// # }
323 /// ```
324 #[must_use]
325 pub fn tos(self, tos: u8) -> Self {
326 Self {
327 tos: TypeOfService(tos),
328 ..self
329 }
330 }
331
332 /// Set the ICMP extensions mode.
333 ///
334 /// # Examples
335 ///
336 /// ```no_run
337 /// # fn main() -> anyhow::Result<()> {
338 /// use std::net::IpAddr;
339 /// use trippy_core::{Builder, IcmpExtensionParseMode};
340 ///
341 /// let addr = IpAddr::from([1, 1, 1, 1]);
342 /// let tracer = Builder::new(addr)
343 /// .icmp_extension_parse_mode(IcmpExtensionParseMode::Enabled)
344 /// .build()?;
345 /// # Ok(())
346 /// # }
347 /// ```
348 #[must_use]
349 pub fn icmp_extension_parse_mode(
350 self,
351 icmp_extension_parse_mode: IcmpExtensionParseMode,
352 ) -> Self {
353 Self {
354 icmp_extension_parse_mode,
355 ..self
356 }
357 }
358
359 /// Set the read timeout.
360 ///
361 /// # Examples
362 ///
363 /// ```no_run
364 /// # fn main() -> anyhow::Result<()> {
365 /// use std::net::IpAddr;
366 /// use std::time::Duration;
367 /// use trippy_core::Builder;
368 ///
369 /// let addr = IpAddr::from([1, 1, 1, 1]);
370 /// let tracer = Builder::new(addr)
371 /// .read_timeout(Duration::from_millis(50))
372 /// .build()?;
373 /// # Ok(())
374 /// # }
375 /// ```
376 #[must_use]
377 pub fn read_timeout(self, read_timeout: Duration) -> Self {
378 Self {
379 read_timeout,
380 ..self
381 }
382 }
383
384 /// Set the TCP connect timeout.
385 ///
386 /// # Examples
387 ///
388 /// ```no_run
389 /// # fn main() -> anyhow::Result<()> {
390 /// use std::net::IpAddr;
391 /// use std::time::Duration;
392 /// use trippy_core::Builder;
393 ///
394 /// let addr = IpAddr::from([1, 1, 1, 1]);
395 /// let tracer = Builder::new(addr)
396 /// .tcp_connect_timeout(Duration::from_millis(100))
397 /// .build()?;
398 /// # Ok(())
399 /// # }
400 /// ```
401 #[must_use]
402 pub fn tcp_connect_timeout(self, tcp_connect_timeout: Duration) -> Self {
403 Self {
404 tcp_connect_timeout,
405 ..self
406 }
407 }
408
409 /// Set the maximum number of rounds.
410 ///
411 /// If set to `None` then the tracer will run indefinitely, otherwise it
412 /// will stop after the given number of rounds.
413 ///
414 /// # Examples
415 ///
416 /// ```no_run
417 /// # fn main() -> anyhow::Result<()> {
418 /// use std::net::IpAddr;
419 /// use trippy_core::Builder;
420 ///
421 /// let addr = IpAddr::from([1, 1, 1, 1]);
422 /// let tracer = Builder::new(addr).max_rounds(Some(10)).build()?;
423 /// # Ok(())
424 /// # }
425 /// ```
426 #[must_use]
427 pub fn max_rounds(self, max_rounds: Option<usize>) -> Self {
428 Self {
429 max_rounds: max_rounds
430 .and_then(|max_rounds| NonZeroUsize::new(max_rounds).map(MaxRounds)),
431 ..self
432 }
433 }
434
435 /// Set the first ttl.
436 ///
437 /// # Examples
438 ///
439 /// ```no_run
440 /// # fn main() -> anyhow::Result<()> {
441 /// use std::net::IpAddr;
442 /// use trippy_core::Builder;
443 ///
444 /// let addr = IpAddr::from([1, 1, 1, 1]);
445 /// let tracer = Builder::new(addr).first_ttl(2).build()?;
446 /// # Ok(())
447 /// # }
448 /// ```
449 #[must_use]
450 pub fn first_ttl(self, first_ttl: u8) -> Self {
451 Self {
452 first_ttl: TimeToLive(first_ttl),
453 ..self
454 }
455 }
456
457 /// Set the maximum ttl.
458 ///
459 /// # Examples
460 ///
461 /// ```no_run
462 /// # fn main() -> anyhow::Result<()> {
463 /// use std::net::IpAddr;
464 /// use trippy_core::Builder;
465 ///
466 /// let addr = IpAddr::from([1, 1, 1, 1]);
467 /// let tracer = Builder::new(addr).max_ttl(16).build()?;
468 /// # Ok(())
469 /// # }
470 /// ```
471 #[must_use]
472 pub fn max_ttl(self, max_ttl: u8) -> Self {
473 Self {
474 max_ttl: TimeToLive(max_ttl),
475 ..self
476 }
477 }
478
479 /// Set the grace duration.
480 ///
481 /// # Examples
482 ///
483 /// ```no_run
484 /// # fn main() -> anyhow::Result<()> {
485 /// use std::net::IpAddr;
486 /// use std::time::Duration;
487 /// use trippy_core::Builder;
488 ///
489 /// let addr = IpAddr::from([1, 1, 1, 1]);
490 /// let tracer = Builder::new(addr)
491 /// .grace_duration(Duration::from_millis(100))
492 /// .build()?;
493 /// # Ok(())
494 /// # }
495 /// ```
496 #[must_use]
497 pub fn grace_duration(self, grace_duration: Duration) -> Self {
498 Self {
499 grace_duration,
500 ..self
501 }
502 }
503
504 /// Set the max number of probes in flight at any given time.
505 ///
506 /// # Examples
507 ///
508 /// ```no_run
509 /// # fn main() -> anyhow::Result<()> {
510 /// use std::net::IpAddr;
511 /// use trippy_core::Builder;
512 ///
513 /// let addr = IpAddr::from([1, 1, 1, 1]);
514 /// let tracer = Builder::new(addr).max_inflight(22).build()?;
515 /// # Ok(())
516 /// # }
517 /// ```
518 #[must_use]
519 pub fn max_inflight(self, max_inflight: u8) -> Self {
520 Self {
521 max_inflight: MaxInflight(max_inflight),
522 ..self
523 }
524 }
525
526 /// Set the initial sequence number.
527 ///
528 /// # Examples
529 ///
530 /// ```no_run
531 /// # fn main() -> anyhow::Result<()> {
532 /// use std::net::IpAddr;
533 /// use trippy_core::Builder;
534 ///
535 /// let addr = IpAddr::from([1, 1, 1, 1]);
536 /// let tracer = Builder::new(addr).initial_sequence(35000).build()?;
537 /// # Ok(())
538 /// # }
539 /// ```
540 #[must_use]
541 pub fn initial_sequence(self, initial_sequence: u16) -> Self {
542 Self {
543 initial_sequence: Sequence(initial_sequence),
544 ..self
545 }
546 }
547
548 /// Set the port direction.
549 ///
550 /// # Examples
551 ///
552 /// ```no_run
553 /// # fn main() -> anyhow::Result<()> {
554 /// use std::net::IpAddr;
555 /// use trippy_core::{Builder, Port, PortDirection};
556 ///
557 /// let addr = IpAddr::from([1, 1, 1, 1]);
558 /// let tracer = Builder::new(addr)
559 /// .port_direction(PortDirection::FixedDest(Port(8080)))
560 /// .build()?;
561 /// # Ok(())
562 /// # }
563 /// ```
564 #[must_use]
565 pub fn port_direction(self, port_direction: PortDirection) -> Self {
566 Self {
567 port_direction,
568 ..self
569 }
570 }
571
572 /// Set the minimum round duration.
573 ///
574 /// # Examples
575 ///
576 /// ```no_run
577 /// # fn main() -> anyhow::Result<()> {
578 /// use std::net::IpAddr;
579 /// use std::time::Duration;
580 /// use trippy_core::Builder;
581 ///
582 /// let addr = IpAddr::from([1, 1, 1, 1]);
583 /// let tracer = Builder::new(addr)
584 /// .min_round_duration(Duration::from_millis(500))
585 /// .build()?;
586 /// # Ok(())
587 /// # }
588 /// ```
589 #[must_use]
590 pub fn min_round_duration(self, min_round_duration: Duration) -> Self {
591 Self {
592 min_round_duration,
593 ..self
594 }
595 }
596
597 /// Set the maximum round duration.
598 ///
599 /// # Examples
600 ///
601 /// ```no_run
602 /// # fn main() -> anyhow::Result<()> {
603 /// use std::net::IpAddr;
604 /// use std::time::Duration;
605 /// use trippy_core::Builder;
606 ///
607 /// let addr = IpAddr::from([1, 1, 1, 1]);
608 /// let tracer = Builder::new(addr)
609 /// .max_round_duration(Duration::from_millis(1500))
610 /// .build()?;
611 /// # Ok(())
612 /// # }
613 /// ```
614 #[must_use]
615 pub fn max_round_duration(self, max_round_duration: Duration) -> Self {
616 Self {
617 max_round_duration,
618 ..self
619 }
620 }
621
622 /// Set the maximum number of samples to record.
623 ///
624 /// # Examples
625 ///
626 /// ```no_run
627 /// # fn main() -> anyhow::Result<()> {
628 /// use std::net::IpAddr;
629 /// use trippy_core::Builder;
630 ///
631 /// let addr = IpAddr::from([1, 1, 1, 1]);
632 /// let tracer = Builder::new(addr).max_samples(256).build()?;
633 /// # Ok(())
634 /// # }
635 /// ```
636 #[must_use]
637 pub fn max_samples(self, max_samples: usize) -> Self {
638 Self {
639 max_samples,
640 ..self
641 }
642 }
643
644 /// Set the maximum number of flows to record.
645 ///
646 /// # Examples
647 ///
648 /// ```no_run
649 /// # fn main() -> anyhow::Result<()> {
650 /// use std::net::IpAddr;
651 /// use trippy_core::Builder;
652 ///
653 /// let addr = IpAddr::from([1, 1, 1, 1]);
654 /// let tracer = Builder::new(addr).max_flows(64).build()?;
655 /// # Ok(())
656 /// # }
657 /// ```
658 #[must_use]
659 pub fn max_flows(self, max_flows: usize) -> Self {
660 Self { max_flows, ..self }
661 }
662
663 /// Drop privileges after connection is established.
664 ///
665 /// # Examples
666 ///
667 /// ```no_run
668 /// # fn main() -> anyhow::Result<()> {
669 /// use std::net::IpAddr;
670 /// use trippy_core::Builder;
671 ///
672 /// let addr = IpAddr::from([1, 1, 1, 1]);
673 /// let tracer = Builder::new(addr).drop_privileges(true).build()?;
674 /// # Ok(())
675 /// # }
676 /// ```
677 #[must_use]
678 pub fn drop_privileges(self, drop_privileges: bool) -> Self {
679 Self {
680 drop_privileges,
681 ..self
682 }
683 }
684
685 /// Build the `Tracer`.
686 ///
687 /// # Examples
688 ///
689 /// ```no_run
690 /// # fn main() -> anyhow::Result<()> {
691 /// use std::net::IpAddr;
692 /// use trippy_core::Builder;
693 ///
694 /// let addr = IpAddr::from([1, 1, 1, 1]);
695 /// let tracer = Builder::new(addr).build()?;
696 /// # Ok(())
697 /// # }
698 /// ```
699 ///
700 /// # Errors
701 ///
702 /// This function will return `Error::BadConfig` if the configuration is invalid.
703 pub fn build(self) -> Result<Tracer> {
704 match (self.protocol, self.port_direction) {
705 (Protocol::Udp, PortDirection::None) => {
706 return Err(Error::BadConfig(
707 "port_direction may not be None for udp protocol".to_string(),
708 ));
709 }
710 (Protocol::Tcp, PortDirection::None) => {
711 return Err(Error::BadConfig(
712 "port_direction may not be None for tcp protocol".to_string(),
713 ));
714 }
715 _ => (),
716 }
717 if self.first_ttl.0 > MAX_TTL {
718 return Err(Error::BadConfig(format!(
719 "first_ttl {} > {MAX_TTL}",
720 self.first_ttl.0
721 )));
722 }
723 if self.max_ttl.0 > MAX_TTL {
724 return Err(Error::BadConfig(format!(
725 "max_ttl {} > {MAX_TTL}",
726 self.max_ttl.0
727 )));
728 }
729 if self.initial_sequence.0 > MAX_INITIAL_SEQUENCE {
730 return Err(Error::BadConfig(format!(
731 "initial_sequence {} > {MAX_INITIAL_SEQUENCE}",
732 self.initial_sequence.0
733 )));
734 }
735 Ok(Tracer::new(
736 self.interface,
737 self.source_addr,
738 self.target_addr,
739 self.privilege_mode,
740 self.protocol,
741 self.packet_size,
742 self.payload_pattern,
743 self.tos,
744 self.icmp_extension_parse_mode,
745 self.read_timeout,
746 self.tcp_connect_timeout,
747 self.trace_identifier,
748 self.max_rounds,
749 self.first_ttl,
750 self.max_ttl,
751 self.grace_duration,
752 self.max_inflight,
753 self.initial_sequence,
754 self.multipath_strategy,
755 self.port_direction,
756 self.min_round_duration,
757 self.max_round_duration,
758 self.max_samples,
759 self.max_flows,
760 self.drop_privileges,
761 ))
762 }
763}
764
765#[cfg(test)]
766mod tests {
767 use super::*;
768 use crate::{config, Port};
769 use config::defaults;
770 use std::net::Ipv4Addr;
771 use std::num::NonZeroUsize;
772
773 const SOURCE_ADDR: IpAddr = IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0));
774 const TARGET_ADDR: IpAddr = IpAddr::V4(Ipv4Addr::new(2, 2, 2, 2));
775
776 #[test]
777 fn test_builder_minimal() {
778 let tracer = Builder::new(TARGET_ADDR).build().unwrap();
779 assert_eq!(TARGET_ADDR, tracer.target_addr());
780 assert_eq!(None, tracer.source_addr());
781 assert_eq!(None, tracer.interface());
782 assert_eq!(defaults::DEFAULT_MAX_SAMPLES, tracer.max_samples());
783 assert_eq!(defaults::DEFAULT_MAX_FLOWS, tracer.max_flows());
784 assert_eq!(defaults::DEFAULT_STRATEGY_PROTOCOL, tracer.protocol());
785 assert_eq!(TraceId::default(), tracer.trace_identifier());
786 assert_eq!(defaults::DEFAULT_PRIVILEGE_MODE, tracer.privilege_mode());
787 assert_eq!(
788 defaults::DEFAULT_STRATEGY_MULTIPATH,
789 tracer.multipath_strategy()
790 );
791 assert_eq!(
792 defaults::DEFAULT_STRATEGY_PACKET_SIZE,
793 tracer.packet_size().0
794 );
795 assert_eq!(
796 defaults::DEFAULT_STRATEGY_PAYLOAD_PATTERN,
797 tracer.payload_pattern().0
798 );
799 assert_eq!(defaults::DEFAULT_STRATEGY_TOS, tracer.tos().0);
800 assert_eq!(
801 defaults::DEFAULT_ICMP_EXTENSION_PARSE_MODE,
802 tracer.icmp_extension_parse_mode()
803 );
804 assert_eq!(
805 defaults::DEFAULT_STRATEGY_READ_TIMEOUT,
806 tracer.read_timeout()
807 );
808 assert_eq!(
809 defaults::DEFAULT_STRATEGY_TCP_CONNECT_TIMEOUT,
810 tracer.tcp_connect_timeout()
811 );
812 assert_eq!(None, tracer.max_rounds());
813 assert_eq!(defaults::DEFAULT_STRATEGY_FIRST_TTL, tracer.first_ttl().0);
814 assert_eq!(defaults::DEFAULT_STRATEGY_MAX_TTL, tracer.max_ttl().0);
815 assert_eq!(
816 defaults::DEFAULT_STRATEGY_GRACE_DURATION,
817 tracer.grace_duration()
818 );
819 assert_eq!(
820 defaults::DEFAULT_STRATEGY_MAX_INFLIGHT,
821 tracer.max_inflight().0
822 );
823 assert_eq!(
824 defaults::DEFAULT_STRATEGY_INITIAL_SEQUENCE,
825 tracer.initial_sequence().0
826 );
827 assert_eq!(PortDirection::None, tracer.port_direction());
828 assert_eq!(
829 defaults::DEFAULT_STRATEGY_MIN_ROUND_DURATION,
830 tracer.min_round_duration()
831 );
832 assert_eq!(
833 defaults::DEFAULT_STRATEGY_MAX_ROUND_DURATION,
834 tracer.max_round_duration()
835 );
836 }
837
838 #[test]
839 fn test_builder_full() {
840 let tracer = Builder::new(TARGET_ADDR)
841 .source_addr(Some(SOURCE_ADDR))
842 .interface(Some("eth0"))
843 .max_samples(10)
844 .max_flows(20)
845 .protocol(Protocol::Udp)
846 .trace_identifier(101)
847 .privilege_mode(PrivilegeMode::Unprivileged)
848 .multipath_strategy(MultipathStrategy::Paris)
849 .packet_size(128)
850 .payload_pattern(0xff)
851 .tos(0x1a)
852 .icmp_extension_parse_mode(IcmpExtensionParseMode::Enabled)
853 .read_timeout(Duration::from_millis(50))
854 .tcp_connect_timeout(Duration::from_millis(100))
855 .max_rounds(Some(10))
856 .first_ttl(2)
857 .max_ttl(16)
858 .grace_duration(Duration::from_millis(100))
859 .max_inflight(22)
860 .initial_sequence(35000)
861 .port_direction(PortDirection::FixedSrc(Port(8080)))
862 .min_round_duration(Duration::from_millis(500))
863 .max_round_duration(Duration::from_millis(1500))
864 .build()
865 .unwrap();
866
867 assert_eq!(TARGET_ADDR, tracer.target_addr());
868 // note that `source_addr` is not set until the tracer is run
869 assert_eq!(None, tracer.source_addr());
870 assert_eq!(Some("eth0"), tracer.interface());
871 assert_eq!(10, tracer.max_samples());
872 assert_eq!(20, tracer.max_flows());
873 assert_eq!(Protocol::Udp, tracer.protocol());
874 assert_eq!(TraceId(101), tracer.trace_identifier());
875 assert_eq!(PrivilegeMode::Unprivileged, tracer.privilege_mode());
876 assert_eq!(MultipathStrategy::Paris, tracer.multipath_strategy());
877 assert_eq!(PacketSize(128), tracer.packet_size());
878 assert_eq!(PayloadPattern(0xff), tracer.payload_pattern());
879 assert_eq!(TypeOfService(0x1a), tracer.tos());
880 assert_eq!(
881 IcmpExtensionParseMode::Enabled,
882 tracer.icmp_extension_parse_mode()
883 );
884 assert_eq!(Duration::from_millis(50), tracer.read_timeout());
885 assert_eq!(Duration::from_millis(100), tracer.tcp_connect_timeout());
886 assert_eq!(
887 Some(MaxRounds(NonZeroUsize::new(10).unwrap())),
888 tracer.max_rounds()
889 );
890 assert_eq!(TimeToLive(2), tracer.first_ttl());
891 assert_eq!(TimeToLive(16), tracer.max_ttl());
892 assert_eq!(Duration::from_millis(100), tracer.grace_duration());
893 assert_eq!(MaxInflight(22), tracer.max_inflight());
894 assert_eq!(Sequence(35000), tracer.initial_sequence());
895 assert_eq!(PortDirection::FixedSrc(Port(8080)), tracer.port_direction());
896 assert_eq!(Duration::from_millis(500), tracer.min_round_duration());
897 assert_eq!(Duration::from_millis(1500), tracer.max_round_duration());
898 }
899
900 #[test]
901 fn test_zero_max_rounds() {
902 let tracer = Builder::new(IpAddr::from([1, 2, 3, 4]))
903 .max_rounds(Some(0))
904 .build()
905 .unwrap();
906 assert_eq!(None, tracer.max_rounds());
907 }
908
909 #[test]
910 fn test_invalid_initial_sequence() {
911 let err = Builder::new(IpAddr::from([1, 2, 3, 4]))
912 .initial_sequence(u16::MAX)
913 .build()
914 .unwrap_err();
915 assert!(matches!(err, Error::BadConfig(s) if s == "initial_sequence 65535 > 64511"));
916 }
917}