/
icmp.erl
128 lines (112 loc) · 4.15 KB
/
icmp.erl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
%% Copyright (c) 2010, Michael Santos <michael.santos@gmail.com>
%% All rights reserved.
%%
%% Redistribution and use in source and binary forms, with or without
%% modification, are permitted provided that the following conditions
%% are met:
%%
%% Redistributions of source code must retain the above copyright
%% notice, this list of conditions and the following disclaimer.
%%
%% Redistributions in binary form must reproduce the above copyright
%% notice, this list of conditions and the following disclaimer in the
%% documentation and/or other materials provided with the distribution.
%%
%% Neither the name of the author nor the names of its contributors
%% may be used to endorse or promote products derived from this software
%% without specific prior written permission.
%%
%% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
%% "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
%% LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
%% FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
%% COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
%% INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
%% BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
%% LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
%% CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
%% LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
%% ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
%% POSSIBILITY OF SUCH DAMAGE.
-module(icmp).
-export([ping/1, ping/2]).
-record(icmp, {
valid,
type, code, checksum,
id, sequence,
gateway,
un,
mtu
}).
-record(state, {
s, % socket
id, % ping ID
seq = 0, % ping sequence number
ip, % IP Address
n % Number of pings
}).
-define(ICMP_ECHO_REPLY, 0).
-define(ICMP_ECHO, 8).
ping(IP) ->
ping(IP, 1).
ping(IP, N) ->
crypto:start(),
Id = crypto:rand_uniform(0, 16#FFFF),
{ok, FD} = procket:open(0, [{protocol, icmp}, {type, raw}, {family, inet}]),
{ok, S} = gen_udp:open(0, [binary, {fd, FD}]),
loop(#state{
s = S,
id = Id,
ip = IP,
n = N
}).
loop(#state{n = N, seq = Seq}) when Seq >= N ->
ok;
loop(#state{s = S, id = Id, seq = Seq, ip = IP, n = N} = State) ->
Packet = make_packet(Id, Seq),
ok = gen_udp:send(S, IP, 0, Packet),
receive
{udp, S, _IP, _Port, <<_:20/bytes, Data/binary>>} ->
{ICMP, <<Mega:32/integer, Sec:32/integer, Micro:32/integer, Payload/binary>>} = icmp(Data),
error_logger:info_report([
{type, ICMP#icmp.type},
{code, ICMP#icmp.code},
{checksum, ICMP#icmp.checksum},
{id, ICMP#icmp.id},
{sequence, ICMP#icmp.sequence},
{payload, Payload},
{time, timer:now_diff(erlang:now(), {Mega, Sec, Micro})}
]),
sleep(N, Seq)
after
5000 ->
error_logger:error_report([{noresponse, Packet}])
end,
loop(State#state{seq = Seq + 1}).
make_packet(Id, Seq) ->
{Mega,Sec,USec} = erlang:now(),
% Pad packet to 64 bytes
Payload = list_to_binary(lists:seq($\s, $K)),
CS = makesum(<<?ICMP_ECHO:8, 0:8, 0:16, Id:16, Seq:16, Mega:32, Sec:32, USec:32, Payload/binary>>),
<<
8:8, % Type
0:8, % Code
CS:16, % Checksum
Id:16, % Id
Seq:16, % Sequence
Mega:32, Sec:32, USec:32, % Payload: time
Payload/binary
>>.
makesum(Hdr) -> 16#FFFF - checksum(Hdr).
checksum(Hdr) ->
lists:foldl(fun compl/2, 0, [ W || <<W:16>> <= Hdr ]).
compl(N) when N =< 16#FFFF -> N;
compl(N) -> (N band 16#FFFF) + (N bsr 16).
compl(N,S) -> compl(N+S).
icmp(<<?ICMP_ECHO_REPLY:8, 0:8, Checksum:16, Id:16, Sequence:16, Payload/binary>>) ->
{#icmp{
type = ?ICMP_ECHO_REPLY, code = 0, checksum = Checksum, id = Id,
sequence = Sequence
}, Payload}.
sleep(N,S) when N =:= S + 1 -> ok;
sleep(_,_) -> timer:sleep(1000).