[libvirt] [PATCH v3 1/2] Introduce Libvirt Wireshark dissector

Yuto KAWAMURA(kawamuray) kawamuray.dadada at gmail.com
Mon Sep 30 12:15:55 UTC 2013


From: "Yuto KAWAMURA(kawamuray)" <kawamuray.dadada at 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 at 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 at 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 at 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 at 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 at 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 at 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




More information about the libvir-list mailing list