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@xxxxxxx> + */ + + +#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@xxxxxxx> + */ + +#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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list