hauleth

hauleth

Gen_icmp - PoC wrapper over `socket` to be able to send ICMP requests (ping)

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.

https://github.com/hauleth/gen_icmp

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

Most Liked

hauleth

hauleth

You can achieve that right now. Just set seq and id in the request.

gen_icmp do not care about timeouts, because there is no need to. It is quite low level library, that do not manage whole ping on its own, and instead make it up to the user. There is no timeouts handled automatically by this library, it just sends and receives packets, without any additional magic.

As I said earlier, it is very “low level” library, so I do not think I should do any filtering in the library (unless there are some flags that can be set to the socket, but I am not aware of such). If you want to filter unwanted messages in your process, then just discard them in the controlling process.

If you have an example (can be in any language) how that does work, then please share. I can look into it, but no promises, as I can be limited to the capabilities of the socket module.

the_wildgoose

the_wildgoose

I REALLY like the direction you are going with this. Just a couple of days ago I was about to implement something similar for my own needs. However, you have knocked off about 80% of what I need! Thanks! (Thanks also for implementing my request to bind to a specific interface!)

The only thing I am not sure that I like is that lack of negative acknowledgements (ie ping NOT received in time), especially as it potentially affects the API. Now I think in the past your thought was to leave this up to an outer wrapper, but as I see that, this means creating another process to filter through, which seems inefficient compared with implementing inside the library?

My use case is:

  • I have a very slow (iridium satellite) connection to monitor.
  • I need to decide if this is up/down to certain destinations
  • However, because it’s so slow, queues can develop on the interfaces, 5+second ping times are not uncommon in light use, you could have huge peaks
  • So it would be desirable to have a) multiple pings in flight (ie set a sequence number) b) to set quite a long timeout as some of these may turn up really quite a long time later (I don’t mind if they take 60 seconds to come home)
  • It would also remove a bunch of complexity to have the library report that packets are overdue
  • I’m undecided if it would be useful to report “arrived late” events though? Probably someone would find it useful? Seems to drop out of the code for free on linux at least?
  • I would also prefer that in most modes I only hear about replies to my own pings, ie on OSX I don’t also get all the icmp data. However, clearly you also desire to implement an “all icmp” mode as well?

The way I would see this working is that echorequest() takes an optional timeout. If this is given then internally we maintain a queue of requests in flight and their expected latest arrival time. In handle(:continue) we would need to set a wakeup timer for the earliest expiry time. If we arrive in the timer event then not reply was received, so we can remove the item from the queue and send a message. Obviously if a reply arrives we need to dequeue the request and cancel/update the timer.

I guess this would also imply that opening the socket needs to take a param as to whether you want all events or only icmpecho (I’m mostly thinking because this affects how you open the socket on linux? We can use a different socket type if we want the kernel to do the reply processing for us?)

If I’m honest, I find it hard work to speak Erlang, which makes it somewhat tricky for me to contribute code here. If you liked the idea above, would you accept a donation for an implementation!? PM me.

the_wildgoose

the_wildgoose

Answering the easiest first:

I think (untested) filtering happens automatically on linux if we open the socket in dgram, icmp mode? I haven’t retested your library, but I thought that was how you were opening the socket on linux? So in that mode the socket won’t receive random other ICMP noticed on the network. I think this is helpful for many use cases

So regarding the low level library aspect. Yes, I agree. However, do you not agree that a frequent use case is also the absence of receiving a message within a time period? (Doesn’t mean it won’t arrive later of course)

Now, it’s not hard to wrap your library in another genserver, which simply wraps it, starts a timer and relays all the messages from your genserver through our genserver and onto the user. However, I think if you consider that it leads to a moderately amount of duplication just to pass through the same API and means another genserver which is simply relaying messages. Hence the request, would you consider adding within your genserver the additional parts to set timers and report an extra message in the absence of the icmp message arriving?

I could make a similar argument for the usefulness of filtering in the library. There could be a lot of icmp messages in some setups, so the further forward we could push the filtering the better. It’s even worth pondering how to end up at active:once in the library?

What do you think?

Where Next?

Popular in Announcing Top

mspanc
I am pleased to announce an initial release of the Membrane Framework - an Elixir-based framework with special focus on processing multim...
New
jakub-zawislak
Hi everyone, I’m coming from the Symfony (PHP) framework. I like Phoenix, but it has a one thing that was build much better in the Symfo...
New
gabrielpoca
Hello everyone! I want to share with you something that I’m really proud of: https://stillstatic.io/ Still is a static site builder for...
New
Crowdhailer
The latest release of Ace (0.10.0) includes serving content over HTTP/2. I have started writing a webserver to teach my self more about...
New
Eiji
ExApi is a library that I’m developing now and hope release soon This library will allow to: list all apis list all api implementation...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 52673 488
New
Crowdhailer
Experimenting with this code. OK.try do user &lt;- fetch_user(1) cart &lt;- fetch_cart(1) order = checkout(cart, user) save_orde...
New
sbs
Only 650 LOC, wrote for fun :slight_smile: https://github.com/sunboshan/qrcode
New
aditya7iyengar
Rummage.Ecto and Rummage.Phoenix provide ways to perform Searching, Sorting and Pagination over Ecto queries and Phoenix collections. Fo...
New
hpopp
After just over two years in development, this latest version of Pigeon is what I finally consider done in regards to my original vision ...
New

Other popular topics Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 29603 241
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 36352 110
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 48342 226
New
AstonJ
Seen any cool LiveView demos, sample apps or examples? Please post them here! :003:
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement