//! `ping [-c count] [-i ms] host`: send ICMP echo requests, time the replies. //! //! ICMP is IP's "control message" protocol. An *echo request* (type 8) asks //! a host to send the same bytes straight back as an *echo reply* (type 0). //! The packet: type, code, checksum (2 bytes), identifier (2), sequence //! number (2), then any payload. We use a Linux-style "ping socket" //! (SOCK_DGRAM + IPPROTO_ICMP): the kernel fills in the identifier and //! checksum and only gives us replies to *our* requests. #![no_std] #![no_main] #[macro_use] #[path = "ulib.rs"] mod ulib; const PAYLOAD: usize = 56; // like Linux's ping: 56 + 8 header = 64 bytes fn main() -> i64 { let mut count = 4u16; let mut interval_ms = 1000; let mut host = None; let mut i = 1; while let Some(a) = ulib::arg(i) { match a { "-c" => { i += 1; count = ulib::arg(i).and_then(|v| v.parse().ok()).unwrap_or(4); } "-i" => { i += 1; interval_ms = ulib::arg(i).and_then(|v| v.parse().ok()).unwrap_or(1000); } _ => host = Some(a), } i += 1; } let Some(host) = host else { eprintln!("usage: ping [-c count] [-i interval_ms] host"); return 2; }; let ip = match ulib::resolve(host) { Ok(ip) => ip, Err(e) => { eprintln!("ping: {}: {}", host, ulib::strerror(e)); return 2; } }; let fd = ulib::socket(ulib::SOCK_DGRAM, ulib::IPPROTO_ICMP); if fd < 0 { eprintln!("ping: socket: {}", ulib::strerror(fd)); return 2; } ulib::set_recv_timeout(fd, 1000); // a reply later than 1 s counts as lost println!("PING {} ({}): {} data bytes", host, ip, PAYLOAD); let (mut received, mut min, mut max, mut total) = (0u64, u64::MAX, 0u64, 0u64); for seq in 1..=count { // Echo request: type 8, code 0, checksum and id (kernel fills in), seq. let mut packet = [0u8; 8 + PAYLOAD]; packet[0] = 8; packet[6..8].copy_from_slice(&seq.to_be_bytes()); for (j, b) in packet[8..].iter_mut().enumerate() { *b = j as u8; // a recognizable payload } let sent_at = ulib::now_us(); let n = ulib::sendto(fd, &packet, ip, 0); if n < 0 { eprintln!("ping: sendto: {}", ulib::strerror(n)); return 2; } // Wait for *this* reply (a late one for an older seq is skipped). let mut reply = [0u8; 8 + PAYLOAD + 64]; loop { let (n, from) = ulib::recvfrom(fd, &mut reply); if n < 0 { println!("Request timeout for icmp_seq={}", seq); break; } let n = n as usize; if n >= 8 && reply[0] == 0 && reply[6..8] == seq.to_be_bytes() { let us = ulib::now_us() - sent_at; let ok = reply[8..n] == packet[8..]; println!( "{} bytes from {}: icmp_seq={} time={}.{:02} ms{}", n, from.ip(), seq, us / 1000, us % 1000 / 10, if ok { "" } else { " (payload differs!)" } ); received += 1; (min, max, total) = (min.min(us), max.max(us), total + us); break; } } if seq < count { ulib::sleep_ms(interval_ms); } } ulib::close(fd); let loss = (count as u64 - received) * 100 / count.max(1) as u64; println!("--- {} ping statistics ---", host); println!("{} packets transmitted, {} received, {}% packet loss", count, received, loss); if received > 0 { let avg = total / received; println!( "rtt min/avg/max = {}.{:02}/{}.{:02}/{}.{:02} ms", min / 1000, min % 1000 / 10, avg / 1000, avg % 1000 / 10, max / 1000, max % 1000 / 10 ); } if received > 0 { 0 } else { 1 } }