[libvirt] [PATCH v3 0/2] Libvirt Wireshark dissector

From: "Yuto KAWAMURA(kawamuray)" <kawamuray.dadada@gmail.com> Changes from version2: * Remove moduleinfo.h * Stop accessing internal XDR struct and just use free() Introduce Wireshark dissector plugin which adds support to Wireshark for dissecting libvirt RPC protocol. This feature was presented by Michal Privoznik year before last[1]. But it did only support dissecting packet headers. This time I enhanced that dissector to support dissecting packet payload. Furthermore, I provide code generator of dissector. So you can get fresh build of dissector from libvirt RPC specification file at any version you like. [1] http://www.redhat.com/archives/libvir-list/2011-October/msg00301.html Yuto KAWAMURA(kawamuray) (2): Introduce Libvirt Wireshark dissector Add sample output of Wireshark dissector Makefile.am | 3 +- cfg.mk | 8 +- configure.ac | 72 +- tools/wireshark/Makefile.am | 29 + tools/wireshark/README.md | 31 + tools/wireshark/samples/libvirt-sample.pdml | 206 ++++++ tools/wireshark/src/.gitignore | 4 + tools/wireshark/src/Makefile.am | 42 ++ tools/wireshark/src/packet-libvirt.c | 512 ++++++++++++++ tools/wireshark/src/packet-libvirt.h | 128 ++++ tools/wireshark/util/genxdrstub.pl | 1009 +++++++++++++++++++++++++++ tools/wireshark/util/make-dissector-reg | 198 ++++++ 12 files changed, 2236 insertions(+), 6 deletions(-) create mode 100644 tools/wireshark/Makefile.am create mode 100644 tools/wireshark/README.md create mode 100644 tools/wireshark/samples/libvirt-sample.pdml create mode 100644 tools/wireshark/src/.gitignore create mode 100644 tools/wireshark/src/Makefile.am create mode 100644 tools/wireshark/src/packet-libvirt.c create mode 100644 tools/wireshark/src/packet-libvirt.h create mode 100755 tools/wireshark/util/genxdrstub.pl create mode 100755 tools/wireshark/util/make-dissector-reg -- 1.8.1.5

From: "Yuto KAWAMURA(kawamuray)" <kawamuray.dadada@gmail.com> Introduce Wireshark dissector plugin which adds support to Wireshark for dissecting libvirt RPC protocol. Added following files to build Wireshark dissector from libvirt source tree. * tools/wireshark/*: Source tree of Wireshark dissector plugin. Added followings to configure.ac or Makefile.am. configure.ac * --with-wireshark-dissector: Enable support for building Wireshark dissector. * --with-ws-plugindir: Specify wireshark plugin directory that dissector will installed. * Added tools/wireshark/{Makefile,src/Makefile} to AC_CONFIG_FILES. Makefile.am * Added tools/wireshark/ to SUBDIR. --- Makefile.am | 3 +- cfg.mk | 8 +- configure.ac | 72 ++- tools/wireshark/Makefile.am | 29 + tools/wireshark/README.md | 31 + tools/wireshark/src/.gitignore | 4 + tools/wireshark/src/Makefile.am | 42 ++ tools/wireshark/src/packet-libvirt.c | 512 ++++++++++++++++ tools/wireshark/src/packet-libvirt.h | 128 ++++ tools/wireshark/util/genxdrstub.pl | 1009 +++++++++++++++++++++++++++++++ tools/wireshark/util/make-dissector-reg | 198 ++++++ 11 files changed, 2030 insertions(+), 6 deletions(-) create mode 100644 tools/wireshark/Makefile.am create mode 100644 tools/wireshark/README.md create mode 100644 tools/wireshark/src/.gitignore create mode 100644 tools/wireshark/src/Makefile.am create mode 100644 tools/wireshark/src/packet-libvirt.c create mode 100644 tools/wireshark/src/packet-libvirt.h create mode 100755 tools/wireshark/util/genxdrstub.pl create mode 100755 tools/wireshark/util/make-dissector-reg diff --git a/Makefile.am b/Makefile.am index 66cb677..eedae46 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,7 +22,8 @@ GENHTML = genhtml SUBDIRS = . gnulib/lib include src daemon tools docs gnulib/tests \ python tests po examples/domain-events/events-c examples/hellolibvirt \ examples/dominfo examples/domsuspend examples/python examples/apparmor \ - examples/xml/nwfilter examples/openauth examples/systemtap + examples/xml/nwfilter examples/openauth examples/systemtap \ + tools/wireshark ACLOCAL_AMFLAGS = -I m4 -I gnulib/m4 diff --git a/cfg.mk b/cfg.mk index dad8a90..2103651 100644 --- a/cfg.mk +++ b/cfg.mk @@ -969,10 +969,10 @@ exclude_file_name_regexp--sc_prohibit_newline_at_end_of_diagnostic = \ ^src/rpc/gendispatch\.pl$$ exclude_file_name_regexp--sc_prohibit_nonreentrant = \ - ^((po|tests)/|docs/.*(py|html\.in)|run.in$$) + ^((po|tests)/|docs/.*(py|html\.in)|run.in$$|tools/wireshark/util/genxdrstub\.pl$$) exclude_file_name_regexp--sc_prohibit_raw_allocation = \ - ^(docs/hacking\.html\.in)|(src/util/viralloc\.[ch]|examples/.*|tests/securityselinuxhelper\.c|tests/vircgroupmock\.c)$$ + ^(docs/hacking\.html\.in)|(src/util/viralloc\.[ch]|examples/.*|tests/securityselinuxhelper\.c|tests/vircgroupmock\.c|tools/wireshark/src/packet-libvirt.c)$$ exclude_file_name_regexp--sc_prohibit_readlink = \ ^src/(util/virutil|lxc/lxc_container)\.c$$ @@ -980,7 +980,7 @@ exclude_file_name_regexp--sc_prohibit_readlink = \ exclude_file_name_regexp--sc_prohibit_setuid = ^src/util/virutil\.c$$ exclude_file_name_regexp--sc_prohibit_sprintf = \ - ^(docs/hacking\.html\.in)|(examples/systemtap/.*stp)|(src/dtrace2systemtap\.pl)|(src/rpc/gensystemtap\.pl)$$ + ^(docs/hacking\.html\.in)|(examples/systemtap/.*stp)|(src/dtrace2systemtap\.pl)|(src/rpc/gensystemtap\.pl)|(tools/wireshark/util/genxdrstub\.pl)$$ exclude_file_name_regexp--sc_prohibit_strncpy = ^src/util/virstring\.c$$ @@ -1013,7 +1013,7 @@ exclude_file_name_regexp--sc_correct_id_types = \ exclude_file_name_regexp--sc_m4_quote_check = m4/virt-lib.m4 exclude_file_name_regexp--sc_prohibit_include_public_headers_quote = \ - ^src/internal\.h$$ + ^(src/internal\.h$$|tools/wireshark/src/packet-libvirt.h$$) exclude_file_name_regexp--sc_prohibit_include_public_headers_brackets = \ ^(python/|tools/|examples/|include/libvirt/(virterror|libvirt-(qemu|lxc))\.h$$) diff --git a/configure.ac b/configure.ac index 553015a..ad67357 100644 --- a/configure.ac +++ b/configure.ac @@ -2569,6 +2569,70 @@ AM_CONDITIONAL([HAVE_LIBNL], [test "$have_libnl" = "yes"]) AC_SUBST([LIBNL_CFLAGS]) AC_SUBST([LIBNL_LIBS]) +dnl wireshark dissector + +AC_ARG_WITH([wireshark-dissector], + [AS_HELP_STRING([--with-wireshark-dissector], + [enable wireshark dissector plugin support @<:@default=check@:>@])], + [ with_wireshark_dissector=$withval ], + [ with_wireshark_dissector=check ]) + +AC_DEFUN([LIBVIRT_WS_HANDLE_ERROR], [ + if test "$with_wireshark_dissector" = "yes"; then + AC_MSG_ERROR([$1]) + else + with_wireshark_dissector=no + fi +]) +if test "$with_wireshark_dissector" != "no"; then + dnl Check for XDR headers existence + AC_CHECK_HEADERS([rpc/types.h]) + + dnl Check for glib-2.0 existence + PKG_CHECK_MODULES([GLIB], [glib-2.0], [ + WS_DISSECTOR_CPPFLAGS="$WS_DISSECTOR_CPPFLAGS `$PKG_CONFIG --cflags glib-2.0`" + ], [ + LIBVIRT_WS_HANDLE_ERROR([pkg-config 'glib-2.0' is required for wireshark-dissector support]) + ]) + + dnl Search for wireshark(or tshark) command + AC_PATH_PROG([WIRESHARK], [wireshark]) + AC_PATH_PROG([WIRESHARK], [tshark]) + if test -z "$WIRESHARK"; then + LIBVIRT_WS_HANDLE_ERROR([command not found wireshark or tshark]) + else + dnl Check for wireshark headers + save_CPPFLAGS="$CPPFLAGS" + WS_DISSECTOR_CPPFLAGS="$WS_DISSECTOR_CPPFLAGS -I`dirname $WIRESHARK`/../include/wireshark" + CPPFLAGS="$CPPFLAGS $WS_DISSECTOR_CPPFLAGS" + AC_CHECK_HEADERS([wireshark/config.h],, [ + LIBVIRT_WS_HANDLE_ERROR([wireshark/config.h is required for wireshark-dissector support]) + ]) + AC_CHECK_HEADERS([wireshark/epan/packet.h wireshark/epan/dissectors/packet-tcp.h],, [ + LIBVIRT_WS_HANDLE_ERROR([wireshark/epan/{packet,packet-tcp}.h are required for wireshark-dissector support]) + ], [ + #include <wireshark/config.h> + ]) + CPPFLAGS="$save_CPPFLAGS" + fi + if test "$with_wireshark_dissector" != "no"; then + with_wireshark_dissector=yes + fi +fi +AC_SUBST([WS_DISSECTOR_CPPFLAGS]) +AM_CONDITIONAL([WITH_WIRESHARK_DISSECTOR], [test "$with_wireshark_dissector" = "yes"]) + +AC_ARG_WITH([ws-plugindir], + [AS_HELP_STRING([--with-ws-plugindir], + [wireshark plugins directory that plugin will installed])], + [ ws_plugindir=$withval ]) + +if test "$with_wireshark_dissector" != "no" && test -z "$ws_plugindir"; then + ws_version=`$WIRESHARK -v | head -1 | cut -f 2 -d' '` + ws_plugindir=`dirname $WIRESHARK`/../lib/wireshark/plugins/$ws_version +fi +AC_SUBST([ws_plugindir]) + # Check for Linux vs. BSD ifreq members AC_CHECK_MEMBERS([struct ifreq.ifr_newname, struct ifreq.ifr_ifindex, @@ -2654,7 +2718,9 @@ AC_CONFIG_FILES([\ examples/python/Makefile \ examples/hellolibvirt/Makefile \ examples/systemtap/Makefile \ - examples/xml/nwfilter/Makefile]) + examples/xml/nwfilter/Makefile \ + tools/wireshark/Makefile \ + tools/wireshark/src/Makefile]) AC_OUTPUT AC_MSG_NOTICE([]) @@ -2814,6 +2880,10 @@ AC_MSG_NOTICE([ XML Catalog: $XML_CATALOG_FILE]) AC_MSG_NOTICE([ Init script: $with_init_script]) AC_MSG_NOTICE([Char device locks: $with_chrdev_lock_files]) AC_MSG_NOTICE([]) +AC_MSG_NOTICE([Developer Tools]) +AC_MSG_NOTICE([]) +AC_MSG_NOTICE([Wireshark dissector: $with_wireshark_dissector]) +AC_MSG_NOTICE([]) AC_MSG_NOTICE([Privileges]) AC_MSG_NOTICE([]) AC_MSG_NOTICE([ QEMU: $QEMU_USER:$QEMU_GROUP]) diff --git a/tools/wireshark/Makefile.am b/tools/wireshark/Makefile.am new file mode 100644 index 0000000..71addec --- /dev/null +++ b/tools/wireshark/Makefile.am @@ -0,0 +1,29 @@ +## Process this file with automake to produce Makefile.in + +# Copyright (C) 2013 Yuto KAWAMURA(kawamuray) <kawamuray.dadada@gmail.com> +# +# 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, see +# <http://www.gnu.org/licenses/>. +# +# Author: Yuto KAWAMURA(kawamuray) +if WITH_WIRESHARK_DISSECTOR +SUBDIRS = src + +# I think wireshark plugin is special case that doesn't need to install +# *.la(libtool archive) files. +# Maybe each plugin functionality should correspond to single file in +# plugins directory. So this hook keeps plugins directory clean. +install-exec-hook: + rm -f $(ws_plugindir)/libvirt.la +endif WITH_WIRESHARK_DISSECTOR diff --git a/tools/wireshark/README.md b/tools/wireshark/README.md new file mode 100644 index 0000000..9f4c9b6 --- /dev/null +++ b/tools/wireshark/README.md @@ -0,0 +1,31 @@ +About +===== +This is the project of Google Summer of Code 2013 accepted by QEMU.org and +libvirt community. The goal of this project is, provide Wireshark dissector for +Libvirt RPC protocol. It will provide Libvirt packet overview/detail analysing +in Wireshark. Furthermore, it will be able to build(generated) from RPC protocol +definition placed in Libvirt source tree to support latest protocol +specification. + +See also: +- http://www.google-melange.com/gsoc/project/google/gsoc2013/kawamuray/7001 +- http://wiki.qemu.org/Features/LibvirtWiresharkDissector + +Installation +============= +Run ./configure with --with-wireshark-dissector option enabled. +Then dissector will compiled with libvirt itself. + +Add/Remove protocol from dissector's support +-------------------------------------------- +Modify variable WS\_DISSECTOR\_PROTO\_FILES in tools/wireshark/src/Makefile.am. + +Changing installation directory +------------------------------- +You can change installation directory of pluggable shared object(libvirt.so) by +specifying --with-ws-plugindir=<path>. + +You can install libvirt.so into your local wireshark plugin directory: + + ./configure --with-wireshark-dissector \ + --with-ws-plugindir=$HOME/.wireshark/plugins diff --git a/tools/wireshark/src/.gitignore b/tools/wireshark/src/.gitignore new file mode 100644 index 0000000..cd51e37 --- /dev/null +++ b/tools/wireshark/src/.gitignore @@ -0,0 +1,4 @@ +*.so +*.o +plugin.c +libvirt diff --git a/tools/wireshark/src/Makefile.am b/tools/wireshark/src/Makefile.am new file mode 100644 index 0000000..86f69ae --- /dev/null +++ b/tools/wireshark/src/Makefile.am @@ -0,0 +1,42 @@ +## Process this file with automake to produce Makefile.in + +# Copyright (C) 2013 Yuto KAWAMURA(kawamuray) <kawamuray.dadada@gmail.com> +# +# 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, see +# <http://www.gnu.org/licenses/>. +# +# Author: Yuto KAWAMURA(kawamuray) +ws_plugin_LTLIBRARIES = libvirt.la +libvirt_la_SOURCES = packet-libvirt.c plugin.c +libvirt_la_CPPFLAGS = $(WS_DISSECTOR_CPPFLAGS) +libvirt_la_LDFLAGS = -avoid-version + +packet-libvirt.c: packet-libvirt.h libvirt/protocol.h + +plugin.c: packet-libvirt.c + $(srcdir)/../util/make-dissector-reg . plugin $< + +WS_DISSECTOR_PROTO_FILES = \ + $(top_srcdir)/src/remote/remote_protocol.x \ + $(top_srcdir)/src/remote/qemu_protocol.x \ + $(top_srcdir)/src/remote/lxc_protocol.x \ + $(top_srcdir)/src/rpc/virkeepaliveprotocol.x + +libvirt/protocol.h: $(srcdir)/../util/genxdrstub.pl $(WS_DISSECTOR_PROTO_FILES) + $(MKDIR_P) libvirt + LIBVIRT_VERSION=$(LIBVIRT_VERSION) \ + $(PERL) $(srcdir)/../util/genxdrstub.pl $(WS_DISSECTOR_PROTO_FILES) + +clean-local: + -rm -rf libvirt plugin.c diff --git a/tools/wireshark/src/packet-libvirt.c b/tools/wireshark/src/packet-libvirt.c new file mode 100644 index 0000000..3c2de35 --- /dev/null +++ b/tools/wireshark/src/packet-libvirt.c @@ -0,0 +1,512 @@ +/* packet-libvirt.c --- Libvirt packet dissector routines. + * + * Copyright (C) 2013 Yuto KAWAMURA(kawamuray) <kawamuray.dadada@gmail.com> + * + * 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, see + * <http://www.gnu.org/licenses/>. + * + * Authors: + * Michal Privoznik <mprivozn redhat com> + * Yuto KAWAMURA(kawamuray) <kawamuray.dadada gmail.com> + */ +#include <config.h> + +#include <stdlib.h> +#include <wireshark/config.h> +#include <wireshark/epan/proto.h> +#include <wireshark/epan/packet.h> +#include <wireshark/epan/dissectors/packet-tcp.h> +#include <glib.h> +#include <glib/gprintf.h> +#ifdef HAVE_RPC_TYPES_H +# include <rpc/types.h> +#endif +#include <rpc/xdr.h> +#include "packet-libvirt.h" + +static int proto_libvirt = -1; +static int hf_libvirt_length = -1; +static int hf_libvirt_program = -1; +static int hf_libvirt_version = -1; +static int hf_libvirt_type = -1; +static int hf_libvirt_serial = -1; +static int hf_libvirt_status = -1; +static int hf_libvirt_stream = -1; +static int hf_libvirt_num_of_fds = -1; +static int hf_libvirt_unknown = -1; +static gint ett_libvirt = -1; + +#define XDR_PRIMITIVE_DISSECTOR(xtype, ctype, ftype) \ + static gboolean \ + dissect_xdr_##xtype(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf) \ + { \ + goffset start; \ + ctype val; \ + start = xdr_getpos(xdrs); \ + if (xdr_##xtype(xdrs, &val)) { \ + proto_tree_add_##ftype(tree, hf, tvb, start, xdr_getpos(xdrs) - start, val); \ + return TRUE; \ + } else { \ + proto_tree_add_item(tree, hf_libvirt_unknown, tvb, start, -1, ENC_NA); \ + return FALSE; \ + } \ + } + +XDR_PRIMITIVE_DISSECTOR(int, gint32, int) +XDR_PRIMITIVE_DISSECTOR(u_int, guint32, uint) +XDR_PRIMITIVE_DISSECTOR(short, gint16, int) +XDR_PRIMITIVE_DISSECTOR(u_short, guint16, uint) +XDR_PRIMITIVE_DISSECTOR(char, gchar, int) +XDR_PRIMITIVE_DISSECTOR(u_char, guchar, uint) +XDR_PRIMITIVE_DISSECTOR(hyper, gint64, int64) +XDR_PRIMITIVE_DISSECTOR(u_hyper, guint64, uint64) +XDR_PRIMITIVE_DISSECTOR(float, gfloat, float) +XDR_PRIMITIVE_DISSECTOR(double, gdouble, double) +XDR_PRIMITIVE_DISSECTOR(bool, bool_t, boolean) + +static gboolean +dissect_xdr_string(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, + guint32 maxlen) +{ + goffset start; + gchar *val = NULL; + + start = xdr_getpos(xdrs); + if (xdr_string(xdrs, &val, maxlen)) { + proto_tree_add_string(tree, hf, tvb, start, xdr_getpos(xdrs) - start, val); + xdr_free((xdrproc_t)xdr_string, (char *)&val); + return TRUE; + } else { + proto_tree_add_item(tree, hf_libvirt_unknown, tvb, start, -1, ENC_NA); + return FALSE; + } +} + +static gchar * +format_xdr_bytes(guint8 *bytes, guint32 length) +{ + gchar *buf; + guint32 i; + + if (length == 0) + return ""; + buf = ep_alloc(length*2 + 1); + for (i = 0; i < length; i++) { + /* We know that buf has enough size to contain + 2 * length + '\0' characters. */ + g_sprintf(buf, "%02x", bytes[i]); + buf += 2; + } + return buf - length*2; +} + +static gboolean +dissect_xdr_opaque(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, + guint32 size) +{ + goffset start; + gboolean rc; + guint8 *val; + + val = g_malloc(size); + start = xdr_getpos(xdrs); + if ((rc = xdr_opaque(xdrs, (caddr_t)val, size))) { + proto_tree_add_bytes_format_value(tree, hf, tvb, start, xdr_getpos(xdrs) - start, + NULL, "%s", format_xdr_bytes(val, size)); + } else { + proto_tree_add_item(tree, hf_libvirt_unknown, tvb, start, -1, ENC_NA); + } + + g_free(val); + return rc; +} + +static gboolean +dissect_xdr_bytes(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, + guint32 maxlen) +{ + goffset start; + guint8 *val = NULL; + guint32 length; + + start = xdr_getpos(xdrs); + if (xdr_bytes(xdrs, (char **)&val, &length, maxlen)) { + proto_tree_add_bytes_format_value(tree, hf, tvb, start, xdr_getpos(xdrs) - start, + NULL, "%s", format_xdr_bytes(val, length)); + /* Seems I can't call xdr_free() for this case. + It will raises SEGV by referencing out of bounds argument stack */ + free(val); + return TRUE; + } else { + proto_tree_add_item(tree, hf_libvirt_unknown, tvb, start, -1, ENC_NA); + return FALSE; + } +} + +static gboolean +dissect_xdr_pointer(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, + vir_xdr_dissector_t dissect) +{ + goffset start; + bool_t isnull; + + start = xdr_getpos(xdrs); + if (!xdr_bool(xdrs, &isnull)) { + proto_tree_add_item(tree, hf_libvirt_unknown, tvb, start, -1, ENC_NA); + return FALSE; + } + if (isnull) { + proto_item *ti; + ti = proto_tree_add_item(tree, hf, tvb, start, xdr_getpos(xdrs) - start, ENC_NA); + proto_item_append_text(ti, ": (null)"); + return TRUE; + } else { + return dissect(tvb, tree, xdrs, hf); + } +} + +static gboolean +dissect_xdr_iterable(tvbuff_t *tvb, proto_item *ti, XDR *xdrs, gint ett, int rhf, + guint32 length, vir_xdr_dissector_t dissect, goffset start) +{ + proto_tree *tree; + guint32 i; + + tree = proto_item_add_subtree(ti, ett); + for (i = 0; i < length; i++) { + if (!dissect(tvb, tree, xdrs, rhf)) + return FALSE; + } + proto_item_set_len(ti, xdr_getpos(xdrs) - start); + return TRUE; +} + +static gboolean +dissect_xdr_vector(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, gint ett, + int rhf, gchar *rtype, guint32 size, vir_xdr_dissector_t dissect) +{ + goffset start; + proto_item *ti; + + start = xdr_getpos(xdrs); + ti = proto_tree_add_item(tree, hf, tvb, start, -1, ENC_NA); + proto_item_append_text(ti, " :: %s[%u]", rtype, size); + return dissect_xdr_iterable(tvb, ti, xdrs, ett, rhf, size, dissect, start); +} + +static gboolean +dissect_xdr_array(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, gint ett, + int rhf, gchar *rtype, guint32 maxlen, vir_xdr_dissector_t dissect) +{ + goffset start; + proto_item *ti; + guint32 length; + + start = xdr_getpos(xdrs); + + if (!xdr_u_int(xdrs, &length)) + return FALSE; + if (length > maxlen) + return FALSE; + + ti = proto_tree_add_item(tree, hf, tvb, start, -1, ENC_NA); + proto_item_append_text(ti, " :: %s<%u>", rtype, length); + return dissect_xdr_iterable(tvb, ti, xdrs, ett, rhf, length, dissect, start); +} + +static vir_xdr_dissector_t +find_payload_dissector(guint32 proc, guint32 type, + const vir_dissector_index_t *pds, gsize length) +{ + const vir_dissector_index_t *pd; + guint32 first, last, direction; + + if (pds == NULL || length < 1) + return NULL; + + first = pds[0].proc; + last = pds[length-1].proc; + if (proc < first || proc > last) { + return NULL; + } + + pd = &pds[proc-first]; + /* There is no guarantee to proc numbers has no gap */ + if (pd->proc != proc) { + direction = (pd->proc < proc) ? 1 : -1; + while (pd->proc != proc) { + if (pd->proc == first || pd->proc == last) + return NULL; + pd += direction; + } + } + + switch (type) { + case VIR_NET_CALL: + case VIR_NET_CALL_WITH_FDS: + return pd->args; + case VIR_NET_REPLY: + case VIR_NET_REPLY_WITH_FDS: + return pd->ret; + case VIR_NET_MESSAGE: + return pd->msg; + } + return NULL; +} + +static void +dissect_libvirt_stream(tvbuff_t *tvb, proto_tree *tree, gint payload_length) +{ + proto_tree_add_item(tree, hf_libvirt_stream, tvb, VIR_HEADER_LEN, + payload_length - VIR_HEADER_LEN, ENC_NA); +} + +static gint32 +dissect_libvirt_num_of_fds(tvbuff_t *tvb, proto_tree *tree) +{ + gint32 nfds; + nfds = tvb_get_ntohl(tvb, VIR_HEADER_LEN); + proto_tree_add_int(tree, hf_libvirt_num_of_fds, tvb, VIR_HEADER_LEN, 4, nfds); + return nfds; +} + +static void +dissect_libvirt_fds(tvbuff_t *tvb, gint start, gint32 nfds) +{ + /* TODO: NOP for now */ +} + +static void +dissect_libvirt_payload_xdr_data(tvbuff_t *tvb, proto_tree *tree, gint payload_length, + gint32 status, vir_xdr_dissector_t dissect) +{ + gint32 nfds = 0; + gint start = VIR_HEADER_LEN; + tvbuff_t *payload_tvb; + caddr_t payload_data; + XDR xdrs; + + if (status == VIR_NET_CALL_WITH_FDS || + status == VIR_NET_REPLY_WITH_FDS) { + nfds = dissect_libvirt_num_of_fds(tvb, tree); + start += 4; + payload_length -= 4; + } + + payload_tvb = tvb_new_subset(tvb, start, -1, payload_length); + payload_data = (caddr_t)tvb_memdup(payload_tvb, 0, payload_length); + xdrmem_create(&xdrs, payload_data, payload_length, XDR_DECODE); + + dissect(payload_tvb, tree, &xdrs, -1); + + xdr_destroy(&xdrs); + g_free(payload_data); + + if (nfds != 0) { + dissect_libvirt_fds(tvb, start + payload_length, nfds); + } +} + +static void +dissect_libvirt_payload(tvbuff_t *tvb, proto_tree *tree, + guint32 prog, guint32 proc, guint32 type, guint32 status) +{ + gssize payload_length; + + payload_length = tvb_length(tvb) - VIR_HEADER_LEN; + if (payload_length <= 0) + return; /* No payload */ + + if (status == VIR_NET_OK) { + vir_xdr_dissector_t xd = find_payload_dissector(proc, type, get_program_data(prog, VIR_PROGRAM_DISSECTORS), + *(gsize *)get_program_data(prog, VIR_PROGRAM_DISSECTORS_LEN)); + if (xd == NULL) + goto unknown; + dissect_libvirt_payload_xdr_data(tvb, tree, payload_length, status, xd); + } else if (status == VIR_NET_ERROR) { + dissect_libvirt_payload_xdr_data(tvb, tree, payload_length, status, VIR_ERROR_MESSAGE_DISSECTOR); + } else if (type == VIR_NET_STREAM) { /* implicitly, status == VIR_NET_CONTINUE */ + dissect_libvirt_stream(tvb, tree, payload_length); + } else { + goto unknown; + } + return; + +unknown: + dbg("Cannot determine payload: Prog=%u, Proc=%u, Type=%u, Status=%u", prog, proc, type, status); + proto_tree_add_item(tree, hf_libvirt_unknown, tvb, VIR_HEADER_LEN, -1, ENC_NA); +} + +static void +dissect_libvirt_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + goffset offset; + guint32 prog, proc, type, serial, status; + const value_string *vs; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "Libvirt"); + col_clear(pinfo->cinfo, COL_INFO); + + offset = 4; /* End of length field */ + prog = tvb_get_ntohl(tvb, offset); offset += 4; + offset += 4; /* Ignore version header field */ + proc = tvb_get_ntohl(tvb, offset); offset += 4; + type = tvb_get_ntohl(tvb, offset); offset += 4; + serial = tvb_get_ntohl(tvb, offset); offset += 4; + status = tvb_get_ntohl(tvb, offset); offset += 4; + + col_add_fstr(pinfo->cinfo, COL_INFO, "Prog=%s", + val_to_str(prog, program_strings, "%x")); + + vs = get_program_data(prog, VIR_PROGRAM_PROCSTRINGS); + if (vs == NULL) { + col_append_fstr(pinfo->cinfo, COL_INFO, " Proc=%u", proc); + } else { + col_append_fstr(pinfo->cinfo, COL_INFO, " Proc=%s", val_to_str(proc, vs, "%d")); + } + + col_append_fstr(pinfo->cinfo, COL_INFO, " Type=%s Serial=%u Status=%s", + val_to_str(type, type_strings, "%d"), serial, + val_to_str(status, status_strings, "%d")); + + if (tree) { + gint hf_proc; + proto_item *ti; + proto_tree *libvirt_tree; + + ti = proto_tree_add_item(tree, proto_libvirt, tvb, 0, tvb_length(tvb), ENC_NA); + libvirt_tree = proto_item_add_subtree(ti, ett_libvirt); + + offset = 0; + proto_tree_add_item(libvirt_tree, hf_libvirt_length, tvb, offset, 4, ENC_NA); offset += 4; + proto_tree_add_item(libvirt_tree, hf_libvirt_program, tvb, offset, 4, ENC_NA); offset += 4; + proto_tree_add_item(libvirt_tree, hf_libvirt_version, tvb, offset, 4, ENC_NA); offset += 4; + + hf_proc = *(int *)get_program_data(prog, VIR_PROGRAM_PROCHFVAR); + if (hf_proc == -1) { + proto_tree_add_none_format(libvirt_tree, -1, tvb, offset, 4, "Unknown proc: %u", proc); + } else { + proto_tree_add_item(libvirt_tree, hf_proc, tvb, offset, 4, ENC_NA); + } + offset += 4; + + proto_tree_add_item(libvirt_tree, hf_libvirt_type, tvb, offset, 4, ENC_NA); offset += 4; + proto_tree_add_item(libvirt_tree, hf_libvirt_serial, tvb, offset, 4, ENC_NA); offset += 4; + proto_tree_add_item(libvirt_tree, hf_libvirt_status, tvb, offset, 4, ENC_NA); offset += 4; + + /* Dissect payload remaining */ + dissect_libvirt_payload(tvb, libvirt_tree, prog, proc, type, status); + } +} + +static guint32 +get_message_len(packet_info *pinfo __attribute__((unused)), tvbuff_t *tvb, int offset) +{ + return tvb_get_ntohl(tvb, offset); +} + +static void +dissect_libvirt(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + /* Another magic const - 4; simply, how much bytes + * is needed to tell the length of libvirt packet. */ + tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 4, get_message_len, dissect_libvirt_message); +} + +void +proto_register_libvirt(void) +{ + static hf_register_info hf[] = { + { &hf_libvirt_length, + { "length", "libvirt.length", + FT_UINT32, BASE_DEC, + NULL, 0x0, + NULL, HFILL} + }, + { &hf_libvirt_program, + { "program", "libvirt.program", + FT_UINT32, BASE_HEX, + VALS(program_strings), 0x0, + NULL, HFILL} + }, + { &hf_libvirt_version, + { "version", "libvirt.version", + FT_UINT32, BASE_DEC, + NULL, 0x0, + NULL, HFILL} + }, + { &hf_libvirt_type, + { "type", "libvirt.type", + FT_INT32, BASE_DEC, + VALS(type_strings), 0x0, + NULL, HFILL} + }, + { &hf_libvirt_serial, + { "serial", "libvirt.serial", + FT_UINT32, BASE_DEC, + NULL, 0x0, + NULL, HFILL} + }, + { &hf_libvirt_status, + { "status", "libvirt.status", + FT_INT32, BASE_DEC, + VALS(status_strings), 0x0, + NULL, HFILL} + }, + + VIR_DYNAMIC_HFSET + + { &hf_libvirt_stream, + { "stream", "libvirt.stream", + FT_BYTES, BASE_NONE, + NULL, 0x0, + NULL, HFILL} + }, + { &hf_libvirt_num_of_fds, + { "num_of_fds", "libvirt.num_of_fds", + FT_INT32, BASE_DEC, + NULL, 0x0, + NULL, HFILL} + }, + { &hf_libvirt_unknown, + { "unknown", "libvirt.unknown", + FT_BYTES, BASE_NONE, + NULL, 0x0, + NULL, HFILL} + }, + }; + + static gint *ett[] = { + VIR_DYNAMIC_ETTSET + &ett_libvirt + }; + + proto_libvirt = proto_register_protocol( + "Libvirt", /* name */ + "libvirt", /* short name */ + "libvirt" /* abbrev */ + ); + + proto_register_field_array(proto_libvirt, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); +} + +void +proto_reg_handoff_libvirt(void) +{ + static dissector_handle_t libvirt_handle; + + libvirt_handle = create_dissector_handle(dissect_libvirt, proto_libvirt); + dissector_add_uint("tcp.port", LIBVIRT_PORT, libvirt_handle); +} diff --git a/tools/wireshark/src/packet-libvirt.h b/tools/wireshark/src/packet-libvirt.h new file mode 100644 index 0000000..0cab637 --- /dev/null +++ b/tools/wireshark/src/packet-libvirt.h @@ -0,0 +1,128 @@ +/* packet-libvirt.h --- Libvirt packet dissector header file. + * + * Copyright (C) 2013 Yuto KAWAMURA(kawamuray) <kawamuray.dadada@gmail.com> + * + * 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, see + * <http://www.gnu.org/licenses/>. + * + * Author: Yuto KAWAMURA(kawamuray) + */ +#ifndef _PACKET_LIBVIRT_H_ +# define _PACKET_LIBVIRT_H_ + +# ifndef LIBVIRT_PORT +# define LIBVIRT_PORT 16509 +# endif + +# define VIR_HEADER_LEN 28 + +# ifdef DEBUG +# define dbg(fmt, ...) \ + g_print("[LIBVIRT] " fmt " at " __FILE__ " line %d\n", ##__VA_ARGS__, __LINE__) +# else +# define dbg(fmt, ...) +# endif + +typedef gboolean (*vir_xdr_dissector_t)(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); + +typedef struct vir_dissector_index vir_dissector_index_t; +struct vir_dissector_index { + guint32 proc; + vir_xdr_dissector_t args; + vir_xdr_dissector_t ret; + vir_xdr_dissector_t msg; +}; + +enum vir_net_message_type { + VIR_NET_CALL = 0, + VIR_NET_REPLY = 1, + VIR_NET_MESSAGE = 2, + VIR_NET_STREAM = 3, + VIR_NET_CALL_WITH_FDS = 4, + VIR_NET_REPLY_WITH_FDS = 5, +}; + +enum vir_net_message_status { + VIR_NET_OK = 0, + VIR_NET_ERROR = 1, + VIR_NET_CONTINUE = 2, +}; + +enum vir_program_data_index { + VIR_PROGRAM_PROCHFVAR, + VIR_PROGRAM_PROCSTRINGS, + VIR_PROGRAM_DISSECTORS, + VIR_PROGRAM_DISSECTORS_LEN, + VIR_PROGRAM_LAST, +}; + +static const value_string type_strings[] = { + { VIR_NET_CALL, "CALL" }, + { VIR_NET_REPLY, "REPLY" }, + { VIR_NET_MESSAGE, "MESSAGE" }, + { VIR_NET_STREAM, "STREAM" }, + { VIR_NET_CALL_WITH_FDS, "CALL_WITH_FDS" }, + { VIR_NET_REPLY_WITH_FDS, "REPLY_WITH_FDS" }, + { -1, NULL } +}; + +static const value_string status_strings[] = { + { VIR_NET_OK, "OK" }, + { VIR_NET_ERROR, "ERROR" }, + { VIR_NET_CONTINUE, "CONTINUE" }, + { -1, NULL } +}; + +/* TODO: These symbols will automatically included in generated headers in the feature */ +# define VIR_SECURITY_MODEL_BUFLEN (256 + 1) +# define VIR_SECURITY_LABEL_BUFLEN (4096 + 1) +# define VIR_SECURITY_DOI_BUFLEN (256 + 1) +# define VIR_UUID_BUFLEN (16) +enum { + VIR_TYPED_PARAM_INT = 1, /* integer case */ + VIR_TYPED_PARAM_UINT = 2, /* unsigned integer case */ + VIR_TYPED_PARAM_LLONG = 3, /* long long case */ + VIR_TYPED_PARAM_ULLONG = 4, /* unsigned long long case */ + VIR_TYPED_PARAM_DOUBLE = 5, /* double case */ + VIR_TYPED_PARAM_BOOLEAN = 6, /* boolean(character) case */ + VIR_TYPED_PARAM_STRING = 7, /* string case */ +}; +/* / */ + +# define VIR_ERROR_MESSAGE_DISSECTOR dissect_xdr_remote_error + +static gboolean dissect_xdr_int(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_u_int(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_short(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_u_short(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_char(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_u_char(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_hyper(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_u_hyper(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_float(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_double(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_bool(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf); +static gboolean dissect_xdr_string(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, guint32 maxlen); +static gboolean dissect_xdr_opaque(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, guint32 size); +static gboolean dissect_xdr_bytes(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, guint32 maxlen); +static gboolean dissect_xdr_pointer(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, + vir_xdr_dissector_t dp); +static gboolean dissect_xdr_vector(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, gint ett, + int rhf, gchar *rtype, guint32 size, vir_xdr_dissector_t dp); +static gboolean dissect_xdr_array(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, gint ett, + int rhf, gchar *rtype, guint32 maxlen, vir_xdr_dissector_t dp); + +# include "libvirt/protocol.h" + +#endif /* _PACKET_LIBVIRT_H_ */ diff --git a/tools/wireshark/util/genxdrstub.pl b/tools/wireshark/util/genxdrstub.pl new file mode 100755 index 0000000..5872a30 --- /dev/null +++ b/tools/wireshark/util/genxdrstub.pl @@ -0,0 +1,1009 @@ +#!/usr/bin/env perl +# genxdrstub.pl --- Generate C header file which used by packet-libvirt.[ch] +# +# Copyright (C) 2013 Yuto KAWAMURA(kawamuray) <kawamuray.dadada@gmail.com> +# +# 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, see +# <http://www.gnu.org/licenses/>. +# +# Author: Yuto KAWAMURA(kawamuray) +# +# For XDR syntax, see http://tools.ietf.org/html/rfc4506#section-6.3 +# This script does not strictly check syntax of xdr protocol specification. +# Make sure the specification files you have are correctly compilable with rpcgen(1). +# If something fails with this script in spite of you had confirmed that the `make' with libvirt was succeed, +# please report your error output to kawamuray<kawamuray.dadada@gmail.com>. +use strict; +use warnings; +use File::Spec; + +my $DEBUG = 0; # Enable if you want to see debug output +sub dbg { print STDERR @_ if $DEBUG } + +die "ERROR: No arguments" unless @ARGV; + +# Context object referenced from entire this script +my $c = Context->new; + +for my $proto (@ARGV) { + # We need to do this heuristic parsing to determine + # variable name of enum <protocol>_procedures. + my ($name) = $proto =~ m{(?:vir)?([^/]+?)_?protocol\.x$}; + unless ($name) { + warn "WARNING: Cannot extract protocol name from $proto, skipping."; + next; + } + $c->add_to_set(progs => $name); + + my $source; + { + open my $fh, '<', $proto + or die "Cannot open $proto: $!"; + local $/; + $source = <$fh>; + close $fh; + } + + $c->add_header_file($name, sub { + dbg "*** Start parsing $proto\n"; + my @lexs = Lexicalizer->parse($source); + for my $lex (@lexs) { + next if $lex->ident eq "enum $name\_procedure"; + + if ($lex->isa('Sym::Variable')) { + $c->print(sprintf "#define %s (%s)\n", $lex->ident, $lex->value); + } elsif ($lex->isa('Sym::Type')) { + # Top level of name path is type identification of itself + $lex->define_dissector($lex->idstrip); + } else { + die "Unkown lexical appeared: $lex"; + } + } + + my $procs = $c->symbol("enum $name\_procedure") + or die "Cannot find procedures enumeration: enum $name\_procedure"; + # Procedure numbers are expected to be containing gaps, but needed to be sorted in ascending order. + my @procedures = sort { $a->value <=> $b->value } @{ $procs->members }; + my @dissectors = map { + (my $ident = lc($_->ident)) =~ s/^$name\_proc/$name/; + +{ + value => $_->value, + map { $_ => $c->rinc($c->symbols->{"$ident\_$_"} ? "dissect_xdr_$ident\_$_" : 'NULL') } + qw{ args ret msg } + }; + } @procedures; + $c->print(PT->render('code.dissectorlist', { + name => $name, + dissectors => \@dissectors, + })); + $c->print(PT->render('code.procedure_strings', { + name => $name, + procedures => \@procedures, + })); + }); +} + +$c->add_header_file('protocol', sub { + for my $prog (@{ $c->get_set('progs') }) { + $c->print("#include \"libvirt/$prog.h\"\n"); + } + + # hf_ variables set + $c->print(PT->render('macro.hfvars', { + programs => $c->get_set('progs'), + hfvars => [ grep $_->{segment}{refcnt}, @{ $c->get_set('hfvars') } ], + })); + # ett_ variables set + $c->print(PT->render('macro.ettvars', { + ettvars => [ map $_->{sym}, grep $_->{refcnt}, @{ $c->get_set('ettvars') } ], + })); + # value_string program_strings + $c->print(PT->render('code.program_strings', { programs => $c->get_set('progs') })); + $c->print("static int hf_$_\_procedure = -1;\n") for @{ $c->get_set('progs') }; + $c->print(PT->render('code.program_data', { programs => $c->get_set('progs') })); +}); + +$c->finalize; exit 0; + +# Used for handy class building +sub register_profile { + my %prof = @_; + my $caller = caller; + no strict 'refs'; + if ($prof{isa}) { + push @{ "$caller\::ISA" }, $prof{isa}; + } + while (my ($name, $v) = each %{ $prof{consts} || {} }) { + *{ "$caller\::$name" } = sub { $v }; + } + for my $attr (@{ $prof{attrs} || [] }) { + *{ "$caller\::$attr" } = sub { + if (@_ > 1) { $_[0]->{$attr} = $_[1]; $_[0] } + else { $_[0]->{$attr} } + }; + } + while (my ($klass, $meths) = each %{ $prof{roles} || {} }) { + for my $meth (@$meths) { + # This assignment cannot be like: *{ "$caller\::$meth" } = \&{ "$klass\::$meth" }. + # "$klass\::$meth" maybe not defined yet(e.g. Methods defined by PT) + *{ "$caller\::$meth" } = sub { goto &{ "$klass\::$meth" } }; + } + } +} + +# Minimal template engine for code generating +package PT; # is PicoTemplate +our $Token; +our %Templates; +INIT { # Load templates from __END__ section + $Token = join '', map { chr(65 + rand(26)) } 1..64; + my $current; + while (my $l = <main::DATA>) { + if ($l =~ /^\@\@\s*(.+)/) { + $current = \($Templates{$1} = ''); + } else { + $$current .= $l if $current; + } + } + for my $name (keys %Templates) { + $Templates{$name} = __PACKAGE__->compile($Templates{$name}); + if ($name =~ /^([\w:]+)#([^#]+)$/) { + no strict 'refs'; + my $meth = "$1\::$2"; + unless (defined &$meth) { + *$meth = $Templates{$name}; + } + } + } +} +sub compile { + my ($class, $tmpl) = @_; + + $tmpl =~ s{<%(=)?(.*?)%>\n?|((?:(?!<%).)+)}{ + $2 ? $1 ? "\$$Token .= qq{\@{[do{ $2 }]}};" : $2 + : "\$$Token .= substr <<$Token, 0, -1;\n".quotemeta($3)."\n$Token\n"; + }gse; + eval "sub { my \$$Token = ''; $tmpl \$$Token }" + or die "ERROR: Cannot compile template: $@"; +} +sub render { + my ($class, $name, $vars, @args) = @_; + local $_ = $vars || {}; + my $renderer = $Templates{$name} + or die "No such template: $name"; + $renderer->(@args); +} +# / package PT + +package Sym; +BEGIN{::register_profile( + attrs => [qw[ ident ]], +)} + +sub new { + my ($class, %args) = @_; + + CORE::bless \%args, $class; +} + +sub bless { + my ($self, $klass) = @_; + + CORE::bless $self, "Sym::$klass" + if ref($self) ne "Sym::$klass"; + $self; +} + +sub idstrip { + my $ident = shift()->ident; + $ident =~ s/^(?:struct|enum|union)\s+// if $ident; + $ident; +} +# / package Sym + +package Sym::Type; +BEGIN{::register_profile( + isa => 'Sym', + attrs => [qw[ alias ]], +)} + +sub is_primitive { !(shift)->alias } + +sub dealias { + my ($self) = @_; + + $self->is_primitive ? $self : $self->alias->dealias; +} + +sub xdr_type { + my ($self) = @_; + + if (!$self->is_primitive) { + return $self->dealias->xdr_type; + } + + my $type = ref $self; + if ($type eq __PACKAGE__) { + $type = $self->ident; + } else { + $type =~ s/^.*:://; + } + uc($type); +} + +sub render_caller { + my ($self, $hfid) = @_; + my $name = $c->rinc( 'dissect_xdr_'.($self->idstrip || lc($self->xdr_type)) ); + "$name(tvb, tree, xdrs, hf)"; +} + +sub ft_type { + my ($self) = @_; + return $self->dealias->ft_type unless $self->is_primitive; + my $xt = $self->xdr_type; + +{ + INT => 'INT32', + U_INT => 'UINT32', + SHORT => 'INT16', + U_SHORT => 'UINT16', + CHAR => 'INT8', + U_CHAR => 'UINT8', + HYPER => 'INT64', + U_HYPER => 'UINT64', + BOOL => 'BOOLEAN', + }->{$xt} || $xt; +} + +sub hf_base { + my ($self) = @_; + $self->is_primitive + ? $self->ft_type =~ /INT/ ? 'DEC' : 'NONE' + : $self->dealias->hf_base; +} + +sub define_dissector { + my ($self, @path) = @_; + $self->declare_hfvar(@path); + my $path = join '__', @path; + my $code = $self->render_dissector($path); + $c->print({ sym => "dissect_xdr_$path", body => $code }) + if $code; +} + +sub declare_hfvar { + my ($self, @path) = @_; + my $path = join '__', @path; + $c->add_to_set(hfvars => { + segment => $c->print({ + sym => "hf_$path", + body => "static int hf_$path = -1;\n" + }), + name => $path[-1], + abbrev => join('.', @path), + ft_type => $self->ft_type, + hf_base => $self->hf_base, + }); +} +# / package Sym + +package Sym::Type::HasAnonTypes; # Types which possibly have anonymous subtypes +BEGIN{::register_profile( + isa => 'Sym::Type', +)} + +sub declare_anontypes { + my ($self, @path) = @_; + + for my $m (@{ $self->members }) { + unless (defined $m->type->ident) { + $m->type->ident(join '__', @path, $m->ident); + } + $m->type->define_dissector(@path, $m->ident); + } +} + +sub define_dissector { + my ($self, @path) = @_; + + $self->declare_anontypes(@path); + $self->SUPER::define_dissector(@path); +} + +package Sym::Type::HasSubtree; # Types which should be declare ett variables + +sub declare_ettvar { + my ($self) = @_; + my $ettvar = 'ett_'.$self->idstrip; + $c->add_to_set(ettvars => $c->print({ + sym => $ettvar, + body => "static gint $ettvar = -1;\n", + })); +} + +package Sym::Type::HasReference; # Types which references subtype +BEGIN{::register_profile( + attrs => [qw[ reftype ]], + consts => { ft_type => 'NONE' }, +)} + +sub render_caller { + my ($self) = @_; + my ($klass) = ref($self) =~ /([^:]+)$/; + sprintf '%s(tvb, tree, xdrs, hf, %s)', + $c->rinc('dissect_xdr_'.lc($klass)), + $c->rinc('dissect_xdr_'.$self->reftype->idstrip); +} + +package Sym::Type::HasLength; # Types which has length attribute +BEGIN{::register_profile( + attrs => [qw[ length ]], + consts => { ft_type => 'NONE' }, +)} + +sub render_caller { + my ($self, $hfid) = @_; + my ($klass) = ref($self) =~ /([^:]+)$/; + sprintf '%s(tvb, tree, xdrs, hf, %s)', + $c->rinc('dissect_xdr_'.lc($klass)), $self->length || '~0'; +} + +package Sym::Type::Struct; +BEGIN{::register_profile( + isa => 'Sym::Type', + attrs => [qw[ members ]], + consts => { ft_type => 'NONE' }, + roles => { + 'Sym::Type::HasAnonTypes' => [qw[ declare_anontypes ]], + 'Sym::Type::HasSubtree' => [qw[ declare_ettvar ]], + }, +)} + +sub define_dissector { + my ($self, @path) = @_; + $self->declare_anontypes(@path); + $self->declare_ettvar; + $self->SUPER::define_dissector(@path); +} + +package Sym::Type::Enum; +BEGIN{::register_profile( + isa => 'Sym::Type', + attrs => [qw[ members ]], + consts => { ft_type => 'UINT32' }, +)} +package Sym::Type::Union; +BEGIN{::register_profile( + isa => 'Sym::Type', + attrs => [qw[ decl case_specs ]], + consts => { ft_type => 'NONE' }, + roles => { + 'Sym::Type::HasAnonTypes' => [qw[ declare_anontypes define_dissector ]], + }, +)} +sub members { + my ($self) = @_; + [ map { $_->[1] } @{ $self->case_specs } ]; +} + +package Sym::Type::String; +BEGIN{::register_profile( + isa => 'Sym::Type', + consts => { ft_type => 'STRING' }, + roles => { + 'Sym::Type::HasLength' => [qw[ length render_caller ]], + }, +)} +package Sym::Type::Opaque; +BEGIN{::register_profile( + isa => 'Sym::Type', + consts => { ft_type => 'BYTES' }, + roles => { + 'Sym::Type::HasLength' => [qw[ length render_caller ]], + }, +)} +package Sym::Type::Bytes; +BEGIN{::register_profile( + isa => 'Sym::Type', + consts => { ft_type => 'BYTES' }, + roles => { + 'Sym::Type::HasLength' => [qw[ length render_caller ]], + }, +)} +package Sym::Type::Pointer; +BEGIN{::register_profile( + isa => 'Sym::Type', + roles => { + 'Sym::Type::HasReference' => [qw[ reftype ft_type render_caller ]], + }, +)} +package Sym::Type::Array; # a.k.a Variable-Length Array +BEGIN{::register_profile( + isa => 'Sym::Type', + roles => { + 'Sym::Type::HasLength' => [qw[ length ft_type ]], + 'Sym::Type::HasReference' => [qw[ reftype ]], + 'Sym::Type::HasSubtree' => [qw[ declare_ettvar ]], + }, +)} + +sub render_caller { + my ($self, $hfid) = @_; + my ($pname) = reverse split /__/, $hfid; + sprintf 'dissect_xdr_array(tvb, tree, xdrs, hf, %s, %s, "%s", %s, %s)', + $c->rinc('ett_'.$self->idstrip), + $c->rinc("hf_$hfid\__$pname"), + $self->reftype->idstrip, + $self->length || '~0', + $c->rinc('dissect_xdr_'.$self->reftype->idstrip); +} + +sub define_dissector { + my ($self, @path) = @_; + $self->reftype->declare_hfvar(@path, $path[-1]); + $self->declare_ettvar; + $self->SUPER::define_dissector(@path); +} + +package Sym::Type::Vector; # a.k.a Fixed-Length Array +BEGIN{::register_profile( + isa => 'Sym::Type', + roles => { + 'Sym::Type::HasLength' => [qw[ length ft_type ]], + 'Sym::Type::HasReference' => [qw[ reftype ]], + 'Sym::Type::Array' => [qw[ define_dissector ]], + 'Sym::Type::HasSubtree' => [qw[ declare_ettvar ]], + }, +)} + +sub render_caller { + my ($self, $hfid) = @_; + my ($pname) = reverse split /__/, $hfid; + sprintf 'dissect_xdr_vector(tvb, tree, xdrs, hf, %s, %s, "%s", %s, %s)', + $c->rinc('ett_'.$self->idstrip), + $c->rinc("hf_$hfid\__$pname"), + $self->reftype->idstrip, + $self->length || '~0', + $c->rinc('dissect_xdr_'.$self->reftype->idstrip); +} + +package Sym::Variable; +BEGIN{::register_profile( + isa => 'Sym', + attrs => [qw[ type value ]], +)} + +package Context; +BEGIN{::register_profile( + attrs => [qw[ symbols ]], +)} + +sub new { + my ($class) = @_; + + bless { + symbols => {}, + segments => {}, + }, $class; +} + +sub symbol { + my ($self, $ident) = @_; + my $sym = $self->symbols->{$ident} ||= Sym->new; + $sym->ident($ident); + # In XDR syntax specification, defining struct/enum/union will automatically + # create alias having symbol which excludes its prefix type specifier. + # e.g: + # struct foo { int bar; }; will convert to: + # struct foo { int bar; }; typedef struct foo foo; + if ($ident =~ s/^(?:struct|enum|union)\s+//) { + $self->symbol($ident)->bless('Type')->alias($sym); + } + $sym; +} + +sub add_to_set { + my ($self, $set, @elems) = @_; + $self->{sets} ||= {}; + $self->{sets}{$set} ||= []; + push @{ $self->{sets}{$set} }, @elems; +} + +sub get_set { + my ($self, $set) = @_; + $self->{sets}{$set} || []; +} + +# $c->print(...string...); # Does work as regular 'print' +# $c->print({ sym => symbol, body => ...string... }); +# Does treat segment as code block should be referenced. +# It will not printed unless it is referenced from other code by $c->rinc(); +sub print { + my $self = shift; + my $content; + if (ref $_[0]) { + $content = $self->{segments}{ $_[0]{sym} } ||= $_[0]; + $content->{refcnt} //= 0; + $content->{body} = $_[0]{body}; + } else { + $content = join '', @_; + } + push @{ $self->{header_contents} }, $content; + $content; +} + +sub rinc { + my ($self, $sym) = @_; + ($self->{segments}{$sym} ||= { sym => $sym, refcnt => 0 })->{refcnt}++; + $sym; +} + +sub add_header_file { + my ($self, $name, $block) = @_; + + $self->{headers} ||= []; + + local $self->{header_contents} = []; + $self->print("/* *DO NOT MODIFY* this file directly. \n"); + $self->print(" * This file was generated by $0 from libvirt version $ENV{LIBVIRT_VERSION} */\n"); + my $ucname = uc $name; + $self->print("#ifndef _$ucname\_H_\n"); + $self->print("#define _$ucname\_H_\n"); + $block->(); + $self->print("#endif /* _$ucname\_H_ */"); + push @{ $self->{headers} }, [ $name, delete $self->{header_contents} ]; +} + +sub finalize { + my ($self) = @_; + + # Referenced from macro defined in packet-libvirt.h + $self->rinc('dissect_xdr_remote_error'); + + for my $header (@{ $self->{headers} || [] }) { + my ($name, $contents) = @$header; + my $file = File::Spec->catfile($ENV{PWD}, 'libvirt', "$name.h"); + open my $fh, '>', $file + or die "Cannot open file $file: $!"; + CORE::print $fh map { ref($_) ? ($_->{refcnt} ? $_->{body} : ()) : $_ } @$contents; + CORE::print $fh "\n"; + close $fh; + } +} +# / package Context + +package Lexicalizer; +our $Depth; + +INIT { # Wrap all lexicalizer subroutine by debugger function + $Depth = 0; + no strict 'refs'; + no warnings 'redefine'; + for my $name (keys %{ __PACKAGE__.'::' }) { + next if $name =~ /^(?:parse|adv)$/; + my $fullname = __PACKAGE__."::$name"; + next unless defined &$fullname; + my $sub = \&$fullname; + *$fullname = sub { + my (undef, undef, $line) = caller; + ::dbg ' 'x($Depth*2), "$name L$line", "\n"; + local $Depth = $Depth + 1; + $sub->(@_); + }; + } +} + +# Check if passed regexp does match to next token and advance position. +# Return matched string if matched. Die else. +sub adv { + my ($rx) = @_; + ::dbg ' 'x($Depth*2+1), "- adv( $rx ) = "; + # Remove Comments Comments C++ style, PP directives + s{\A(?:\s*(?:/\*.*?\*/|(?://|%).*?(?:\n+|\z)))*\s*}{}s; + if (s/^(?:$rx)//s) { + ::dbg "'$&'\n"; + return $&; + } + ::dbg "UNMATCH\n"; + die; +} + +sub lexor { + my $snapshot = $_; + while (my $handler = shift) { + my $ret = eval { $handler->() }; + if (defined $ret) { + return $ret; + } + $_ = $snapshot; + } + die; +} + +sub decimal_constant { + adv '\-?[0-9]+'; +} + +sub hexadecimal_constant { + adv '\-?0x[0-9A-Fa-f]+'; +} + +sub octal_constant { + adv '\-?0[0-9]+'; +} + +sub constant { + lexor \&hexadecimal_constant, \&octal_constant, \&decimal_constant; +} + +sub identifier { + adv '[_a-zA-Z][_a-zA-Z0-9]*'; +} + +sub value { + lexor \&constant, \&identifier; +} + +sub enum_type_spec { + adv 'enum'; + my $body = lexor \&enum_body, \&identifier; + if (ref $body eq 'ARRAY') { + Sym::Type::Enum->new(members => $body); + } else { + $c->symbol("enum $body")->bless('Type::Enum'); + } +} + +sub enum_body { + adv '{'; + my @members; + do { + my $ident = identifier(); + adv '='; + my $value = value(); + push @members, $c->symbol($ident)->bless('Variable')->value($value); + } while adv('[},]') eq ','; + \@members; +} + +sub struct_type_spec { + adv 'struct'; + my $body = lexor \&struct_body, \&identifier; + if (ref $body eq 'ARRAY') { + Sym::Type::Struct->new(members => $body); + } else { + $c->symbol("struct $body")->bless('Type::Struct'); + } +} + +sub struct_body { + adv '{'; + local $c->{symbols} = { %{ $c->{symbols} } }; + my @members; + while (my $decl = lexor \&declaration, sub { adv('}') }) { + last if $decl eq '}'; + adv ';'; + push @members, $decl; + } + \@members; +} + +sub case_spec { + my @cases; + while (my $case = eval { adv 'case' }) { + push @cases, value(); + adv ':'; + } + my $decl = declaration(); + adv ';'; + [ \@cases, $decl ]; +} + +sub union_type_spec { + adv 'union'; + local $c->{symbols} = { %{ $c->{symbols} } }; + my $body = lexor \&union_body, \&identifier; + if (ref $body eq 'ARRAY') { + Sym::Type::Union->new(decl => $body->[0], case_specs => $body->[1]); + } else { + $c->symbol("union $body")->bless('Type::Union'); + } +} + +sub union_body { + adv 'switch'; adv '\('; + my $decl = declaration(); + adv '\)'; adv '{'; + my @case_specs; + while (my $spec = eval { case_spec() }) { + push @case_specs, $spec; + } + # TODO: parse default + adv '}'; + [ $decl, \@case_specs ]; +} + +sub constant_def { + adv 'const'; + my $ident = identifier(); + adv '='; + my $value = lexor \&constant, \&identifier; + adv ';'; + + $c->symbol($ident)->bless('Variable')->value($value); +} + +sub type_def { + my $ret = lexor sub { + adv 'typedef'; + my $var = declaration(); + my $type = $var->type; + $var->bless('Type')->alias($type); + }, sub { + adv 'enum'; + my $ident = identifier(); + my $body = enum_body(); + $c->symbol("enum $ident")->bless('Type::Enum')->members($body); + }, sub { + adv 'struct'; + my $ident = identifier(); + my $body = struct_body(); + $c->symbol("struct $ident")->bless('Type::Struct')->members($body); + }, sub { + adv 'union'; + my $ident = identifier(); + my $body = union_body(); + $c->symbol("union $ident")->bless('Type::Union') + ->decl($body->[0])->case_specs($body->[1]); + }; + adv ';'; + $ret; +} + +sub type_specifier { + lexor sub { + my $ts = adv '(?:unsigned\s+)?(?:int|hyper|char|short)|float|double|quadruple|bool'; + $ts =~ s/^unsigned\s+/u_/; + $c->symbol($ts)->bless('Type'); + }, \&enum_type_spec, \&struct_type_spec, \&union_type_spec, sub { + my $ident = identifier(); + $c->symbol($ident)->bless('Type'); + }; +} + +sub declaration { + lexor sub { + my $type = lexor sub { + my $type = adv 'opaque|string'; + my $klass = ucfirst $type; + "Sym::Type::$klass"->new; + }, \&type_specifier; + my $ident = identifier(); + # I know that type 'string' does not accept '[]'(fixed length), but I don't care about that + if (my $ex = eval { adv '[<\[]' }) { + my $value = eval { value() }; + die if !$value && $ex ne '<'; # Length could be null if it is variable length + + adv($ex eq '<' ? '>' : '\]'); + if (ref($type) eq 'Sym::Type') { # Expect Array or Vector + my $vtype = ($ex eq '<') ? 'Array' : 'Vector'; + $type = "Sym::Type::$vtype"->new(length => $value, reftype => $type); + } else { + $type->length($value); + $type->bless('Type::Bytes') if $type->isa('Sym::Type::Opaque') && $ex eq '<'; + } + } elsif ($type->can('length')) { # Found String or Opaque but not followed by length specifier + die; + } + + $c->symbol($ident)->bless('Variable')->type($type); + }, sub { + my $type = type_specifier(); + adv '\*'; + my $ident = identifier(); + + $c->symbol($ident)->bless('Variable')->type( + Sym::Type::Pointer->new(reftype => $type)); + }, sub { + adv 'void'; + $c->symbol('void')->bless('Type'); + }; +} + +sub definition { + lexor \&type_def, \&constant_def; +} + +sub parse { + my ($class, $source) = @_; + + my $nlines = @{[$source =~ /\n/g]}; + my @lexs; + while ($source =~ /\S/s) { + (local $_ = $source) =~ s/\A\s*//s; + my $lex = eval { definition() }; + if (!$lex) { + my $line = $nlines - @{[/\n/g]} + 1; + my ($near) = /\A((?:.+?\n){0,5})/s; + die "ERROR: Unexpected character near line $line.\n", + "Please check debug output by enabling \$DEBUG flag at top of script.\n", + join("\n", map { ">> $_" } split /\n/, $near); + } + ::dbg ' 'x($Depth*2), sprintf "*** Found %s<%s>\n", ref($lex), $lex->ident; + push @lexs, $lex; + $source = $_; + } + @lexs; +} + +# Followings are code templates handled by PT +__END__<<DUMMY # Dummy heredoc to disable perl syntax highlighting +@@ Sym::Type#render_dissector +<% +my ($self, $ident) = @_; +return if $self->is_primitive; +%> +static gboolean dissect_xdr_<%= $ident %>(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf) +{ + return <%= $self->dealias->render_caller($self->ident eq $ident ? undef : $ident) %>; +} +@@ Sym::Type::Struct#render_dissector +<% my ($self, $ident) = @_; + my $hfvar = $c->rinc('hf_'.$self->idstrip); +%> +static gboolean dissect_xdr_<%= $ident %>(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf) +{ + goffset start; + proto_item *ti; + + start = xdr_getpos(xdrs); + if (hf == -1) { + ti = proto_tree_add_item(tree, <%= $hfvar %>, tvb, start, -1, ENC_NA); + } else { + header_field_info *hfinfo; + hfinfo = proto_registrar_get_nth(<%= $hfvar %>); + ti = proto_tree_add_item(tree, hf, tvb, start, -1, ENC_NA); + proto_item_append_text(ti, " :: %s", hfinfo->name); + } + tree = proto_item_add_subtree(ti, <%= $c->rinc('ett_'.$self->idstrip) %>); +<% for my $m (@{ $self->members }) { %> + + hf = <%= $c->rinc('hf_'.$ident.'__'.$m->ident) %>; + if (!<%= $m->type->render_caller($ident.'__'.$m->ident) %>) return FALSE; +<% } %> + proto_item_set_len(ti, xdr_getpos(xdrs) - start); + return TRUE; +} +@@ Sym::Type::Enum#render_dissector +<% my ($self, $ident) = @_; %> +static gboolean dissect_xdr_<%= $ident %>(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf) +{ + goffset start; + enum { DUMMY } es; + + start = xdr_getpos(xdrs); + if (xdr_enum(xdrs, (enum_t *)&es)) { + switch ((guint)es) { +<% for my $m (@{ $self->members }) { %> + case <%= $m->value %>: + proto_tree_add_uint_format_value(tree, hf, tvb, start, xdr_getpos(xdrs) - start, (guint)es, "<%= $m->idstrip %>(<%= $m->value %>)"); + return TRUE; +<% } %> + } + } else { + proto_tree_add_text(tree, tvb, start, -1, "(unknown)"); + } + return FALSE; +} +@@ Sym::Type::Union#render_dissector +<% +my ($self, $ident) = @_; +my $decl_type = $self->decl->type->idstrip; +%> +static gboolean dissect_xdr_<%= $ident %>(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf) +{ + gboolean rc = TRUE; + goffset start; + <%= $decl_type %> type = 0; + + start = xdr_getpos(xdrs); + if (!xdr_<%= $decl_type %>(xdrs, &type)) + return FALSE; + switch (type) { +<% for my $cs (@{ $self->case_specs }) { + my ($vals, $decl) = @$cs; +%> +<% for my $v (@$vals) { %> + case <%= $v %>: +<% } %> + hf = <%= $c->rinc('hf_'.$ident.'__'.$decl->ident) %>; + rc = <%= $decl->type->render_caller($ident.'__'.$decl->ident) %>; break; +<% } %> + } + if (!rc) { + proto_tree_add_text(tree, tvb, start, -1, "(unknown)"); + } + return rc; +} +@@ macro.hfvars +#define VIR_DYNAMIC_HFSET \ +<% for my $prog (@{ $_->{programs} }) { %> + { &hf_<%= $prog %>_procedure,\ + { "procedure", "libvirt.procedure",\ + FT_INT32, BASE_DEC,\ + VALS(<%= $prog %>_procedure_strings), 0x0,\ + NULL, HFILL}\ + },\ +<% } %> +<% for my $hf (@{ $_->{hfvars} }) { %> + { &<%= $hf->{segment}{sym} %>,\ + { "<%= $hf->{name} %>", "libvirt.<%= $hf->{abbrev} %>",\ + FT_<%= $hf->{ft_type} %>, BASE_<%= $hf->{hf_base} %>,\ + NULL, 0x0,\ + NULL, HFILL}\ + },\ +<% } %> +/* End of #define VIR_DYNAMIC_HFSET */ + +@@ macro.ettvars +#define VIR_DYNAMIC_ETTSET \ +<% for my $ett (@{ $_->{ettvars} }) { %> +&<%= $ett %>,\ +<% } %> +/* End of #define VIR_DYNAMIC_ETTSET */ + +@@ code.dissectorlist +static const vir_dissector_index_t <%= $_->{name} %>_dissectors[] = { +<% for my $d (@{ $_->{dissectors} }) { %> + { <%= $d->{value} %>, <%= $d->{args} %>, <%= $d->{ret} %>, <%= $d->{msg} %> }, +<% } %> +}; +static const gsize <%= $_->{name} %>_dissectors_len = array_length(<%= $_->{name} %>_dissectors); +@@ code.procedure_strings +static const value_string <%= $_->{name} %>_procedure_strings[] = { +<% for my $proc (@{ $_->{procedures} }) { + my $ident = $proc->ident; + $ident =~ s/^$_->{name}_proc_//i; +%> + { <%= $proc->value %>, "<%= $ident %>" }, +<% } %> + { 0, NULL } +}; +@@ code.program_strings +static const value_string program_strings[] = { +<% for my $prog (map uc, @{ $_->{programs} }) { %> + { <%= $c->symbol("$prog\_PROGRAM")->value %>, "<%= $prog %>" }, +<% } %> + { 0, NULL } +}; +@@ code.program_data +static const void *program_data[][VIR_PROGRAM_LAST] = { +<% for my $p (@{ $_->{programs} }) { %> + { &hf_<%= $p %>_procedure, <%= $p %>_procedure_strings, <%= $p %>_dissectors, &<%= $p %>_dissectors_len }, +<% } %> +}; + +static const void * +get_program_data(guint32 prog, enum vir_program_data_index index) +{ + if (index < VIR_PROGRAM_LAST) { + switch (prog) { +<% my $i = 0; %> +<% for my $prog (@{ $_->{programs} }) { %> + case <%= uc($prog) %>_PROGRAM: + return program_data[<%= $i++ %>][index]; +<% } %> + } + } + return NULL; +} diff --git a/tools/wireshark/util/make-dissector-reg b/tools/wireshark/util/make-dissector-reg new file mode 100755 index 0000000..b130499 --- /dev/null +++ b/tools/wireshark/util/make-dissector-reg @@ -0,0 +1,198 @@ +#! /bin/sh +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. +# +# Copied from Wireshark(http://www.wireshark.org/) + +# +# The first argument is the directory in which the source files live. +# +srcdir="$1" +shift + +# +# The second argument is either "plugin" or "dissectors"; if it's +# "plugin", we build a plugin.c for a plugin, and if it's +# "dissectors", we build a register.c for libwireshark. +# +registertype="$1" +shift +if [ "$registertype" = plugin ] +then + outfile="plugin.c" +elif [ "$registertype" = dissectors ] +then + outfile="register.c" +else + echo "Unknown output type '$registertype'" 1>&2 + exit 1 +fi + +# +# All subsequent arguments are the files to scan. +# +rm -f ${outfile}-tmp +echo '/* Do not modify this file. */' >${outfile}-tmp +echo '/* It is created automatically by the Makefile. */'>>${outfile}-tmp +if [ "$registertype" = plugin ] +then + cat <<"EOF" >>${outfile}-tmp +#include "config.h" + +#include <gmodule.h> + +/* plugins are DLLs */ +#define WS_BUILD_DLL +#include "ws_symbol_export.h" + +#ifndef ENABLE_STATIC +WS_DLL_PUBLIC_NOEXTERN const gchar version[] = VERSION; + +/* Start the functions we need for the plugin stuff */ + +WS_DLL_PUBLIC_NOEXTERN void +plugin_register (void) +{ +EOF +# +# Build code to call all the protocol registration routines. +# +for f in "$@" +do + if [ -f $f ] + then + srcfile=$f + else + srcfile=$srcdir/$f + fi + grep '^proto_register_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';' +done | sed -e 's/^.*://' -e 's/^\([a-z_0-9A-Z]*\).*/ {extern void \1 (void); \1 ();}/' >>${outfile}-tmp +for f in "$@" +do + if [ -f $f ] + then + srcfile=$f + else + srcfile=$srcdir/$f + fi + grep '^void proto_register_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';' +done | sed -e 's/^.*://' -e 's/^void \([a-z_0-9A-Z]*\).*/ {extern void \1 (void); \1 ();}/' >>${outfile}-tmp +else + cat <<"EOF" >>${outfile}-tmp +#include "register.h" +void +register_all_protocols(register_cb cb, gpointer client_data) +{ +EOF +# +# Build code to call all the protocol registration routines. +# +for f in "$@" +do + if [ -f $f ] + then + srcfile=$f + else + srcfile=$srcdir/$f + fi + grep '^proto_register_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';' +done | sed -e 's/^.*://' -e 's/^\([a-z_0-9A-Z]*\).*/ {extern void \1 (void); if(cb) (*cb)(RA_REGISTER, \"\1\", client_data); \1 ();}/' >>${outfile}-tmp +for f in "$@" +do + if [ -f $f ] + then + srcfile=$f + else + srcfile=$srcdir/$f + fi + grep '^void proto_register_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';' +done | sed -e 's/^.*://' -e 's/^void \([a-z_0-9A-Z]*\).*/ {extern void \1 (void); if(cb) (*cb)(RA_REGISTER, \"\1\", client_data); \1 ();}/' >>${outfile}-tmp + +fi +echo '}' >>${outfile}-tmp + + +# +# Build code to call all the protocol handoff registration routines. +# +if [ "$registertype" = plugin ] +then + cat <<"EOF" >>${outfile}-tmp +WS_DLL_PUBLIC_NOEXTERN void +plugin_reg_handoff(void) +{ +EOF +for f in "$@" +do + if [ -f $f ] + then + srcfile=$f + else + srcfile=$srcdir/$f + fi + grep '^proto_reg_handoff_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';' +done | sed -e 's/^.*://' -e 's/^\([a-z_0-9A-Z]*\).*/ {extern void \1 (void); \1 ();}/' >>${outfile}-tmp +for f in "$@" +do + if [ -f $f ] + then + srcfile=$f + else + srcfile=$srcdir/$f + fi + grep '^void proto_reg_handoff_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';' +done | sed -e 's/^.*://' -e 's/^void \([a-z_0-9A-Z]*\).*/ {extern void \1 (void); \1 ();}/' >>${outfile}-tmp +else + cat <<"EOF" >>${outfile}-tmp +void +register_all_protocol_handoffs(register_cb cb, gpointer client_data) +{ +EOF +for f in "$@" +do + if [ -f $f ] + then + srcfile=$f + else + srcfile=$srcdir/$f + fi + grep '^proto_reg_handoff_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';' +done | sed -e 's/^.*://' -e 's/^\([a-z_0-9A-Z]*\).*/ {extern void \1 (void); if(cb) (*cb)(RA_HANDOFF, \"\1\", client_data); \1 ();}/' >>${outfile}-tmp +for f in "$@" +do + if [ -f $f ] + then + srcfile=$f + else + srcfile=$srcdir/$f + fi + grep '^void proto_reg_handoff_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';' +done | sed -e 's/^.*://' -e 's/^void \([a-z_0-9A-Z]*\).*/ {extern void \1 (void); if(cb) (*cb)(RA_HANDOFF, \"\1\", client_data); \1 ();}/' >>${outfile}-tmp +fi +echo '}' >>${outfile}-tmp +if [ "$registertype" = plugin ] +then + echo '#endif' >>${outfile}-tmp +else + cat <<"EOF" >>${outfile}-tmp +gulong register_count(void) +{ +EOF + proto_regs=`grep RA_REGISTER ${outfile}-tmp | wc -l` + handoff_regs=`grep RA_HANDOFF ${outfile}-tmp | wc -l` + echo " return $proto_regs + $handoff_regs;" >>${outfile}-tmp + echo '}' >>${outfile}-tmp +fi + +# Only overwrite outfile if it differs from newly generated file +diff ${outfile}-tmp ${outfile} >/dev/null || mv ${outfile}-tmp ${outfile} -- 1.8.1.5

On 30.09.2013 14:15, Yuto KAWAMURA(kawamuray) wrote:
From: "Yuto KAWAMURA(kawamuray)" <kawamuray.dadada@gmail.com>
Introduce Wireshark dissector plugin which adds support to Wireshark for dissecting libvirt RPC protocol. Added following files to build Wireshark dissector from libvirt source tree. * tools/wireshark/*: Source tree of Wireshark dissector plugin.
Added followings to configure.ac or Makefile.am. configure.ac * --with-wireshark-dissector: Enable support for building Wireshark dissector. * --with-ws-plugindir: Specify wireshark plugin directory that dissector will installed. * Added tools/wireshark/{Makefile,src/Makefile} to AC_CONFIG_FILES. Makefile.am * Added tools/wireshark/ to SUBDIR. --- Makefile.am | 3 +- cfg.mk | 8 +- configure.ac | 72 ++- tools/wireshark/Makefile.am | 29 + tools/wireshark/README.md | 31 + tools/wireshark/src/.gitignore | 4 + tools/wireshark/src/Makefile.am | 42 ++ tools/wireshark/src/packet-libvirt.c | 512 ++++++++++++++++ tools/wireshark/src/packet-libvirt.h | 128 ++++ tools/wireshark/util/genxdrstub.pl | 1009 +++++++++++++++++++++++++++++++ tools/wireshark/util/make-dissector-reg | 198 ++++++ 11 files changed, 2030 insertions(+), 6 deletions(-) create mode 100644 tools/wireshark/Makefile.am create mode 100644 tools/wireshark/README.md create mode 100644 tools/wireshark/src/.gitignore create mode 100644 tools/wireshark/src/Makefile.am create mode 100644 tools/wireshark/src/packet-libvirt.c create mode 100644 tools/wireshark/src/packet-libvirt.h create mode 100755 tools/wireshark/util/genxdrstub.pl create mode 100755 tools/wireshark/util/make-dissector-reg
I think we want tools/wireshark/src/.gitignore merged to global $(srcdir)/.gitignore. Moreover, I've noticed a strange behavior when dissecting some strings. Try to dissect an opening sequence. The client calls CONNECT_OPEN function with 2 arguments: libvirt.remote_connect_open_args.name libvirt.remote_connect_open_args.flags While @flags are correctly dissected, the @name isn't. For example, while executing "virsh -c qemu+tcp:///system list" I got this: 0000 00 00 00 38 20 00 80 86 00 00 00 01 00 00 00 01 ...8 ........... 0010 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 01 ................ 0020 00 00 00 0e 71 65 6d 75 3a 2f 2f 2f 73 79 73 74 ....qemu:///syst 0030 65 6d 00 00 00 00 00 00 em...... where the @name is at 0x1c-10x1f and @flags at 0x20-0x24. However, some strings are still dissected correctly And when running 'virsh domfstrim $dom' I've encountered: [Dissector bug, protocol libvirt: proto.c:2541: failed assertion "hfinfo->type == FT_STRING || hfinfo->type == FT_STRINGZ"] Besides this I like this approach the most and once you solve the string dissecting bugs I will give you my ACK. Michal

From: "Yuto KAWAMURA(kawamuray)" <kawamuray.dadada@gmail.com> Add directory tools/wireshark/samples/ and libvirt-sample.pdml which is sample output of dissector. --- tools/wireshark/samples/libvirt-sample.pdml | 206 ++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 tools/wireshark/samples/libvirt-sample.pdml diff --git a/tools/wireshark/samples/libvirt-sample.pdml b/tools/wireshark/samples/libvirt-sample.pdml new file mode 100644 index 0000000..f6a4c28 --- /dev/null +++ b/tools/wireshark/samples/libvirt-sample.pdml @@ -0,0 +1,206 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/xsl" href="pdml2html.xsl"?> +<!-- *** + This file has been reduced for ineffective packets. + Real output contains more and more elements, but structure + and hierarchy of XML is same as this exmaple. +*** --> +<!-- You can find pdml2html.xsl in /usr/share/wireshark or at http://anonsvn.wireshark.org/trunk/wireshark/pdml2html.xsl. --> +<pdml version="0" creator="wireshark/1.10.2" time="Thu Sep 19 18:09:24 2013" capture_file=""> +<!-- Program = REMOTE, Procedure = AUTH_LIST --> +<packet> + <proto name="libvirt" showname="Libvirt" size="28" pos="66"> + <field name="libvirt.length" showname="length: 28" size="4" pos="66" show="28" value="0000001c"/> + <field name="libvirt.program" showname="program: REMOTE (0x20008086)" size="4" pos="70" show="0x20008086" value="20008086"/> + <field name="libvirt.version" showname="version: 1" size="4" pos="74" show="1" value="00000001"/> + <field name="libvirt.procedure" showname="procedure: AUTH_LIST (66)" size="4" pos="78" show="66" value="00000042"/> + <field name="libvirt.type" showname="type: CALL (0)" size="4" pos="82" show="0" value="00000000"/> + <field name="libvirt.serial" showname="serial: 0" size="4" pos="86" show="0" value="00000000"/> + <field name="libvirt.status" showname="status: OK (0)" size="4" pos="90" show="0" value="00000000"/> + </proto> +</packet> +<packet> + <proto name="libvirt" showname="Libvirt" size="36" pos="66"> + <field name="libvirt.length" showname="length: 36" size="4" pos="66" show="36" value="00000024"/> + <field name="libvirt.program" showname="program: REMOTE (0x20008086)" size="4" pos="70" show="0x20008086" value="20008086"/> + <field name="libvirt.version" showname="version: 1" size="4" pos="74" show="1" value="00000001"/> + <field name="libvirt.procedure" showname="procedure: AUTH_LIST (66)" size="4" pos="78" show="66" value="00000042"/> + <field name="libvirt.type" showname="type: REPLY (1)" size="4" pos="82" show="1" value="00000001"/> + <field name="libvirt.serial" showname="serial: 0" size="4" pos="86" show="0" value="00000000"/> + <field name="libvirt.status" showname="status: OK (0)" size="4" pos="90" show="0" value="00000000"/> + <field name="libvirt.remote_auth_list_ret" showname="remote_auth_list_ret" size="8" pos="94" show="" value=""> + <field name="libvirt.remote_auth_list_ret.types" showname="types :: remote_auth_type<1>" size="8" pos="94" show="" value=""> + <field name="libvirt.remote_auth_list_ret.types.types" showname="types: REMOTE_AUTH_NONE(0)" size="4" pos="98" show="0" value="00000000"/> + </field> + </field> + </proto> +</packet> + +<!-- Program = REMOTE, Procedure = CONNECT_OPEN --> +<packet> + <proto name="libvirt" showname="Libvirt" size="56" pos="66"> + <field name="libvirt.length" showname="length: 56" size="4" pos="66" show="56" value="00000038"/> + <field name="libvirt.program" showname="program: REMOTE (0x20008086)" size="4" pos="70" show="0x20008086" value="20008086"/> + <field name="libvirt.version" showname="version: 1" size="4" pos="74" show="1" value="00000001"/> + <field name="libvirt.procedure" showname="procedure: CONNECT_OPEN (1)" size="4" pos="78" show="1" value="00000001"/> + <field name="libvirt.type" showname="type: CALL (0)" size="4" pos="82" show="0" value="00000000"/> + <field name="libvirt.serial" showname="serial: 2" size="4" pos="86" show="2" value="00000002"/> + <field name="libvirt.status" showname="status: OK (0)" size="4" pos="90" show="0" value="00000000"/> + <field name="libvirt.remote_connect_open_args" showname="remote_connect_open_args" size="8" pos="94" show="" value=""> + <field name="libvirt.remote_connect_open_args.name" showname="name: (null)" size="4" pos="94" show="" value=""/> + <field name="libvirt.remote_connect_open_args.flags" showname="flags: 15" size="4" pos="98" show="15" value="0000000f"/> + </field> + </proto> +</packet> +<packet> + <proto name="libvirt" showname="Libvirt" size="28" pos="66"> + <field name="libvirt.length" showname="length: 28" size="4" pos="66" show="28" value="0000001c"/> + <field name="libvirt.program" showname="program: REMOTE (0x20008086)" size="4" pos="70" show="0x20008086" value="20008086"/> + <field name="libvirt.version" showname="version: 1" size="4" pos="74" show="1" value="00000001"/> + <field name="libvirt.procedure" showname="procedure: CONNECT_OPEN (1)" size="4" pos="78" show="1" value="00000001"/> + <field name="libvirt.type" showname="type: REPLY (1)" size="4" pos="82" show="1" value="00000001"/> + <field name="libvirt.serial" showname="serial: 2" size="4" pos="86" show="2" value="00000002"/> + <field name="libvirt.status" showname="status: OK (0)" size="4" pos="90" show="0" value="00000000"/> + </proto> +</packet> + +<!-- Program = REMOTE, Procedure = DOMAIN_LOOKUP_BY_NAME --> +<packet> + <proto name="libvirt" showname="Libvirt" size="40" pos="66"> + <field name="libvirt.length" showname="length: 40" size="4" pos="66" show="40" value="00000028"/> + <field name="libvirt.program" showname="program: REMOTE (0x20008086)" size="4" pos="70" show="0x20008086" value="20008086"/> + <field name="libvirt.version" showname="version: 1" size="4" pos="74" show="1" value="00000001"/> + <field name="libvirt.procedure" showname="procedure: DOMAIN_LOOKUP_BY_NAME (23)" size="4" pos="78" show="23" value="00000017"/> + <field name="libvirt.type" showname="type: CALL (0)" size="4" pos="82" show="0" value="00000000"/> + <field name="libvirt.serial" showname="serial: 4" size="4" pos="86" show="4" value="00000004"/> + <field name="libvirt.status" showname="status: OK (0)" size="4" pos="90" show="0" value="00000000"/> + <field name="libvirt.remote_domain_lookup_by_name_args" showname="remote_domain_lookup_by_name_args" size="12" pos="94" show="" value=""> + <field name="libvirt.remote_domain_lookup_by_name_args.name" showname="name: domain1" size="12" pos="94" show="domain1" value="00000007646f6d61696e3100"/> + </field> + </proto> +</packet> +<packet> + <proto name="libvirt" showname="Libvirt" size="60" pos="66"> + <field name="libvirt.length" showname="length: 60" size="4" pos="66" show="60" value="0000003c"/> + <field name="libvirt.program" showname="program: REMOTE (0x20008086)" size="4" pos="70" show="0x20008086" value="20008086"/> + <field name="libvirt.version" showname="version: 1" size="4" pos="74" show="1" value="00000001"/> + <field name="libvirt.procedure" showname="procedure: DOMAIN_LOOKUP_BY_NAME (23)" size="4" pos="78" show="23" value="00000017"/> + <field name="libvirt.type" showname="type: REPLY (1)" size="4" pos="82" show="1" value="00000001"/> + <field name="libvirt.serial" showname="serial: 4" size="4" pos="86" show="4" value="00000004"/> + <field name="libvirt.status" showname="status: OK (0)" size="4" pos="90" show="0" value="00000000"/> + <field name="libvirt.remote_domain_lookup_by_name_ret" showname="remote_domain_lookup_by_name_ret" size="32" pos="94" show="" value=""> + <field name="libvirt.remote_domain_lookup_by_name_ret.dom" showname="dom :: remote_nonnull_domain" size="32" pos="94" show="" value=""> + <field name="libvirt.remote_nonnull_domain.name" showname="name: domain1" size="12" pos="94" show="domain1" value="00000007646f6d61696e3100"/> + <field name="libvirt.remote_nonnull_domain.uuid" showname="uuid: 4c8b6b6d0a2907334b8398a02c3a4710" size="16" pos="106" show="4c:8b:6b:6d:0a:29:07:33:4b:83:98:a0:2c:3a:47:10" value="4c8b6b6d0a2907334b8398a02c3a4710"/> + <field name="libvirt.remote_nonnull_domain.id" showname="id: -1" size="4" pos="122" show="-1" value="ffffffff"/> + </field> + </field> + </proto> +</packet> + +<!-- Program = REMOTE, Procedure = NODE_GET_CPU_MAP --> +<packet> + <proto name="libvirt" showname="Libvirt" size="40" pos="66"> + <field name="libvirt.length" showname="length: 40" size="4" pos="66" show="40" value="00000028"/> + <field name="libvirt.program" showname="program: REMOTE (0x20008086)" size="4" pos="70" show="0x20008086" value="20008086"/> + <field name="libvirt.version" showname="version: 1" size="4" pos="74" show="1" value="00000001"/> + <field name="libvirt.procedure" showname="procedure: NODE_GET_CPU_MAP (293)" size="4" pos="78" show="293" value="00000125"/> + <field name="libvirt.type" showname="type: CALL (0)" size="4" pos="82" show="0" value="00000000"/> + <field name="libvirt.serial" showname="serial: 5" size="4" pos="86" show="5" value="00000005"/> + <field name="libvirt.status" showname="status: OK (0)" size="4" pos="90" show="0" value="00000000"/> + <field name="libvirt.remote_node_get_cpu_map_args" showname="remote_node_get_cpu_map_args" size="12" pos="94" show="" value=""> + <field name="libvirt.remote_node_get_cpu_map_args.need_map" showname="need_map: 0" size="4" pos="94" show="0" value="00000000"/> + <field name="libvirt.remote_node_get_cpu_map_args.need_online" showname="need_online: 0" size="4" pos="98" show="0" value="00000000"/> + <field name="libvirt.remote_node_get_cpu_map_args.flags" showname="flags: 0" size="4" pos="102" show="0" value="00000000"/> + </field> + </proto> +</packet> +<packet> + <proto name="libvirt" showname="Libvirt" size="40" pos="66"> + <field name="libvirt.length" showname="length: 40" size="4" pos="66" show="40" value="00000028"/> + <field name="libvirt.program" showname="program: REMOTE (0x20008086)" size="4" pos="70" show="0x20008086" value="20008086"/> + <field name="libvirt.version" showname="version: 1" size="4" pos="74" show="1" value="00000001"/> + <field name="libvirt.procedure" showname="procedure: NODE_GET_CPU_MAP (293)" size="4" pos="78" show="293" value="00000125"/> + <field name="libvirt.type" showname="type: REPLY (1)" size="4" pos="82" show="1" value="00000001"/> + <field name="libvirt.serial" showname="serial: 5" size="4" pos="86" show="5" value="00000005"/> + <field name="libvirt.status" showname="status: OK (0)" size="4" pos="90" show="0" value="00000000"/> + <field name="libvirt.remote_node_get_cpu_map_ret" showname="remote_node_get_cpu_map_ret" size="12" pos="94" show="" value=""> + <field name="libvirt.remote_node_get_cpu_map_ret.cpumap" showname="cpumap: " size="4" pos="94" show="00:00:00:00" value="00000000"/> + <field name="libvirt.remote_node_get_cpu_map_ret.online" showname="online: 0" size="4" pos="98" show="0" value="00000000"/> + <field name="libvirt.remote_node_get_cpu_map_ret.ret" showname="ret: 8" size="4" pos="102" show="8" value="00000008"/> + </field> + </proto> +</packet> + +<!-- Program = REMOTE, Procedure = DOMAIN_GET_BLKIO_PARAMETERS --> +<packet> + <proto name="libvirt" showname="Libvirt" size="68" pos="66"> + <field name="libvirt.length" showname="length: 68" size="4" pos="66" show="68" value="00000044"/> + <field name="libvirt.program" showname="program: REMOTE (0x20008086)" size="4" pos="70" show="0x20008086" value="20008086"/> + <field name="libvirt.version" showname="version: 1" size="4" pos="74" show="1" value="00000001"/> + <field name="libvirt.procedure" showname="procedure: DOMAIN_GET_BLKIO_PARAMETERS (206)" size="4" pos="78" show="206" value="000000ce"/> + <field name="libvirt.type" showname="type: CALL (0)" size="4" pos="82" show="0" value="00000000"/> + <field name="libvirt.serial" showname="serial: 7" size="4" pos="86" show="7" value="00000007"/> + <field name="libvirt.status" showname="status: OK (0)" size="4" pos="90" show="0" value="00000000"/> + <field name="libvirt.remote_domain_get_blkio_parameters_args" showname="remote_domain_get_blkio_parameters_args" size="40" pos="94" show="" value=""> + <field name="libvirt.remote_domain_get_blkio_parameters_args.dom" showname="dom :: remote_nonnull_domain" size="32" pos="94" show="" value=""> + <field name="libvirt.remote_nonnull_domain.name" showname="name: domain1" size="12" pos="94" show="domain1" value="00000007646f6d61696e3100"/> + <field name="libvirt.remote_nonnull_domain.uuid" showname="uuid: 4c8b6b6d0a2907334b8398a02c3a4710" size="16" pos="106" show="4c:8b:6b:6d:0a:29:07:33:4b:83:98:a0:2c:3a:47:10" value="4c8b6b6d0a2907334b8398a02c3a4710"/> + <field name="libvirt.remote_nonnull_domain.id" showname="id: -1" size="4" pos="122" show="-1" value="ffffffff"/> + </field> + <field name="libvirt.remote_domain_get_blkio_parameters_args.nparams" showname="nparams: 2" size="4" pos="126" show="2" value="00000002"/> + <field name="libvirt.remote_domain_get_blkio_parameters_args.flags" showname="flags: 4" size="4" pos="130" show="4" value="00000004"/> + </field> + </proto> +</packet> +<packet> + <proto name="libvirt" showname="Libvirt" size="84" pos="66"> + <field name="libvirt.length" showname="length: 84" size="4" pos="66" show="84" value="00000054"/> + <field name="libvirt.program" showname="program: REMOTE (0x20008086)" size="4" pos="70" show="0x20008086" value="20008086"/> + <field name="libvirt.version" showname="version: 1" size="4" pos="74" show="1" value="00000001"/> + <field name="libvirt.procedure" showname="procedure: DOMAIN_GET_BLKIO_PARAMETERS (206)" size="4" pos="78" show="206" value="000000ce"/> + <field name="libvirt.type" showname="type: REPLY (1)" size="4" pos="82" show="1" value="00000001"/> + <field name="libvirt.serial" showname="serial: 7" size="4" pos="86" show="7" value="00000007"/> + <field name="libvirt.status" showname="status: OK (0)" size="4" pos="90" show="0" value="00000000"/> + <field name="libvirt.remote_domain_get_blkio_parameters_ret" showname="remote_domain_get_blkio_parameters_ret" size="56" pos="94" show="" value=""> + <field name="libvirt.remote_domain_get_blkio_parameters_ret.params" showname="params :: remote_typed_param<2>" size="52" pos="94" show="" value=""> + <field name="libvirt.remote_domain_get_blkio_parameters_ret.params.params" showname="params :: remote_typed_param" size="20" pos="98" show="" value=""> + <field name="libvirt.remote_typed_param.field" showname="field: weight" size="12" pos="98" show="weight" value="000000067765696768740000"/> + <field name="libvirt.remote_typed_param_value.ui" showname="ui: 0" size="4" pos="114" show="0" value="00000000"/> + </field> + <field name="libvirt.remote_domain_get_blkio_parameters_ret.params.params" showname="params :: remote_typed_param" size="28" pos="118" show="" value=""> + <field name="libvirt.remote_typed_param.field" showname="field: device_weight" size="20" pos="118" show="device_weight" value="0000000d6465766963655f776569676874000000"/> + <field name="libvirt.remote_typed_param_value.s" showname="s: " size="4" pos="142" show="" value="00000000"/> + </field> + </field> + <field name="libvirt.remote_domain_get_blkio_parameters_ret.nparams" showname="nparams: 0" size="4" pos="146" show="0" value="00000000"/> + </field> + </proto> +</packet> + +<!-- Error reply (struct remote_error) --> +<packet> + <proto name="libvirt" showname="Libvirt" size="360" pos="66"> + <field name="libvirt.length" showname="length: 360" size="4" pos="66" show="360" value="00000168"/> + <field name="libvirt.program" showname="program: REMOTE (0x20008086)" size="4" pos="70" show="0x20008086" value="20008086"/> + <field name="libvirt.version" showname="version: 1" size="4" pos="74" show="1" value="00000001"/> + <field name="libvirt.procedure" showname="procedure: DOMAIN_GET_VCPUS (20)" size="4" pos="78" show="20" value="00000014"/> + <field name="libvirt.type" showname="type: REPLY (1)" size="4" pos="82" show="1" value="00000001"/> + <field name="libvirt.serial" showname="serial: 7" size="4" pos="86" show="7" value="00000007"/> + <field name="libvirt.status" showname="status: ERROR (1)" size="4" pos="90" show="1" value="00000001"/> + <field name="libvirt.remote_error" showname="remote_error" size="44" pos="94" show="" value=""> + <field name="libvirt.remote_error.code" showname="code: 55" size="4" pos="94" show="55" value="00000037"/> + <field name="libvirt.remote_error.domain" showname="domain: 10" size="4" pos="98" show="10" value="0000000a"/> + <field name="libvirt.remote_error.message" showname="message: (null)" size="4" pos="102" show="" value=""/> + <field name="libvirt.remote_error.level" showname="level: 136" size="4" pos="106" show="136" value="00000088"/> + <field name="libvirt.remote_error.dom" showname="dom: (null)" size="4" pos="110" show="" value=""/> + <field name="libvirt.remote_error.str1" showname="str1: (null)" size="4" pos="114" show="" value=""/> + <field name="libvirt.remote_error.str2" showname="str2: (null)" size="4" pos="118" show="" value=""/> + <field name="libvirt.remote_error.str3" showname="str3: (null)" size="4" pos="122" show="" value=""/> + <field name="libvirt.remote_error.int1" showname="int1: -1819417411" size="4" pos="126" show="-1819417411" value="938de4bd"/> + <field name="libvirt.remote_error.int2" showname="int2: -1662811729" size="4" pos="130" show="-1662811729" value="9ce381af"/> + <field name="libvirt.remote_error.net" showname="net: (null)" size="4" pos="134" show="" value=""/> + </field> + </proto> +</packet> +</pdml> -- 1.8.1.5
participants (2)
-
Michal Privoznik
-
Yuto KAWAMURA(kawamuray)