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@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(a)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(a)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