Some people was already asking how to implement ping in their Erlang/Elixir applications. Previously it wasn’t really possible, because there was no access to the BSD sockets API without using external NIFs. With OTP 21 we got a built-in NIF with access for these, and with later updates we became allowed to use any type/protocol combination, not only these “allowed” by the Erlang team.
With all that work I wanted to present an example implementation of gen_tcp/gen_udp-like server that would provide higher-level API for one of the available protocols. That is how gen_icmp was born, which, obviously, implement Internet Control Message Protocol which is used by tools like ping.
It is currently not available on Hex, as I want to have tests first, however that may be a little bit complex, as it is not commonly accessible protocol (it requires raw sockets which are accessible only by root, with exception for Linux machines and Darwin, where it can be used on top of datagram sockets).
Usage is quite simple, to send simple ECHOREQ message (also known as PING) and receive response from 1.1.1.1 (CloudFlare DNS servers):
{:ok, socket} = :gen_icmp.open() # require macOS or Linux with user being in group that is within `net.ipv4.ping_group_range`
:ok = :gen_icmp.echoreq(socket, {1, 1, 1, 1}, <<1,2,3,4>>)
receive do
{:icmp, socket, {1,1,1,1}, {echorep, %{data: <<1,2,3,4>>}}} ->
IO.puts("Echo received")
end
For some reason the message need to be padded to 16 bits (it has to have even number of bytes in the message and I am trying to find out why and how to mitigate that).






















