
Show HN: Sping – A modern, multi-target network diagnostic tool written in Rust
A modern ping for humans. Terminal-native, real-time connectivity monitor that doesn't just measure packets — it interprets network context.
以下正文同步自 Hacker News 最新,版权归原站所有,已转换为易读排版。
A modern ping for humans. Terminal-native, real-time connectivity monitor that doesn't just measure packets — it interprets network context.
Status: functional and in active use, with a frozen JSON contract. Linux, macOS and Windows; x86_64, aarch64 and armv7 (Raspberry Pi).
sping google.com (216.58.204.174) via eth0 ⠙ probing...
Pulse █▇▆█▁▃█▄ Latency now 13.8 ms · avg 13.9 ms · min 13.6 ms · max 14.1 ms · jitter 0.19 ms · dns 18.5 ms Status Reachable · loss 0% · sent 8 · recv 8 · ttl 112 · hops ~16 Context if eth0 · 192.168.1.100/24 · gw ● · wan ● · public Quality A (100) · excellent
What's new in 1.8.0
Unreachable is not a timeout: when a router or this host answers the probe with a destination unreachable, sping says who sent it and what it said: From 192.0.2.1: seq=3 Destination Host Unreachable in the classic view, 192.0.2.1 reports host unreachable in Events, icmp_unreachable in JSON. On Linux, macOS (IPv4) and Windows.
sping --ip: this host's addresses, every interface, IPv4 and IPv6, with their prefix, kind and IPv6 state, the source each family uses and the default gateway. It sends nothing; with --external it also asks a STUN server which address it sees. See Host addresses.
--diagnose in a long outage: while the target stays down, the same checks run again 5 minutes after each diagnosis concludes.
--icmp: the ICMP probe, named. It changes nothing today; it keeps that probe for scripts when 2.0 changes what sping target does (see Roadmap).
Fixes in --diagnose: on Windows, an unreachable answer for the other family's address now means that family fails too, as on Linux; on Linux, a redirect during the traceroute no longer ends its round with recv failed.
Every change, version by version: CHANGELOG.md.
sping 192.168.1.1 8.8.8.8 google.com
Target RTT Avg Loss Qual Trend TTL Hops ─────────────────────────────────────────────────────────────────────────────── 192.168.1.1 0.31 ms 0.30 ms 0% A100 stable 64(Unix) ~0 8.8.8.8 13.6 ms 13.6 ms 0% A100 stable 112 ~16 google.com (216.58.… 13.7 ms 13.6 ms 0% A100 stable 112 ~16
The table is live and needs a terminal: with stdout a file or a pipe, sping prints only each target's summary at the end (-c N stops it). Batch mode (--batch, below) prints per-host results or JSON.
Method Command Platforms Man page and completions
Homebrew brew install LambdaBytes/tap/sping macOS yes
Scoop scoop install lambdabytes/sping, once the bucket is added (see below) Windows x86_64 no
crates.io cargo install sping Linux, macOS, Windows, with Rust 1.88+ no
Pre-built binary from the Releases page Linux, macOS, Windows no
Debian / Ubuntu package .deb from the Releases page Debian 12, Ubuntu 22.04, Raspberry Pi OS (Bookworm) or later yes
From source cargo build --release Linux, macOS, Windows, with Rust 1.88+ no
Homebrew
From v1.6.0 on. Builds from source and installs the binary, the man page and the shell completions:
brew install LambdaBytes/tap/sping
Scoop
From v1.6.0 on. Installs the release's Windows binary, the one listed in its SHA256SUMS:
scoop bucket add lambdabytes https://github.com/LambdaBytes/scoop-bucket scoop install lambdabytes/sping
scoop update sping moves it to a newer release.
Requires Rust 1.88+. Installs the binary only (no man page or shell completions):
cargo install sping
Pre-built binaries
Download from the Releases page:
The Linux binaries and packages need glibc 2.34 or newer: Debian 12, Ubuntu 22.04, Raspberry Pi OS (Bookworm) or later. The -musl binaries are static and run on any Linux of their architecture, Alpine and older systems included.
Verify with SHA256SUMS from the same release. From v1.5.5 on, every file of a GitHub release also has a build provenance attestation, which tells the commit and the workflow run that produced it:
gh attestation verify sping-vX.Y.Z-linux-x86_64 --repo LambdaBytes/sping
Debian / Ubuntu (.deb)
sudo dpkg -i sping_1.8.0-1_amd64.deb # or _arm64.deb on aarch64
The package installs:
From v1.5.5 on, the macOS binary of a release is signed with a Developer ID and notarized by Apple, so it runs once it is made executable:
chmod +x sping-vX.Y.Z-macos-arm64 ./sping-vX.Y.Z-macos-arm64 8.8.8.8
macOS refuses an earlier, unsigned binary until its quarantine attribute is removed: xattr -d com.apple.quarantine sping-vX.Y.Z-macos-arm64.
Requires Rust 1.88+ (edition 2024 + let-chains).
git clone https://github.com/LambdaBytes/sping.git cd sping cargo build --release
Binary at ./target/release/sping
Usage
Single target — compact view (default)
sping 8.8.8.8
Multiple targets — dashboard table
sping 192.168.1.1 1.1.1.1 api.example.com
Hostname
sping google.com
Fast probing (200ms interval)
sping 8.8.8.8 -i 200
Bind to an interface (Linux) / a source IP (Linux, macOS)
sping 8.8.8.8 -I eth0 sping 8.8.8.8 -S 192.168.1.100
Another WAN reference for the gateway/WAN check (default 1.1.1.1)
sping 8.8.8.8 --wan-probe 9.9.9.9
Ping-style flags: count, timeout, payload size, TTL, quiet, family
sping 8.8.8.8 -c 5 # stop after 5 probes (any view) sping 8.8.8.8 -W 1 # 1s per-probe timeout sping 8.8.8.8 -s 120 # 120-byte payload sping 8.8.8.8 -t 10 # outgoing TTL / hop limit sping 8.8.8.8 -c 10 -q --view classic # quiet: summary only sping example.com -4 # force IPv4 (-6 for IPv6) sping fe80::1%eth0 # IPv6 link-local: the zone is required
TCP connect instead of ICMP (see "TCP connect" below)
sping example.com --tcp 443 sping 192.168.1.1 10.0.0.5 --tcp 22 # every target, the same port
The ICMP probe, what sping runs without an option until 2.0 (see Roadmap)
sping example.com --icmp
When the target stops answering, find out why (experimental; one target)
sping example.com --diagnose
ASCII-only output: automatic on non-UTF-8 locales and on the legacy
Windows console (SPING_ASCII=0 forces Unicode); NO_COLOR honored
sping 8.8.8.8 --ascii
This host's addresses: every interface, IPv4 and IPv6, the source each
family uses and the gateway; sends nothing (see "Host addresses" below)
sping --ip sping --ip --view json
... and the address a STUN server sees (asks stun.cloudflare.com)
sping --ip --external sping --ip --external stun.example.net:3478 sping --list-interfaces # the IPv4 list it has always printed
Debug mode
sping --debug 8.8.8.8
Exit codes are ping-compatible: 0 replies received, 1 no replies, 2 error.
Create a hosts file (one target per line, # comments allowed)
cat > hosts.txt << EOF 8.8.8.8 1.1.1.1 google.com 192.168.1.20 EOF
Summary to stdout (10 probes per host)
sping --batch hosts.txt -c 10
JSON output
sping --batch hosts.txt -c 20 --view json
Save to file
sping --batch hosts.txt -c 10 -o results.txt
JSON to file
sping --batch hosts.txt -c 10 -o results.json --view json
Fast interval
sping --batch hosts.txt -c 50 -i 200
Output (summary):
8.8.8.8 10/10 0% avg 13.6 ms min 13.6 ms max 13.7 ms jitter 0.05 ms ttl 112 hops ~16 A(100) stable 1.1.1.1 10/10 0% avg 20.0 ms min 19.9 ms max 20.1 ms jitter 0.08 ms ttl 52 hops ~12 A(100) stable
All targets are probed in parallel. Full diagnostics (quality, trend, spikes) included.
sping 8.8.8.8 # compact (default) — fixed TUI sping 8.8.8.8 --view extended # compact + RTT dist + echo path sping 8.8.8.8 --view classic # traditional ping scrolling sping 8.8.8.8 --view json # NDJSON for scripting sping 8.8.8.8 --view json | jq '{rtt: .rtt_last, ttl: .ttl, quality: .quality}'
TCP connect
--tcp PORT times the TCP handshake with the port instead of sending ICMP echoes. It needs no ICMP permission and gets through networks that filter ICMP; it works in every mode (single target, multi-target, batch) and view.
--diagnose finds out, when the target stops answering, what still answers on the way, and says what it observed, nothing more. One target only.
sping example.com --diagnose sping 203.0.113.7 --diagnose -c 30 --view json
It starts when the target goes offline (three probes lost in a row), or at the end of a run that got no answer at all (-c 1 --diagnose too).
It uses sockets of its own, never counted in the target's statistics, and checks:
the source address the system has for the target;
the same name's address in the other family: three echoes, or with --tcp PORT one connect to that port;
TCP 443 and 80 on the target (not with --tcp, whose port the probe already tries);
a bounded traceroute: 30 hops, 3 probes each, 20 s at most, stopping after 3 silent hops.
While the target stays down, it checks again 5 minutes after each diagnosis concludes; the conclusions shown stay until the new ones are in.
Events shows diagnosing…, then the conclusions: silent after hop 3 (203.0.113.1), hop 5 (198.51.100.9) reports host unreachable, target answers the trace at hop 7, TCP 443 answers, IPv6 works.
The extended view adds an Untested line with what could not be told and why (other family not tried (address given), trace unavailable: needs privileges).
The classic view prints --- lines, again with each new check.
JSON lines carry active_diagnosis (see JSON_CONTRACT.md).
Traffic is bounded: at most 100 extra probes in any 60 s, three operations at once, the traceroute alone.
An address given, -4/-6 or -S keep the other family out. -W, -s, -I and -S apply to the extra probes; -t does not.
With -c N, the last line waits for the diagnosis, at most what is left of its budget; otherwise it says incomplete. Ctrl+C stops it at once. The exit codes do not change.
The traceroute runs on Linux (with the same sockets and permissions as the probe, see ICMP permissions), on macOS for IPv4 targets, without privileges, and on Windows. An IPv6 target on macOS gets no traceroute yet: trace unavailable on this system.
Experimental: its texts and JSON codes may still change.
Host addresses (--ip)
From 1.8 on, sping --ip shows what the system knows about this host's addresses, and exits:
eth0 192.168.1.100/24 private 2001:db8::5b1:c2ff:1/64 global · temporary fe80::1c2:3ff:fe45:6789/64 link-local lo 127.0.0.1/8 loopback ::1/128 loopback
Source IPv4 192.168.1.100 (eth0) to 1.1.1.1 · no IPv6 source to 2606:4700:4700::1111 Gateway IPv4 192.168.1.1 (eth0)
Every address of every interface, with its prefix and kind (loopback, link-local, private, shared (CGNAT), global). IPv6 addresses also show their state: temporary, deprecated, tentative or duplicate. Windows reports no temporary address yet: it is null in JSON.
The source each family would use towards a global reference address. It is that route's source, so a VPN with routes of its own can make it differ from the default route's.
The IPv4 default gateway.
Without --external, nothing is sent and no privilege is needed: the source comes from a UDP connect, which sends no packet. What the system does not say is said as unknown, never guessed.
--view json prints one object instead (see JSON_CONTRACT.md). --list-interfaces still prints its IPv4 list as before.
With --external, sping --ip also asks a STUN server (RFC 8489, over UDP) which address and port it sees each family's request come from:
Source IPv4 192.168.1.100 (eth0) to 1.1.1.1 · no IPv6 source to 2606:4700:4700::1111 Gateway IPv4 192.168.1.1 (eth0) External IPv4 203.0.113.5:40312 from 192.168.1.100:51234 · IPv6 unknown (no route to the server) (STUN server stun.cloudflare.com:3478)
It asks a third party, so only when given: stun.cloudflare.com:3478 by default, or --external SERVER[:PORT] ([address]:port for an IPv6 address with a port). The system resolver looks the name up once.
At most three requests per family (at 0, 0.5 and 1.5 s) and 3 s in all, both families at once. An answer counts only with the request's transaction ID.
from is this host's socket the request left from. With the address the server saw, it is the translation on the way out (NAT, CGNAT, a VPN, or none); sping does not name it.
What the server did not say (no answer, an error, no route) is unknown, with the reason, and the exit code stays 0.
ICMP permissions
sping works without admin/
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。