SNOOP(7P) SNOOP(7P) NAME snoop - network monitoring protocol SYNOPSIS #include <sys/types.h> #include <net/raw.h> s = socket(PF_RAW, SOCK_RAW, RAWPROTO_SNOOP); DESCRIPTION The Snoop protocol provides promiscuous packet capture with filtering. It treats packets as datagrams containing a link-layer header followed by data. Snoop uses the Raw address format, assigning a unique port to a socket bound to port zero, otherwise binding the specified port if it is valid. Valid ports range from SNOOP_MINPORT to SNOOP_MAXPORT. Snoop associates a set of SNOOP_MAXFILTERS packet filters with each network interface. Each filter contains an array of mask bits, a parallel array of bits to match against the masked packet's bits, the filter's index in the interface's filter table, and a port identifying the socket that added this filter. The user can set only sf_mask, sf_match, and sf_port; all other members are set by the kernel. struct snoopfilter { u_long sf_mask[SNOOP_FILTERLEN]; u_long sf_match[SNOOP_FILTERLEN]; u_short sf_allocated:1, sf_active:1, sf_promisc:1, sf_allmulti:1, sf_index:SNOOP_MAXFILTSHIFT; u_short sf_port; }; The mask is applied to at most SNOOP_FILTERLEN long integers of the packet. If the link-layer header size is not congruent with RAW_ALIGNGRAIN, the mask and match arrays begin with RAW_HDRPAD(hdrsize) bytes of padding, in order to preserve native addressability of packet data. The RAW_HDR(addr, hdrtype) macro adjusts and coerces a RAW_ALIGNGRAIN-congruent address into a pointer to the packet header type. Use RAW_HDR to convert sf_mask and sf_match into appropriately padded, typed pointers. Call ioctl(2) on a bound Snoop socket with the SIOCADDSNOOP command and the address of a snoopfilter, to add a filter. The SIOCDELSNOOP ioctl command takes the address of an integer telling the index of a filter to delete. The SIOCSNOOPLEN command takes the address of an integer telling how many bytes of packet data to capture (the link-layer header is always captured). By default, all received bytes of packet data are captured. The SIOCSNOOPING command takes the address of an integer boolean telling whether to start or stop capture. The SIOCERRSNOOP ioctl command establishes an error filter. It takes the address of an integer containing error flag bits (see below), and designates the socket being operated on as the error snooper. There may be at most one error snooper per network interface. Only packets received with errors indicated by bits in the integer argument will be captured. Snoop applies filters to a non-erroneous packet in index order, matching all filters against the packet. It then prepends the following header to the alignment-padded, link-layer header: struct snoopheader { u_long snoop_seq; u_short snoop_flags; u_short snoop_packetlen; struct timeval snoop_timestamp; }; A snoopheader contains a reception sequence number, packet state flags, length in bytes of the link-layer packet excluding frame check and preamble, and a reception timestamp. The bits in snoop_flags describe the packet's state as follows: SN_PROMISC packet was not destined for this interface SN_ERROR receive error specified by the following bits: SNERR_FRAME packet received, but with framing error SNERR_CHECKSUM packet received, but with CRC error SNERR_TOOBIG packet received, truncated to fit buffer SNERR_TOOSMALL packet not received, size less than minimum SNERR_NOBUFS no packet received, out of buffers SNERR_OVERFLOW no packet received, input silo overflow SNERR_MEMORY no packet received, buffer memory error The snoop_timestamp member contains the packet's reception time, with precision limited by the operating system's clock tick parameter (see times(2)). Output on a Snoop socket, using write(2) or send(2), takes a buffer address pointing at the link-layer packet to be transmitted. Output buffers may need to begin with RAW_HDRPAD bytes of padding to ensure addressability of structured data, but such padding is not passed to write. EXAMPLES To capture all packets from an Ethernet network interface, first declare an input buffer structure: #include <sys/types.h> #include <net/raw.h> #include <netinet/if_ether.h> #define ETHERHDRPAD RAW_HDRPAD(sizeof(struct ether_header)) struct etherpacket { struct snoopheader snoop; char pad[ETHERHDRPAD]; struct ether_header ether; char data[ETHERMTU]; }; Then create a Snoop socket (error handling is omitted for clarity). Bind it to the desired interface, e.g., ec0 (to bind to the primary interface, zero sr_ifname): int s; struct sockaddr_raw sr; s = socket(PF_RAW, SOCK_RAW, RAWPROTO_SNOOP); sr.sr_family = AF_RAW; sr.sr_port = 0; strncpy(sr.sr_ifname, "ec0", sizeof sr.sr_ifname); bind(s, &sr, sizeof sr); Initialize a filter with no mask bits set, in order to match all packets. Add it to the interface's filter set: struct snoopfilter sf; bzero((char *) &sf, sizeof sf); ioctl(s, SIOCADDSNOOP, &sf); Increase the socket's receive buffer size to a generous upper bound, to cope with promiscuous reception of heavy traffic. Turn snooping on and read captured packets: struct etherpacket ep; int cc = 60000, on = 1; setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *) &cc, sizeof cc); ioctl(s, SIOCSNOOPING, &on); for (;;) { cc = read(s, (char *) &ep, sizeof ep); /* . . . */ } To capture ARP packets from a specific Ethernet host, first declare an appropriate input buffer structure: #include <sys/types.h> #include <net/raw.h> #include <net/if_arp.h> #include <netinet/if_ether.h> #define ETHERHDRPAD RAW_HDRPAD(sizeof(struct ether_header)) struct arp_packet { struct snoopheader snoop; char pad[ETHERHDRPAD]; struct ether_header ether; struct ether_arp arp; }; Create and bind a Snoop socket as shown in the previous example. Then initialize a filter to capture all ARP requests originating from a remote_ether_addr: struct snoopfilter sf; struct ether_header *eh; bzero((char *) &sf, sizeof sf); eh = RAW_HDR(sf.sf_mask, struct ether_header); memset(eh->ether_dhost, 0xff, sizeof eh->ether_dhost); eh->ether_type = 0xffff; eh = RAW_HDR(sf.sf_match, struct ether_header); bcopy(remote_ether_addr, eh->ether_dhost, sizeof eh->ether_dhost); eh->ether_type = htons(ETHERTYPE_ARP); Finally, add the filter and start capturing packets: struct arp_packet ap; int cc, on = 1; ioctl(s, SIOCADDSNOOP, &sf); ioctl(s, SIOCSNOOPING, &on); for (;;) { cc = read(s, (char *) &ap, sizeof ap); /* . . . */ } DIAGNOSTICS A socket operation may fail with one of the following errors returned: [EISCONN] when trying to establish a connection on a socket which already has one, or when trying to send a datagram with the destination address specified and the socket is already connected; [ENOBUFS] when the system runs out of memory for an internal data structure or a send or receive buffer; [EADDRINUSE] when an attempt is made to create a socket with a port which has already been allocated; [EADDRNOTAVAIL] when an attempt is made to bind an address naming a non- existent network interface to a Raw family socket. [EOPNOTSUPP] when an ioctl operation not supported by the Snoop protocol is attempted. [EINVAL] when a Snoop ioctl argument is out of bounds or otherwise invalid. [EBUSY] when an error snooper is running and another process attempts to set an error filter with SIOCERRSNOOP. SEE ALSO getsockopt(2), socket(2), intro(3), ethernet(7), raw(7F), drain(7P) Page 5