PCAP is an abstraction over these different interfaces. epcap uses a system process linked to the PCAP library to read ethernet frames and send them as messages into Erlang using the port interface. Using procket with the PF_PACKET socket option, I've been playing with reading packets directly off the network and generating them as well.
The PF_PACKET interface is used by passing options to socket().
int socket(int domain, int type, int protocol);
- The protocol family is, of course, PF_PACKET.
- The type may be either SOCK_RAW or SOCK_DGRAM. SOCK_RAW will return the whole packet, including the ethernet header. A process sending a packet must prepend a link layer header. A socket with type SOCK_DGRAM will strip off the link layer header and generate a valid header for outgoing packets.
- The protocol is selected from one of the values in linux/if_ether.h.
#define ETH_P_IP 0x0800 #define ETH_P_ALL 0x0003
ETH_P_ALL will retrieve all network packets and ETH_P_IP just the IP packets. The values are in host-endian format and will need to be converted to network byte order before being used as arguments to socket().
Receving Packets using recvfrom()
To send and receive packets from a socket using PF_PACKET, the normal connection-less socket operations are used: sendto() and recvfrom(). By default, socket operations will block, unless the O_NONBLOCK flag is set using fcntl(). The gen_udp module in Erlang internally calls recvfrom(), so it can deal with raw sockets. Another example of using gen_udp in this way is for sending ICMP packets and reading the ICMP ECHO replies. Alternatively, I've added a recvfrom/2 function to the procket NIF for testing.Sniffing Packets in Erlang
To read packets from the network device, either gen_udp or the NIF recvfrom/2 can be used. Using gen_udp:-module(packet). -export([sniff/0]). -define(ETH_P_IP, 16#0008). -define(ETH_P_ALL, 16#0300). sniff() -> {ok, S} = procket:listen(0, [ {protocol, ?ETH_P_ALL}, {type, raw}, {family, packet} ]), {ok, S1} = gen_udp:open(0, [binary, {fd, S}, {active, false}]), loop(S1). loop(S) -> Data = gen_udp:recv(S, 2048), error_logger:info_report([{data, Data}]), loop(State).The definitions of ETH_P_IP and ETH_P_ALL are in big endian format. The port is irrelevant and is set to 0 in procket:listen/2. The type is raw but can also be set to dgram. Using the NIF, the process must poll the socket. procket:recvfrom/2 will return the atom nodata if the socket returns EAGAIN; the tuple {ok, binary} with the binary data representing the packet or a tuple holding the value of errno, e.g., {error, {errno, strerror(errno)}}. The return values will probably change in the future.
sniff() -> {ok, S} = procket:listen(0, [ {protocol, ?ETH_P_ALL}, {type, raw}, {family, packet} ]), loop(S). loop(S) -> case procket:recvfrom(S, 2048) of nodata -> timer:sleep(1000), loop(S); {ok, Data} -> error_logger:info_report([{data, Data}]), loop(S); Error -> error_logger:error_report(Error) end.
No comments:
Post a Comment
Note: Only a member of this blog may post a comment.