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}