*/
#ifndef lint
static const char rcsid[] =
- "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.45 2000-12-22 11:53:27 guy Exp $ (LBL)";
+ "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.70 2001-10-25 08:27:18 guy Exp $ (LBL)";
#endif
/*
* do, if another socket also requested promiscuous mode between
* the time when we opened the socket and the time when we close
* the socket.
+ *
+ * - MSG_TRUNC isn't supported, so you can't specify that "recvfrom()"
+ * return the amount of data that you could have read, rather than
+ * the amount that was returned, so we can't just allocate a buffer
+ * whose size is the snapshot length and pass the snapshot length
+ * as the byte count, and also pass MSG_TRUNC, so that the return
+ * value tells us how long the packet was on the wire.
+ *
+ * This means that, if we want to get the actual size of the packet,
+ * so we can return it in the "len" field of the packet header,
+ * we have to read the entire packet, not just the part that fits
+ * within the snapshot length, and thus waste CPU time copying data
+ * from the kernel that our caller won't see.
+ *
+ * We have to get the actual size, and supply it in "len", because
+ * otherwise, the IP dissector in tcpdump, for example, will complain
+ * about "truncated-ip", as the packet will appear to have been
+ * shorter, on the wire, than the IP header said it should have been.
*/
#include <net/if.h>
#include <netinet/in.h>
#include <linux/if_ether.h>
-#include <netinet/if_ether.h>
+#include <net/if_arp.h>
+
+/*
+ * If PF_PACKET is defined, we can use {SOCK_RAW,SOCK_DGRAM}/PF_PACKET
+ * sockets rather than SOCK_PACKET sockets.
+ *
+ * To use them, we include <linux/if_packet.h> rather than
+ * <netpacket/packet.h>; we do so because
+ *
+ * some Linux distributions (e.g., Slackware 4.0) have 2.2 or
+ * later kernels and libc5, and don't provide a <netpacket/packet.h>
+ * file;
+ *
+ * not all versions of glibc2 have a <netpacket/packet.h> file
+ * that defines stuff needed for some of the 2.4-or-later-kernel
+ * features, so if the system has a 2.4 or later kernel, we
+ * still can't use those features.
+ *
+ * We're already including a number of other <linux/XXX.h> headers, and
+ * this code is Linux-specific (no other OS has PF_PACKET sockets as
+ * a raw packet capture mechanism), so it's not as if you gain any
+ * useful portability by using <netpacket/packet.h>
+ *
+ * XXX - should we just include <linux/if_packet.h> even if PF_PACKET
+ * isn't defined? It only defines one data structure in 2.0.x, so
+ * it shouldn't cause any problems.
+ */
+#ifdef PF_PACKET
+# include <linux/if_packet.h>
+
+ /*
+ * On at least some Linux distributions (for example, Red Hat 5.2),
+ * there's no <netpacket/packet.h> file, but PF_PACKET is defined if
+ * you include <sys/socket.h>, but <linux/if_packet.h> doesn't define
+ * any of the PF_PACKET stuff such as "struct sockaddr_ll" or any of
+ * the PACKET_xxx stuff.
+ *
+ * So we check whether PACKET_HOST is defined, and assume that we have
+ * PF_PACKET sockets only if it is defined.
+ */
+# ifdef PACKET_HOST
+# define HAVE_PF_PACKET_SOCKETS
+# endif /* PACKET_HOST */
+#endif /* PF_PACKET */
-#ifdef HAVE_NETPACKET_PACKET_H
-#include <netpacket/packet.h>
-#endif
#ifdef SO_ATTACH_FILTER
#include <linux/types.h>
#include <linux/filter.h>
#endif
#ifndef MSG_TRUNC
-#define MSG_TRUNC 0
+/*
+ * This is being compiled on a system that lacks MSG_TRUNC; define it
+ * with the value it has in the 2.2 and later kernels, so that, on
+ * those kernels, when we pass it in the flags argument to "recvfrom()"
+ * we're passing the right value and thus get the MSG_TRUNC behavior
+ * we want. (We don't get that behavior on 2.0[.x] kernels, because
+ * they didn't support MSG_TRUNC.)
+ */
+#define MSG_TRUNC 0x20
#endif
#define MAX_LINKHEADER_SIZE 256
/*
* Prototypes for internal functions
*/
-static int map_arphrd_to_dlt(int arptype );
+static void map_arphrd_to_dlt(pcap_t *, int);
static int live_open_old(pcap_t *, char *, int, int, char *);
static int live_open_new(pcap_t *, char *, int, int, char *);
static int pcap_read_packet(pcap_t *, pcap_handler, u_char *);
/*
* Wrap some ioctl calls
*/
-#ifdef HAVE_NETPACKET_PACKET_H
+#ifdef HAVE_PF_PACKET_SOCKETS
static int iface_get_id(int fd, const char *device, char *ebuf);
#endif
static int iface_get_mtu(int fd, const char *device, char *ebuf);
static int iface_get_arptype(int fd, const char *device, char *ebuf);
-#ifdef HAVE_NETPACKET_PACKET_H
+#ifdef HAVE_PF_PACKET_SOCKETS
static int iface_bind(int fd, int ifindex, char *ebuf);
#endif
static int iface_bind_old(int fd, const char *device, char *ebuf);
#ifdef SO_ATTACH_FILTER
static int fix_program(pcap_t *handle, struct sock_fprog *fcode);
static int fix_offset(struct bpf_insn *p);
+static int set_kernel_filter(pcap_t *handle, struct sock_fprog *fcode);
+static int reset_kernel_filter(pcap_t *handle);
+
+static struct sock_filter total_insn
+ = BPF_STMT(BPF_RET | BPF_K, 0);
+static struct sock_fprog total_fcode
+ = { 1, &total_insn };
#endif
/*
pcap_t *
pcap_open_live(char *device, int snaplen, int promisc, int to_ms, char *ebuf)
{
+ pcap_t *handle;
+ int mtu;
+ struct utsname utsname;
+
/* Allocate a handle for this session. */
- pcap_t *handle = malloc(sizeof(*handle));
+ handle = malloc(sizeof(*handle));
if (handle == NULL) {
snprintf(ebuf, PCAP_ERRBUF_SIZE, "malloc: %s",
pcap_strerror(errno));
if (!device || strcmp(device, "any") == 0) {
device = NULL;
handle->md.device = strdup("any");
+ if (promisc) {
+ promisc = 0;
+ /* Just a warning. */
+ snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "Promiscuous mode not supported on the \"any\" device");
+ }
+
} else
handle->md.device = strdup(device);
return NULL;
}
- /*
- * Okay, now we have a packet stream open. Maybe we need to handle
- * a timeout? In that case we set the filehandle to nonblocking
- * so pcap_read can try reading the fd and call select if no data
- * is available at first.
+ /*
+ * Compute the buffer size.
+ *
+ * If we're using SOCK_PACKET, this might be a 2.0[.x] kernel,
+ * and might require special handling - check.
*/
-
- if (to_ms > 0) {
- int flags = fcntl(handle->fd, F_GETFL);
- if (flags != -1) {
- flags |= O_NONBLOCK;
- flags = fcntl(handle->fd, F_SETFL, flags);
- }
- if (flags == -1) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE, "fcntl: %s",
- pcap_strerror(errno));
- pcap_close(handle);
+ if (handle->md.sock_packet && (uname(&utsname) < 0 ||
+ strncmp(utsname.release, "2.0", 3) == 0)) {
+ /*
+ * We're using a SOCK_PACKET structure, and either
+ * we couldn't find out what kernel release this is,
+ * or it's a 2.0[.x] kernel.
+ *
+ * In the 2.0[.x] kernel, a "recvfrom()" on
+ * a SOCK_PACKET socket, with MSG_TRUNC set, will
+ * return the number of bytes read, so if we pass
+ * a length based on the snapshot length, it'll
+ * return the number of bytes from the packet
+ * copied to userland, not the actual length
+ * of the packet.
+ *
+ * This means that, for example, the IP dissector
+ * in tcpdump will get handed a packet length less
+ * than the length in the IP header, and will
+ * complain about "truncated-ip".
+ *
+ * So we don't bother trying to copy from the
+ * kernel only the bytes in which we're interested,
+ * but instead copy them all, just as the older
+ * versions of libpcap for Linux did.
+ *
+ * The buffer therefore needs to be big enough to
+ * hold the largest packet we can get from this
+ * device. Unfortunately, we can't get the MRU
+ * of the network; we can only get the MTU. The
+ * MTU may be too small, in which case a packet larger
+ * than the buffer size will be truncated *and* we
+ * won't get the actual packet size.
+ *
+ * However, if the snapshot length is larger than
+ * the buffer size based on the MTU, we use the
+ * snapshot length as the buffer size, instead;
+ * this means that with a sufficiently large snapshot
+ * length we won't artificially truncate packets
+ * to the MTU-based size.
+ *
+ * This mess just one of many problems with packet
+ * capture on 2.0[.x] kernels; you really want a
+ * 2.2[.x] or later kernel if you want packet capture
+ * to work well.
+ */
+ mtu = iface_get_mtu(handle->fd, device, ebuf);
+ if (mtu == -1) {
+ close(handle->fd);
+ free(handle->md.device);
+ free(handle);
return NULL;
}
+ handle->bufsize = MAX_LINKHEADER_SIZE + mtu;
+ if (handle->bufsize < handle->snapshot)
+ handle->bufsize = handle->snapshot;
+ } else {
+ /*
+ * This is a 2.2[.x] or later kernel (we know that
+ * either because we're not using a SOCK_PACKET
+ * socket - PF_PACKET is supported only in 2.2
+ * and later kernels - or because we checked the
+ * kernel version).
+ *
+ * We can safely pass "recvfrom()" a byte count
+ * based on the snapshot length.
+ */
+ handle->bufsize = handle->snapshot;
+ }
+
+ /* Allocate the buffer */
+
+ handle->buffer = malloc(handle->bufsize + handle->offset);
+ if (!handle->buffer) {
+ snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "malloc: %s", pcap_strerror(errno));
+ close(handle->fd);
+ free(handle->md.device);
+ free(handle);
+ return NULL;
}
return handle;
* Read at most max_packets from the capture stream and call the callback
* for each of them. Returns the number of packets handled or -1 if an
* error occured.
- *
- * XXX: Can I rely on the Linux-specified behaviour of select (returning
- * the time left in the timeval structure)? I really don't want to query
- * the system time before each select call...
- *
- * pcap_read currently gets not only a packet from the kernel but also
- * the sockaddr_ll returned as source of the packet. This way we can at
- * some time extend tcpdump and libpcap to sniff on all devices at a time
- * and find the right printing routine by using the information in the
- * sockaddr_ll structure.
*/
int
pcap_read(pcap_t *handle, int max_packets, pcap_handler callback, u_char *user)
{
- int status, packets;
- fd_set read_fds;
- struct timeval tv;
-
/*
- * Fill in a timeval structure for select if we need to obeye a
- * timeout.
+ * Currently, on Linux only one packet is delivered per read,
+ * so we don't loop.
*/
- if (handle->md.timeout > 0) {
- tv.tv_usec = (handle->md.timeout % 1000) * 1000;
- tv.tv_sec = (handle->md.timeout / 1000);
- }
-
- /*
- * Read packets until the packet limit has been reached or
- * an error occured while reading. Call the user function
- * for each received packet.
- */
- for (packets = 0; max_packets == -1 || packets < max_packets;)
- {
- status = pcap_read_packet(handle, callback, user);
-
- if (status > 0) {
- packets += status;
- continue;
- } else if (status == -1)
- return -1;
-
- /*
- * If no packet is available we go to sleep. FIXME: This
- * might be better implemented using poll(?)
- */
- FD_ZERO(&read_fds);
- FD_SET(handle->fd, &read_fds);
- status = select(handle->fd + 1,
- &read_fds, NULL, NULL, &tv);
-
- if (status == -1) {
- if (errno == EINTR)
- return packets;
- snprintf(handle->errbuf, sizeof(handle->errbuf),
- "select: %s", pcap_strerror(errno));
- return -1;
- }
- else if (status == 0 ||
- (tv.tv_usec == 0 && tv.tv_sec == 0))
- return packets;
- }
-
- return packets;
+ return pcap_read_packet(handle, callback, user);
}
/*
static int
pcap_read_packet(pcap_t *handle, pcap_handler callback, u_char *userdata)
{
+ u_char *bp;
int offset;
-#ifdef HAVE_NETPACKET_PACKET_H
+#ifdef HAVE_PF_PACKET_SOCKETS
struct sockaddr_ll from;
struct sll_header *hdrp;
#else
int packet_len, caplen;
struct pcap_pkthdr pcap_header;
-#ifdef HAVE_NETPACKET_PACKET_H
+#ifdef HAVE_PF_PACKET_SOCKETS
/*
* If this is a cooked device, leave extra room for a
* fake packet header.
/* Receive a single packet from the kernel */
+ bp = handle->buffer + handle->offset;
do {
fromlen = sizeof(from);
packet_len = recvfrom(
- handle->fd, handle->buffer + offset + handle->offset,
- handle->md.readlen - offset, MSG_TRUNC,
+ handle->fd, bp + offset,
+ handle->bufsize - offset, MSG_TRUNC,
(struct sockaddr *) &from, &fromlen);
} while (packet_len == -1 && errno == EINTR);
}
}
-#ifdef HAVE_NETPACKET_PACKET_H
+#ifdef HAVE_PF_PACKET_SOCKETS
/*
* If this is from the loopback device, reject outgoing packets;
* we'll see the packet as an incoming packet as well, and
return 0;
#endif
-#ifdef HAVE_NETPACKET_PACKET_H
+#ifdef HAVE_PF_PACKET_SOCKETS
/*
* If this is a cooked device, fill in the fake packet header.
*/
*/
packet_len += SLL_HDR_LEN;
- hdrp = (struct sll_header *)handle->buffer;
- hdrp->sll_pkttype = htons(from.sll_pkttype);
- if (from.sll_protocol == ETH_P_802_2) {
- /*
- * This is an 802.3 packet; set the packet type
- * field to the length, in network byte order.
- */
- hdrp->sll_protocol = htons(packet_len);
- } else
- hdrp->sll_protocol = from.sll_protocol;
+ hdrp = (struct sll_header *)bp;
+
+ /*
+ * Map the PACKET_ value to a LINUX_SLL_ value; we
+ * want the same numerical value to be used in
+ * the link-layer header even if the numerical values
+ * for the PACKET_ #defines change, so that programs
+ * that look at the packet type field will always be
+ * able to handle DLT_LINUX_SLL captures.
+ */
+ switch (from.sll_pkttype) {
+
+ case PACKET_HOST:
+ hdrp->sll_pkttype = htons(LINUX_SLL_HOST);
+ break;
+
+ case PACKET_BROADCAST:
+ hdrp->sll_pkttype = htons(LINUX_SLL_BROADCAST);
+ break;
+
+ case PACKET_MULTICAST:
+ hdrp->sll_pkttype = htons(LINUX_SLL_MULTICAST);
+ break;
+
+ case PACKET_OTHERHOST:
+ hdrp->sll_pkttype = htons(LINUX_SLL_OTHERHOST);
+ break;
+
+ case PACKET_OUTGOING:
+ hdrp->sll_pkttype = htons(LINUX_SLL_OUTGOING);
+ break;
+
+ default:
+ hdrp->sll_pkttype = -1;
+ break;
+ }
+
hdrp->sll_hatype = htons(from.sll_hatype);
hdrp->sll_halen = htons(from.sll_halen);
memcpy(hdrp->sll_addr, from.sll_addr,
(from.sll_halen > SLL_ADDRLEN) ?
SLL_ADDRLEN :
from.sll_halen);
+ hdrp->sll_protocol = from.sll_protocol;
}
#endif
/* Run the packet filter if not using kernel filter */
if (!handle->md.use_bpf && handle->fcode.bf_insns) {
- if (bpf_filter(handle->fcode.bf_insns, handle->buffer,
+ if (bpf_filter(handle->fcode.bf_insns, bp,
packet_len, caplen) == 0)
{
/* rejected by filter */
pcap_header.caplen = caplen;
pcap_header.len = packet_len;
- /* Call the user supplied callback function */
+ /*
+ * Count the packet.
+ *
+ * Arguably, we should count them before we check the filter,
+ * as on many other platforms "ps_recv" counts packets
+ * handed to the filter rather than packets that passed
+ * the filter, but if filtering is done in the kernel, we
+ * can't get a count of packets that passed the filter,
+ * and that would mean the meaning of "ps_recv" wouldn't
+ * be the same on all Linux systems.
+ *
+ * XXX - it's not the same on all systems in any case;
+ * ideally, we should have a "get the statistics" call
+ * that supplies more counts and indicates which of them
+ * it supplies, so that we supply a count of packets
+ * handed to the filter only on platforms where that
+ * information is available.
+ *
+ * We count them here even if we can get the packet count
+ * from the kernel, as we can only determine at run time
+ * whether we'll be able to get it from the kernel (if
+ * HAVE_TPACKET_STATS isn't defined, we can't get it from
+ * the kernel, but if it is defined, the library might
+ * have been built with a 2.4 or later kernel, but we
+ * might be running on a 2.2[.x] kernel without Alexey
+ * Kuznetzov's turbopacket patches, and thus the kernel
+ * might not be able to supply those statistics). We
+ * could, I guess, try, when opening the socket, to get
+ * the statistics, and if we can not increment the count
+ * here, but it's not clear that always incrementing
+ * the count is more expensive than always testing a flag
+ * in memory.
+ */
handle->md.stat.ps_recv++;
- callback(userdata, &pcap_header, handle->buffer + handle->offset);
+
+ /* Call the user supplied callback function */
+ callback(userdata, &pcap_header, bp);
return 1;
}
/*
* Get the statistics for the given packet capture handle.
- * FIXME: Currently does not report the number of dropped packets.
+ * Reports the number of dropped packets iff the kernel supports
+ * the PACKET_STATISTICS "getsockopt()" argument (2.4 and later
+ * kernels, and 2.2[.x] kernels with Alexey Kuznetzov's turbopacket
+ * patches); otherwise, that information isn't available, and we lie
+ * and report 0 as the count of dropped packets.
*/
int
pcap_stats(pcap_t *handle, struct pcap_stat *stats)
{
+#ifdef HAVE_TPACKET_STATS
+ struct tpacket_stats kstats;
+ socklen_t len;
+
+ /*
+ * Try to get the packet counts from the kernel.
+ */
+ if (getsockopt(handle->fd, SOL_PACKET, PACKET_STATISTICS,
+ &kstats, &len) > -1) {
+ handle->md.stat.ps_recv = (kstats.tp_packets - kstats.tp_drops);
+ handle->md.stat.ps_drop = kstats.tp_drops;
+ }
+#endif
+ /*
+ * "ps_recv" counts only packets that passed the filter.
+ *
+ * "ps_drop" is maintained only on systems that support
+ * the PACKET_STATISTICS "getsockopt()" argument.
+ */
*stats = handle->md.stat;
return 0;
}
}
if (can_filter_in_kernel) {
- if (setsockopt(handle->fd, SOL_SOCKET, SO_ATTACH_FILTER,
- &fcode, sizeof(fcode)) == 0)
+ if (set_kernel_filter(handle, &fcode) == 0)
{
/* Installation succeded - using kernel filter. */
handle->md.use_bpf = 1;
}
}
+ /*
+ * If we're not using the kernel filter, get rid of any kernel
+ * filter that might've been there before, e.g. because the
+ * previous filter could work in the kernel, or because some other
+ * code attached a filter to the socket by some means other than
+ * calling "pcap_setfilter()". Otherwise, the kernel filter may
+ * filter out packets that would pass the new userland filter.
+ */
+ if (!handle->md.use_bpf)
+ reset_kernel_filter(handle);
+
/*
* Free up the copy of the filter that was made by "fix_program()".
*/
/*
* Linux uses the ARP hardware type to identify the type of an
* interface. pcap uses the DLT_xxx constants for this. This
- * function maps the ARPHRD_xxx constant to an appropriate
- * DLT_xxx constant.
+ * function takes a pointer to a "pcap_t", and an ARPHRD_xxx
+ * constant, as arguments, and sets "handle->linktype" to the
+ * appropriate DLT_XXX constant and sets "handle->offset" to
+ * the appropriate value (to make "handle->offset" plus link-layer
+ * header length be a multiple of 4, so that the link-layer payload
+ * will be aligned on a 4-byte boundary when capturing packets).
+ * (If the offset isn't set here, it'll be 0; add code as appropriate
+ * for cases where it shouldn't be 0.)
*
- * Returns -1 if unable to map the type; we print a message and,
- * if we're using PF_PACKET/SOCK_RAW rather than PF_INET/SOCK_PACKET,
- * we fall back on using PF_PACKET/SOCK_DGRAM.
+ * Sets the link type to -1 if unable to map the type.
*/
-static int map_arphrd_to_dlt(int arptype)
+static void map_arphrd_to_dlt(pcap_t *handle, int arptype)
{
switch (arptype) {
+
case ARPHRD_ETHER:
case ARPHRD_METRICOM:
- case ARPHRD_LOOPBACK: return DLT_EN10MB;
- case ARPHRD_EETHER: return DLT_EN3MB;
- case ARPHRD_AX25: return DLT_AX25;
- case ARPHRD_PRONET: return DLT_PRONET;
- case ARPHRD_CHAOS: return DLT_CHAOS;
+ case ARPHRD_LOOPBACK:
+ handle->linktype = DLT_EN10MB;
+ handle->offset = 2;
+ break;
+
+ case ARPHRD_EETHER:
+ handle->linktype = DLT_EN3MB;
+ break;
+
+ case ARPHRD_AX25:
+ handle->linktype = DLT_AX25;
+ break;
+
+ case ARPHRD_PRONET:
+ handle->linktype = DLT_PRONET;
+ break;
+
+ case ARPHRD_CHAOS:
+ handle->linktype = DLT_CHAOS;
+ break;
+
#ifndef ARPHRD_IEEE802_TR
#define ARPHRD_IEEE802_TR 800 /* From Linux 2.4 */
#endif
case ARPHRD_IEEE802_TR:
- case ARPHRD_IEEE802: return DLT_IEEE802;
- case ARPHRD_ARCNET: return DLT_ARCNET;
- case ARPHRD_FDDI: return DLT_FDDI;
+ case ARPHRD_IEEE802:
+ handle->linktype = DLT_IEEE802;
+ handle->offset = 2;
+ break;
+
+ case ARPHRD_ARCNET:
+ handle->linktype = DLT_ARCNET;
+ break;
+
+ case ARPHRD_FDDI:
+ handle->linktype = DLT_FDDI;
+ handle->offset = 3;
+ break;
#ifndef ARPHRD_ATM /* FIXME: How to #include this? */
#define ARPHRD_ATM 19
#endif
- case ARPHRD_ATM: return DLT_ATM_CLIP;
+ case ARPHRD_ATM:
+ /*
+ * The Classical IP implementation in ATM for Linux
+ * supports both what RFC 1483 calls "LLC Encapsulation",
+ * in which each packet has an LLC header, possibly
+ * with a SNAP header as well, prepended to it, and
+ * what RFC 1483 calls "VC Based Multiplexing", in which
+ * different virtual circuits carry different network
+ * layer protocols, and no header is prepended to packets.
+ *
+ * They both have an ARPHRD_ type of ARPHRD_ATM, so
+ * you can't use the ARPHRD_ type to find out whether
+ * captured packets will have an LLC header, and,
+ * while there's a socket ioctl to *set* the encapsulation
+ * type, there's no ioctl to *get* the encapsulation type.
+ *
+ * This means that
+ *
+ * programs that dissect Linux Classical IP frames
+ * would have to check for an LLC header and,
+ * depending on whether they see one or not, dissect
+ * the frame as LLC-encapsulated or as raw IP (I
+ * don't know whether there's any traffic other than
+ * IP that would show up on the socket, or whether
+ * there's any support for IPv6 in the Linux
+ * Classical IP code);
+ *
+ * filter expressions would have to compile into
+ * code that checks for an LLC header and does
+ * the right thing.
+ *
+ * Both of those are a nuisance - and, at least on systems
+ * that support PF_PACKET sockets, we don't have to put
+ * up with those nuisances; instead, we can just capture
+ * in cooked mode. That's what we'll do.
+ */
+ handle->linktype = DLT_LINUX_SLL;
+ break;
+
+#ifndef ARPHRD_IEEE80211 /* From Linux 2.4.6 */
+#define ARPHRD_IEEE80211 801
+#endif
+ case ARPHRD_IEEE80211:
+ handle->linktype = DLT_IEEE802_11;
+ break;
case ARPHRD_PPP:
+ /*
+ * Some PPP code in the kernel supplies no link-layer
+ * header whatsoever to PF_PACKET sockets; other PPP
+ * code supplies PPP link-layer headers ("syncppp.c");
+ * some PPP code might supply random link-layer
+ * headers (PPP over ISDN - there's code in Ethereal,
+ * for example, to cope with PPP-over-ISDN captures
+ * with which the Ethereal developers have had to cope,
+ * heuristically trying to determine which of the
+ * oddball link-layer headers particular packets have).
+ *
+ * As such, we just punt, and run all PPP interfaces
+ * in cooked mode.
+ */
+ handle->linktype = DLT_LINUX_SLL;
+ break;
+
+ case ARPHRD_HDLC:
+ handle->linktype = DLT_C_HDLC;
+ break;
+
/* Not sure if this is correct for all tunnels, but it
* works for CIPE */
case ARPHRD_TUNNEL:
+#ifndef ARPHRD_SIT
+#define ARPHRD_SIT 776 /* From Linux 2.2.14 */
+#endif
case ARPHRD_SIT:
case ARPHRD_CSLIP:
case ARPHRD_SLIP6:
case ARPHRD_CSLIP6:
- case ARPHRD_SLIP: return DLT_RAW;
- }
+ case ARPHRD_ADAPT:
+ case ARPHRD_SLIP:
+ /*
+ * XXX - should some of those be mapped to DLT_LINUX_SLL
+ * instead? Should we just map all of them to DLT_LINUX_SLL?
+ */
+ handle->linktype = DLT_RAW;
+ break;
+
+ case ARPHRD_LOCALTLK:
+ handle->linktype = DLT_LTALK;
+ break;
- return -1;
+ default:
+ handle->linktype = -1;
+ break;
+ }
}
/* ===== Functions to interface to the newer kernels ================== */
live_open_new(pcap_t *handle, char *device, int promisc,
int to_ms, char *ebuf)
{
-#ifdef HAVE_NETPACKET_PACKET_H
- int sock_fd = -1, device_id, mtu, arptype;
+#ifdef HAVE_PF_PACKET_SOCKETS
+ int sock_fd = -1, device_id, arptype;
struct packet_mreq mr;
/* One shot loop used for error handling - bail out with break */
*/
handle->md.lo_ifindex = iface_get_id(sock_fd, "lo", ebuf);
+ /*
+ * Default value for offset to align link-layer payload
+ * on a 4-byte boundary.
+ */
+ handle->offset = 0;
+
/*
* What kind of frames do we have to deal with? Fall back
* to cooked mode if we have an unknown interface type.
arptype = iface_get_arptype(sock_fd, device, ebuf);
if (arptype == -1)
break;
- handle->linktype = map_arphrd_to_dlt(arptype);
+ map_arphrd_to_dlt(handle, arptype);
if (handle->linktype == -1 ||
+ handle->linktype == DLT_LINUX_SLL ||
(handle->linktype == DLT_EN10MB &&
(strncmp("isdn", device, 4) == 0 ||
strncmp("isdY", device, 4) == 0))) {
/*
- * Unknown interface type (-1), or an ISDN
- * device (whose link-layer type we
- * can only determine by using APIs
- * that may be different on different
+ * Unknown interface type (-1), or a
+ * device we explicitly chose to run
+ * in cooked mode (e.g., PPP devices),
+ * or an ISDN device (whose link-layer
+ * type we can only determine by using
+ * APIs that may be different on different
* kernels) - reopen in cooked mode.
- *
- * XXX - do that with DLT_RAW as well?
*/
if (close(sock_fd) == -1) {
snprintf(ebuf, PCAP_ERRBUF_SIZE,
* update "map_arphrd_to_dlt()"
* to handle the new type.
*/
- fprintf(stderr,
- "Warning: arptype %d not "
+ snprintf(ebuf, PCAP_ERRBUF_SIZE,
+ "arptype %d not "
"supported by libpcap - "
"falling back to cooked "
- "socket\n",
+ "socket",
arptype);
}
handle->linktype = DLT_LINUX_SLL;
}
#endif
- /* Compute the buffersize */
-
- mtu = iface_get_mtu(sock_fd, device, ebuf);
- if (mtu == -1)
- break;
- handle->bufsize = MAX_LINKHEADER_SIZE + mtu;
-
- /* Fill in the pcap structure */
+ /* Save the socket FD in the pcap structure */
handle->fd = sock_fd;
- handle->offset = 0;
- handle->buffer = malloc(handle->bufsize);
- if (!handle->buffer) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE,
- "malloc: %s", pcap_strerror(errno));
- break;
- }
-
- /*
- * This is a 2.2 or later kernel, as it has PF_PACKET;
- * "recvfrom()", when passed the MSG_TRUNC flag, will
- * return the actual length of the packet, not the
- * number of bytes from the packet copied to userland,
- * so we can safely pass it a byte count based on the
- * snapshot length.
- */
- handle->md.readlen = handle->snapshot;
return 1;
} while(0);
#endif
}
-#ifdef HAVE_NETPACKET_PACKET_H
+#ifdef HAVE_PF_PACKET_SOCKETS
/*
* Return the index of the given device name. Fill ebuf and return
* -1 on failure.
live_open_old(pcap_t *handle, char *device, int promisc,
int to_ms, char *ebuf)
{
- int sock_fd = -1, mtu, arptype;
- struct utsname utsname;
+ int sock_fd = -1, arptype;
struct ifreq ifr;
do {
}
}
- /* Compute the buffersize */
-
- mtu = iface_get_mtu(sock_fd, device, ebuf);
- if (mtu == -1)
- break;
- handle->bufsize = MAX_LINKHEADER_SIZE + mtu;
- if (handle->bufsize < handle->snapshot)
- handle->bufsize = handle->snapshot;
-
/* All done - fill in the pcap handle */
arptype = iface_get_arptype(sock_fd, device, ebuf);
if (arptype == -1)
break;
+ /* Save the socket FD in the pcap structure */
+
handle->fd = sock_fd;
+
+ /*
+ * Default value for offset to align link-layer payload
+ * on a 4-byte boundary.
+ */
handle->offset = 0;
- handle->linktype = map_arphrd_to_dlt(arptype);
+
/*
* XXX - handle ISDN types here? We can't fall back on
* cooked sockets, so we'd have to figure out from the
* type that has only an Ethernet packet type as
* a link-layer header.
*/
+ map_arphrd_to_dlt(handle, arptype);
if (handle->linktype == -1) {
snprintf(ebuf, PCAP_ERRBUF_SIZE,
"interface type of %s not supported", device);
break;
}
- handle->buffer = malloc(handle->bufsize);
- if (!handle->buffer) {
- snprintf(ebuf, PCAP_ERRBUF_SIZE,
- "malloc: %s", pcap_strerror(errno));
- break;
- }
- /*
- * This might be a 2.0[.x] kernel - check.
- */
- if (uname(&utsname) < 0 ||
- strncmp(utsname.release, "2.0", 3) == 0) {
- /*
- * Either we couldn't find out what kernel release
- * this is, or it's a 2.0[.x] kernel.
- *
- * In the 2.0[.x] kernel, a "recvfrom()" on
- * a SOCK_PACKET socket, with MSG_TRUNC set, will
- * return the number of bytes read, so if we pass
- * a length based on the snapshot length, it'll
- * return the number of bytes from the packet
- * copied to userland, not the actual length
- * of the packet.
- *
- * This means that, for example, the IP dissector
- * in tcpdump will get handed a packet length less
- * than the length in the IP header, and will
- * complain about "truncated-ip".
- *
- * So we don't bother trying to copy from the
- * kernel only the bytes in which we're interested,
- * but instead copy them all, just as the older
- * versions of libpcap for Linux did.
- *
- * Just one of many problems with packet capture
- * on 2.0[.x] kernels; you really want a 2.2[.x]
- * or later kernel if you want packet capture to
- * work well.
- */
- handle->md.readlen = handle->bufsize;
- } else {
- /*
- * This is a 2.2[.x] or later kernel (although
- * why we're using SOCK_PACKET on such a system
- * is unknown to me).
- *
- * We can safely pass "recvfrom()" a byte count
- * based on the snapshot length.
- */
- handle->md.readlen = handle->snapshot;
- }
return 1;
} while (0);
return ifr.ifr_hwaddr.sa_family;
}
-#ifdef HAVE_NETPACKET_PACKET_H
+#ifdef SO_ATTACH_FILTER
static int
fix_program(pcap_t *handle, struct sock_fprog *fcode)
{
* header.
*/
p->k -= SLL_HDR_LEN;
- } else if (p->k == 2) {
+ } else if (p->k == 14) {
/*
* It's the protocol field; map it to the special magic
* kernel offset for that field.
}
return 0;
}
+
+static int
+set_kernel_filter(pcap_t *handle, struct sock_fprog *fcode)
+{
+ int total_filter_on = 0;
+ int save_mode;
+ int ret;
+ int save_errno;
+
+ /*
+ * The socket filter code doesn't discard all packets queued
+ * up on the socket when the filter is changed; this means
+ * that packets that don't match the new filter may show up
+ * after the new filter is put onto the socket, if those
+ * packets haven't yet been read.
+ *
+ * This means, for example, that if you do a tcpdump capture
+ * with a filter, the first few packets in the capture might
+ * be packets that wouldn't have passed the filter.
+ *
+ * We therefore discard all packets queued up on the socket
+ * when setting a kernel filter. (This isn't an issue for
+ * userland filters, as the userland filtering is done after
+ * packets are queued up.)
+ *
+ * To flush those packets, we put the socket in read-only mode,
+ * and read packets from the socket until there are no more to
+ * read.
+ *
+ * In order to keep that from being an infinite loop - i.e.,
+ * to keep more packets from arriving while we're draining
+ * the queue - we put the "total filter", which is a filter
+ * that rejects all packets, onto the socket before draining
+ * the queue.
+ *
+ * This code deliberately ignores any errors, so that you may
+ * get bogus packets if an error occurs, rather than having
+ * the filtering done in userland even if it could have been
+ * done in the kernel.
+ */
+ if (setsockopt(handle->fd, SOL_SOCKET, SO_ATTACH_FILTER,
+ &total_fcode, sizeof(total_fcode)) == 0) {
+ char drain[1];
+
+ /*
+ * Note that we've put the total filter onto the socket.
+ */
+ total_filter_on = 1;
+
+ /*
+ * Save the socket's current mode, and put it in
+ * non-blocking mode; we drain it by reading packets
+ * until we get an error (which we assume is a
+ * "nothing more to be read" error).
+ */
+ save_mode = fcntl(handle->fd, F_GETFL, 0);
+ if (save_mode != -1 &&
+ fcntl(handle->fd, F_SETFL, save_mode | O_NONBLOCK) >= 0) {
+ while (recv(handle->fd, &drain, sizeof drain,
+ MSG_TRUNC) >= 0)
+ ;
+ fcntl(handle->fd, F_SETFL, save_mode);
+ }
+ }
+
+ /*
+ * Now attach the new filter.
+ */
+ ret = setsockopt(handle->fd, SOL_SOCKET, SO_ATTACH_FILTER,
+ fcode, sizeof(*fcode));
+ if (ret == -1 && total_filter_on) {
+ /*
+ * Well, we couldn't set that filter on the socket,
+ * but we could set the total filter on the socket.
+ *
+ * This could, for example, mean that the filter was
+ * too big to put into the kernel, so we'll have to
+ * filter in userland; in any case, we'll be doing
+ * filtering in userland, so we need to remove the
+ * total filter so we see packets.
+ */
+ save_errno = errno;
+
+ /*
+ * XXX - if this fails, we're really screwed;
+ * we have the total filter on the socket,
+ * and it won't come off. What do we do then?
+ */
+ reset_kernel_filter(handle);
+
+ errno = save_errno;
+ }
+ return ret;
+}
+
+static int
+reset_kernel_filter(pcap_t *handle)
+{
+ /* setsockopt() barfs unless it get a dummy parameter */
+ int dummy;
+
+ return setsockopt(handle->fd, SOL_SOCKET, SO_DETACH_FILTER,
+ &dummy, sizeof(dummy));
+}
#endif