Skip to content

Commit

Permalink
Rewrite arp responder.
Browse files Browse the repository at this point in the history
  • Loading branch information
archaelus committed Mar 10, 2013
1 parent 9b59548 commit 04307b8
Showing 1 changed file with 72 additions and 105 deletions.
177 changes: 72 additions & 105 deletions src/enet_arp_responder.erl
Expand Up @@ -9,25 +9,25 @@


-behaviour(gen_server). -behaviour(gen_server).


-include("logging.hrl"). -include("../include/logging.hrl").
-include("enet_types.hrl"). -include("../include/enet_types.hrl").
-include("enet_arp_cache.hrl").
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").


%% API %% API
-export([start_link/0]). -export([start/0]).
-export([attach/2 -export([attach/1
,eth_addr/2 ,eth_addr/2
,ip_addr/2 ,ip_addr/2
,arp_filter/1
,publish/3
]). ]).


%% gen_server callbacks %% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]). terminate/2, code_change/3]).


-record(state, {tid}). -record(state, {cache}).

%% Table entries
-define(ARP_ENTRY(Mac, Addr), {Addr, Mac}).


%%==================================================================== %%====================================================================
%% API %% API
Expand All @@ -37,121 +37,86 @@
%% @doc Starts the server %% @doc Starts the server
%% @end %% @end
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
start_link() -> start() ->
gen_server:start_link(?MODULE, [], []). gen_server:start(?MODULE, [], []).


attach(Interface) -> attach(Interface) ->
{ok, Pid} = start_link(), {ok, Pid} = start(),
attach(Pid, Interface). attach(Pid, Interface).


attach(Dumper, Interface) -> attach(Dumper, Interface) ->
gen_server:call(Dumper, {sub, Interface}), gen_server:call(Dumper, {sub, Interface}),
{ok, Dumper}. {ok, Dumper}.


eth_addr(Cache, IpAddr) -> eth_addr(Pid, IpAddr) ->
gen_server:call(Cache, {eth_addr, IpAddr}). gen_server:call(Pid, {eth_addr, IpAddr}).

ip_addr(Pid, EthAddr) ->
gen_server:call(Pid, {ip_addr, EthAddr}).


ip_addr(Cache, EthAddr) -> publish(Pid, EthAddr, IpAddr) ->
gen_server:call(Cache, {ip_addr, EthAddr}). gen_server:call(Pid, {publish, EthAddr, IpAddr}).


arp_filter({enet, _, {rx, _, #eth{type=arp}}}) -> true;
arp_filter(_) -> false.


%%==================================================================== %%====================================================================
%% gen_server callbacks %% gen_server callbacks
%%==================================================================== %%====================================================================


%%--------------------------------------------------------------------
%% @private %% @private
%% @spec init(Args) -> {ok, State} |
%% {ok, State, Timeout} |
%% ignore |
%% {stop, Reason}
%% @doc Initialises the server's state
%% @end
%%--------------------------------------------------------------------
init([]) -> init([]) ->
Tid = ets:new(?MODULE, []), {ok, #state{cache = enet_arp_cache:new()}}.
{ok, #state{tid=Tid}}.


%%--------------------------------------------------------------------
%% @private %% @private
%% @spec handle_call({publish, EthAddr, IpAddr}, _From,
%% handle_call(Request, From, State) -> {reply, Reply, State} | State = #state{cache = OldCache}) ->
%% {reply, Reply, State, Timeout} | NewCache = enet_arp_cache:publish(EthAddr, IpAddr, OldCache),
%% {noreply, State} | {reply, ok, State#state{cache = NewCache}};
%% {noreply, State, Timeout} | handle_call({eth_addr, IpAddr}, _From, State = #state{cache = Cache}) ->
%% {stop, Reason, Reply, State} | case enet_arp_cache:lookup_ip_addr(IpAddr, Cache) of
%% {stop, Reason, State} not_found ->
%% @doc Call message handler callbacks {reply, not_found, State};
%% @end #entry{ethaddr = Addr} ->
%%-------------------------------------------------------------------- {reply, Addr, State}
end;

handle_call({ip_addr, EthAddr}, _From, State = #state{cache = Cache}) ->
case enet_arp_cache:lookup_eth_addr(EthAddr, Cache) of
not_found ->
{reply, not_found, State};
#entry{ipaddr = Addr} ->
{reply, Addr, State}
end;


handle_call({sub, Interface}, _From, State) -> handle_call({sub, Interface}, _From, State) ->
{reply, pubsub:sync_subscribe(Interface), State}; {reply, pubsub:sync_subscribe(Interface, fun ?MODULE:arp_filter/1), State};


handle_call(Call, _From, State) -> handle_call(Call, _From, State) ->
?WARN("Unexpected call ~p.", [Call]), ?WARN("Unexpected call ~p.", [Call]),
{noreply, State}. {noreply, State}.


%%--------------------------------------------------------------------
%% @private %% @private
%% @spec
%% handle_cast(Msg, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% @doc Cast message handler callbacks
%% @end
%%--------------------------------------------------------------------
handle_cast(Msg, State) -> handle_cast(Msg, State) ->
?WARN("Unexpected cast ~p", [Msg]), ?WARN("Unexpected cast ~p", [Msg]),
{noreply, State}. {noreply, State}.


%%-------------------------------------------------------------------- handle_info({enet, IF, {RX, _Frame, Pkt = #eth{type=arp}}}, State)
%% @private
%% @spec
%% handle_info(Info, State) -> {noreply, State} |
%% {noreply, State, Timeout} |
%% {stop, Reason, State}
%% @doc Non gen-server message handler callbacks
%% @end
%%--------------------------------------------------------------------

%% handle_info({enet, _IF, {tx, Frame}}, State) ->
%% P = enet_codec:decode(eth, Frame, [all]),
%% print([{dir, send}, {raw, Frame}, {packet, P}], State),
%% {noreply, State};
%% handle_info({enet, _IF, {RX, Frame}}, State)
%% when RX =:= rx;
%% RX =:= promisc_rx ->
%% print([{dir, recv}, {raw, Frame}], State),
%% {noreply, State};
handle_info({enet, IF, {RX, Frame, Pkt = #eth{type=arp}}}, State)
when RX =:= rx; when RX =:= rx;
RX =:= promisc_rx -> RX =:= promisc_rx ->
handle_arp_rx(IF, Pkt, State), handle_arp_rx(IF, Pkt, State),
{noreply, State}; {noreply, State};



handle_info(Info, State) -> handle_info(Info, State) ->
?WARN("Unexpected info ~p", [Info]), ?WARN("Unexpected info ~p", [Info]),
{noreply, State}. {noreply, State}.


%%--------------------------------------------------------------------
%% @private %% @private
%% @spec terminate(Reason, State) -> void()
%% @doc This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%% @end
%%--------------------------------------------------------------------
terminate(_Reason, _State) -> terminate(_Reason, _State) ->
ok. ok.


%%--------------------------------------------------------------------
%% @private %% @private
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
%% @doc Convert process state when code is changed
%% @end
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.


Expand All @@ -160,40 +125,42 @@ code_change(_OldVsn, State, _Extra) ->
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------


handle_arp_rx(IF, #eth{type=arp,data=Pkt}, State) -> handle_arp_rx(IF, #eth{type=arp,data=Pkt}, State) ->
try enet_codec:decode(arp, Pkt) of try enet_codec:decode(arp, Pkt, [{decode_types, [arp]}]) of
{error, bad_packet} -> #arp{htype = ethernet,
%% XXX - log corrupt arp packet somehow? ptype = ipv4,
ignore; op=request} = Q ->
#arp{} = Q -> handle_arp_request(IF, Q, State);
handle_arp_rx(IF, Q, State) _ ->
?INFO("Ignoring arp:~n~p", [Pkt]),
ignore
catch catch
_Type:_Error -> Class:Error ->
?WARN("~p:~p while decoding~n~p~nStack:~p",
[Class, Error, Pkt, erlang:get_stacktrace()]),
%% XXX - couldn't decode Pkt, Type:Error. %% XXX - couldn't decode Pkt, Type:Error.
ignore ignore
end; end.


handle_arp_rx(IF, handle_arp_request({enet_eth_iface, IF},
Q = #arp{htype = ethernet, #arp{htype = ethernet,
ptype = Type, ptype = ipv4,
op = request, op = request,
sender = Sender = {SMac, SAddr}, sender = Sender = {SMac, _SAddr},
target = {TMac, TAddr} target = {_TMac, TAddr}},
}, #state{cache = Cache}) ->
State) -> case enet_arp_cache:lookup_ip_addr(TAddr, Cache) of
case cache_lookup(TAddr, State) of not_found -> ignore;
[] -> #entry{publish = false} -> ignore;
ignore; #entry{publish = true,
[?ARP_ENTRY(CMac, CAddr)] -> ethaddr = CMac,
ipaddr = CAddr} ->
R = #arp{op = reply, R = #arp{op = reply,
htype = ethernet, htype = ethernet,
ptype = Type, ptype = ipv4,
sender = {CMac, CAddr}, sender = {CMac, CAddr},
target = Sender target = Sender
}, },
Reply = #eth{dst=SMac, type=arp, data=enet_codec:encode(arp, R)}, Reply = #eth{dst=SMac, type=arp,
enet_host:send(Reply) data=enet_codec:encode(arp, R, [])},
enet_eth_iface:send(IF, Reply)
end. end.


cache_lookup(Key, #state{tid=T}) ->
ets:lookup(T, Key).

0 comments on commit 04307b8

Please sign in to comment.