Repository: fujiapple852/trippy (language: rust)

Issue:
Do not conflate AddressInUse and AddrNotAvailable errors
The tracer has the ability to "skip" probes which fail with either `AddressInUse` or `AddrNotAvailable` errors.  

This was added for TCP, where the the network layer performs a socket `connect()` after binding to the local socket address for each probe.  By default for TCP tracing, the src port is set to be the sequence number and so if any of the src ports are in use (i.e. `TIME_WAIT` status) then the `connect()` operation will fail.  The tracer can detect this situation and "skip" probes which cannot be bound.

Currently the tracer will do this if for both `AddressInUse` or `AddrNotAvailable` errors, however this is incorrect, it should only do this for the `AddressInUse` error.

macOS defines these errors as:

```
[EADDRINUSE]       The address is already in use.

[EADDRNOTAVAIL]    The specified address is not available on this machine.
```

Linux defines these errors as:

```
EADDRINUSE
      Local address is already in use.

EADDRNOTAVAIL
      (Internet  domain  sockets) The socket referred to by sockfd had not previously been bound 
to an address and, upon attempting to bind it to an ephemeral port, it was determined that all 
port numbers in the ephemeral port range are currently in use.
```

Testing on macOS confirms that only `EADDRINUSE` occurs in this scenario.  Note that the stream socket sets `SO_REUSEPORT` which, on Linux, prevents this error from occurring.  It does not appear to have any effect on macOS, the man page for `setsockopt(2)` says it should, but for UDP only:

> SO_REUSEPORT allows completely duplicate bindings by multiple processes if they all set SO_REUSEPORT before binding the port.  This option permits multiple instances of a program to each receive UDP/IP multicast or broadcast datagrams destined for the bound port.


Discussion / hints:

Fix patch:
diff --git a/crates/trippy-core/src/error.rs b/crates/trippy-core/src/error.rs
index 7b34a3af7..cfb323b8f 100644
--- a/crates/trippy-core/src/error.rs
+++ b/crates/trippy-core/src/error.rs
@@ -22,8 +22,8 @@ pub enum Error {
     IoError(#[from] IoError),
     #[error("insufficient buffer capacity")]
     InsufficientCapacity,
-    #[error("address {0} not available")]
-    AddressNotAvailable(SocketAddr),
+    #[error("address {0} in use")]
+    AddressInUse(SocketAddr),
     #[error("source IP address {0} could not be bound")]
     InvalidSourceAddr(IpAddr),
     #[error("missing address from socket call")]
diff --git a/crates/trippy-core/src/net/common.rs b/crates/trippy-core/src/net/common.rs
index 0a408264b..00b6e7b44 100644
--- a/crates/trippy-core/src/net/common.rs
+++ b/crates/trippy-core/src/net/common.rs
@@ -3,8 +3,10 @@ use crate::net::platform::in_progress_error;
 use std::io::ErrorKind;
 use std::net::SocketAddr;
 
-/// Helper function to convert an `IoResult` to a `TraceResult` with special handling for
-/// `AddressNotAvailable`.
+/// Helper function to convert an `IoResult` to a `Result`.
+///
+/// The `AddressInUse` error is handled separately to allow for more specific
+/// handling upstream.
 pub fn process_result(addr: SocketAddr, res: IoResult<()>) -> Result<()> {
     match res {
         Ok(()) => Ok(()),
@@ -13,9 +15,7 @@ pub fn process_result(addr: SocketAddr, res: IoResult<()>) -> Result<()> {
                 Ok(())
             } else {
                 match err.kind() {
-                    ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable => {
-                        Err(Error::AddressNotAvailable(addr))
-                    }
+                    ErrorKind::AddrInUse => Err(Error::AddressInUse(addr)),
                     _ => Err(Error::IoError(err)),
                 }
             }
@@ -56,7 +56,7 @@ mod tests {
         ));
         let trace_res = process_result(ADDR, res);
         let trace_err = trace_res.unwrap_err();
-        assert!(matches!(trace_err, Error::AddressNotAvailable(ADDR)));
+        assert!(matches!(trace_err, Error::AddressInUse(ADDR)));
     }
 
     #[test]
@@ -67,7 +67,7 @@ mod tests {
         ));
         let trace_res = process_result(ADDR, res);
         let trace_err = trace_res.unwrap_err();
-        assert!(matches!(trace_err, Error::AddressNotAvailable(ADDR)));
+        assert!(matches!(trace_err, Error::IoError(_)));
     }
 
     #[test]
diff --git a/crates/trippy-core/src/strategy.rs b/crates/trippy-core/src/strategy.rs
index 820941eaa..4692412e4 100644
--- a/crates/trippy-core/src/strategy.rs
+++ b/crates/trippy-core/src/strategy.rs
@@ -110,7 +110,7 @@ impl<F: Fn(&Round<'_>)> Strategy<F> {
                     };
                     while let Err(err) = network.send_probe(probe) {
                         match err {
-                            Error::AddressNotAvailable(_) => {
+                            Error::AddressInUse(_) => {
                                 if st.round_has_capacity() {
                                     probe = st.reissue_probe(SystemTime::now());
                                 } else {


Test patch: