1#![cfg(feature = "agave-unstable-api")]
2pub mod banlist;
5mod ip_echo_client;
6mod ip_echo_server;
7pub mod multihomed_sockets;
8pub mod pinned_xdp_sender;
9pub mod quic_socket;
10pub mod socket_addr_space;
11pub mod sockets;
12#[cfg(any(target_os = "android", target_os = "windows"))]
13#[path = "test_port_allocator_legacy.rs"]
14pub(crate) mod test_port_allocator;
15#[cfg(not(any(target_os = "android", target_os = "windows")))]
16pub(crate) mod test_port_allocator;
17pub mod token_bucket;
18
19#[cfg(feature = "dev-context-only-utils")]
20pub mod tooling_for_tests;
21
22pub use {
23 agave_xdp::transmitter::TrySendError,
24 ip_echo_client::IpEchoClientError,
25 ip_echo_server::{
26 DEFAULT_IP_ECHO_SERVER_THREADS, IpEchoServer, MAX_PORT_COUNT_PER_MESSAGE, ip_echo_server,
27 },
28 pinned_xdp_sender::PinnedXdpSender,
29 socket_addr_space::SocketAddrSpace,
30};
31use {
32 ip_echo_client::{ip_echo_server_request, ip_echo_server_request_with_binding},
33 ip_echo_server::IpEchoServerMessage,
34 rand::{Rng, rng},
35 std::{
36 io::{self},
37 net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener, ToSocketAddrs, UdpSocket},
38 },
39 url::Url,
40};
41
42pub struct UdpSocketPair {
44 pub addr: SocketAddr, pub receiver: UdpSocket, pub sender: UdpSocket, }
48
49pub type PortRange = (u16, u16);
50
51#[cfg(not(debug_assertions))]
52pub const VALIDATOR_PORT_RANGE: PortRange = (8000, 10_000);
54
55#[cfg(debug_assertions)]
58pub const VALIDATOR_PORT_RANGE: PortRange = (
59 crate::sockets::UNIQUE_ALLOC_BASE_PORT - 512,
60 crate::sockets::UNIQUE_ALLOC_BASE_PORT,
61);
62
63pub const MINIMUM_VALIDATOR_PORT_RANGE_WIDTH: u16 = 26; #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
67pub enum Protocol {
68 UDP,
69 QUIC,
70}
71
72pub(crate) const HEADER_LENGTH: usize = 4;
73pub(crate) const IP_ECHO_SERVER_RESPONSE_LENGTH: usize = HEADER_LENGTH + 23;
74
75pub fn get_public_ip_addr_with_binding(
78 ip_echo_server_addr: &SocketAddr,
79 bind_address: IpAddr,
80) -> Result<IpAddr, IpEchoClientError> {
81 let fut = ip_echo_server_request_with_binding(
82 *ip_echo_server_addr,
83 IpEchoServerMessage::default(),
84 bind_address,
85 );
86 let rt = tokio::runtime::Builder::new_current_thread()
87 .enable_all()
88 .build()?;
89 let resp = rt.block_on(fut)?;
90 Ok(resp.address)
91}
92
93pub fn get_cluster_shred_version(ip_echo_server_addr: &SocketAddr) -> Result<u16, String> {
95 let fut = ip_echo_server_request(*ip_echo_server_addr, IpEchoServerMessage::default());
96 let rt = tokio::runtime::Builder::new_current_thread()
97 .enable_all()
98 .build()
99 .map_err(|e| e.to_string())?;
100 let resp = rt.block_on(fut).map_err(|e| e.to_string())?;
101 resp.shred_version
102 .ok_or_else(|| "IP echo server does not return a shred-version".to_owned())
103}
104
105pub fn get_cluster_shred_version_with_binding(
108 ip_echo_server_addr: &SocketAddr,
109 bind_address: IpAddr,
110) -> Result<u16, IpEchoClientError> {
111 let fut = ip_echo_server_request_with_binding(
112 *ip_echo_server_addr,
113 IpEchoServerMessage::default(),
114 bind_address,
115 );
116 let rt = tokio::runtime::Builder::new_current_thread()
117 .enable_all()
118 .build()?;
119 let resp = rt.block_on(fut)?;
120 resp.shred_version.ok_or_else(|| {
121 IpEchoClientError::InvalidResponse(
122 "IP echo server does not return a shred-version".to_owned(),
123 )
124 })
125}
126
127const MAX_PORT_VERIFY_THREADS: usize = 64;
130
131pub fn verify_all_reachable_udp(
136 ip_echo_server_addr: &SocketAddr,
137 udp_sockets: &[&UdpSocket],
138) -> bool {
139 let rt = tokio::runtime::Builder::new_current_thread()
140 .enable_all()
141 .max_blocking_threads(MAX_PORT_VERIFY_THREADS)
142 .build()
143 .expect("Tokio builder should be able to reliably create a current thread runtime");
144 let fut = ip_echo_client::verify_all_reachable_udp(
145 *ip_echo_server_addr,
146 udp_sockets,
147 ip_echo_client::TIMEOUT,
148 ip_echo_client::DEFAULT_RETRY_COUNT,
149 );
150 rt.block_on(fut)
151}
152
153pub fn verify_all_reachable_tcp(
158 ip_echo_server_addr: &SocketAddr,
159 tcp_listeners: Vec<TcpListener>,
160) -> bool {
161 let rt = tokio::runtime::Builder::new_current_thread()
162 .enable_all()
163 .max_blocking_threads(MAX_PORT_VERIFY_THREADS)
164 .build()
165 .expect("Tokio builder should be able to reliably create a current thread runtime");
166 let fut = ip_echo_client::verify_all_reachable_tcp(
167 *ip_echo_server_addr,
168 tcp_listeners,
169 ip_echo_client::TIMEOUT,
170 );
171 rt.block_on(fut)
172}
173
174pub fn parse_port_or_addr(optstr: Option<&str>, default_addr: SocketAddr) -> SocketAddr {
175 if let Some(addrstr) = optstr {
176 if let Ok(port) = addrstr.parse() {
177 let mut addr = default_addr;
178 addr.set_port(port);
179 addr
180 } else if let Ok(addr) = addrstr.parse() {
181 addr
182 } else {
183 default_addr
184 }
185 } else {
186 default_addr
187 }
188}
189
190pub fn parse_port_range(port_range: &str) -> Option<PortRange> {
191 let ports: Vec<&str> = port_range.split('-').collect();
192 if ports.len() != 2 {
193 return None;
194 }
195
196 let start_port = ports[0].parse();
197 let end_port = ports[1].parse();
198
199 if start_port.is_err() || end_port.is_err() {
200 return None;
201 }
202 let start_port = start_port.unwrap();
203 let end_port = end_port.unwrap();
204 if end_port < start_port {
205 return None;
206 }
207 Some((start_port, end_port))
208}
209
210fn select_ipv4<T>(
211 host: &str,
212 mut values: impl Iterator<Item = T>,
213 mut ip_addr: impl FnMut(&T) -> IpAddr,
214) -> Result<T, String> {
215 let Some(first_value) = values.next() else {
216 return Err(format!("Unable to resolve host: {host}"));
217 };
218
219 if ip_addr(&first_value).is_ipv4() {
220 return Ok(first_value);
221 }
222
223 values
224 .find(|value| ip_addr(value).is_ipv4())
225 .ok_or_else(|| format!("IPv6 addresses are not supported: {host}"))
226}
227
228pub fn parse_host(host: &str) -> Result<IpAddr, String> {
229 if let Ok(IpAddr::V6(_)) = host.parse::<IpAddr>() {
230 return Err(format!("IPv6 addresses are not supported: {host}"));
231 }
232
233 let parsed_url = Url::parse(&format!("http://{host}")).map_err(|e| e.to_string())?;
236 if parsed_url.port().is_some() {
237 return Err(format!("Expected port in URL: {host}"));
238 }
239
240 let ips = (host, 0)
242 .to_socket_addrs()
243 .map_err(|err| err.to_string())?
244 .map(|socket_address| socket_address.ip());
245
246 select_ipv4(host, ips, |ip| *ip)
247}
248
249pub fn is_host(string: String) -> Result<(), String> {
250 parse_host(&string).map(|_| ())
251}
252
253pub fn parse_host_port(host_port: &str) -> Result<SocketAddr, String> {
254 let addrs = host_port
255 .to_socket_addrs()
256 .map_err(|err| format!("Unable to resolve host {host_port}: {err}"))?;
257 select_ipv4(host_port, addrs, SocketAddr::ip)
258}
259
260pub fn is_host_port(string: String) -> Result<(), String> {
261 parse_host_port(&string).map(|_| ())
262}
263
264pub fn bind_in_range(ip_addr: IpAddr, range: PortRange) -> io::Result<(u16, UdpSocket)> {
265 let config = sockets::SocketConfiguration::default();
266 sockets::bind_in_range_with_config(ip_addr, range, config)
267}
268
269pub fn bind_to_unspecified() -> io::Result<UdpSocket> {
270 let config = sockets::SocketConfiguration::default();
271 sockets::bind_to_with_config(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0, config)
272}
273
274pub fn find_available_port_in_range(ip_addr: IpAddr, range: PortRange) -> io::Result<u16> {
281 let [port] = find_available_ports_in_range(ip_addr, range)?;
282 Ok(port)
283}
284
285pub fn find_available_ports_in_range<const N: usize>(
290 ip_addr: IpAddr,
291 range: PortRange,
292) -> io::Result<[u16; N]> {
293 let mut result = [0u16; N];
294 let range = range.0..range.1;
295 let mut next_port_to_try = range
296 .clone()
297 .cycle() .skip(rng().random_range(range.clone()) as usize) .take(range.len()) .peekable();
301 let mut num = 0;
302 let config = sockets::SocketConfiguration::default();
303 while num < N {
304 let port_to_try = next_port_to_try.next().unwrap(); let bind = sockets::bind_common_with_config(ip_addr, port_to_try, config);
306 match bind {
307 Ok(_) => {
308 result[num] = port_to_try;
309 num = num.saturating_add(1);
310 }
311 Err(err) => {
312 if next_port_to_try.peek().is_none() {
313 return Err(err);
314 }
315 }
316 }
317 }
318 Ok(result)
319}
320
321#[cfg(test)]
322mod tests {
323 use {
324 super::*, ip_echo_server::IpEchoServerResponse, itertools::Itertools, std::net::Ipv4Addr,
325 };
326
327 #[test]
328 fn test_response_length() {
329 let resp = IpEchoServerResponse {
330 address: IpAddr::from([u16::MAX; 8]), shred_version: Some(u16::MAX),
332 };
333 let resp_size = bincode::serialized_size(&resp).unwrap();
334 assert_eq!(
335 IP_ECHO_SERVER_RESPONSE_LENGTH,
336 HEADER_LENGTH + resp_size as usize
337 );
338 }
339
340 #[test]
342 fn test_backward_compat() {
343 let address = IpAddr::from([
344 525u16, 524u16, 523u16, 522u16, 521u16, 520u16, 519u16, 518u16,
345 ]);
346 let response = IpEchoServerResponse {
347 address,
348 shred_version: Some(42),
349 };
350 let mut data = vec![0u8; IP_ECHO_SERVER_RESPONSE_LENGTH];
351 bincode::serialize_into(&mut data[HEADER_LENGTH..], &response).unwrap();
352 data.truncate(HEADER_LENGTH + 20);
353 assert_eq!(
354 bincode::deserialize::<IpAddr>(&data[HEADER_LENGTH..]).unwrap(),
355 address
356 );
357 }
358
359 #[test]
361 fn test_forward_compat() {
362 let address = IpAddr::from([
363 525u16, 524u16, 523u16, 522u16, 521u16, 520u16, 519u16, 518u16,
364 ]);
365 let mut data = [0u8; IP_ECHO_SERVER_RESPONSE_LENGTH];
366 bincode::serialize_into(&mut data[HEADER_LENGTH..], &address).unwrap();
367 let response: Result<IpEchoServerResponse, _> =
368 bincode::deserialize(&data[HEADER_LENGTH..]);
369 assert_eq!(
370 response.unwrap(),
371 IpEchoServerResponse {
372 address,
373 shred_version: None,
374 }
375 );
376 }
377
378 #[test]
379 fn test_parse_port_or_addr() {
380 let p1 = parse_port_or_addr(Some("9000"), SocketAddr::from(([1, 2, 3, 4], 1)));
381 assert_eq!(p1.port(), 9000);
382 let p2 = parse_port_or_addr(Some("127.0.0.1:7000"), SocketAddr::from(([1, 2, 3, 4], 1)));
383 assert_eq!(p2.port(), 7000);
384 let p2 = parse_port_or_addr(Some("hi there"), SocketAddr::from(([1, 2, 3, 4], 1)));
385 assert_eq!(p2.port(), 1);
386 let p3 = parse_port_or_addr(None, SocketAddr::from(([1, 2, 3, 4], 1)));
387 assert_eq!(p3.port(), 1);
388 }
389
390 #[test]
391 fn test_parse_port_range() {
392 assert_eq!(parse_port_range("garbage"), None);
393 assert_eq!(parse_port_range("1-"), None);
394 assert_eq!(parse_port_range("1-2"), Some((1, 2)));
395 assert_eq!(parse_port_range("1-2-3"), None);
396 assert_eq!(parse_port_range("2-1"), None);
397 }
398
399 #[test]
400 fn test_parse_host() {
401 parse_host("localhost:1234").unwrap_err();
402 parse_host("localhost").unwrap();
403 parse_host("127.0.0.0:1234").unwrap_err();
404 parse_host("127.0.0.0").unwrap();
405 parse_host("2001:db8:abcd:42::dead:beef").unwrap_err();
406
407 assert_eq!(
408 select_ipv4(
409 "ipv6-only.test",
410 [IpAddr::V6(std::net::Ipv6Addr::LOCALHOST)].into_iter(),
411 |ip| *ip,
412 )
413 .unwrap_err(),
414 "IPv6 addresses are not supported: ipv6-only.test",
415 );
416 assert_eq!(
417 select_ipv4(
418 "dual-stack.test",
419 [
420 IpAddr::V6(std::net::Ipv6Addr::LOCALHOST),
421 IpAddr::V4(Ipv4Addr::LOCALHOST),
422 ]
423 .into_iter(),
424 |ip| *ip,
425 )
426 .unwrap(),
427 IpAddr::V4(Ipv4Addr::LOCALHOST),
428 );
429 }
430
431 #[test]
432 fn test_parse_host_port() {
433 parse_host_port("localhost:1234").unwrap();
434 parse_host_port("localhost").unwrap_err();
435 parse_host_port("127.0.0.0:1234").unwrap();
436 parse_host_port("127.0.0.0").unwrap_err();
437 assert_eq!(
438 parse_host_port("[2001:db8:abcd:42::dead:beef]:1234").unwrap_err(),
439 "IPv6 addresses are not supported: [2001:db8:abcd:42::dead:beef]:1234",
440 );
441 }
442
443 #[test]
444 fn test_is_host_port() {
445 assert!(is_host_port("localhost:1234".to_string()).is_ok());
446 assert!(is_host_port("localhost".to_string()).is_err());
447 }
448
449 #[test]
450 fn test_find_available_port_in_range() {
451 let ip_addr = IpAddr::V4(Ipv4Addr::LOCALHOST);
452 let range = sockets::unique_port_range_for_tests(4);
453 let (pr_s, pr_e) = (range.start, range.end);
454 assert_eq!(
455 find_available_port_in_range(ip_addr, (pr_s, pr_s + 1)).unwrap(),
456 pr_s
457 );
458 let port = find_available_port_in_range(ip_addr, (pr_s, pr_e)).unwrap();
459 assert!((pr_s..pr_e).contains(&port));
460
461 let _socket = sockets::bind_to(ip_addr, port).unwrap();
462 find_available_port_in_range(ip_addr, (port, port + 1)).unwrap_err();
463 }
464
465 #[test]
466 fn test_find_available_ports_in_range() {
467 let ip_addr = IpAddr::V4(Ipv4Addr::LOCALHOST);
468 let port_range = sockets::localhost_port_range_for_tests();
469 assert!(port_range.1 - port_range.0 > 16);
470 let sock = sockets::bind_to_with_config(
472 ip_addr,
473 port_range.0 + 2,
474 sockets::SocketConfiguration::default(),
475 )
476 .unwrap();
477 let ports: [u16; 15] = find_available_ports_in_range(ip_addr, port_range).unwrap();
478 let mut ports_vec = Vec::from(ports);
479 ports_vec.push(sock.local_addr().unwrap().port());
480 let res: Vec<_> = ports_vec.into_iter().unique().collect();
481 assert_eq!(res.len(), 16, "Should reserve 16 unique ports");
482 }
483}