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).