-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, <>} 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, <>} -> <> = 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(<>, #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, <>} ]), Seq+1.