*/
#ifndef lint
static const char rcsid[] =
- "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.64 2001-08-24 09:27:14 guy Exp $ (LBL)";
+ "@(#) $Header: /tcpdump/master/libpcap/pcap-linux.c,v 1.70 2001-10-25 08:27:18 guy Exp $ (LBL)";
#endif
/*
/*
* Prototypes for internal functions
*/
-static int map_arphrd_to_dlt(pcap_t *, int);
+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 *);
#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
/*
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);
}
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()".
*/
* (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(pcap_t *handle, int arptype)
+static void map_arphrd_to_dlt(pcap_t *handle, int arptype)
{
switch (arptype) {
#define ARPHRD_ATM 19
#endif
case ARPHRD_ATM:
- handle->linktype = DLT_ATM_CLIP;
+ /*
+ * 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 */
break;
default:
- return -1;
+ handle->linktype = -1;
+ break;
}
- return 0;
}
/* ===== Functions to interface to the newer kernels ================== */
arptype = iface_get_arptype(sock_fd, device, ebuf);
if (arptype == -1)
break;
- if (map_arphrd_to_dlt(handle, arptype) == -1 ||
+ map_arphrd_to_dlt(handle, arptype);
+ if (handle->linktype == -1 ||
handle->linktype == DLT_LINUX_SLL ||
(handle->linktype == DLT_EN10MB &&
(strncmp("isdn", device, 4) == 0 ||
* 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;
* type that has only an Ethernet packet type as
* a link-layer header.
*/
- if (map_arphrd_to_dlt(handle, arptype) == -1) {
+ map_arphrd_to_dlt(handle, arptype);
+ if (handle->linktype == -1) {
snprintf(ebuf, PCAP_ERRBUF_SIZE,
"interface type of %s not supported", device);
break;
return ifr.ifr_hwaddr.sa_family;
}
-#ifdef HAVE_PF_PACKET_SOCKETS
+#ifdef SO_ATTACH_FILTER
static int
fix_program(pcap_t *handle, struct sock_fprog *fcode)
{
}
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