tcpdump mailing list archives

Linux evdev capture support (draft patch)


From: David Gibson <david () gibson dropbear id au>
Date: Tue, 9 Dec 2008 19:40:29 +1100

I've implemented a first cut at adding support to libpcap to capture
from the Linux /dev/input/event* (evdev) devices.  Draft patch is
included below.

However, I've realised there's a problem.  Since it's an internal-only
protocol, the evdev devices return packets which are in native-endian
format.  Obviously that's problematic once the packets go into pcap
files which could be moved to other machines.

As I see it, I have 3 options here:
        1. Ask for a second DLT_ value, and use different DLT values
for the capture depending on the endianness of the capturing machine.
        2. Covert the packets at capture time to either little or
big-endian (arbitrary choice).
        3. Capture and record in native-endian, and rely on programs
reading the pcap file to deduce the endianness from other data.  This
will generally be possible in practice because the 16-bit 'type' field
has no assigned values above 0xff.

I'm inclined towards option (2), but I don't know if there's a
pre-existing libpcap rule of thumb about this sort of thing.

Index: libpcap/Makefile.in
===================================================================
--- libpcap.orig/Makefile.in    2008-11-22 00:31:34.000000000 +1100
+++ libpcap/Makefile.in 2008-11-22 00:32:08.000000000 +1100
@@ -77,7 +77,7 @@ YACC = @V_YACC@
        @rm -f $@
        $(CC) $(CFLAGS) -c $(srcdir)/$*.c
 
-PSRC = pcap-@V_PCAP@.c @USB_SRC@ @BT_SRC@
+PSRC = pcap-@V_PCAP@.c @USB_SRC@ @BT_SRC@ @EVDEV_SRC@
 FSRC =  fad-@V_FINDALLDEVS@.c
 SSRC =  @SSRC@
 CSRC = pcap.c inet.c gencode.c optimize.c nametoaddr.c \
Index: libpcap/configure.in
===================================================================
--- libpcap.orig/configure.in   2008-11-22 00:32:01.000000000 +1100
+++ libpcap/configure.in        2008-11-22 00:32:08.000000000 +1100
@@ -1072,6 +1072,26 @@ if test "x$enable_bluetooth" != "xno" ; 
        AC_SUBST(BT_SRC)
 fi
 
+dnl check for Linux evdev sniffing support
+AC_MSG_CHECKING(for Linux evdev sniffing support)
+case "$host_os" in
+linux*)
+       AC_CHECK_HEADER(linux/input.h,
+         [
+           AC_DEFINE(PCAP_SUPPORT_EVDEV, 1, [target host supports evdev sniffing])
+           EVDEV_SRC=pcap-evdev-linux.c
+           AC_MSG_RESULT(yes)
+         ],
+         AC_MSG_NOTICE(Need linux/input.h to support evdev sniffing)
+       )
+       ;;
+*)
+       AC_MSG_NOTICE(no Linux evdev sniffing support)
+       ;;
+esac
+AC_SUBST(PCAP_SUPPORT_EVDEV)
+AC_SUBST(EVDEV_SRC)
+
 AC_PROG_INSTALL
 
 AC_CONFIG_HEADER(config.h)
Index: libpcap/pcap-evdev-linux.c
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ libpcap/pcap-evdev-linux.c  2008-11-22 00:32:08.000000000 +1100
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2008 David Gibson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Sniffing support for the Linux evdev interface.
+ * By David Gibson <david () gibson dropbear id au>
+ *
+ */
+#ifndef lint
+static const char rcsid[] _U_ =
+    "@(#) $Header$ (LBL)";
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "pcap-int.h"
+#include "pcap-evdev-linux.h"
+
+#ifdef NEED_STRERROR_H
+#include "strerror.h"
+#endif
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <dirent.h>
+#include <byteswap.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <linux/input.h>
+
+#define EVDEV_IFACE            "event"
+#define EVDEV_DEV_DIR          "/dev/input"
+#define EVDEV_KNOWN_VERSION    0x010000
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define htols(s) s
+#define htoll(l) l
+#define htol64(ll) ll
+#else
+#define htols(s) bswap_16(s)
+#define htoll(l) bswap_32(l)
+#define htol64(ll) bswap_64(ll)
+#endif
+
+static int evdev_dev_add(pcap_if_t** alldevsp, const char *dev_name, char *err_str)
+{
+       char dev_descr[30];
+
+       snprintf(dev_descr, 30, "input layer evdev device %s", dev_name);
+
+       if (pcap_add_if(alldevsp, dev_name, 0, dev_descr, err_str) < 0)
+               return -1;
+       return 0;
+}
+
+int evdev_platform_finddevs(pcap_if_t **alldevsp, char *err_str)
+{
+       struct dirent *data;
+       int ret = 0;
+       DIR *dir;
+
+       /* scan udev directory */
+       dir = opendir(EVDEV_DEV_DIR);
+       if (!dir)
+               return 0;
+       while ((ret == 0) && ((data = readdir(dir)) != 0)) {
+               int n;
+               char *name = data->d_name;
+               int len = strlen(name);
+
+               /* Check if this is an event device */
+               if (strncmp(name, EVDEV_IFACE, strlen(EVDEV_IFACE)) != 0)
+                       continue;
+
+               ret = evdev_dev_add(alldevsp, name, err_str);
+       }
+
+       closedir(dir);
+       return ret;
+}
+
+static int evdev_inject_linux(pcap_t *handle, const void *buf, size_t size)
+{
+       snprintf(handle->errbuf, PCAP_ERRBUF_SIZE, "inject not supported on "
+                "evdev devices");
+       return (-1);
+}
+
+static int evdev_setfilter_linux(pcap_t *p, struct bpf_program *fp)
+{
+       return 0;
+}
+
+static int evdev_setdirection_linux(pcap_t *p, pcap_direction_t d)
+{
+       p->direction = d;
+       return 0;
+}
+
+static int evdev_stats_linux(pcap_t *handle, struct pcap_stat *stats)
+{
+       stats->ps_recv = handle->md.packets_read;
+       stats->ps_ifdrop = 0;
+       return 0;
+}
+
+static int evdev_read_linux(pcap_t *handle, int max_packets,
+                           pcap_handler callback, u_char *user)
+{
+       int ret;
+       struct pcap_pkthdr pkth;
+       struct input_event *ie = (struct input_event *)handle->buffer;
+
+       ret = read(handle->fd, handle->buffer, sizeof(struct input_event));
+       if (ret < 0) {
+               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                        "Can't read from fd %d: %s", handle->fd, strerror(errno));
+               return -1;
+       }
+       if (ret < sizeof(struct input_event)) {
+               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                        "Short read from fd %d: %d of %d bytes",
+                        handle->fd, ret, sizeof(struct input_event));
+               return -1;
+       }
+
+       pkth.caplen = pkth.len = sizeof(struct input_event);
+       if (handle->snapshot < pkth.caplen)
+               pkth.caplen = handle->snapshot;
+       pkth.ts = ie->time;
+
+       handle->md.packets_read++;
+       callback(user, &pkth, handle->buffer);
+       return 1;
+}
+
+static int evdev_activate(pcap_t *handle)
+{
+       char evdev_path[PATH_MAX];
+       int evdev_version;
+       int err;
+
+       /* Initialize some components of the pcap structure. */
+       handle->bufsize = sizeof(struct input_event);
+       handle->offset = 0;
+       handle->linktype = DLT_LINUX_EVDEV;
+
+       handle->inject_op = evdev_inject_linux;
+       handle->setfilter_op = evdev_setfilter_linux;
+       handle->setdirection_op = evdev_setdirection_linux;
+       handle->set_datalink_op = NULL; /* can't change data link type */
+       handle->getnonblock_op = pcap_getnonblock_fd;
+       handle->setnonblock_op = pcap_setnonblock_fd;
+
+       /* get index from device name */
+       if (sscanf(handle->opt.source, EVDEV_IFACE"%d", &handle->md.ifindex) != 1) {
+               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                       "Can't get USB bus index from %s", handle->opt.source);
+               return PCAP_ERROR;
+       }
+
+       snprintf(evdev_path, PATH_MAX, EVDEV_DEV_DIR "/%s", handle->opt.source);
+       handle->fd = open(evdev_path, O_RDONLY, 0);
+       if (handle->fd < 0) {
+               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                        "Can't open evdev device %s: %s", evdev_path,
+                        strerror(errno));
+               return PCAP_ERROR;
+       }
+
+       err = ioctl(handle->fd, EVIOCGVERSION, &evdev_version);
+       if (err < 0) {
+               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                        "EVIOCGVERSION failed (%s), looks like %s isn't an evdev",
+                        strerror(errno), evdev_path);
+               return PCAP_ERROR;
+       }
+
+       if (evdev_version != EVDEV_KNOWN_VERSION) {
+               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                        "Don't understand evdev protocol version 0x%06x",
+                        evdev_version);
+               return PCAP_ERROR;
+       }
+
+       if (handle->opt.rfmon)
+               /* Monitor mode doesn't apply to evdev devices. */
+               return PCAP_ERROR_RFMON_NOTSUP;
+
+       handle->stats_op = evdev_stats_linux;
+       handle->read_op = evdev_read_linux;
+
+       /*
+        * "handle->fd" is a real file, so "select()" and "poll()"
+        * work on it.
+        */
+       handle->selectable_fd = handle->fd;
+
+       handle->buffer = malloc(handle->bufsize);
+       if (!handle->buffer) {
+               snprintf(handle->errbuf, PCAP_ERRBUF_SIZE,
+                        "malloc: %s", pcap_strerror(errno));
+               return PCAP_ERROR;
+       }
+       return 0;
+}
+
+pcap_t *evdev_create(const char *device, char *ebuf)
+{
+       pcap_t *p;
+
+       p = pcap_create_common(device, ebuf);
+       if (p == NULL)
+               return (NULL);
+
+       p->activate_op = evdev_activate;
+       return (p);
+}
Index: libpcap/pcap-linux.c
===================================================================
--- libpcap.orig/pcap-linux.c   2008-11-22 00:32:01.000000000 +1100
+++ libpcap/pcap-linux.c        2008-11-22 00:32:08.000000000 +1100
@@ -126,6 +126,10 @@ static const char rcsid[] _U_ =
 #include "pcap-bt-linux.h"
 #endif
 
+#ifdef PCAP_SUPPORT_EVDEV
+#include "pcap-evdev-linux.h"
+#endif
+
 /*
  * If PF_PACKET is defined, we can use {SOCK_RAW,SOCK_DGRAM}/PF_PACKET
  * sockets rather than SOCK_PACKET sockets.
@@ -315,6 +319,12 @@ pcap_create(const char *device, char *eb
        }
 #endif /* HAVE_SEPTEL_API */
 
+#ifdef PCAP_SUPPORT_EVDEV
+       if (strstr(device, "event")) {
+               return evdev_create(device, ebuf);
+       }
+#endif
+
 #ifdef PCAP_SUPPORT_BT
        if (strstr(device, "bluetooth")) {
                return bt_create(device, ebuf);
@@ -1156,6 +1166,11 @@ pcap_platform_finddevs(pcap_if_t **allde
                return (-1);
 #endif /* HAVE_SEPTEL_API */
 
+#ifdef PCAP_SUPPORT_EVDEV
+       if (evdev_platform_finddevs(alldevsp, errbuf) < 0)
+               return (-1);
+#endif
+
 #ifdef PCAP_SUPPORT_BT
        if (bt_platform_finddevs(alldevsp, errbuf) < 0)
                return (-1);
Index: libpcap/pcap-evdev-linux.h
===================================================================
--- /dev/null   1970-01-01 00:00:00.000000000 +0000
+++ libpcap/pcap-evdev-linux.h  2008-11-22 00:32:08.000000000 +1100
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2008 David Gibson.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Sniffing support for the Linux evdev interface.
+ * By David Gibson <david () gibson dropbear id au>
+ *
+ * @(#) $Header$ (LBL)
+ */
+
+/*
+ * Prototypes for evdev-related functions
+ */
+int evdev_platform_finddevs(pcap_if_t **alldevsp, char *err_str);
+pcap_t *evdev_create(const char *device, char *ebuf);


-- 
David Gibson                    | I'll have my music baroque, and my code
david AT gibson.dropbear.id.au  | minimalist, thank you.  NOT _the_ _other_
                                | _way_ _around_!
http://www.ozlabs.org/~dgibson

Attachment: signature.asc
Description: Digital signature


Current thread: