Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
Conflicts:
	src/gen_icmp.app.src
  • Loading branch information
msantos committed May 17, 2013
2 parents bfd9c24 + 89cec71 commit 19cbb1c
Show file tree
Hide file tree
Showing 5 changed files with 264 additions and 234 deletions.
216 changes: 110 additions & 106 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,106 +18,111 @@ version. If you just need a simple example of sending a ping, also see:
open() -> {ok, Socket}
open(SocketOptions) -> {ok, Socket}
open(RawOptions, SocketOptions) -> {ok, Socket}

Types Socket = pid()
RawOptions = [ RawOption | {setuid,boolean()} ]
RawOptions = [ RawOption ]
RawOption = options()
SocketOptions = SocketOpt
SocketOpt = [ {active, true} | {active, once} |
{active, false} | inet | inet6 ]

See the procket README for the raw socket options. If the
{setuid,false} option is used, gen_icmp will not fork the setuid
helper program to open the raw socket. The Erlang VM will require
the appropriate privileges to obtain the socket. For example, running
as root or on Linux granting the CAP_NET_RAW capability to beam:


See the procket README for the raw socket options and for
instructions on setting up the setuid helper.

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, Packet}

{icmp, Socket, Address, TTL, Packet}

Where Socket is the pid of the gen_icmp process, Address is a tuple
representing the IPv4 source address and Packet is the complete
ICMP packet including the ICMP headers.

close(Socket) -> ok | {error, Reason}

Types Socket = pid()
Reason = posix()

Close the ICMP socket.

send(Socket, Address, Packet) -> ok | {error, Reason}

Types Socket = pid()
Address = tuple()
Packet = binary()
Reason = not_owner | posix()

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

recv(Socket, Length) -> {ok, {Address, Packet}} | {error, Reason}
recv(Socket, Length, Timeout) -> {ok, {Address, Packet}} | {error, Reason}

Types Socket = socket()
Length = int()
Address = ip_address()
Packet = [char()] | binary()
Timeout = int() | infinity
Reason = not_owner | posix()

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

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

controlling_process(Socket, Pid) -> ok

Types Socket = pid()
Pid = pid()

Change the process owning the socket. Allows another process to
receive the ICMP responses.

setopts(Socket, Options) ->

Types Socket = pid()
Options = list()

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

ping(Host) -> Responses
ping(Host, Options) -> Responses
ping(Socket, Hosts, Options) -> Responses

Types Socket = pid()
Host = Address | Hostname | Hosts
Address = tuple()
Address = ReplyAddr = tuple()
Hostname = string()
Hosts = [ tuple() | string() ]
Options = [ Option ]
Option = {id, Id} | {sequence, Sequence} | {timeout, Timeout} | {data, Data} |
{timestamp, boolean()} | inet | inet6
{timestamp, boolean()} | {ttl, TTL} | inet | inet6
Id = uint16()
Sequence = uint16()
Timeout = int()
Timeout = int()
TTL = uint8()
Data = binary()
Responses = [ Response ]
Response = {ok, Host, Address, ReplyAddr, {{Id, Sequence, Elapsed}, Payload}} |
{{error, Error}, Host, Address, ReplyAddr, {{Id, Sequence}, Payload}} | {{error, timeout}, Address}
ReplyAddr = tuple()
Elapsed = int()
Response = {ok, Host, Address, ReplyAddr, Details, Payload}
| {error, Error, Host, Address, ReplyAddr, Details, Payload}
| {error, timeout, Host, Address}
Details = {Id, Sequence, TTL, Elapsed}
Elapsed = int() | undefined
Payload = binary()
Error = unreach_host | timxceed_intrans

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.
Expand Down Expand Up @@ -161,32 +166,32 @@ version. If you just need a simple example of sending a ping, also see:
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.

echo(Id, Sequence) -> Packet

Types Id = uint16()
Sequence = uint16()
Packet = binary()

Creates an ICMP echo packet with the results of erlang:now() used
as the timestamp and a payload consisting of ASCII 32 to 75.

echo(Id, Sequence, Payload) -> Packet

Types Id = uint16()
Sequence = uint16()
Payload = binary()
Packet = binary()

Creates an ICMP echo packet with the results of erlang:now() used
as the timestamp and a user specified payload (which should pad the
packet to 64 bytes).


packet(Header, Payload) -> Packet

Types Header = [ #icmp{} | Options ]
Options = [ Opts ]
Opts = [{type, Type} | {code, Code} | {id, Id} | {sequence, Sequence} |
Expand All @@ -204,7 +209,7 @@ version. If you just need a simple example of sending a ping, also see:
Sequence = uint16()
Payload = binary()
Packet = binary()

Convenience function for creating arbitrary ICMP packets. This
function will calculate the ICMP checksum and insert it into the
packet.
Expand Down Expand Up @@ -289,40 +294,39 @@ executable needs superuser privileges).

1> gen_icmp:ping("www.google.com").
[{ok,"www.google.com",
{74,125,228,84},
{74,125,228,84},
{{3275,0,317434},
<<" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJK">>}}]

2> gen_icmp:ping(["www.yahoo.com", {192,168,213,4}, "193.180.168.20", {192,0,32,10}]).

[{{error,timeout},"193.180.168.20",{193,180,168,20}},
{{error,unreach_host},
{192,168,213,4},
{192,168,213,4},
{192,168,213,54},
{{3275,2},
<<69,0,0,84,0,0,64,0,64,1,15,29,192,168,213,54,192,168,
213,4,...>>}},
{ok,{192,0,32,10},
{192,0,32,10},
{192,0,32,10},
{{3275,1,80903},
<<" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJK">>}},
{ok,"www.google.com",
{74,125,228,84},
{74,125,228,84},
{{3275,0,40248},
<<" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJK">>}}]
{173,194,64,99},
{173,194,64,99},
18411,0,50466,
<<" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJK">>}]

2> gen_icmp:ping(["www.google.com", {192,168,213,4}, "193.180.168.20", {192,0,32,10}]).
[{error,timeout,"193.180.168.20",{193,180,168,20}},
{error,unreach_host,
{192,168,213,4},
{192,168,213,4},
{192,168,213,54},
{18411,2,undefined},
<<69,0,0,84,0,0,64,0,64,1,15,29,192,168,213,54,192,168,
213,4,...>>},
{ok,{192,0,32,10},
{192,0,32,10},
{192,0,32,10},
{18411,1,103336},
<<" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJK">>},
{ok,"www.google.com",
{173,194,77,99},
{173,194,77,99},
{18411,0,50139},
<<" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJK">>}]

### IPv6

1> gen_icmp:ping("ipv6.google.com", [inet6]).
[{ok,"ipv6.google.com",
{9735,63664,16386,2049,0,0,0,4116},
{9735,63664,16386,2049,0,0,0,4116},
{{3275,0,205890},
<<" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJK">>}}]
{9735,63664,16396,3073,0,0,0,99},
{9735,63664,16396,3073,0,0,0,99},
3275,0,41790,
<<" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJK">>}]

2> tracert:host("ipv6.google.com", [inet6]).

Expand All @@ -336,23 +340,14 @@ Keeping the ICMP socket around between runs is more efficient:
P2 = gen_icmp:ping(Socket, [{10,2,2,2}, "www.yahoo.com"], []),
gen_icmp:close(Socket).

To avoid forking the setuid binary (beam needs the appropriate
capabilities):

{ok, Socket} = gen_icmp:open([{setuid,false}], []),
P1 = gen_icmp:ping(Socket, [{10,1,1,1}, "www.google.com"], []),
P2 = gen_icmp:ping(Socket, [{10,2,2,2}, "www.yahoo.com"], []),
gen_icmp:close(Socket).


### Working with ICMP sockets

{ok, Socket} = gen_icmp:open().

% ICMP host unreachable, empty payload (should contain an IPv4 header
% and the first 8 bytes of the packet data)
Packet = gen_icmp:packet([{type, 3}, {code, 0}], <<0:160, 0:64>>).

gen_icmp:send(Socket, {127,0,0,1}, Packet).


Expand Down Expand Up @@ -380,22 +375,31 @@ To use the proxy on host1:

### Traceroute

1> Path = tracert:host({8,8,8,8}).
[{{216,239,46,191},
36149,
{icmp,<<11,0,111,150,0,0,0,0,69,128,0,84,0,0,64,...>>}},
{{216,239,47,189},
51459,
{icmp,<<11,0,111,150,0,0,0,0,69,128,0,84,0,0,...>>}},
{{8,8,8,8},
34946,
{icmp,<<0,0,170,0,219,104,0,0,32,33,34,35,36,...>>}}]

2> tracert:path(Path).
[{{216,239,46,191},62815,timxceed_intrans},
{{216,239,47,189},44244,timxceed_intrans},
{{8,8,8,8},34825,echoreply}]
* ICMP traceroute

1> Path = tracert:host({8,8,8,8}).
[{{216,239,46,191},
36149,
{icmp,<<11,0,111,150,0,0,0,0,69,128,0,84,0,0,64,...>>}},
{{216,239,47,189},
51459,
{icmp,<<11,0,111,150,0,0,0,0,69,128,0,84,0,0,...>>}},
{{8,8,8,8},
34946,
{icmp,<<0,0,170,0,219,104,0,0,32,33,34,35,36,...>>}}]

2> tracert:path(Path).
[{{216,239,46,191},62815,timxceed_intrans},
{{216,239,47,189},44244,timxceed_intrans},
{{8,8,8,8},34825,echoreply}]

* UDP traceroute

1> Path = tracert:host({8,8,8,8}, [{protocol, udp}]).

* IPv6 traceroute

1> Path = tracert:host({8,8,8,8}).

### TODO

Expand Down
2 changes: 1 addition & 1 deletion src/gen_icmp.app.src
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{application, gen_icmp,
[{description, "Interface to ICMP sockets"},
{vsn, "0.4.0"},
{vsn, "0.5.0"},
{registered, []},
{applications, [kernel, stdlib]}
]}.
Loading

0 comments on commit 19cbb1c

Please sign in to comment.