zebra_test/net.rs
1//! Network testing utility functions for Zebra.
2
3use std::env;
4
5/// The name of the env var that skips Zebra tests which need reliable,
6/// fast network connectivity.
7///
8/// We use a constant so that the compiler detects typos.
9const ZEBRA_SKIP_NETWORK_TESTS: &str = "ZEBRA_SKIP_NETWORK_TESTS";
10
11/// The name of the env var that skips Zebra's IPv6 tests.
12///
13/// We use a constant so that the compiler detects typos.
14const ZEBRA_SKIP_IPV6_TESTS: &str = "ZEBRA_SKIP_IPV6_TESTS";
15
16/// Should we skip Zebra tests which need reliable, fast network connectivity?
17//
18// TODO: separate "good and reliable" from "any network"?
19#[allow(clippy::print_stderr)]
20pub fn zebra_skip_network_tests() -> bool {
21 if env::var_os(ZEBRA_SKIP_NETWORK_TESTS).is_some() {
22 // This message is captured by the test runner, use
23 // `cargo test -- --nocapture` to see it.
24 eprintln!("Skipping network test because '$ZEBRA_SKIP_NETWORK_TESTS' is set.");
25 return true;
26 }
27
28 false
29}
30
31/// Should we skip Zebra tests which need a local IPv6 network stack and
32/// IPv6 interface addresses?
33///
34/// Since `zebra_skip_network_tests` only disables tests which need reliable network connectivity,
35/// we allow IPv6 tests even when `ZEBRA_SKIP_NETWORK_TESTS` is set.
36#[allow(clippy::print_stderr)]
37pub fn zebra_skip_ipv6_tests() -> bool {
38 if env::var_os(ZEBRA_SKIP_IPV6_TESTS).is_some() {
39 eprintln!("Skipping IPv6 network test because '$ZEBRA_SKIP_IPV6_TESTS' is set.");
40 return true;
41 }
42
43 // TODO: if we separate "good and reliable" from "any network",
44 // also skip IPv6 tests when we're skipping all network tests.
45 false
46}
47
48#[cfg(windows)]
49/// Returns a random port number from the ephemeral port range.
50///
51/// Does not check if the port is already in use. It's impossible to do this
52/// check in a reliable, cross-platform way.
53///
54/// ## Usage
55///
56/// If you want a once-off random unallocated port, use
57/// `random_unallocated_port`. Don't use this function if you don't need
58/// to - it has a small risk of port conflicts.
59///
60/// Use this function when you need to use the same random port multiple
61/// times. For example: setting up both ends of a connection, or reusing
62/// the same port multiple times.
63pub fn random_known_port() -> u16 {
64 use rand::Rng;
65 // Use the intersection of the IANA/Windows/macOS ephemeral port range,
66 // and the Linux ephemeral port range:
67 // - https://en.wikipedia.org/wiki/Ephemeral_port#Range
68 // excluding ports less than 53500, to avoid:
69 // - typical Hyper-V reservations up to 52000:
70 // - https://github.com/googlevr/gvr-unity-sdk/issues/1002
71 // - https://github.com/docker/for-win/issues/3171
72 // - the MOM-Clear port 51515
73 // - https://docs.microsoft.com/en-us/troubleshoot/windows-server/networking/service-overview-and-network-port-requirements
74 // - the LDAP Kerberos byte-swapped reservation 53249
75 // - https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/ldap-kerberos-server-reset-tcp-sessions
76 // - macOS and Windows sequential ephemeral port allocations,
77 // starting from 49152:
78 // - https://dataplane.org/ephemeralports.html
79
80 rand::thread_rng().gen_range(53500..60999)
81}
82
83#[cfg(not(windows))]
84/// Uses the "magic" port number that tells the operating system to
85/// choose a random unallocated port.
86///
87/// The OS chooses a different port each time it opens a connection or
88/// listener with this magic port number.
89///
90/// Creates a TcpListener to find a random unallocated port, then drops the TcpListener to close the socket.
91///
92/// Returns the unallocated port number.
93///
94/// ## Usage
95///
96/// If you want a once-off random unallocated port, use
97/// `random_unallocated_port`. Don't use this function if you don't need
98/// to - it has a small risk of port conflicts when there is a delay
99/// between this fn call and binding the tcp listener.
100///
101/// Use this function when you need to use the same random port multiple
102/// times. For example: setting up both ends of a connection, or reusing
103/// the same port multiple times.
104///
105/// ## Panics
106///
107/// If the OS finds no available ports
108///
109/// If there is an OS error when getting the socket address
110pub fn random_known_port() -> u16 {
111 use std::net::{Ipv4Addr, SocketAddrV4, TcpListener};
112
113 let host_ip = Ipv4Addr::new(127, 0, 0, 1);
114 let socket = TcpListener::bind(SocketAddrV4::new(host_ip, random_unallocated_port()))
115 .expect("needs an available port")
116 .local_addr()
117 .expect("OS error: could not get socket addr");
118 socket.port()
119}
120
121/// Returns the "magic" port number that tells the operating system to
122/// choose a random unallocated port.
123///
124/// The OS chooses a different port each time it opens a connection or
125/// listener with this magic port number.
126///
127/// ## Usage
128///
129/// See the usage note for `random_known_port`.
130pub fn random_unallocated_port() -> u16 {
131 0
132}