ityonemo
Zing, basic Elixir ICMP ('ping') server using Zig NIF
This is a basic demonstration of how to write an ICMP (‘ping’) server using a NIF in about 100 lines of elixir and 100 lines of Zig.
This might be able to be deployed as a ‘real’ library. Would anyone be interested in using this? I am very happy to put more work into making this a quality release (modulo zig itself being still experimental) if people are interested, as there really isn’t a satisfactory ICMP solution in the BEAM (and the :socket library maintainers have suggested that ICMP is not going to happen)
Most Liked
hauleth
To prove that it is possible:
defmodule GenICMP do
@data <<0xdeadbeef::size(32)>>
def ping(addr) do
{:ok, s} = open()
data = @data
req_echo(s, addr, data: data)
case recv_echo(s) do
{:ok, %{data: ^data}} = resp -> resp
{:ok, other} -> {:error, other}
_ -> {:error, :invalid_resp}
end
end
def open, do: :socket.open(:inet, :dgram, :icmp)
def req_echo(socket, addr, opts \\ []) do
data = Keyword.get(opts, :data, @data)
id = Keyword.get(opts, :id, 0)
seq = Keyword.get(opts, :seq, 0)
sum = checksum(<<8, 0, 0::size(16), id, seq, data::binary>>)
msg = <<8, 0, sum::binary, id, seq, data::binary>>
:socket.sendto(socket, msg, %{family: :inet, port: 1, addr: addr})
end
def recv_echo(socket, timeout \\ 5000) do
{:ok, data} = :socket.recv(socket, 0, [], timeout)
<<_::size(160), pong::binary>> = data
case pong do
<<0, 0, _::size(16), id, seq, data::binary>> ->
{:ok, %{
id: id, seq: seq, data: data
}}
_ -> {:error, pong}
end
end
defp checksum(bin), do: checksum(bin, 0)
defp checksum(<<x::integer-size(16), rest::binary>>, sum), do: checksum(rest, sum + x)
defp checksum(<<x>>, sum), do: checksum(<<>>, sum + x)
defp checksum(<<>>, sum) do
<<x::size(16), y::size(16)>> = <<sum::size(32)>>
res = :erlang.bnot(x + y)
<<res::big-size(16)>>
end
end
API is simple:
GenICMP.ping({127, 0, 0, 1})
Tested on macOS, but should work on Linux as well. I am not sure about BSDs.
EDIT:
Just in case, this project seems as a good example of how to use Zigler to write Erlang NIFs, and it is great. However sending ICMP echo requests via socket is absolutely possible.
hauleth
I have implemented ICMP, but ICMPv6 is still not done, if you want then I would be glad for PR. It uses slightly different API from what You are using as I wanted something “more basic” so users could build larger functionalities on top of this.
ityonemo
Lol, I tried this two months ago and support for the :icmp mode wasn’t there!! I think I was on 22.2, and I asked Peter dimitrov at codebeam and he said they weren’t going to do icmp ![]()
Last Post!
the_wildgoose
Hi, could I persuade you to explain the code a little more here please?
I see the line referenced
decode(<<4:4, IHL:4, Rest/binary>>)
But I’m really not clear why that can’t match an unfortunate (or carefully constructed) ping response
Phrasing the question another way, is there a tight binary match for an IPv4 header that couldn’t match the ICMP body response (on Linux)?
I wonder why you don’t check the OS during init and then match on that? (Stash it in the state perhaps?)
Agreed that the continue handling is very neat!
Popular in Discussions
Other popular topics
Chat & Discussions>Discussions
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #hex
- #security









