[libvirt] [PATCH] Proof of concept on dumping network traffic with libpcap

Hendrik Schwartke hendrik at os-t.de
Mon Jul 23 14:26:03 UTC 2012


---
 include/libvirt/libvirt.h.in |    2 ++
 src/driver.h                 |    9 +++++
 src/interface/netcf_driver.c |   80 ++++++++++++++++++++++++++++++++++++++++++
 src/libvirt.c                |   47 +++++++++++++++++++++++++
 src/libvirt_public.syms      |    5 +++
 src/remote/remote_driver.c   |    1 +
 src/remote/remote_protocol.x |   11 +++++-
 tools/virsh.c                |   79 +++++++++++++++++++++++++++++++++++++++++
 8 files changed, 233 insertions(+), 1 deletion(-)

diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in
index e34438c..e71e8a2 100644
--- a/include/libvirt/libvirt.h.in
+++ b/include/libvirt/libvirt.h.in
@@ -4153,4 +4153,6 @@ typedef virMemoryParameter *virMemoryParameterPtr;
 }
 #endif
 
+int virInterfaceDumpTraffic(virInterfacePtr iface, virStreamPtr st, unsigned int flags);
+
 #endif /* __VIR_VIRLIB_H__ */
diff --git a/src/driver.h b/src/driver.h
index b3c1740..d43c67c 100644
--- a/src/driver.h
+++ b/src/driver.h
@@ -1178,6 +1178,14 @@ typedef int
         (*virDrvInterfaceChangeRollback)(virConnectPtr conn,
                                          unsigned int flags);
 
+typedef int
+        (*virDrvInterfaceDumpTraffic)   (virInterfacePtr iface,
+                                         virStreamPtr st,
+                                         const char *filter,
+                                         int promisc,
+                                         unsigned int snaplen,
+                                         unsigned int flags);
+
 typedef struct _virInterfaceDriver virInterfaceDriver;
 typedef virInterfaceDriver *virInterfaceDriverPtr;
 
@@ -1210,6 +1218,7 @@ struct _virInterfaceDriver {
     virDrvInterfaceChangeBegin       interfaceChangeBegin;
     virDrvInterfaceChangeCommit      interfaceChangeCommit;
     virDrvInterfaceChangeRollback    interfaceChangeRollback;
+    virDrvInterfaceDumpTraffic       interfaceDumpTraffic;
 };
 
 
diff --git a/src/interface/netcf_driver.c b/src/interface/netcf_driver.c
index 45e6442..0d19e49 100644
--- a/src/interface/netcf_driver.c
+++ b/src/interface/netcf_driver.c
@@ -24,12 +24,15 @@
 #include <config.h>
 
 #include <netcf.h>
+#include <fcntl.h>
+#include <pcap.h>
 
 #include "virterror_internal.h"
 #include "datatypes.h"
 #include "netcf_driver.h"
 #include "interface_conf.h"
 #include "memory.h"
+#include "fdstream.h"
 
 #define VIR_FROM_THIS VIR_FROM_INTERFACE
 
@@ -37,6 +40,8 @@
     virReportErrorHelper(VIR_FROM_THIS, code, __FILE__,               \
                          __FUNCTION__, __LINE__, __VA_ARGS__)
 
+#define PCAP_DEFAULT_SNAPLEN 65535
+
 /* Main driver state */
 struct interface_driver
 {
@@ -44,6 +49,12 @@ struct interface_driver
     struct netcf *netcf;
 };
 
+struct pcapInfo {
+    char *iface;
+    virStreamPtr stream;
+    char *filter;
+    unsigned int snaplen;
+};
 
 static void interfaceDriverLock(struct interface_driver *driver)
 {
@@ -567,6 +578,74 @@ cleanup:
     return ret;
 }
 
+
+static void pcapCallback(u_char *opaque, const struct pcap_pkthdr *hdr, const u_char *p) {
+    pcap_dump((pcap_dumper_t *)opaque, hdr, p);
+    /* TODO: retval */
+    pcap_dump_flush((pcap_dumper_t *)opaque);
+}
+
+static void pcapSniffThread(void *opaque) {
+    struct pcapInfo *pcap_info = opaque;
+    char errbuf[PCAP_ERRBUF_SIZE];
+    pcap_t *handle;
+    pcap_dumper_t *dumper;
+
+    handle = pcap_open_live(pcap_info->iface, pcap_info->snaplen, -1, 0, errbuf);
+    if (handle == NULL) {
+        /* TODO: error reporting */
+        return;
+    }
+    /* TODO: compile and set filter */
+
+    /*
+     * TODO: remove named pipe hack
+     * pcap_dump_open can only write to stdout or to a named file, so
+     * a named pipe is used for testing purposes.
+     * This will likely replaced by fork() and a redirect of stdout.
+    */
+    if((dumper = pcap_dump_open(handle, "/tmp/pcapfifo")) == NULL) {
+        /* TODO: error reporting */
+        return;
+    }
+    
+    /* TODO: retval */
+    pcap_loop(handle, -1, pcapCallback, (u_char*)dumper);
+
+    /* TODO: close dumper */
+
+    /* TODO: free pcap_info */
+}
+
+static int interfaceDumpTraffic(virInterfacePtr ifinfo, virStreamPtr st,
+                                const char *filter, int promisc,
+                                unsigned int snaplen, unsigned int flags)
+{
+    virThread thread;
+    struct pcapInfo *pcap_info;
+
+    /* TODO: retval */
+    VIR_ALLOC(pcap_info);
+    pcap_info->iface = strdup(ifinfo->name);
+    pcap_info->stream = st;
+    pcap_info->filter = filter ? strdup(filter) : NULL;
+    pcap_info->snaplen = snaplen ? snaplen : PCAP_DEFAULT_SNAPLEN;
+
+    if (virThreadCreate(&thread, false, pcapSniffThread,
+                        pcap_info) != 0) {
+        /* TODO: err reporting */
+        return -1;
+    }
+
+    /* TODO: remove named pipe hack */
+    if (virFDStreamOpenFile(pcap_info->stream, "/tmp/pcapfifo", 0, 0, O_RDONLY) < 0) {
+        /* TODO: err reporting */
+        return -1;
+    }
+
+    return 0;
+}
+
 #ifdef HAVE_NETCF_TRANSACTIONS
 static int interfaceChangeBegin(virConnectPtr conn, unsigned int flags)
 {
@@ -654,6 +733,7 @@ static virInterfaceDriver interfaceDriver = {
     .interfaceCreate = interfaceCreate, /* 0.7.0 */
     .interfaceDestroy = interfaceDestroy, /* 0.7.0 */
     .interfaceIsActive = interfaceIsActive, /* 0.7.3 */
+    .interfaceDumpTraffic = interfaceDumpTraffic, /* 0.10.0 */
 #ifdef HAVE_NETCF_TRANSACTIONS
     .interfaceChangeBegin = interfaceChangeBegin, /* 0.9.2 */
     .interfaceChangeCommit = interfaceChangeCommit, /* 0.9.2 */
diff --git a/src/libvirt.c b/src/libvirt.c
index df78e8a..caeca32 100644
--- a/src/libvirt.c
+++ b/src/libvirt.c
@@ -20,6 +20,7 @@
 #include <sys/wait.h>
 #include <time.h>
 #include <gcrypt.h>
+#include <fcntl.h>
 
 #include <libxml/parser.h>
 #include <libxml/xpath.h>
@@ -44,6 +45,7 @@
 #include "virnodesuspend.h"
 #include "virrandom.h"
 #include "viruri.h"
+#include "fdstream.h"
 
 #ifdef WITH_TEST
 # include "test/test_driver.h"
@@ -3051,6 +3053,51 @@ error:
 }
 
 /**
+ * virInterfaceDumpTraffic:
+ * @iface: a interface object
+ * @st: stream to use as output
+ * @flags: TODO
+ *
+ * TODO
+ *
+ * Returns TODO
+*/
+int virInterfaceDumpTraffic(virInterfacePtr iface, virStreamPtr st,
+                            const char *filter, int promisc,
+                            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->interfaceDumpTraffic) {
+        if(conn->interfaceDriver->interfaceDumpTraffic(iface, st,
+                                                       filter, promisc,
+                                                       snaplen, flags))
+            goto error;
+        return 0;
+    }
+
+    virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__);
+
+error:
+    virDispatchError(iface->conn);
+    return -1;
+}
+
+/**
  * virDomainScreenshot:
  * @domain: a domain object
  * @stream: stream to use as output
diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms
index 2913a81..05b77d2 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:
+        virInterfaceDumpTraffic;
+} 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..31e6b9b 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 */
+    .interfaceDumpTraffic = remoteInterfaceDumpTraffic, /* 0.10.0 */
 };
 
 static virStorageDriver storage_driver = {
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index 8f1d9b5..3aaef0f 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -2366,6 +2366,14 @@ struct remote_domain_open_console_args {
     unsigned int flags;
 };
 
+struct remote_interface_dump_traffic_args {
+    remote_nonnull_interface iface;
+    remote_string filter;
+    int promisc;
+    unsigned int snaplen;
+    unsigned int flags;
+};
+
 struct remote_storage_vol_upload_args {
     remote_nonnull_storage_vol vol;
     unsigned hyper offset;
@@ -2844,7 +2852,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_DUMP_TRAFFIC = 277 /* autogen autogen | readstream at 1 */
 
     /*
      * Notice how the entries are grouped in sets of 10 ?
diff --git a/tools/virsh.c b/tools/virsh.c
index 1e00049..de17c60 100644
--- a/tools/virsh.c
+++ b/tools/virsh.c
@@ -9253,6 +9253,83 @@ cmdInterfaceName(vshControl *ctl, const vshCmd *cmd)
 }
 
 /*
+ * "iface-dumptraffic" command
+ */
+static const vshCmdInfo info_interface_dumptraffic[] = {
+    {"help", N_("dumps traffic on an interface")},
+    {"desc", ""},
+    {NULL, NULL}
+};
+
+static const vshCmdOptDef opts_interface_dumptraffic[] = {
+    {"interface", VSH_OT_DATA, VSH_OFLAG_REQ, N_("interface name")},
+    {"filter", VSH_OT_DATA, 0, N_("packet filter")},
+    {"file", VSH_OT_DATA, 0, N_("file to store packets. If ommited then stdout is used.")},
+    {"snaplen", VSH_OT_INT, 0, N_("capture snaplen")},
+    {"promisc", VSH_OT_BOOL, 0, N_("put the interface into promiscuous mode")},
+    {NULL, 0, 0, NULL}
+};
+
+static bool
+cmdInterfaceDumpTraffic(vshControl *ctl, const vshCmd *cmd)
+{
+    virInterfacePtr iface;
+    const char *iface_name=NULL;
+    virStreamPtr stream = NULL;
+    int fd = STDOUT_FILENO;
+    const char* file = NULL;
+    const char* filter = NULL;
+    bool promisc;
+    unsigned int snaplen=0;
+
+    if (!vshConnectionUsability(ctl, ctl->conn))
+        return false;
+    if (vshCommandOptString(cmd, "filter", &filter) < 0)
+        return false;
+    if (vshCommandOptString(cmd, "file", &file) < 0)
+        return false;
+    if (vshCommandOptUInt(cmd, "snaplen", &snaplen) < 0)
+        return false;
+    promisc = vshCommandOptBool(cmd, "promisc");
+
+    if (!(iface = vshCommandOptInterfaceBy(ctl, cmd, NULL, NULL,
+                                           VSH_BYNAME)))
+        return false;
+    iface_name = virInterfaceGetName(iface);
+
+    stream = virStreamNew(ctl->conn, 0);
+
+    if(virInterfaceDumpTraffic(iface, stream, filter, promisc, snaplen, 0)) {
+        vshError(ctl, _("error virInterfaceDumpTraffic %s"), iface_name);
+        goto cleanup;
+    }
+
+    if (file && (fd = open(file, O_WRONLY|O_CREAT|O_EXCL, 0660)) < 0) {
+        if (errno != EEXIST ||
+            (fd = open(file, O_WRONLY|O_TRUNC, 0660)) < 0) {
+            vshError(ctl, _("cannot create file %s"), file);
+            goto cleanup;
+        }
+    }
+
+    if (virStreamRecvAll(stream, vshStreamSink, &fd) < 0) {
+        vshError(ctl, _("could not receive data from interface %s"), iface_name);
+        goto cleanup;
+    }
+
+    if (virStreamFinish(stream) < 0) {
+        vshError(ctl, _("cannot close stream on interface %s"), iface_name);
+        goto cleanup;
+    }
+
+cleanup:
+    virStreamFree(stream);
+    virInterfaceFree(iface);
+
+    return true;
+}
+
+/*
  * "iface-mac" command
  */
 static const vshCmdInfo info_interface_mac[] = {
@@ -18352,6 +18429,8 @@ static const vshCmdDef ifaceCmds[] = {
      info_interface_define, 0},
     {"iface-destroy", cmdInterfaceDestroy, opts_interface_destroy,
      info_interface_destroy, 0},
+    {"iface-dumptraffic", cmdInterfaceDumpTraffic,
+     opts_interface_dumptraffic, info_interface_dumptraffic, 0},
     {"iface-dumpxml", cmdInterfaceDumpXML, opts_interface_dumpxml,
      info_interface_dumpxml, 0},
     {"iface-edit", cmdInterfaceEdit, opts_interface_edit,
-- 
1.7.9.5




More information about the libvir-list mailing list