-
Notifications
You must be signed in to change notification settings - Fork 0
ICMP Ping
Platform support: Linux only for now. Windows and macOS are planned, see Roadmap.
The ping protocol sends ICMP echo requests using a native C addon (N-API). It does not spawn the ping command and it does not use a JavaScript raw-socket library.
import { Pingflux } from "pingflux";
const pf = new Pingflux();
pf.watch({ protocol: "ping", url: "1.1.1.1" });
pf.watch({ protocol: "ping", url: "2606:4700:4700::1111", interval: 3000 });
pf.on("up", (e) => console.log(`${e.target} ${e.latency}ms`));
pf.on("probe_error", (e) => console.error(`${e.target}: ${e.error}`));| Format | Example |
|---|---|
| IPv4 | 1.1.1.1 |
| IPv4 with port (port is ignored) | 1.1.1.1:80 |
| IPv6 | ::1 |
| IPv6 in brackets | [::1] |
| IPv6 in brackets with port (ignored) | [::1]:80 |
Hostnames are not resolved. Pass a plain IP address. If you only have a hostname, resolve it first:
import { promises as dns } from "dns";
const { address } = await dns.lookup("example.com");
pf.watch({ protocol: "ping", url: address });-
pingProbe()extracts the IP from the target and detects IPv4 or IPv6. - It calls the native
pingIcmp()function. That function runs on the libuv threadpool throughnapi_async_work, so the event loop is never blocked. - The addon opens an ICMP socket, sends one echo request, and waits for a reply that matches the packet ID and sequence number.
- The result is returned as a Promise and becomes an
up,down,sloworprobe_errorevent.
Pingflux.watch()
└─ Monitor
└─ pingProbe(url) src/probes/ping.ts
└─ rawPing(host, id, seq) src/utils/probes/ping.ts
└─ pingIcmp() native/src/addon.c (libuv threadpool)
├─ icmp_socket_open / send / recv socket.c
└─ icmp_build_packet / parse_reply icmp.c
The addon tries two socket types in this order:
| Order | Socket type | Needs root? |
|---|---|---|
| 1 |
SOCK_DGRAM + IPPROTO_ICMP
|
No, when the kernel allows it |
| 2 |
SOCK_RAW + IPPROTO_ICMP
|
Yes, or the CAP_NET_RAW capability |
In most cases it works as a normal user. The kernel setting behind option 1 is:
cat /proc/sys/net/ipv4/ping_group_rangeIf your group ID falls inside that range, unprivileged ping works. To allow all groups:
sudo sysctl -w net.ipv4.ping_group_range="0 2147483647"To keep it after a reboot:
echo "net.ipv4.ping_group_range = 0 2147483647" | sudo tee /etc/sysctl.d/99-ping.conf
sudo sysctl --systemOther options:
# run as root
sudo node app.js
# or give the capability to the node binary
sudo setcap cap_net_raw+ep $(which node)| Error | Meaning |
|---|---|
Cannot open ICMP socket — permission denied... |
Socket creation failed with EPERM or EACCES. See Permissions above. |
ERR_TIMEOUT |
No matching reply within the timeout (default 2000 ms). |
ERR_SEND_FAILED:<errno> |
The packet could not be sent, for example no route or network down. |
Invalid IP — hostname resolution not supported |
The target is not an IP address. |
ICMP (ping) is currently supported on Linux only |
You are on Windows or macOS. |
Failed to load native ICMP addon: ... |
The compiled addon was not found. See Native-Addon. |
Destination Unreachable and Time Exceeded replies from routers are also reported as errors, with the reason mapped by icmp_error_reason().
The native layer returns more information than the public event exposes:
| Field | Description |
|---|---|
ok |
true when a matching echo reply arrived |
latency |
Round-trip time in ms, null on failure |
ttl |
TTL or hop limit of the reply |
hops |
Estimated hop count, based on the nearest initial TTL of 64, 128 or 255 |
icmpType, icmpCode
|
Raw ICMP type and code of the reply |
error, errorReason
|
Error code, null on success |
stats |
Only when count > 1: sent, received, lossPercent, min, max, avg, jitter
|
ttl and hops depend on the socket mode. In SOCK_DGRAM mode for IPv4 the kernel removes the IP header before the data reaches the addon, so ttl is null. IPv6 gets the hop limit through ancillary data in both modes.
An echo reply is accepted only when all three match:
- ICMP type is echo reply (
0for IPv4,129for IPv6) - identifier equals the one that was sent
- sequence number equals the one that was sent
In SOCK_DGRAM mode the kernel assigns the identifier itself (it uses the local port). The addon reads it back with getsockname() and matches against that value.
ICMP
Help