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:
- Linux evdev capture support (draft patch) David Gibson (Dec 09)
- Re: Linux evdev capture support (draft patch) ronnie sahlberg (Dec 09)
- Re: Linux evdev capture support (draft patch) David Gibson (Dec 09)
- Re: Linux evdev capture support (draft patch) ronnie sahlberg (Dec 09)