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
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.
See also: icmp6_filter_setpass/2.
Update a filter to allow an ICMPv6 type.
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().
-type uint8_t() :: 0..255.
-type uint16_t() :: 0..65535.
-type uint32_t() :: 0..4294967295.
Functions
-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
-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.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,...>>
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}
Get underlying file descriptor for socket.
Examples
1> {ok, S} = gen_icmp:open().
{ok,<0.221.0>}
2> gen_icmp:getfd(S).
20
-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.
-spec icmp6_filter_setblockall() -> icmp6_filter().
See also: icmp6_filter_setpass/2.
-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.
-spec icmp6_filter_setpassall() -> icmp6_filter().
See also: icmp6_filter_setblock/2.
-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
-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>>
-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.
-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
-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
-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}
-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}]).