Pure Erlang/OTP ICMP ping library using the modern :socket API.
No NIFs, no external dependencies. Requires Elixir 1.17+ (OTP 25+).
Usage
# Single ping
{:ok, rtt_ms} = RawPing.ping("8.8.8.8")
{:ok, rtt_ms} = RawPing.ping({8, 8, 8, 8})
# With options
{:ok, rtt_ms} = RawPing.ping("8.8.8.8", timeout: 2000)
# Multiple pings with stats
{:ok, stats} = RawPing.ping_stats("8.8.8.8", count: 5)
# => {:ok, %{min: 10.5, max: 15.2, avg: 12.3, success_rate: 1.0, ...}}
# Batch ping multiple hosts
results = RawPing.ping_batch(["8.8.8.8", "1.1.1.1", "192.168.1.1"])
# => %{"8.8.8.8" => {:ok, 12.5}, "1.1.1.1" => {:ok, 8.2}, ...}Privileges
Most hosts need none. By default an unprivileged ICMP datagram socket is used, falling back to a raw socket only where that is unavailable:
RawPing.socket_mode()
#=> {:ok, :dgram} # no privileges required
#=> {:ok, :raw} # fell back; needed root or CAP_NET_RAWOn Linux the datagram path is gated by net.ipv4.ping_group_range, which must
include the process's GID; many distributions ship it wide open. Where it is
restricted, the raw path still needs root, CAP_NET_RAW on the BEAM
(setcap cap_net_raw+ep /path/to/beam.smp), or a container granted NET_RAW.
See RawPing.Socket for the trade-offs and the platform differences.
How It Works
Uses Erlang's :socket module to open an ICMP socket, builds ICMP echo
request packets manually, sends them, and parses the echo replies to calculate
round-trip time.
Summary
Functions
Ping a host and return the round-trip time in milliseconds.
Ping multiple hosts concurrently.
Ping a host multiple times and return statistics.
Report which socket mode is available to this process.
Types
@type ip_address() :: String.t() | :inet.ip_address() | [integer()]
@type ping_stats() :: %{ min: float() | nil, max: float() | nil, avg: float() | nil, success_rate: float(), success_count: non_neg_integer(), failure_count: non_neg_integer(), rtts: [float()] }
Functions
@spec ping( ip_address(), keyword() ) :: ping_result()
Ping a host and return the round-trip time in milliseconds.
Options
:timeout- Timeout in milliseconds (default: 5000):payload_size- Size of ICMP payload in bytes (default: 56):mode- Socket mode,:auto(default),:dgram, or:raw. SeeRawPing.Socketfor what each requires.
Examples
{:ok, rtt} = RawPing.ping("8.8.8.8")
{:ok, rtt} = RawPing.ping({8, 8, 8, 8}, timeout: 1000)
{:error, :timeout} = RawPing.ping("10.255.255.1", timeout: 100)
@spec ping_batch( [ip_address()], keyword() ) :: %{required(String.t()) => ping_result()}
Ping multiple hosts concurrently.
Returns a map of host => result.
Options
:timeout- Timeout per ping in milliseconds (default: 5000):max_concurrency- Maximum concurrent pings (default: 50)
Examples
results = RawPing.ping_batch(["8.8.8.8", "1.1.1.1"])
# %{"8.8.8.8" => {:ok, 12.5}, "1.1.1.1" => {:ok, 8.2}}
@spec ping_stats( ip_address(), keyword() ) :: {:ok, ping_stats()} | {:error, term()}
Ping a host multiple times and return statistics.
Options
:count- Number of pings to send (default: 1):timeout- Timeout per ping in milliseconds (default: 5000):payload_size- Size of ICMP payload in bytes (default: 56):mode- Socket mode,:auto(default),:dgram, or:raw. SeeRawPing.Socketfor what each requires.
Examples
{:ok, stats} = RawPing.ping_stats("8.8.8.8", count: 5)
# %{min: 10.2, max: 15.8, avg: 12.5, success_rate: 1.0, ...}
@spec socket_mode(keyword()) :: {:ok, RawPing.Socket.mode()} | {:error, term()}
Report which socket mode is available to this process.
Opens a socket using the same negotiation as ping/2 and closes it
immediately. :dgram means ICMP works with no elevated privileges; :raw
means it fell back to a raw socket, which required root or CAP_NET_RAW.
Useful for confirming a deployment is running unprivileged.
{:ok, :dgram} = RawPing.socket_mode()