From: "Yuto KAWAMURA(kawamuray)" <kawamuray.dadada(a)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.
---
.gitignore | 2 +
Makefile.am | 3 +-
cfg.mk | 8 +-
configure.ac | 72 ++-
tools/wireshark/Makefile.am | 22 +
tools/wireshark/README.md | 31 +
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 | 1011 +++++++++++++++++++++++++++++++
tools/wireshark/util/make-dissector-reg | 198 ++++++
11 files changed, 2023 insertions(+), 6 deletions(-)
create mode 100644 tools/wireshark/Makefile.am
create mode 100644 tools/wireshark/README.md
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/.gitignore b/.gitignore
index 7005500..96b7211 100644
--- a/.gitignore
+++ b/.gitignore
@@ -226,6 +226,8 @@
/tools/virsh-*-edit.c
/tools/virt-*-validate
/tools/virt-sanlock-cleanup
+/tools/wireshark/src/plugin.c
+/tools/wireshark/src/libvirt
/update.log
GPATH
GRTAGS
diff --git a/Makefile.am b/Makefile.am
index eb88fee..9847ff0 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -22,7 +22,8 @@ GENHTML = genhtml
SUBDIRS = . gnulib/lib include src daemon tools docs gnulib/tests \
tests po examples/object-events examples/hellolibvirt \
examples/dominfo examples/domsuspend examples/apparmor \
- examples/xml/nwfilter examples/openauth examples/systemtap
+ examples/xml/nwfilter examples/openauth examples/systemtap \
+ tools/wireshark
ACLOCAL_AMFLAGS = -I m4
diff --git a/cfg.mk b/cfg.mk
index 5591065..d82d925 100644
--- a/cfg.mk
+++ b/cfg.mk
@@ -987,10 +987,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$$
@@ -998,7 +998,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$$
@@ -1031,7 +1031,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 = \
^(tools/|examples/|include/libvirt/(virterror|libvirt-(qemu|lxc))\.h$$)
diff --git a/configure.ac b/configure.ac
index f5dccf1..cd30782 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2502,6 +2502,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,
@@ -2585,7 +2649,9 @@ AC_CONFIG_FILES([\
examples/openauth/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([])
@@ -2746,6 +2812,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..b6fa57c
--- /dev/null
+++ b/tools/wireshark/Makefile.am
@@ -0,0 +1,22 @@
+## Process this file with automake to produce Makefile.in
+
+# Copyright (C) 2013 Yuto KAWAMURA(kawamuray) <kawamuray.dadada(a)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
+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/Makefile.am b/tools/wireshark/src/Makefile.am
new file mode 100644
index 0000000..81e7041
--- /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(a)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 -module
+
+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..2d0350c
--- /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(a)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 call 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 not_null;
+
+ start = xdr_getpos(xdrs);
+ if (!xdr_bool(xdrs, ¬_null)) {
+ proto_tree_add_item(tree, hf_libvirt_unknown, tvb, start, -1, ENC_NA);
+ return FALSE;
+ }
+ if (not_null) {
+ return dissect(tvb, tree, xdrs, hf);
+ } else {
+ 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;
+ }
+}
+
+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(a)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..3358a6b
--- /dev/null
+++ b/tools/wireshark/util/genxdrstub.pl
@@ -0,0 +1,1011 @@
+#!/usr/bin/env perl
+# genxdrstub.pl --- Generate C header file which used by packet-libvirt.[ch]
+#
+# Copyright (C) 2013 Yuto KAWAMURA(kawamuray) <kawamuray.dadada(a)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(a)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 =~ /^\@\(a)\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 render_caller ]],
+ },
+)}
+sub ft_type { (shift)->reftype->ft_type }
+
+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..4f8b4f2
--- /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
+mv ${outfile}-tmp ${outfile}
--
1.8.3.2