[libvirt] [PATCH v3 1/2] Add virNetDevCapture

Hendrik Schwartke hendrik at os-t.de
Thu Jul 26 11:15:09 UTC 2012


Added function virNetDevCapture and util/virnetdevcapture.{c|h}.
virNetDevCapture offers the possibility to sniff network traffic and
dump it to a stream ussing the default pcap format.
---
 include/libvirt/libvirt.h.in |   13 +++
 src/Makefile.am              |    1 +
 src/driver.h                 |    8 ++
 src/interface/netcf_driver.c |   15 ++-
 src/libvirt.c                |   51 +++++++++
 src/libvirt_private.syms     |    4 +
 src/libvirt_public.syms      |    5 +
 src/remote/remote_driver.c   |    1 +
 src/remote/remote_protocol.x |   10 +-
 src/util/virnetdevcapture.c  |  251 ++++++++++++++++++++++++++++++++++++++++++
 src/util/virnetdevcapture.h  |   32 ++++++
 11 files changed, 389 insertions(+), 2 deletions(-)
 create mode 100644 src/util/virnetdevcapture.c
 create mode 100644 src/util/virnetdevcapture.h

diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index e34438c..03b885c 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -2319,6 +2319,19 @@ int                     virInterfaceChangeCommit  (virConnectPtr conn,
                                                    unsigned int flags);
 int                     virInterfaceChangeRollback(virConnectPtr conn,
                                                    unsigned int flags);
+int                     virInterfaceCapture       (virInterfacePtr iface,
+                                                   virStreamPtr st,
+                                                   const char *filter,
+                                                   unsigned int snaplen,
+                                                   unsigned int flags);
+
+/**
+ * VIR_NET_DEV_CAPTURE_PROMISC:
+ *
+ * Macro for capturing paickets in promiscuous mode. Even if not set,
+ * the interface could be in promiscuous mode for some other reason.
+ */
+#define VIR_NET_DEV_CAPTURE_PROMISC 1
 
 /**
  * virStoragePool:
diff --git a/src/Makefile.am b/src/Makefile.am
index bfe74d3..be3d075 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -100,6 +100,7 @@ UTIL_SOURCES =							\
 		util/virnetdevbandwidth.h util/virnetdevbandwidth.c \
 		util/virnetdevbridge.h util/virnetdevbridge.c	\
 		util/virnetdevmacvlan.c util/virnetdevmacvlan.h	\
+		util/virnetdevcapture.c util/virnetdevcapture.h	\
 		util/virnetdevopenvswitch.h util/virnetdevopenvswitch.c \
 		util/virnetdevtap.h util/virnetdevtap.c		\
 		util/virnetdevveth.h util/virnetdevveth.c \
diff --git a/src/driver.h b/src/driver.h
index b3c1740..64d32b3 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -1178,6 +1178,13 @@ typedef int
         (*virDrvInterfaceChangeRollback)(virConnectPtr conn,
                                          unsigned int flags);
 
+typedef int
+        (*virDrvInterfaceCapture)       (virInterfacePtr iface,
+                                         virStreamPtr st,
+                                         const char *filter,
+                                         unsigned int snaplen,
+                                         unsigned int flags);
+
 typedef struct _virInterfaceDriver virInterfaceDriver;
 typedef virInterfaceDriver *virInterfaceDriverPtr;
 
@@ -1210,6 +1217,7 @@ struct _virInterfaceDriver {
     virDrvInterfaceChangeBegin       interfaceChangeBegin;
     virDrvInterfaceChangeCommit      interfaceChangeCommit;
     virDrvInterfaceChangeRollback    interfaceChangeRollback;
+    virDrvInterfaceCapture           interfaceCapture;
 };
 
 
diff --git a/src/interface/netcf_driver.c b/src/interface/netcf_driver.c
index 45e6442..f2236a9 100644
--- a/src/interface/netcf_driver.c
+++ b/src/interface/netcf_driver.c
@@ -30,6 +30,7 @@
 #include "netcf_driver.h"
 #include "interface_conf.h"
 #include "memory.h"
+#include "virnetdevcapture.h"
 
 #define VIR_FROM_THIS VIR_FROM_INTERFACE
 
@@ -44,7 +45,6 @@ struct interface_driver
     struct netcf *netcf;
 };
 
-
 static void interfaceDriverLock(struct interface_driver *driver)
 {
     virMutexLock(&driver->lock);
@@ -638,6 +638,18 @@ static int interfaceChangeRollback(virConnectPtr conn, unsigned int flags)
 }
 #endif /* HAVE_NETCF_TRANSACTIONS */
 
+static int interfaceCapture(virInterfacePtr iface, virStreamPtr st,
+                            const char *filter, unsigned int snaplen,
+                            unsigned int flags ) {
+    int res = virNetDevCapture(iface->name, st, filter, snaplen, flags);
+    if(res < 0) {
+        interfaceReportError(res, _("unable to start capture on interface %s"
+                             " with filter '%s'"), iface->name, filter);
+        return -1;
+    }
+    return 0;
+}
+
 static virInterfaceDriver interfaceDriver = {
     "Interface",
     .open = interfaceOpenInterface, /* 0.7.0 */
@@ -659,6 +671,7 @@ static virInterfaceDriver interfaceDriver = {
     .interfaceChangeCommit = interfaceChangeCommit, /* 0.9.2 */
     .interfaceChangeRollback = interfaceChangeRollback, /* 0.9.2 */
 #endif /* HAVE_NETCF_TRANSACTIONS */
+    .interfaceCapture = interfaceCapture, /* 0.10.0 */
 };
 
 int interfaceRegister(void) {
diff --git a/src/libvirt.c b/src/libvirt.c
index df78e8a..1a24007 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -11184,6 +11184,57 @@ error:
     return -1;
 }
 
+/**
+ * virInterfaceCapture:
+ * @iface: the interface object
+ * @st: stream to use as output
+ * @filter: packet filter in pcap format
+ * @snaplen: capture snaplen. If zero then capture PCAP_iDEFAULT_SNAPLEN bytes
+ *           per paket, which means in nearly any case capturing the whole
+ *           packet.
+ * @flags: extra flags; if VIR_NET_DEV_CAPTURE_PROMISC is set packets will
+ *         be captured in promiscuous mode. Even if not set, the interface
+ *         could be in promiscuous mode for some other reason.
+ *
+ * virInterfaceCapture sniffs packets on iface and writes them to st
+ * using the standard pcap format.
+ *
+ * Returns 0 in case of success, -1 in case of error.
+*/
+int virInterfaceCapture(virInterfacePtr iface, virStreamPtr st,
+                        const char *filter, unsigned int snaplen,
+                        unsigned int flags)
+{
+    virConnectPtr conn;
+    VIR_DEBUG("iface=%p, flags=%x", iface, flags);
+
+    virResetLastError();
+
+    if (!VIR_IS_CONNECTED_INTERFACE(iface)) {
+        virLibInterfaceError(VIR_ERR_INVALID_INTERFACE, __FUNCTION__);
+        virDispatchError(NULL);
+        return -1;
+    }
+
+    conn = iface->conn;
+    if (conn->flags & VIR_CONNECT_RO) {
+        virLibInterfaceError(VIR_ERR_OPERATION_DENIED, __FUNCTION__);
+        goto error;
+    }
+
+    if (conn->interfaceDriver && conn->interfaceDriver->interfaceCapture) {
+        if(conn->interfaceDriver->interfaceCapture(iface, st, filter, snaplen,
+                                                   flags))
+            goto error;
+        return 0;
+    }
+
+    virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__);
+
+error:
+    virDispatchError(iface->conn);
+    return -1;
+}
 
 /**
  * virStoragePoolGetConnect:
diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 03f7f3e..b57a7b4 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1342,6 +1342,10 @@ virNetDevBridgeSetSTP;
 virNetDevBridgeSetSTPDelay;
 
 
+# virnetdevcapture.h
+virNetDevCapture;
+
+
 # virnetdevmacvlan.h
 virNetDevMacVLanCreate;
 virNetDevMacVLanDelete;
diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms
index 2913a81..e8e04cb 100644
--- a/src/libvirt_public.syms
+++ b/src/libvirt_public.syms
@@ -544,4 +544,9 @@ LIBVIRT_0.9.13 {
         virDomainSnapshotRef;
 } LIBVIRT_0.9.11;
 
+LIBVIRT_0.10.0 {
+    global:
+        virInterfaceCapture;
+} LIBVIRT_0.9.13;
+
 # .... define new API here using predicted next version number ....
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index 3314f80..7ebf93e 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -5379,6 +5379,7 @@ static virInterfaceDriver interface_driver = {
     .interfaceChangeBegin = remoteInterfaceChangeBegin, /* 0.9.2 */
     .interfaceChangeCommit = remoteInterfaceChangeCommit, /* 0.9.2 */
     .interfaceChangeRollback = remoteInterfaceChangeRollback, /* 0.9.2 */
+    .interfaceCapture = remoteInterfaceCapture, /* 0.10.0 */
 };
 
 static virStorageDriver storage_driver = {
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index 8f1d9b5..7840ee2 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -2366,6 +2366,13 @@ struct remote_domain_open_console_args {
     unsigned int flags;
 };
 
+struct remote_interface_capture_args {
+    remote_nonnull_interface iface;
+    remote_string filter;
+    unsigned int snaplen;
+    unsigned int flags;
+};
+
 struct remote_storage_vol_upload_args {
     remote_nonnull_storage_vol vol;
     unsigned hyper offset;
@@ -2844,7 +2851,8 @@ enum remote_procedure {
     REMOTE_PROC_CONNECT_LIST_ALL_DOMAINS = 273, /* skipgen skipgen priority:high */
     REMOTE_PROC_DOMAIN_LIST_ALL_SNAPSHOTS = 274, /* skipgen skipgen priority:high */
     REMOTE_PROC_DOMAIN_SNAPSHOT_LIST_ALL_CHILDREN = 275, /* skipgen skipgen priority:high */
-    REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE = 276 /* autogen autogen */
+    REMOTE_PROC_DOMAIN_EVENT_BALLOON_CHANGE = 276, /* autogen autogen */
+    REMOTE_PROC_INTERFACE_CAPTURE = 277 /* autogen autogen | readstream at 1 */
 
     /*
      * Notice how the entries are grouped in sets of 10 ?
diff --git a/src/util/virnetdevcapture.c b/src/util/virnetdevcapture.c
new file mode 100644
index 0000000..181634b
--- /dev/null
+++ b/src/util/virnetdevcapture.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc.
+ * Copyright (C) 2012 Open Source Training Ralf Spenneberg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Authors:
+ *     Hendrik Schwartke <hendrik at os-t.de>
+ */
+
+
+#include <config.h>
+
+#include "virnetdevcapture.h"
+#include "virterror_internal.h"
+
+#define VIR_FROM_THIS VIR_FROM_NONE
+
+#ifdef HAVE_LIBPCAP
+
+# include <stdint.h>
+# include <pcap.h>
+
+# include "datatypes.h"
+# include "fdstream.h"
+# include "memory.h"
+# include "logging.h"
+
+# define PCAP_DEFAULT_SNAPLEN 65535
+/*
+ * The read timeout is the time in ms to collect packets
+ * before deliver them to the client. Not all platforms
+ * support a read timeout
+ */
+# define PCAP_READ_TIMEOUT 100
+
+# define PCAP_HEADER_MAGIC 0xa1b2c3d4
+# define PCAP_HEADER_MAJOR 2
+# define PCAP_HEADER_MINOR 4
+
+struct pcapInfo {
+    char *iface;
+    pcap_t *handle;
+    int fd[2];
+    char *filter;
+    int promisc;
+    unsigned int snaplen;
+};
+
+static void pcapInfoFree(struct pcapInfo *pcap_info) {
+    if(!pcap_info)
+        return;
+
+    if(pcap_info->fd[1] && close(pcap_info->fd[1])) {
+        char errbuf[1024];
+        virReportSystemError(errno, _("unable to close file handler: %s"),
+                             virStrerror(errno, errbuf, sizeof(errbuf)));
+    }
+    VIR_FREE(pcap_info->iface);
+    VIR_FREE(pcap_info->filter);
+    VIR_FREE(pcap_info);
+}
+
+/*
+ * file format is documented at:
+ * http://wiki.wireshark.org/Development/LibpcapFileFormat
+ */
+static int pcapWriteFileHeader(int fd, unsigned int linktype,
+                               unsigned int snaplen) {
+        struct pcap_file_header hdr;
+
+        hdr.magic = PCAP_HEADER_MAGIC;
+        hdr.version_major = PCAP_HEADER_MAJOR;
+        hdr.version_minor = PCAP_HEADER_MINOR;
+
+        /* timezone is GMT */
+        hdr.thiszone = 0;
+        hdr.sigfigs = 0;
+        hdr.linktype = linktype;
+        hdr.snaplen = snaplen;
+
+        if(safewrite(fd, &hdr, sizeof(hdr)) < 0) {
+            char errbuf[1024];
+            virReportSystemError(errno, _("unable to write file to stream: %s"),
+                                 virStrerror(errno, errbuf, sizeof(errbuf)));
+            return -1;
+        }
+        return 0;
+}
+
+/*
+ * file format is documented at:
+ * http://wiki.wireshark.org/Development/LibpcapFileFormat
+ */
+static void pcapCallback(u_char *opaque, const struct pcap_pkthdr *hdr,
+                         const u_char *p) {
+    struct pcapInfo *pcap_info = (struct pcapInfo*)opaque;
+    int fd=pcap_info->fd[1];
+    struct {
+        uint32_t sec;
+        uint32_t usec;
+        uint32_t caplen;
+        uint32_t len;
+    } header = {
+        hdr->ts.tv_sec,
+        hdr->ts.tv_usec,
+        hdr->caplen,
+        hdr->len
+    };
+    if(safewrite(fd, &header, sizeof(header)) < 0 ||
+       safewrite(fd, p, header.caplen) < 0) {
+        char errbuf[1024];
+        virReportSystemError(errno, _("unable to write packet to stream: %s"),
+                             virStrerror(errno, errbuf, sizeof(errbuf)));
+        /* stop the pcap loop and thus this thread */
+        pcap_breakloop(pcap_info->handle);
+    }
+}
+
+static void pcapThread(void *opaque) {
+    struct pcapInfo *pcap_info = opaque;
+    int res = pcap_loop(pcap_info->handle, -1, pcapCallback,
+                        (u_char*)(pcap_info));
+    /* check for -1 only, everything else is not an error */
+    if(res == -1) {
+            virReportSystemError(res,
+                                _("error while sniffinf packets "
+                                  "on interface '%s':  %s"),
+                                pcap_info->iface,
+                                pcap_geterr(pcap_info->handle));
+    }
+
+    pcapInfoFree(pcap_info);
+    return;
+}
+
+int virNetDevCapture(const char *iface, virStreamPtr st, const char *filter,
+                     unsigned int snaplen, unsigned int flags)
+{
+    struct pcapInfo *pcap_info = NULL;
+    pcap_t *handle;
+    virThread thread;
+    char errbuf[PCAP_ERRBUF_SIZE > 1024 ? PCAP_ERRBUF_SIZE : 1024];
+    int res;
+
+    res = VIR_ALLOC(pcap_info);
+    if(res) {
+        virReportSystemError(res, "%s", _("unable to allocate memory"));
+        goto error;
+    }
+    memset(pcap_info, 0, sizeof(struct pcapInfo));
+    
+    if(!(pcap_info->iface = strdup(iface)) ||
+       (filter && !(pcap_info->filter = strdup(filter)))) {
+        virReportSystemError(errno, "%s", _("unable to allocate memory"));
+        goto error;
+    }
+
+    res = pipe(pcap_info->fd);
+    if(res) {
+        virReportSystemError(errno, _("unable to create file handler: %s"),
+                             virStrerror(errno, errbuf, sizeof(errbuf)));
+        goto error;
+    }
+
+    res = virFDStreamOpen(st, pcap_info->fd[0]);
+    if(res < 0) {
+        virReportSystemError(res, "%s", _("unable to open file stream"));
+        goto error;
+    }
+
+    pcap_info->promisc = flags | VIR_NET_DEV_CAPTURE_PROMISC;
+    pcap_info->snaplen = snaplen ? snaplen : PCAP_DEFAULT_SNAPLEN;
+
+    handle = pcap_open_live(pcap_info->iface, pcap_info->snaplen,
+                            pcap_info->promisc, PCAP_READ_TIMEOUT, errbuf);
+    if (handle == NULL) {
+        virReportSystemError(-1, _("unable to open interface '%s': %s"),
+                             pcap_info->iface, errbuf);
+        goto error;
+    }
+    pcap_info->handle = handle;
+
+    if(pcap_info->filter) {
+        struct bpf_program fp;
+        bpf_u_int32 net, mask;
+
+        if(pcap_lookupnet(pcap_info->iface, &net, &mask, errbuf) < 0) {
+            VIR_WARN("couldn't determine netmask, so checking for"
+                     " broadcast addresses won't work.");
+            mask=0;
+        }
+
+        if(pcap_compile(handle, &fp, pcap_info->filter, 1, mask) == -1) {
+            virReportSystemError(res,
+                                _("unable to compile pcap filter '%s':  %s"),
+                                pcap_info->filter, pcap_geterr(handle));
+            goto error;
+        }
+
+        res = pcap_setfilter(handle, &fp);
+        pcap_freecode(&fp);
+        if(res == -1) {
+            virReportSystemError(res, _("unable to set pcap filter '%s' : %s"),
+                                 pcap_info->filter, pcap_geterr(handle));
+            goto error;
+        }
+    }
+    if(pcapWriteFileHeader(pcap_info->fd[1], pcap_datalink(handle),
+                           pcap_info->snaplen) < 0)
+        goto error;
+
+    res = virThreadCreate(&thread, false, pcapThread, pcap_info);
+    if(res != 0) {
+        virReportSystemError(res, "%s", _("unable to create thread"));
+        goto error;
+    }
+
+    return 0;
+
+error:
+    pcapInfoFree(pcap_info);
+    virStreamFinish(st);
+    return -1;
+}
+
+#else /* HAVE_LIBPCAP */
+
+int virNetDevCapture(const char *iface ATTRIBUTE_UNUSED,
+                     virStreamPtr st ATTRIBUTE_UNUSED,
+                     const char *filter ATTRIBUTE_UNUSED,
+                     unsigned int snaplen ATTRIBUTE_UNUSED,
+                     unsigned int flags ATTRIBUTE_UNUSED) {
+    virReportSystemError(VIR_ERR_NO_SUPPORT, "%s",
+                         _("capturing network traffic is not supported"));
+    return -1;
+}
+
+#endif /* HAVE_LIBPCAP */
diff --git a/src/util/virnetdevcapture.h b/src/util/virnetdevcapture.h
new file mode 100644
index 0000000..241db8e
--- /dev/null
+++ b/src/util/virnetdevcapture.h
@@ -0,0 +1,32 @@
+/*
+ * virnetdevcapture.h: network capture support
+ *
+ * Copyright (C) 2012 Red Hat, Inc.
+ * Copyright (C) 2012 Open Source Training Ralf Spenneberg
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ *
+ * Author: Hendrik Schwartke <hendrik at os-t.de>
+ */
+
+#ifndef __LIBVIRT_NETDEVCAPTURE_H__
+# define __LIBVIRT_NETDEVCAPTURE_H__
+
+#include "internal.h"
+
+int virNetDevCapture(const char *iface, virStreamPtr st, const char *filter,
+                     unsigned int snaplen, unsigned int flags);
+
+#endif /* __LIBVIRT_NETDEVCAPTURE_H__ */
-- 
1.7.9.5




More information about the libvir-list mailing list