View Source gen_icmp (gen_icmp v0.6.2)

gen_icmp is an interface for using ICMP and ICMPv6 sockets

gen_icmp uses raw sockets and abuses gen_udp for the socket handling. gen_icmp should work on Linux and BSDs.

Examples

  1> gen_icmp:ping("hex.pm").
  [{ok,"hex.pm",
       {35,241,14,39},
       {35,241,14,39},
       {61261,0,116,215},
       <<" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO">>}]

Summary

Types

ICMP packet time to live (renamed to avoid conflict with ttl() defined in the pkt header file)

Functions

Resolve a host list.

Close the ICMP socket.

Change the controlling process of the ICMP socket.

Generate ICMP ECHO_REQUEST payload.

Generate ICMP ECHO_REQUEST with user specified payload.

Get socket family.

Get ICMPv6 socket filter.

Set ICMPv6 socket filter.

Get the TTL for a file descriptor.

Get underlying file descriptor for socket.

Update a filter to block an ICMPv6 type.

ICMPv6 filtering: block all ICMP types.

See also: icmp6_filter_setpass/2.

Update a filter to allow an ICMPv6 type.

IPv6 ICMP filtering: allow all ICMP types.

See also: icmp6_filter_setblock/2.

Test if a filter blocks an ICMPv6 type.

Test if a filter allows an ICMPv6 type.

Open an ICMP socket.

Open an ICMP socket with raw socket options.

Open an ICMP socket with options.

Create an ICMP packet.

Construct an ICMP or ICMPv6 packet payload.

Parse or resolve an IPv4 host identifier.

Parse or resolve a host identifier.

Send an ICMP ECHO_REQUEST.

Send an ICMP ECHO_REQUEST with options.

Send an ICMP ECHO_REQUEST.

Read data from an ICMP socket.

Read data from an ICMP socket with timeout.

Send data via an ICMP socket.

Set the TTL on a file descriptor.

Set socket options.

Types

-type elapsed() :: int32_t() | undefined.
-type fd() :: int32_t().
-type icmp6_filter() :: <<_:256>>.
-type id() :: uint16_t().
-type int32_t() :: -2147483647..2147483647.
-type packet_option() ::
    {type, atom | uint8_t()} |
    {code, atom | uint8_t()} |
    {id, id()} |
    {sequence, sequence()} |
    {gateway, pkt:in_addr()} |
    {un, binary()} |
    {mtu, uint16_t()} |
    {pointer, uint8_t() | uint32_t()} |
    {ts_orig, uint32_t()} |
    {ts_recv, uint32_t()} |
    {ts_tx, uint32_t()} |
    {maxdelay, uint16_t()} |
    {saddr, pkt:in6_addr()} |
    {daddr, pkt:in6_addr()} |
    {len, non_neg_integer()} |
    {next, uint8_t()}.
-type sequence() :: uint16_t().
-type socket() :: pid().
-type ttlx() :: uint8_t().
ICMP packet time to live (renamed to avoid conflict with ttl() defined in the pkt header file)
-type uint8_t() :: 0..255.
-type uint16_t() :: 0..65535.
-type uint32_t() :: 0..4294967295.

Functions

Link to this function

addr_list(Family, Hosts, Multi)

View Source
-spec addr_list(inet | inet6, [inet:socket_address() | inet:hostname()], boolean()) ->
             [{ok, inet:hostname(), inet:socket_address()} |
              {error, inet:posix(), inet:hostname(), undefined}].

Resolve a host list.

Examples

  1> gen_icmp:addr_list(inet, ["8.8.8.8", "google.com", "cloudflare.com", {8,8,8,8}, "8.8.8.8"], false).
  [{ok,"8.8.8.8",{8,8,8,8}},
   {ok,"google.com",{172,217,165,14}},
   {ok,"cloudflare.com",{104,16,133,229}},
   {ok,{8,8,8,8},{8,8,8,8}},
   {ok,"8.8.8.8",{8,8,8,8}}]
 
  2> [{ok,"8.8.8.8",{8,8,8,8}},
   {ok,"google.com",{172,217,165,14}},
   {ok,"cloudflare.com",{104,16,132,229}},
   {ok,"cloudflare.com",{104,16,133,229}},
   {ok,{8,8,8,8},{8,8,8,8}},
   {ok,"8.8.8.8",{8,8,8,8}}]
-spec close(socket()) -> ok.

Close the ICMP socket.

Examples

  1> {ok, S} = gen_icmp:open().
  {ok,<0.224.0>}
  2> gen_icmp:close(S).
  ok
Link to this function

controlling_process(Socket, Pid)

View Source
-spec controlling_process(socket(), pid()) -> ok.

Change the controlling process of the ICMP socket.

Change the process owning the socket. Allows another process to receive the ICMP responses.
-spec echo(inet | inet6, id(), sequence()) -> binary().

Generate ICMP ECHO_REQUEST payload.

Creates an ICMP echo packet with an 8 byte timestamp and a payload consisting of ASCII 32 to 79.

Examples

  1> gen_icmp:echo(inet, 0, 0).
  <<8,0,166,49,0,0,0,0,255,253,243,182,71,2,233,209,32,33,
    34,35,36,37,38,39,40,41,42,43,44,...>>
Link to this function

echo(Family, Id, Seq, Payload)

View Source
-spec echo(inet | inet6, id(), sequence(), <<_:64>>) -> binary().

Generate ICMP ECHO_REQUEST with user specified payload.

Creates an ICMP echo packet with the results of erlang:monotonic_time(micro_seconds) used as the timestamp and a user specified payload (padded to 64 bytes).

Examples

  1> gen_icmp:echo(inet, 0, 0, <<"ping", 0:(60*8)>>).
  <<8,0,25,47,0,0,0,0,112,105,110,103,0,0,0,0,0,0,0,0,0,0,0,
    0,0,0,0,0,0,...>>
-spec family(socket()) -> inet | inet6.

Get socket family.

Returns the socket family: inet (IPv4), inet6 (IPv6)

Examples

  1> {ok, S} = gen_icmp:open([inet6]).
  {ok,<0.224.0>}
  2> gen_icmp:family(S).
  inet6
-spec filter(socket()) -> {ok, icmp6_filter()} | {error, unsupported | inet:posix()}.

Get ICMPv6 socket filter.

Retrieves the ICMPv6 filter for a socket. For ICMPv4 sockets, the atom unsupported is returned.

Examples

  1> {ok, S} = gen_icmp:open([inet6]).
  {ok,<0.299.0>}
  2> gen_icmp:filter(S).
  {ok,<<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
        0,0,...>>}
-spec filter(socket(), icmp6_filter()) -> ok | {error, unsupported | inet:posix()}.

Set ICMPv6 socket filter.

Sets an ICMPv6 filter on a socket. For ICMPv4 sockets, the atom unsupported is returned.

Filters can be generated by using the icmp6_filter functions.

Examples

  1> {ok, S} = gen_icmp:open([inet6]).
  {ok,<0.299.0>}
  2> gen_icmp:filter(S, gen_icmp:icmp6_filter_setpassall()).
  ok
-spec get_ttl(fd(), inet | inet6) -> {ok, int32_t()} | {error, inet:posix()}.

Get the TTL for a file descriptor.

Examples

  1> gen_icmp:get_ttl(gen_icmp:getfd(S), gen_icmp:family(S)).
  {ok,64}
-spec getfd(socket()) -> fd().

Get underlying file descriptor for socket.

Examples

  1> {ok, S} = gen_icmp:open().
  {ok,<0.221.0>}
  2> gen_icmp:getfd(S).
  20
Link to this function

icmp6_filter_setblock(Type0, Filter)

View Source
-spec icmp6_filter_setblock(atom() | uint8_t(), icmp6_filter()) -> icmp6_filter().

Update a filter to block an ICMPv6 type.

Examples

To generate a filter that allowed only ICMP6_PACKET_TOO_BIG messages:

  1> {ok, Socket} = gen_icmp:open([inet6]).
  {ok,<0.300.0>}
  2> Filter = gen_icmp:icmp6_filter_setblock(packet_too_big, gen_icmp:icmp6_filter_setpassall()).
  <<4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
      0,...>>
  3> gen_icmp:filter(Socket, Filter).
  ok

See also: filter/2.

Link to this function

icmp6_filter_setblockall()

View Source
-spec icmp6_filter_setblockall() -> icmp6_filter().
ICMPv6 filtering: block all ICMP types.

See also: icmp6_filter_setpass/2.

Link to this function

icmp6_filter_setpass(Type0, Filter)

View Source
-spec icmp6_filter_setpass(atom() | uint8_t(), icmp6_filter()) -> icmp6_filter().

Update a filter to allow an ICMPv6 type.

Examples

To generate a filter that allowed only ICMP6_ECHO_REPLY messages:

  {ok, Socket} = gen_icmp:open([inet6]),
  Filter = gen_icmp:icmp6_filter_setpass(echo_reply, gen_icmp:icmp6_filter_setblockall()),
  ok = gen_icmp:filter(Socket, Filter).

See also: filter/2.

Link to this function

icmp6_filter_setpassall()

View Source
-spec icmp6_filter_setpassall() -> icmp6_filter().
IPv6 ICMP filtering: allow all ICMP types.

See also: icmp6_filter_setblock/2.

Link to this function

icmp6_filter_willblock(Type0, Filter)

View Source
-spec icmp6_filter_willblock(atom() | uint8_t(), icmp6_filter()) -> boolean().

Test if a filter blocks an ICMPv6 type.

Examples

  1> Filter = gen_icmp:icmp6_filter_setblock(packet_too_big, gen_icmp:icmp6_filter_setpassall()).
  <<4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,...>>
  2> gen_icmp:icmp6_filter_willblock(echo_reply, Filter).
  false
  3> gen_icmp:icmp6_filter_willblock(packet_too_big, Filter).
  true
Link to this function

icmp6_filter_willpass(Type0, Filter)

View Source
-spec icmp6_filter_willpass(atom() | uint8_t(), icmp6_filter()) -> boolean().

Test if a filter allows an ICMPv6 type.

Examples

  1> Filter = gen_icmp:icmp6_filter_setblock(packet_too_big, gen_icmp:icmp6_filter_setpassall()).
  <<4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
    0,...>>
  2> gen_icmp:icmp6_filter_willpass(echo_reply, Filter).
  true
  3> gen_icmp:icmp6_filter_willpass(packet_too_big, Filter).
  false
-spec open() -> {ok, socket()} | {error, system_limit | inet:posix()}.

Open an ICMP socket.

By default, the ICMP socket is opened in {active,false} mode. No packets will be received by the socket. setopts/2 can be used to place the socket in {active,true} mode.

gen_icmp first attempts to natively open the socket and falls back to forking the setuid helper program if beam does not have the appropriate privileges. Privileges to open a raw socket can be given by, for example, running as root or, on Linux, granting the CAP_NET_RAW capability to beam:

setcap cap_net_raw=ep /usr/local/lib/erlang/erts-5.8.3/bin/beam.smp

Only the owning process will receive ICMP packets (see controlling_process/2 to change the owner). The process owning the raw socket will receive all ICMP packets sent to the host.

Messages sent to the controlling process are:

{icmp, Socket, Address, TTL, Packet}

Where:

* Socket is the pid of the gen_icmp process

* Address is a tuple representing the IPv4 or IPv6 source address

* TTL: IPv4: TTL taken from the IP header

* TTL: IPv6: the socket's hop limit returned from getsockopt(IPV6_UNICAST_HOPS) (this is not the packet's TTL, it is the socket's max TTL)

* Packet is the complete ICMP packet including the ICMP headers

Examples

  1> gen_icmp:open().
  {ok,<0.299.0>}
-spec open(proplists:proplist()) -> {ok, socket()} | {error, system_liimt | inet:posix()}.

Open an ICMP socket with raw socket options.

See the https://github.com/msantos/procket for the raw socket options and for instructions on setting up the setuid helper.

Examples

  1> gen_icmp:open([{ttl, 1}, inet6]).
  {ok,<0.302.0>}
-spec open(proplists:proplist(), [inet:inet_backend() | gen_udp:open_option()]) ->
        {ok, socket()} | {error, system_liimt | inet:posix()}.

Open an ICMP socket with options.

Examples

  1> gen_icmp:open([{ttl, 1}, inet6], [list]).
  {ok,<0.302.0>}
-spec packet(#icmp{} | #icmp6_pseudohdr{} | [packet_option()], binary()) -> binary().

Create an ICMP packet.

Convenience function for creating arbitrary ICMP packets. This function will calculate the ICMP checksum and insert it into the packet.

Examples

  1> rr("_build/default/lib/pkt/include/pkt_icmp.hrl").
  [icmp]
  2> gen_icmp:packet(#icmp{}, <<"abc">>).
  <<8,0,51,157,0,0,0,0,97,98,99>>
Link to this function

packet(_, Header, Payload)

View Source
-spec packet(inet | inet6, [packet_option()], binary()) -> binary().

Construct an ICMP or ICMPv6 packet payload.

Examples

  1> gen_icmp:packet(inet, [{type, dest_unreach}, {code, unreach_port}], <<"payload goes here">>).
  <<3,3,135,239,0,0,0,0,112,97,121,108,111,97,100,32,103,
    111,101,115,32,104,101,114,101>>
-spec parse(inet:socket_address() | inet:hostname()) ->
         {ok, [inet:socket_address()]} | {error, inet:posix()}.

Parse or resolve an IPv4 host identifier.

Examples

  1> gen_icmp:parse("8.8.8.8").
  {ok,[{8,8,8,8}]}
 
  2> gen_icmp:parse({8,8,8,8}).
  {ok,[{8,8,8,8}]}
 
  3> gen_icmp:parse("2001:4860:4860::8888").
  {ok,[{8193,18528,18528,0,0,0,0,34952}]}
 
  4> gen_icmp:parse("cloudflare.com").
  {ok,[{104,16,132,229},{104,16,133,229}]}
 
  5> gen_icmp:parse("foo").
  {error,nxdomain}
-spec parse(inet | inet6, inet:socket_address() | inet:hostname()) ->
         {ok, [inet:socket_address()]} | {error, inet:posix()}.

Parse or resolve a host identifier.

Examples

  1> gen_icmp:parse(inet, "cloudflare.com").
  {ok,[{104,16,133,229},{104,16,132,229}]}
 
  2> gen_icmp:parse(inet6, "cloudflare.com").
  {ok,[{9734,18176,0,0,0,0,26640,34021},
       {9734,18176,0,0,0,0,26640,34277}]}
-spec ping(inet:socket_address() | inet:hostname() | [inet:socket_address() | inet:hostname()]) ->
        [{ok,
          inet:socket_address() | inet:hostname(),
          inet:ip_address(),
          inet:ip_address(),
          {id(), sequence(), ttlx(), elapsed()},
          binary()} |
         {error,
          unreach_host | timxceed_intrans,
          [inet:socket_address() | inet:hostname()],
          inet:ip_address(),
          inet:ip_address(),
          {id(), sequence(), ttlx(), elapsed()},
          binary()} |
         {error,
          timeout | inet:posix(),
          [inet:socket_address() | inet:hostname()],
          inet:ip_address()}].

Send an ICMP ECHO_REQUEST.

ping/1 is a convenience function to send a single ping packet. The argument to ping/1 can be either a hostname or a list of hostnames.

Examples

  1> gen_icmp:ping("google.com").
  [{ok,"google.com",
       {142,251,41,46},
       {142,251,41,46},
       {61261,0,116,84},
       <<" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO">>}]

See also: ping/3.

-spec ping(inet:socket_address() | inet:hostname() | [inet:socket_address() | inet:hostname()],
     proplists:proplist()) ->
        [{ok,
          inet:socket_address() | inet:hostname(),
          inet:ip_address(),
          inet:ip_address(),
          {id(), sequence(), ttlx(), elapsed()},
          binary()} |
         {error,
          unreach_host | timxceed_intrans,
          [inet:socket_address() | inet:hostname()],
          inet:ip_address(),
          inet:ip_address(),
          {id(), sequence(), ttlx(), elapsed()},
          binary()} |
         {error,
          timeout | inet:posix(),
          [inet:socket_address() | inet:hostname()],
          inet:ip_address()}].

Send an ICMP ECHO_REQUEST with options.

Ping a host or a list of hosts.

Examples

  1> gen_icmp:ping("google.com", [inet6]).
  [{ok,"google.com",
       {9735,63664,16395,2052,0,0,0,8206},
       {9735,63664,16395,2052,0,0,0,8206},
       {61261,0,64,53},
       <<" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO">>}]

See also: ping/3.

Link to this function

ping(Socket, Hosts, Options)

View Source
-spec ping(socket(), [inet:socket_address() | inet:hostname()], proplists:proplist()) ->
        [{ok,
          inet:socket_address() | inet:hostname(),
          inet:ip_address(),
          inet:ip_address(),
          {id(), sequence(), ttlx(), elapsed()},
          binary()} |
         {error,
          unreach_host | timxceed_intrans,
          [inet:socket_address() | inet:hostname()],
          inet:ip_address(),
          inet:ip_address(),
          {id(), sequence(), ttlx(), elapsed()},
          binary()} |
         {error,
          timeout | inet:posix(),
          [inet:socket_address() | inet:hostname()],
          inet:ip_address()}].

Send an ICMP ECHO_REQUEST.

To prevent the process mailbox from being flooded with ICMP messages, ping/3 will put the socket into {active,false} mode after completing.

The ping/3 function blocks until either an ICMP ECHO REPLY is received from all hosts or Timeout is reached.

Id and sequence are used to differentiate ping responses. Usually, the sequence is incremented for each ping in one run.

A list of responses is returned. If the ping was successful, the elapsed time in milliseconds is included (calculated by subtracting the current time from the time we sent in the ICMP ECHO packet and returned to us in the ICMP ECHOREPLY payload) where:

* Host: the provided hostname

* Address: the resolved IPv4 or IPv6 network address represented as a 4 or 8-tuple used in the ICMP echo request

* ReplyAddr: the IPv4 or IPv6 network address originating the ICMP echo reply

The timeout is set for all ICMP packets and is set after all packets have been sent out.

By default only one address per hostname is pinged. To enable pinging all addresses per hostname pass {multi, true} to options.

A ping payload contains an 8 byte timestamp in microseconds. When creating a custom payload, the first 8 bytes of the ICMP echo reply payload will be used for calculating the elapsed time. To disable this behaviour, use the option {timestamp,false} (the elapsed time in the return value will be set to 0).

The timeout defaults to 5 seconds.

ICMPv6 sockets can restrict which ICMPv6 types are received by the socket using the filter option. The filter argument is a binary generated using the icmp6_filter functions described below.

The default filter allows: ICMP6_ECHO_REPLY, ICMP6_DST_UNREACH, ICMP6_PACKET_TOO_BIG, ICMP6_TIME_EXCEEDED and ICMP6_PARAM_PROB. Note: ping/3 does not restore the original filter on the socket.

Examples

  1> {ok, S} = gen_icmp:open([inet6]).
  {ok,<0.299.0>}
  2> gen_icmp:ping(S, ["google.com"], []).
  [{ok,"google.com",
       {9735,63664,16395,2051,0,0,0,8206},
       {9735,63664,16395,2051,0,0,0,8206},
       {61261,0,64,110},
       <<" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO">>}]
  3> gen_icmp:ping(S, ["2001:4860:4860::8888"], []).
  [{ok,"2001:4860:4860::8888",
       {8193,18528,18528,0,0,0,0,34952},
       {8193,18528,18528,0,0,0,0,34952},
       {61261,0,64,20},
       <<" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO">>}]
  4> gen_icmp:ping(S, ["2001:4860:4860::8888", "google.com"], []).
  [{ok,"google.com",
       {9735,63664,16395,2051,0,0,0,8206},
       {9735,63664,16395,2051,0,0,0,8206},
       {61261,1,64,23},
       <<" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO">>},
   {ok,"2001:4860:4860::8888",
       {8193,18528,18528,0,0,0,0,34952},
       {8193,18528,18528,0,0,0,0,34952},
       {61261,0,64,23},
       <<" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNO">>}]
  5> gen_icmp:close(S).
  ok
-spec recv(socket(), non_neg_integer()) ->
        {ok, inet:ip_address() | inet:returned_non_ip_address(), string() | binary()} |
        {error, not_owner | timeout | inet:posix()}.

Read data from an ICMP socket.

This function receives a packet from a socket in passive mode.

Examples

  1> {ok, S} = gen_icmp:open().
  {ok,<0.224.0>}
  2>gen_icmp:send(S, "google.com", gen_icmp:echo(inet, 0, 0)).
  ok
  3> gen_icmp:recv(S, 1).
  {ok,{{142,251,32,78},
       <<69,0,0,84,0,0,0,0,116,1,214,38,142,251,32,78,100,115,
              92,198,0,0,19,125,0,...>>}}
  4> gen_icmp:close(S).
  ok
Link to this function

recv(Socket, Length, Timeout)

View Source
-spec recv(socket(), non_neg_integer(), timeout()) ->
        {ok, inet:ip_address() | inet:returned_non_ip_address(), string() | binary()} |
        {error, not_owner | timeout | inet:posix()}.

Read data from an ICMP socket with timeout.

The optional Timeout parameter specifies a timeout in milliseconds. The default value is infinity.

Examples

  1> {ok, S} = gen_icmp:open().
  {ok,<0.299.0>}
  2> gen_icmp:send(S, "google.com", gen_icmp:echo(inet, 0, 0)).
  ok
  3> gen_icmp:recv(S, 1, 5000).
  {ok,{{142,251,41,78},
            <<69,0,0,84,0,0,0,0,116,1,205,38,142,251,41,78,100,115,
                     92,198,0,0,100,77,0,...>>}}
  4> gen_icmp:recv(S, 1, 5000).
  {error,timeout}
  5> gen_icmp:close(S).
  ok
Link to this function

send(Socket, Address, Packet)

View Source
-spec send(socket(),
     {inet:ip_address(), inet:port_number()} |
     inet:family_address() |
     socket:sockaddr_in() |
     socket:sockaddr_in6(),
     iodata()) ->
        ok | {error, not_owner | inet:posix()}.

Send data via an ICMP socket.

Like the gen_udp and gen_tcp modules, any process can send ICMP packets but only the owner will receive the responses.

Examples

  1> {ok, S} = gen_icmp:open().
  {ok,<0.224.0>}
  2>gen_icmp:send(S, "google.com", gen_icmp:echo(inet, 0, 0)).
  ok
  3> gen_icmp:recv(S, 1).
  {ok,{{142,251,32,78},
       <<69,0,0,84,0,0,0,0,116,1,214,38,142,251,32,78,100,115,
              92,198,0,0,19,125,0,...>>}}
  4> gen_icmp:close(S).
  ok
-spec set_ttl(fd(), inet | inet6, int32_t()) -> ok | {error, inet:posix()}.

Set the TTL on a file descriptor.

Examples

  1> gen_icmp:set_ttl(gen_icmp:getfd(S), gen_icmp:family(S), 1).
  ok
  2> gen_icmp:get_ttl(gen_icmp:getfd(S), gen_icmp:family(S)).
  {ok,1}
Link to this function

setopts(Socket, Options)

View Source
-spec setopts(socket(), [inet:socket_setopt()]) -> ok | {error, inet:posix()}.

Set socket options.

For options, see the inet man page. Simply calls inet:setopts/2 on the gen_udp socket.

setopts/2 can be used to toggle the socket between passive and active mode.

Examples

  {ok, Socket} = gen_icmp:open(), % socket is {active,false}
  ok = gen_icmp:setopts(Socket, [{active, true}]),
  % do stuff with the socket
  ok = gen_icmp:setopts(Socket, [{active, false}]).