### Running Surge Ping Example Source: https://github.com/kolapapa/surge-ping/blob/main/README.md Command-line examples for running the `simple` and `cmd` examples provided with the surge-ping library. Use `cargo run --example -- ` to execute. ```shell $ git clone https://github.com/kolapapa/surge-ping.git $ cd surge-ping $ cargo run --example simple -- -h 8.8.8.8 -s 56 V4(Icmpv4Packet { source: 8.8.8.8, destination: 10.1.40.79, ttl: 53, icmp_type: IcmpType(0), icmp_code: IcmpCode(0), size: 64, real_dest: 8.8.8.8, identifier: 111, sequence: 0 }) 112.36ms $ cargo run --example cmd -- -h google.com -c 5 PING google.com (172.217.24.238): 56 data bytes 64 bytes from 172.217.24.238: icmp_seq=0 ttl=115 time=109.902 ms 64 bytes from 172.217.24.238: icmp_seq=1 ttl=115 time=73.684 ms 64 bytes from 172.217.24.238: icmp_seq=2 ttl=115 time=65.865 ms 64 bytes from 172.217.24.238: icmp_seq=3 ttl=115 time=66.328 ms 64 bytes from 172.217.24.238: icmp_seq=4 ttl=115 time=68.707 ms --- google.com ping statistics --- 5 packets transmitted, 5 packets received, 0.00% packet loss round-trip min/avg/max/stddev = 65.865/76.897/109.902/16.734 ms ``` -------------------------------- ### Config / ConfigBuilder — Socket configuration Source: https://context7.com/kolapapa/surge-ping/llms.txt Allows customization of socket-level settings for creating a `Client`. Use `Config::builder()` to start and chain configuration options, then call `.build()` to finalize. ```APIDOC ## `Config` / `ConfigBuilder` — Socket configuration `Config` carries all socket-level settings passed when constructing a `Client`. Build one with `Config::builder()` and chain the desired options; call `.build()` to get the final `Config`. The default produces a DGRAM-first ICMPv4 socket with no interface binding and no custom TTL. ### Methods - **`Config::default()`**: Returns a default `Config` for IPv4. - **`Config::builder()`**: Returns a `ConfigBuilder` to construct a custom `Config`. ### `ConfigBuilder` Options - **`kind(ICMP)`**: Sets the ICMP protocol version (V4 or V6). - **`ttl(u8)`**: Sets the Time-To-Live value for outgoing packets. - **`interface(impl AsRef)`**: Sets the network interface to use by name. - **`bind(SocketAddr)`**: Sets the source IP address and port to bind to. - **`interface_index(NonZeroU32)`**: Sets the network interface to use by its kernel index. - **`fib(u32)`**: Sets the FIB (Forwarding Information Base) table ID. - **`build()`**: Finalizes the configuration and returns a `Config` object. ### Request Example ```rust use std::num::NonZeroU32; use surge_ping::{Config, ICMP}; fn main() { // Default IPv4 config (DGRAM socket preferred, no TTL override) let _v4_default = Config::default(); // Custom IPv6 config: RAW socket, TTL 128, bound to eth0 let v6_config = Config::builder() .kind(ICMP::V6) .ttl(128) .interface("eth0") .build(); println!("{:?}", v6_config); // Config { sock_type_hint: RAW, kind: V6, bind: None, interface: Some("eth0"), // interface_index: None, ttl: Some(128), fib: None } // Bind source address, set interface by kernel index let idx = NonZeroU32::new(2).unwrap(); let bound_config = Config::builder() .bind("192.168.1.100:0".parse().unwrap()) .interface_index(idx) .ttl(64) .build(); println!("{:?}", bound_config); } ``` ``` -------------------------------- ### Troubleshooting Permission Denied Errors Source: https://github.com/kolapapa/surge-ping/blob/main/README.md Steps to resolve 'Permission denied' errors when using surge-ping, focusing on system configuration and capabilities. ```bash # Check `net.ipv4.ping_group_range` as shown above # Some container environments may have additional restrictions # As a fallback, run with `sudo` or add `CAP_NET_RAW` capability: # sudo setcap cap_net_raw+ep your-binary ``` -------------------------------- ### Create a shared ping client with Client::new Source: https://context7.com/kolapapa/surge-ping/llms.txt Constructs a `Client` from a `Config`. Internally spawns a background tokio task that continuously reads from the socket and dispatches replies to waiting `Pinger` instances. The `Client` is `Clone`, so pass it to multiple tasks without extra allocation. ```rust use surge_ping::{Client, Config, ICMP}; #[tokio::main] async fn main() -> Result<(), Box> { // One client for IPv4, one for IPv6 let client_v4 = Client::new(&Config::default())?; let client_v6 = Client::new(&Config::builder().kind(ICMP::V6).build())?; // Clone is cheap — the underlying socket is shared via Arc let client_v4_clone = client_v4.clone(); // Spawn two concurrent tasks sharing the same socket let h1 = tokio::spawn(async move { let mut pinger = client_v4_clone .pinger("1.1.1.1".parse().unwrap(), surge_ping::PingIdentifier(1)) .await; pinger.ping(surge_ping::PingSequence(0), &[0u8; 8]).await }); let h2 = tokio::spawn(async move { let mut pinger = client_v4 .pinger("8.8.8.8".parse().unwrap(), surge_ping::PingIdentifier(2)) .await; pinger.ping(surge_ping::PingSequence(0), &[0u8; 8]).await }); let (r1, r2) = tokio::join!(h1, h2); println!("1.1.1.1: {:?}", r1??); println!("8.8.8.8: {:?}", r2??); Ok(()) } ``` -------------------------------- ### Client::new — Create a shared ping client Source: https://context7.com/kolapapa/surge-ping/llms.txt Constructs a `Client` instance from a given `Config`. This client manages a shared underlying socket and spawns a background task for reading replies, allowing cheap cloning for concurrent use across multiple async tasks. ```APIDOC ## `Client::new` — Create a shared ping client Constructs a `Client` from a `Config`. Internally spawns a background tokio task that continuously reads from the socket and dispatches replies to waiting `Pinger` instances. The `Client` is `Clone`, so pass it to multiple tasks without extra allocation. ### Parameters - **config**: `&Config` - A reference to the socket configuration. ### Returns - `Ok(Client)` on successful creation. - `Err(SurgeError)` if the client cannot be created (e.g., socket errors). ### Usage The `Client` can be cloned cheaply and used concurrently across multiple async tasks. Each task can create a `Pinger` from the client to send individual ping requests. ### Request Example ```rust use surge_ping::{Client, Config, ICMP}; #[tokio::main] async fn main() -> Result<(), Box> { // One client for IPv4, one for IPv6 let client_v4 = Client::new(&Config::default())?; let client_v6 = Client::new(&Config::builder().kind(ICMP::V6).build())?; // Clone is cheap — the underlying socket is shared via Arc let client_v4_clone = client_v4.clone(); // Spawn two concurrent tasks sharing the same socket let h1 = tokio::spawn(async move { let mut pinger = client_v4_clone .pinger("1.1.1.1".parse().unwrap(), surge_ping::PingIdentifier(1)) .await; pinger.ping(surge_ping::PingSequence(0), &[0u8; 8]).await }); let h2 = tokio::spawn(async move { let mut pinger = client_v4 .pinger("8.8.8.8".parse().unwrap(), surge_ping::PingIdentifier(2)) .await; pinger.ping(surge_ping::PingSequence(0), &[0u8; 8]).await }); let (r1, r2) = tokio::join!(h1, h2); println!("1.1.1.1: {:?}", r1??); println!("8.8.8.8: {:?}", r2??); Ok(()) } ``` ``` -------------------------------- ### Simple Ping Usage in Rust Source: https://github.com/kolapapa/surge-ping/blob/main/README.md Demonstrates basic single-address ping functionality. Ensure `surge-ping` and `tokio` are added to your Cargo.toml. ```rust /* Cargo.toml [dependencies] surge-ping = "last version" tokio = { version = "1.21.2", features = ["full"] } */ #[tokio::main] async fn main() -> Result<(), Box> { let payload = [0; 8]; let (_packet, duration) = surge_ping::ping("127.0.0.1".parse()?, &payload).await?; println!("Ping took {:.3?}", duration); Ok(()) } ``` -------------------------------- ### Configure socket options with Config::builder Source: https://context7.com/kolapapa/surge-ping/llms.txt Builds a `Config` for socket-level settings. The default produces a DGRAM-first ICMPv4 socket with no interface binding and no custom TTL. Custom configurations can specify ICMP version, TTL, interface binding, and source address. ```rust use std::num::NonZeroU32; use surge_ping::{Config, ICMP}; fn main() { // Default IPv4 config (DGRAM socket preferred, no TTL override) let _v4_default = Config::default(); // Custom IPv6 config: RAW socket, TTL 128, bound to eth0 let v6_config = Config::builder() .kind(ICMP::V6) .ttl(128) .interface("eth0") .build(); println!("{:?}", v6_config); // Config { sock_type_hint: RAW, kind: V6, bind: None, interface: Some("eth0"), // interface_index: None, ttl: Some(128), fib: None } // Bind source address, set interface by kernel index let idx = NonZeroU32::new(2).unwrap(); let bound_config = Config::builder() .bind("192.168.1.100:0".parse().unwrap()) .interface_index(idx) .ttl(64) .build(); println!("{:?}", bound_config); } ``` -------------------------------- ### Create a Pinger for a Target Host Source: https://context7.com/kolapapa/surge-ping/llms.txt Creates a Pinger for a specific IP address and a random identifier. The Pinger is not cloneable and is scoped to one target. Timeout can be overridden. ```rust use rand::random; use surge_ping::{Client, Config, PingIdentifier, PingSequence}; use std::time::Duration; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(&Config::default())?; let host = "93.184.216.34".parse()?; let mut pinger = client.pinger(host, PingIdentifier(random())).await; pinger.timeout(Duration::from_millis(500)); let payload = vec![0u8; 56]; let (packet, rtt) = pinger.ping(PingSequence(0), &payload).await?; println!("Received: {:?} RTT: {:.3?}", packet, rtt); Ok(()) } ``` -------------------------------- ### Client::pinger Source: https://context7.com/kolapapa/surge-ping/llms.txt Creates a Pinger for a target host, configured with a specific IP address and ping identifier. The Pinger is not cloneable and is scoped to a single target. ```APIDOC ## `Client::pinger` — Create a `Pinger` for a target host Returns a `Pinger` configured for a specific `IpAddr` and ping identifier. The `Pinger` is not `Clone` and is scoped to one target; create a new one per host. The identifier is automatically set to `None` on Linux DGRAM sockets (the kernel manages it) and to the provided value on RAW/non-Linux sockets. ```rust use rand::random; use surge_ping::{Client, Config, PingIdentifier, PingSequence}; use std::time::Duration; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(&Config::default())?; let host = "93.184.216.34".parse()?; let mut pinger = client.pinger(host, PingIdentifier(random())).await; pinger.timeout(Duration::from_millis(500)); let payload = vec![0u8; 56]; let (packet, rtt) = pinger.ping(PingSequence(0), &payload).await?; println!("Received: {:?} RTT: {:.3?}", packet, rtt); Ok(()) } ``` ``` -------------------------------- ### Concurrent Multi-host Ping with Single Socket in Rust Source: https://context7.com/kolapapa/surge-ping/llms.txt Use this pattern for network monitoring daemons or health-check sidecars that need to probe many endpoints simultaneously with minimal resource usage. It requires a tokio runtime and the surge_ping library. ```rust use futures::future::join_all; use rand::random; use std::{net::IpAddr, time::Duration}; use surge_ping::{Client, Config, IcmpPacket, PingIdentifier, PingSequence, ICMP}; use tokio::time; #[tokio::main] async fn main() -> Result<(), Box> { let targets = ["8.8.8.8", "1.1.1.1", "9.9.9.9", "208.67.222.222"]; // Single socket shared across all tasks let client = Client::new(&Config::default())?; let mut handles = Vec::new(); for ip_str in &targets { let addr: IpAddr = ip_str.parse()?; let c = client.clone(); handles.push(tokio::spawn(async move { let payload = [0u8; 56]; let mut pinger = c.pinger(addr, PingIdentifier(random())). pinger.timeout(Duration::from_secs(2)); let mut interval = time::interval(Duration::from_secs(1)); for seq in 0u16..3 { interval.tick().await; match pinger.ping(PingSequence(seq), &payload).await { Ok((IcmpPacket::V4(p), dur)) => println!( "{}: seq={} ttl={{:?}} rtt={{:.2?}}", p.get_source(), p.get_sequence(), p.get_ttl(), dur ), Ok((IcmpPacket::V6(p), dur)) => println!( "{}: seq={} hlim={{}} rtt={{:.2?}}", p.get_source(), p.get_sequence(), p.get_max_hop_limit(), dur ), Err(e) => println!("{addr}: {e}"), } } })); } join_all(handles).await; Ok(()) } // Output (interleaved, order non-deterministic): // 8.8.8.8: seq=0 ttl=Some(115) rtt=11.23ms // 1.1.1.1: seq=0 ttl=Some(57) rtt=9.87ms // ... ``` -------------------------------- ### Inspect ICMPv4 and ICMPv6 Reply Packets Source: https://context7.com/kolapapa/surge-ping/llms.txt Match on IcmpPacket variants to access typed information for IPv4 and IPv6 replies. Requires successful ping operation. ```rust use surge_ping::{Client, Config, IcmpPacket, PingIdentifier, PingSequence}; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(&Config::default())?; let mut pinger = client .pinger("1.1.1.1".parse()?, PingIdentifier(99)) .await; let (packet, rtt) = pinger.ping(PingSequence(0), &[0u8; 16]).await?; match packet { IcmpPacket::V4(p) => { println!("Source: {}", p.get_source()); println!("Destination: {}", p.get_destination()); println!("Real dest: {}", p.get_real_dest()); println!("TTL: {:?}", p.get_ttl()); println!("ICMP type: {:?}", p.get_icmp_type()); println!("ICMP code: {:?}", p.get_icmp_code()); println!("Size: {} bytes", p.get_size()); println!("Identifier: {}", p.get_identifier()); println!("Sequence: {}", p.get_sequence()); } IcmpPacket::V6(p) => { println!("Source: {}", p.get_source()); println!("Hop limit: {}", p.get_max_hop_limit()); println!("ICMPv6 type:{:?}", p.get_icmpv6_type()); println!("Size: {} bytes", p.get_size()); } } println!("RTT: {rtt:.3?}"); Ok(()) } // Output: // Source: 1.1.1.1 // TTL: Some(57) // Size: 24 bytes // RTT: 9.102ms ``` -------------------------------- ### One-shot convenience ping with surge_ping::ping Source: https://context7.com/kolapapa/surge-ping/llms.txt Sends a single ICMP echo request to a host. Creates a fresh internal `Client` on every call, so it should only be used for occasional one-off pings. Supports both IPv4 and IPv6. ```rust use surge_ping::SurgeError; #[tokio::main] async fn main() -> Result<(), Box> { let payload = [0u8; 8]; match surge_ping::ping("8.8.8.8".parse()?, &payload).await { Ok((packet, duration)) => println!("Reply: {:?} RTT: {:.2?}", packet, duration), Err(SurgeError::Timeout { seq }) => println!("Timeout on seq {seq}"), Err(e) => eprintln!("Error: {e}"), } // IPv6 works the same way — the function selects the right ICMP version automatically match surge_ping::ping("2001:4860:4860::8888".parse()?, &payload).await { Ok((_pkt, dur)) => println!("IPv6 RTT: {dur:.2?}"), Err(e) => eprintln!("IPv6 error: {e}"), } Ok(()) } ``` -------------------------------- ### Send Echo Request and Await Reply Source: https://context7.com/kolapapa/surge-ping/llms.txt Sends an ICMP echo request and waits for a reply within the configured timeout. Handles both IPv4 and IPv6 replies, as well as timeouts and other errors. Each sequence number must be unique. ```rust use surge_ping::{Client, Config, IcmpPacket, PingIdentifier, PingSequence, SurgeError}; use std::time::Duration; use tokio::time; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(&Config::default())?; let mut pinger = client .pinger("8.8.8.8".parse()?, PingIdentifier(0xABCD)) .await; pinger.timeout(Duration::from_secs(2)); let payload = [0u8; 56]; let mut interval = time::interval(Duration::from_secs(1)); for seq in 0u16..5 { interval.tick().await; match pinger.ping(PingSequence(seq), &payload).await { Ok((IcmpPacket::V4(reply), dur)) => println!( "{} bytes from {}: icmp_seq={} ttl={:?} time={:.3?}", reply.get_size(), reply.get_source(), reply.get_sequence(), reply.get_ttl(), dur ), Ok((IcmpPacket::V6(reply), dur)) => println!( "{} bytes from {}: icmp_seq={} hlim={} time={:.3?}", reply.get_size(), reply.get_source(), reply.get_sequence(), reply.get_max_hop_limit(), dur ), Err(SurgeError::Timeout { seq }) => println!("Request timeout for icmp_seq {seq}"), Err(e) => eprintln!("Error: {e}"), } } Ok(()) } ``` -------------------------------- ### Checking System Configuration for Non-Privileged Ping Source: https://github.com/kolapapa/surge-ping/blob/main/README.md Commands to check and enable non-privileged ICMP ping on Linux systems. This allows ping operations without root privileges. ```bash # Check if non-privileged ICMP is enabled sysctl net.ipv4.ping_group_range # Typical output: "0 2147483647" (enabled for all groups) # If output is "1 0", it's disabled # To enable non-privileged ping temporarily: sudo sysctl -w net.ipv4.ping_group_range="0 2147483647" # To make the change persistent: echo "net.ipv4.ping_group_range=0 2147483647" | sudo tee -a /etc/sysctl.conf sudo sysctl -p ``` -------------------------------- ### Send Echo Request (Fire-and-Forget) Source: https://context7.com/kolapapa/surge-ping/llms.txt Sends an ICMP echo packet without waiting for a reply. This is useful for probe scenarios where external confirmation is handled or not required. ```rust use surge_ping::{Client, Config, PingIdentifier, PingSequence}; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(&Config::default())?; let pinger = client .pinger("192.168.1.1".parse()?, PingIdentifier(42)) .await; let payload = [0u8; 8]; pinger.send_ping(PingSequence(0), &payload).await?; println!("Probe sent to 192.168.1.1"); Ok(()) } ``` -------------------------------- ### Configure Per-Pinger Timeout and Scope ID Source: https://context7.com/kolapapa/surge-ping/llms.txt Allows overriding the default ping timeout and setting the IPv6 scope ID for link-local addresses. The scope ID is necessary when pinging addresses like fe80::1%eth0. ```rust use surge_ping::{Client, Config, PingIdentifier, PingSequence, ICMP}; use std::time::Duration; #[tokio::main] async fn main() -> Result<(), Box> { let config = Config::builder().kind(ICMP::V6).build(); let client = Client::new(&config)?; let link_local: std::net::IpAddr = "fe80::1".parse()?; let mut pinger = client.pinger(link_local, PingIdentifier(1)).await; pinger.scope_id(2); pinger.timeout(Duration::from_millis(300)); match pinger.ping(PingSequence(0), &[0u8; 8]).await { Ok((pkt, dur)) => println!("Reply: {:?} in {:.2?}", pkt, dur), Err(e) => println!("Error: {e}"), } Ok(()) } ``` -------------------------------- ### Handle SurgeError Variants for Ping Operations Source: https://context7.com/kolapapa/surge-ping/llms.txt Catch and handle specific SurgeError types returned by ping operations, such as timeouts, I/O errors, and malformed packets. Configure custom timeouts for ping requests. ```rust use surge_ping::{Client, Config, PingIdentifier, PingSequence, SurgeError}; use std::time::Duration; #[tokio::main] async fn main() { let client = Client::new(&Config::default()).unwrap(); let mut pinger = client .pinger("192.0.2.1".parse().unwrap(), PingIdentifier(7)) // TEST-NET (unreachable) .await; pinger.timeout(Duration::from_millis(200)); match pinger.ping(PingSequence(0), &[0u8; 8]).await { Ok(_) => println!("Got reply"), Err(SurgeError::Timeout { seq }) => println!("Timed out: seq={seq}"), Err(SurgeError::IOError(e)) => eprintln!("Socket error: {e}"), Err(SurgeError::IdenticalRequests { host, ident, seq }) => { eprintln!("Duplicate in-flight request to {host} ident={ident:?} seq={seq}") } Err(SurgeError::ClientDestroyed) => eprintln!("Client was dropped"), Err(SurgeError::MalformedPacket(e)) => eprintln!("Bad packet: {e}"), Err(e) => eprintln!("Other error: {e}"), } // Expected output: Timed out: seq=0 } ``` -------------------------------- ### surge_ping::ping — One-shot convenience ping Source: https://context7.com/kolapapa/surge-ping/llms.txt Sends a single ICMP echo request to a specified host and returns the reply packet and round-trip duration. This function is suitable for occasional one-off pings as it creates a new internal client on each call. ```APIDOC ## `surge_ping::ping` — One-shot convenience ping Sends a single ICMP echo request to `host` with the given `payload` and returns the reply packet and round-trip duration. Creates a fresh internal `Client` on every call, so it should only be used for occasional one-off pings, not high-frequency or multi-host scenarios. ### Parameters - **host**: `IpAddr` - The target IP address to ping. - **payload**: `&[u8]` - The data payload to include in the ICMP echo request. ### Returns - `Ok((IcmpPacket, Duration))` on success, where `IcmpPacket` is the decoded reply and `Duration` is the round-trip time. - `Err(SurgeError)` on failure, including `SurgeError::Timeout`. ### Request Example ```rust use surge_ping::SurgeError; #[tokio::main] async fn main() -> Result<(), Box> { let payload = [0u8; 8]; match surge_ping::ping("8.8.8.8".parse()?, &payload).await { Ok((packet, duration)) => println!("Reply: {:?} RTT: {:.2?}", packet, duration), Err(SurgeError::Timeout { seq }) => println!("Timeout on seq {seq}"), Err(e) => eprintln!("Error: {e}"), } // IPv6 works the same way — the function selects the right ICMP version automatically match surge_ping::ping("2001:4860:4860::8888".parse()?, &payload).await { Ok((_pkt, dur)) => println!("IPv6 RTT: {dur:.2?}"), Err(e) => eprintln!("IPv6 error: {e}"), } Ok(()) } ``` ### Response Example ``` Reply: V4(Icmpv4Packet { source: 8.8.8.8, ... }) RTT: 12.34ms IPv6 RTT: 15.67ms ``` ``` -------------------------------- ### Pinger::timeout / Pinger::scope_id Source: https://context7.com/kolapapa/surge-ping/llms.txt Allows per-pinger configuration. `timeout` sets a custom deadline for echo replies, overriding the default. `scope_id` is used to specify the IPv6 scope ID for link-local addresses. ```APIDOC ## `Pinger::timeout` / `Pinger::scope_id` — Per-pinger configuration `timeout` overrides the default 2-second per-ping deadline. `scope_id` sets the IPv6 scope ID on the destination socket address, required when pinging link-local addresses (e.g., `fe80::1%eth0`). ```rust use surge_ping::{Client, Config, PingIdentifier, PingSequence, ICMP}; use std::time::Duration; #[tokio::main] async fn main() -> Result<(), Box> { let config = Config::builder().kind(ICMP::V6).build(); let client = Client::new(&config)?; let link_local: std::net::IpAddr = "fe80::1".parse()?; let mut pinger = client.pinger(link_local, PingIdentifier(1)).await; pinger.scope_id(2); pinger.timeout(Duration::from_millis(300)); match pinger.ping(PingSequence(0), &[0u8; 8]).await { Ok((pkt, dur)) => println!("Reply: {:?} in {:.2?}", pkt, dur), Err(e) => println!("Error: {e}"), } Ok(()) } ``` ``` -------------------------------- ### IcmpPacket, Icmpv4Packet, Icmpv6Packet — Reply packet inspection Source: https://context7.com/kolapapa/surge-ping/llms.txt Inspects ICMP reply packets. The `IcmpPacket` enum provides variants for IPv4 (`V4`) and IPv6 (`V6`) packets, allowing access to specific details like source/destination addresses, TTL/hop-limit, ICMP type and code, packet size, identifier, and sequence number. ```APIDOC ## `IcmpPacket`, `Icmpv4Packet`, `Icmpv6Packet` — Reply packet inspection `IcmpPacket` is the enum returned by every successful ping. Match on its `V4` / `V6` variants to access typed accessors for source/destination addresses, TTL/hop-limit, ICMP type and code, packet size, identifier, and sequence number. ```rust use surge_ping::{Client, Config, IcmpPacket, PingIdentifier, PingSequence}; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(&Config::default())?; let mut pinger = client .pinger("1.1.1.1".parse()?, PingIdentifier(99)) .await; let (packet, rtt) = pinger.ping(PingSequence(0), &[0u8; 16]).await?; match packet { IcmpPacket::V4(p) => { println!("Source: {}", p.get_source()); println!("Destination: {}", p.get_destination()); println!("Real dest: {}", p.get_real_dest()); println!("TTL: {:?}", p.get_ttl()); println!("ICMP type: {:?}", p.get_icmp_type()); println!("ICMP code: {:?}", p.get_icmp_code()); println!("Size: {} bytes", p.get_size()); println!("Identifier: {}", p.get_identifier()); println!("Sequence: {}", p.get_sequence()); } IcmpPacket::V6(p) => { println!("Source: {}", p.get_source()); println!("Hop limit: {}", p.get_max_hop_limit()); println!("ICMPv6 type:{:?}", p.get_icmpv6_type()); println!("Size: {} bytes", p.get_size()); } } println!("RTT: {rtt:.3?}"); Ok(()) } // Output: // Source: 1.1.1.1 // TTL: Some(57) // Size: 24 bytes // RTT: 9.102ms ``` ``` -------------------------------- ### SurgeError — Error handling Source: https://context7.com/kolapapa/surge-ping/llms.txt Handles errors from fallible operations in the Surge Ping library. The `SurgeError` enum covers various error conditions including socket I/O, timeouts, malformed packets, duplicate requests, and client issues. ```APIDOC ## `SurgeError` — Error handling All fallible operations return `Result`. The error variants cover socket I/O, reply timeouts, malformed packets, duplicate in-flight requests, and use of a dropped client. ```rust use surge_ping::{Client, Config, PingIdentifier, PingSequence, SurgeError}; use std::time::Duration; #[tokio::main] async fn main() { let client = Client::new(&Config::default()).unwrap(); let mut pinger = client .pinger("192.0.2.1".parse().unwrap(), PingIdentifier(7)) // TEST-NET (unreachable) .await; pinger.timeout(Duration::from_millis(200)); match pinger.ping(PingSequence(0), &[0u8; 8]).await { Ok(_) => println!("Got reply"), Err(SurgeError::Timeout { seq }) => println!("Timed out: seq={seq}"), Err(SurgeError::IOError(e)) => eprintln!("Socket error: {e}"), Err(SurgeError::IdenticalRequests { host, ident, seq }) => { eprintln!("Duplicate in-flight request to {host} ident={ident:?} seq={seq}") } Err(SurgeError::ClientDestroyed) => eprintln!("Client was dropped"), Err(SurgeError::MalformedPacket(e)) => eprintln!("Bad packet: {e}"), Err(e) => eprintln!("Other error: {e}"), } // Expected output: Timed out: seq=0 } ``` ``` -------------------------------- ### Pinger::send_ping Source: https://context7.com/kolapapa/surge-ping/llms.txt Sends an ICMP echo request without waiting for a reply. This is suitable for probe scenarios where confirmation is handled externally or not required. ```APIDOC ## `Pinger::send_ping` — Fire-and-forget echo request Sends an ICMP echo packet without registering a reply waiter. Useful for probe-style scenarios where receipt confirmation is handled externally or is not needed. ```rust use surge_ping::{Client, Config, PingIdentifier, PingSequence}; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(&Config::default())?; let pinger = client .pinger("192.168.1.1".parse()?, PingIdentifier(42)) .await; let payload = [0u8; 8]; pinger.send_ping(PingSequence(0), &payload).await?; println!("Probe sent to 192.168.1.1"); Ok(()) } ``` ``` -------------------------------- ### Pinger::ping Source: https://context7.com/kolapapa/surge-ping/llms.txt Sends an ICMP echo request and waits for a reply within the configured timeout. Returns the ICMP packet and round-trip duration on success. Reusing a sequence number before its reply is received results in an error. ```APIDOC ## `Pinger::ping` — Send an echo request and await the reply Sends one ICMP echo request with the given sequence number and payload, then waits up to the configured timeout for the matching echo reply. Returns `(IcmpPacket, Duration)` on success. Each sequence number must be unique per host/identifier combination; reusing it while a reply is still pending yields `SurgeError::IdenticalRequests`. ```rust use surge_ping::{Client, Config, IcmpPacket, PingIdentifier, PingSequence, SurgeError}; use std::time::Duration; use tokio::time; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::new(&Config::default())?; let mut pinger = client .pinger("8.8.8.8".parse()?, PingIdentifier(0xABCD)) .await; pinger.timeout(Duration::from_secs(2)); let payload = [0u8; 56]; let mut interval = time::interval(Duration::from_secs(1)); for seq in 0u16..5 { interval.tick().await; match pinger.ping(PingSequence(seq), &payload).await { Ok((IcmpPacket::V4(reply), dur)) => println!( "{} bytes from {}: icmp_seq={} ttl={:?} time={:.3?}", reply.get_size(), reply.get_source(), reply.get_sequence(), reply.get_ttl(), dur ), Ok((IcmpPacket::V6(reply), dur)) => println!( "{} bytes from {}: icmp_seq={} hlim={} time={:.3?}", reply.get_size(), reply.get_source(), reply.get_sequence(), reply.get_max_hop_limit(), dur ), Err(SurgeError::Timeout { seq }) => println!("Request timeout for icmp_seq {seq}"), Err(e) => eprintln!("Error: {e}"), } } Ok(()) } ``` ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.