Skip to content

Instantly share code, notes, and snippets.

@msantos
Created December 4, 2010 01:41
Show Gist options
  • Select an option

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

Select an option

Save msantos/727821 to your computer and use it in GitHub Desktop.
ICMP Ping tunnel
-module(ptun).
-include("epcap_net.hrl").
-export([client/2, server/2]).
-define(TIMEOUT, 5000).
-define(PORT, 8787).
-record(state, {
addr,
port,
is,
ts,
id,
seq = 1
}).
server(Addr, Port) ->
{ok, ICMP} = gen_icmp:open(),
{ok, Socket} = gen_tcp:listen(Port, [
binary,
{packet, 0},
{active, true},
{reuseaddr, true},
{ip, {127,0,0,1}}
]),
accept(Addr, ICMP, Socket).
client(Addr, Port) ->
{ok, ICMP} = gen_icmp:open(),
State = #state{
addr = Addr,
port = Port,
is = ICMP,
id = crypto:rand_uniform(0, 16#FFFF)
},
proxy(State).
accept(Addr, ICMP, Listen) ->
{ok, Socket} = gen_tcp:accept(Listen),
gen_tcp:close(Listen),
State = #state{
addr = Addr,
is = ICMP,
ts = Socket,
id = crypto:rand_uniform(0, 16#FFFF)
},
[{ok, Addr, _}] = gen_icmp:ping(ICMP, [Addr], [
{id, State#state.id},
{sequence, 0},
{timeout, ?TIMEOUT}
]),
proxy(State).
proxy(#state{
is = IS,
ts = TS,
addr = Addr,
port = Port
} = State) ->
receive
% TCP socket events
{tcp, TS, Data} ->
Seq = send(Data, State),
proxy(State#state{seq = Seq});
{tcp_closed, TS} ->
ok;
{tcp_error, TS, Error} ->
{error, Error};
% ICMP socket events
% client: open a connection on receiving the first ICMP ping
{icmp, IS, Addr,
<<?ICMP_ECHO:8, 0:8, _Checksum:16, _Id:16, Seq:16, _Data/binary>>}
when TS == undefined, Seq == 0 ->
{ok, Socket} = gen_tcp:connect("127.0.0.1", Port, [binary, {packet, 0}]),
error_logger:info_report([{connect, {{127,0,0,1},Port}}]),
proxy(State#state{ts = Socket});
{icmp, IS, Addr,
<<?ICMP_ECHO:8, 0:8, _Checksum:16, _Id:16, _Seq:16, Len:16, Data/binary>>} ->
<<Data1:Len/bytes, _/binary>> = Data,
ok = gen_tcp:send(TS, Data1),
proxy(State#state{ts = TS});
{icmp, IS, Addr, Packet} ->
error_logger:info_report([{dropping, Packet},{address, Addr}]),
proxy(State)
end.
% To keep it simple, we use 64 byte packets
% 4 bytes header, 2 bytes type, 2 bytes code, 12 bytes timestamp, 2 bytes data length, 42 bytes data
send(<<Data:42/bytes, Rest/binary>>, #state{is = Socket, addr = Addr, id = Id, seq = Seq} = State) ->
[{ok, Addr, _}] = gen_icmp:ping(Socket, [Addr], [
{id, Id},
{sequence, Seq},
{timeout, ?TIMEOUT},
{data, <<(byte_size(Data)):16, Data/bytes>>}
]),
send(Rest, State#state{seq = Seq + 1});
send(Data, #state{is = Socket, addr = Addr, id = Id, seq = Seq}) ->
Len = byte_size(Data),
[{ok, Addr, _}] = gen_icmp:ping(Socket, [Addr], [
{id, Id},
{sequence, Seq},
{timeout, ?TIMEOUT},
{data, <<Len:16, Data/bytes, 0:((42-Len)*8)>>}
]),
Seq+1.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment