msantos / procket

Erlang interface to low level socket operations
http://blog.listincomprehension.com/search/label/procket
BSD 3-Clause "New" or "Revised" License
283 stars 80 forks source link

gen_udp:open returns {error,einval} for ETH_P_ALL socket #17

Closed RushOnline closed 9 years ago

RushOnline commented 9 years ago

I've wrote function to listen raw ethernet packets:

ethernet_socket( Device ) ->
    {ok, Socket} = procket:socket(?AF_PACKET, ?SOCK_RAW, ?ETH_P_ALL), 
    Ifindex = packet:ifindex(Socket, Device),
    Sockaddr_ll = <<
        ?PF_PACKET:16/native,   % sll_family: PF_PACKET
        ?ETH_P_ALL:16,          % sll_protocol: Physical layer protocol
        Ifindex: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
    >>,
    ok = procket:bind(Socket, Sockaddr_ll),
    {ok, Socket}.

I want to receive messages from this socket:

% Erlang/OTP 17 [erts-6.3] [source] [64-bit] [smp:4:4] [async-threads:10] [kernel-poll:false]

> {ok, EthernetSocket} = ethernet_socket( Device ).
{ok, 8}.

> {ok, Socket} = gen_udp:open(0, [ { fd, EthernetSocket } ] ). 
** exception error: no match of right hand side value {error,einval}

What I'm doing wrong? Please help!

msantos commented 9 years ago

Your code is correct! gen_udp:open/2 does not allow non-IP sockets. However bypassing the inet driver and passing the file descriptor directly into an erlang port works:

Socket =  erlang:open_port({fd, FD, FD}, [stream, binary]).

Some background here: https://github.com/msantos/inert#alternatives

RushOnline commented 9 years ago

Thank you! Seems like write C port is much simpler for my task.

msantos commented 9 years ago

Do you mean using a dedicated, external Unix port process? Yes, depending on what you're doing, definitely that can be simpler sometimes.

If I can help with anything, feel free to re-open this issue!

RushOnline commented 9 years ago

Do you mean using a dedicated, external Unix port process?

Yes, I'm working on communication with specific chip over backplane. "Ethernet" is chip specific too, so I need raw socket. But you code is very good, I was learn many things from it. Thank you again!