Skip to content

Instantly share code, notes, and snippets.

@msantos
Created June 14, 2010 23:42
Show Gist options
  • Select an option

  • Save msantos/438488 to your computer and use it in GitHub Desktop.

Select an option

Save msantos/438488 to your computer and use it in GitHub Desktop.
%% 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(pkt).
-export([ping/2]).
-define(SIOCGIFINDEX, 16#8933).
-define(IPPROTO_ICMP, 1).
-define(ICMP_ECHO_REPLY, 0).
-define(ICMP_ECHO, 8).
-define(PF_PACKET, 17).
-define(ETH_P_IP, 16#0800). % native (little) endian
-define(ETH_P_ALL, 16#0003).% native (little) endian
-record(icmp, {
valid,
type, code, checksum,
id, sequence,
gateway,
un,
mtu
}).
-record(state, {
s, % socket
dev, % device, e.g., eth0
id, % ping ID
seq = 0, % ping sequence number
srcmac, % Source MAC address
dstmac, % Destination MAC address
srcip, % Source IP Address
dstip % Destination IP Address
}).
ping({Device, SrcMac, SrcIP}, {DstMac, DstIP}) when length(Device) < 16->
crypto:start(),
Id = crypto:rand_uniform(0, 16#FFFF),
{ok, FD} = procket:listen(0, [{protocol, 16#0008}, {type, raw}, {family, packet}]),
prepare(#state{
s = FD,
dev = Device,
id = Id,
srcmac = SrcMac,
dstmac = DstMac,
srcip = SrcIP,
dstip = DstIP
}).
prepare(State) ->
Packet = packet(State),
Interface = ifindex(State),
send(Interface, Packet, State).
packet(#state{id = Id, seq = Seq,
srcmac = {SM1,SM2,SM3,SM4,SM5,SM6},
dstmac = {DM1,DM2,DM3,DM4,DM5,DM6},
srcip = {SA1,SA2,SA3,SA4}, dstip = {DA1,DA2,DA3,DA4}}) ->
% IPv4 header
TTL = 64,
PseudoIPv4 = <<
4:4, 5:4, 0:8, 84:16,
Id:16, 0:1, 1:1, 0:1,
0:13, TTL:8, ?IPPROTO_ICMP:8, 0:16,
SA1:8, SA2:8, SA3:8, SA4:8,
DA1:8, DA2:8, DA3:8, DA4:8
>>,
IPv4CS = makesum(PseudoIPv4),
{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>>),
<<
% Ethernet header
DM1:8, DM2:8, DM3:8, DM4:8, DM5:8, DM6:8, % destination host
SM1:8, SM2:8, SM3:8, SM4:8, SM5:8, SM6:8, % source host
16#08, 16#00, % type: ETH_P_IP
% IPv4 header
4:4, 5:4, 0:8, 84:16,
Id:16, 0:1, 1:1, 0:1,
0:13, TTL:8, ?IPPROTO_ICMP:8, IPv4CS:16,
SA1:8, SA2:8, SA3:8, SA4:8,
DA1:8, DA2:8, DA3:8, DA4:8,
% ICMP header
8:8, % Type
0:8, % Code
CS:16, % Checksum
Id:16, % Id
Seq:16, % Sequence
% ICMP payload
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).
ifindex(#state{s = S, dev = Dev}) ->
% struct ifreq {
% char ifrn_name[16];
% int ifr_ifindex;
% }
{ok, <<_Ifname:16/bytes, Ifr:32, _/binary>>} = procket:ioctl(S,
?SIOCGIFINDEX,
list_to_binary([
Dev, <<0:((16*8) - (length(Dev)*8)), 0:128>>
])),
error_logger:info_report([{ifr, Ifr}]),
Ifr.
send(Interface, Packet, #state{s = S}) ->
procket:sendto(S, Packet, 0,
<<
?PF_PACKET:16/native, % sll_family: PF_PACKET
0:16, % sll_protocol: Physical layer protocol
Interface:32/native, % sll_ifindex: Interface number
0:16, % sll_hatype: Header type
0:8, % sll_pkttype: Packet type
0:8, % sll_halen: address length
0:8, % sll_addr[8]: physical layer address
0:8, % sll_addr[8]: physical layer address
0:8, % sll_addr[8]: physical layer address
0:8, % sll_addr[8]: physical layer address
0:8, % sll_addr[8]: physical layer address
0:8, % sll_addr[8]: physical layer address
0:8, % sll_addr[8]: physical layer address
0:8 % sll_addr[8]: physical layer address
>>).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment