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