Low level socket programming in Elixir

Not really, you can run ICMP socket as a regular user if your user ID is within sysctl net.ipv4.ping_group_range. However that will require you to use datagram socket instead of raw socket:

{:ok, socket} = :socket.open(:inet, :dgram, :icmp)

It also has slightly reduced API, as for example on Linux you cannot specify id and seq fields for ICMP packet, as these will be always set for you.

There is library that I have created in the past that provide slightly higher API for ICMP sockets (still quite limited):

It can be used in form of (to match the top message):

{:ok, socket} = :gen_icmp.open(raw: true)

:gen_icmp.echoreq(socket, {8, 8, 8, 8}, <<1,2,3,4>>)

receive do
  {:icmp, _addr, {:echorep, %{data: <<1,2,3,4>>}}} ->
    IO.puts("received pong")
end

And about the question of “how to get list of interfaces in the system”. Instead of playing with socket directly (especially as you are doing network namespacing, so obviously your view on interfaces will be limited) you should use:

:inet.getifaddrs()

That will return data like that:

{:ok,
 [
   {'lo0',
    [
      flags: [:up, :loopback, :running, :multicast],
      addr: {127, 0, 0, 1},
      netmask: {255, 0, 0, 0},
      addr: {0, 0, 0, 0, 0, 0, 0, 1},
      netmask: {65535, 65535, 65535, 65535, 65535, 65535, 65535, 65535},
      addr: {65152, 0, 0, 0, 0, 0, 0, 1},
      netmask: {65535, 65535, 65535, 65535, 0, 0, 0, 0}
    ]},
   {'gif0', [flags: [:pointtopoint, :multicast]]},
   {'stf0', [flags: []]},
   ...]}

That describe all interfaces available in your system and do not require any special privileges like being able to create ICMP raw socket.

2 Likes