---
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@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