msantos / pkt

Erlang network protocol library
http://blog.listincomprehension.com/search/label/epcap
BSD 3-Clause "New" or "Revised" License
151 stars 44 forks source link

Failure in decoding ARP / ARP reply #29

Closed amuessig closed 8 years ago

amuessig commented 8 years ago

I want to receive packets and check if they are ARP replies. Thus, I look for 'arp' in the Frame, but it never matches. I couldn't find a single ARP packet which was decoded.


receive
        {Port, {data, Data}} -> 
            Frame = pkt:decapsulate(Data),
            ARP = lists:keyfind(arp,1,Frame),
            io:format("Frame: ~p~n", [Frame]),
            io:format("Data: ~p~n", [Data]),
            case ARP of
                false ->
                    ok;
                _ ->
                    Type = ARP#arp.op,
                    case Type of
                        ?ARPOP_REPLY ->
                            % Get the ARP sender ip which is the key
                            Key = ARP#arp.sip,
                            (#{Key := ListData} = Queue),
                            DestMAC = ARP#arp.sha,
                            io:format("ARP_REPLY Key: ~p~n", [Key]),
                            io:format("ARP_REPLY DestMac: ~p~n", [DestMAC]),
                            sendQueuedFrames(ListData, DestMAC, ListenSock, IfIndex);
                        _ ->
                            ok
                    end
            end
    end

Am I wrong or is there a bug on decoding? Thanks!

msantos commented 8 years ago

Can you provide an example of a frame with an ARP that is failing? Feel free to change the mac address to dummy values.

Note pkt:decapsulate/1 returns the headers in order so you can use pattern matching:

[#ether{}, #arp{} = ARP, _Payload] = pkt:decapsulate(Data)

That might simplify the code a bit:

case Frame of
    [#ether{}, #arp{op = ?ARPOP_REPLY, sip = Key, sha = DestMAC}, _Payload] ->
        ....
    _ ->
        ok
end
amuessig commented 8 years ago

I did not use pattern matching because I am not only receiving ARP frames but any frames and did not try out what happens if it is not an ARP.

After further investigating, it came out that I don't even receive the ARP frames.. I actually don't know why. I am using a raw socket with procket and receive any other Data which can be parsed and processed. ARPs are not there. Should I open an issue there? I have no clue what is happening, since even a wireshark/tcpdump on this interface shows the ARP.

msantos commented 8 years ago

On Mar 17, 2016 8:40 AM, "amuessig" notifications@github.com wrote:

I did not use pattern matching because I am not only receiving ARP frames but any frames and did not try out what happens if it is not an ARP.

Code should have the same behavior, any unmatched packets return OK.

After further investigating, it came out that I don't even receive the ARP frames.. I actually don't know why. I am using a raw socket with procket and receive any other Data which can be parsed and processed. ARPs are not there. Should I open an issue there? I have no clue what is happening, since even a wireshark/tcpdump on this interface shows the ARP.

A raw socket or a PF_PACKET socket? If you can include self-contained, runnable code, it will be easier to see what is going on.

— You are receiving this because you commented.

Reply to this email directly or view it on GitHub

amuessig commented 8 years ago

As you are asking, I think it is a PF_PACKET socket. But PF_PACKET should also allow capturing at OSI Layer 2? Actually, I need to capture ANY traffic going through this socket.

setup_socket() ->

    {ok, ListenSock} = procket:open(0, [{protocol, 16#0008}, {type, raw}, {family, packet}]),

    Dev ="eth1",
    % Binding to an interface
    IfIndex = packet:ifindex(ListenSock, Dev),
    ok = packet:bind(ListenSock, IfIndex),
    Port = erlang:open_port({fd, ListenSock, ListenSock}, [binary, stream]),

    %Get HW address and IP address
    {ok, PL} = inet:ifget(Dev, [addr, hwaddr]),
    IP = proplists:get_value(addr, PL),
    MAC = list_to_binary(proplists:get_value(hwaddr, PL)),

    % For Queuing packets when local MAC lookup fails
    Queue = #{},    
    test_receive(ListenSock, IfIndex, Port, IP, MAC, Queue).

test_receive(ListenSock, IfIndex, Port, IP, MAC, Queue) ->

    receive
        {Port, {data, Data}} -> 
            Frame = pkt:decapsulate(Data),

            ARP = lists:keyfind(arp,1,Frame),
            io:format("Is it ARP? Frame: ~p~n", [Frame]),
            io:format("Is it ARP? Data: ~p~n", [Data]),
            case ARP of
                false ->
                   ok;
                _ ->
                   Type = ARP#arp.op,
                   case Type of
                       ?ARPOP_REPLY ->
                           % Get the ARP sender ip which is the key
                           Key = ARP#arp.sip,
                           (#{Key := ListData} = Queue),
                           DestMAC = ARP#arp.sha,
                           io:format("It's a ARP Reply, doing different stuff here~p~n", [DestMAC]);
                       _ ->
                           ok
                   end
           end

    end,
    test_receive(ListenSock, IfIndex, Port, IP, MAC, Queue).

Update: Am I right with the assumption, that I need to change the protocol to 16#0300? {ok, ListenSock} = procket:open(0, [{protocol, 16#0300}, {type, raw}, {family, packet}])

I will check this tomorrow.

msantos commented 8 years ago

@amuessig that is correct. The socket is listening for IP packets only (ETH_P_IP). Switching to ETH_P_ALL (0x0003 in big endian format, needs to be converted to the native endian format on Linux) should receive everything including ARPs.

amuessig commented 8 years ago

This solved the problem. Thanks for the explanation!