/*
 * packet-ocfs2.c
 *
 * Routines for OCFS2's networking protocol disassembly (o2net and o2dlm)
 * The OCFS2 cluster file system is available in the mainline Linux kernel.
 *
 * Copyright (C) 2006, 2011 Oracle. All rights reserved.
 *
 * Authors:
 * Kurt Hackel		<kurt.hackel@oracle.com>
 * Zach Brown		<zach.brown@oracle.com>
 * Sunil Mushran	<sunil.mushran@oracle.com>
 * Jeff Liu		<jeff.liu@oracle.com>
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include "config.h"


#include <epan/packet.h>
#include "packet-tcp.h"

void proto_register_ocfs2(void);
void proto_reg_handoff_ocfs2(void);

static gint ett_ocfs2 = -1;
static gint ett_dtm_lock_flags = -1;
static gint ett_mres_flags = -1;
static gint ett_migrate_lockres_locks = -1;
static gint ett_query_nodeinfo = -1;

static int proto_ocfs2 = -1;
static int hf_msg_magic = -1;
static int hf_msg_data_len = -1;
static int hf_msg_msg_type = -1;
static int hf_msg_sys_status = -1;
static int hf_msg_status = -1;
static int hf_msg_key = -1;
static int hf_msg_msg_num = -1;
static int hf_msg_pad = -1;

static int hf_dlm_node_idx = -1;
static int hf_dlm_lock_flags = -1;
static int hf_dlm_lock_flag_unused1 = -1;
static int hf_dlm_lock_flag_orphan = -1;
static int hf_dlm_lock_flag_parentable = -1;
static int hf_dlm_lock_flag_block = -1;
static int hf_dlm_lock_flag_local = -1;
static int hf_dlm_lock_flag_valblk = -1;
static int hf_dlm_lock_flag_noqueue = -1;
static int hf_dlm_lock_flag_convert = -1;
static int hf_dlm_lock_flag_nodlckwt = -1;
static int hf_dlm_lock_flag_unlock = -1;
static int hf_dlm_lock_flag_cancel = -1;
static int hf_dlm_lock_flag_deqall = -1;
static int hf_dlm_lock_flag_invvalblk = -1;
static int hf_dlm_lock_flag_syncsts = -1;
static int hf_dlm_lock_flag_timeout = -1;
static int hf_dlm_lock_flag_sngldlck = -1;
static int hf_dlm_lock_flag_findlocal = -1;
static int hf_dlm_lock_flag_proc_owned = -1;
static int hf_dlm_lock_flag_xid = -1;
static int hf_dlm_lock_flag_xid_conflict = -1;
static int hf_dlm_lock_flag_force = -1;
static int hf_dlm_lock_flag_revvalblk = -1;
static int hf_dlm_lock_flag_unused2 = -1;
static int hf_dlm_lock_flag_migration = -1;
static int hf_dlm_lock_flag_put_lvb = -1;
static int hf_dlm_lock_flag_get_lvb = -1;
static int hf_dlm_lock_flag_recovery = -1;
static int hf_dlm_am_flags = -1;
static int hf_dlm_fr_flags = -1;
static int hf_dlm_namelen = -1;
static int hf_dlm_name = -1;
static int hf_dlm_cookie = -1;
static int hf_dlm_requested_type = -1;
static int hf_dlm_lvb1 = -1;
static int hf_dlm_lvb2 = -1;
static int hf_dlm_lvb3 = -1;
static int hf_dlm_ast_type = -1;
static int hf_dlm_blocked_type = -1;
static int hf_dlm_dead_node = -1;
static int hf_dlm_domain_name_len = -1;
static int hf_dlm_domain_name = -1;
static int hf_dlm_proto_ver = -1;
static int hf_dlm_fs_proto_ver = -1;
static int hf_dlm_node_map = -1;
static int hf_dlm_master = -1;
static int hf_dlm_new_master = -1;
static int hf_dlm_mres_num_locks = -1;
static int hf_dlm_mres_flags = -1;
static int hf_dlm_mres_flag_recovery = -1;
static int hf_dlm_mres_flag_migration = -1;
static int hf_dlm_mres_flag_all_done = -1;
static int hf_dlm_mres_total_locks = -1;
static int hf_dlm_mres_mig_cookie = -1;
static int hf_dlm_mres_list = -1;
static int hf_dlm_mres_ml_flags = -1;
static int hf_dlm_mres_type = -1;
static int hf_dlm_mres_convert_type = -1;
static int hf_dlm_mres_highest_blocked = -1;
static int hf_dlm_mres_node = -1;
static int hf_dlm_qr_node = -1;
static int hf_dlm_qr_numregions = -1;
static int hf_dlm_qr_namelen = -1;
static int hf_dlm_qr_domain = -1;
static int hf_dlm_qr_region = -1;
static int hf_dlm_qn_nodenum = -1;
static int hf_dlm_qn_numnodes = -1;
static int hf_dlm_qn_namelen = -1;
static int hf_dlm_qn_domain = -1;
static int hf_dlm_qn_node = -1;
static int hf_dlm_qn_port = -1;
static int hf_dlm_qn_ip = -1;
static int hf_dlm_reco_lvb = -1;
static int hf_dlm_pad8 = -1;
static int hf_dlm_pad16 = -1;
static int hf_dlm_pad32 = -1;
static int hf_dlm_flags = -1;
static int hf_dlm_payload = -1;

#define O2NM_MAX_NAME_LEN	64
#define O2NM_NODE_MAP_IN_BYTES	32

#define OCFS2_DENTRY_LOCK_INO_START 18

/*
 * generic o2net constants
 */

#define O2NET_MSG_MAGIC			0xfa55
#define O2NET_MSG_STATUS_MAGIC		0xfa56
#define O2NET_MSG_KEEP_REQ_MAGIC	0xfa57
#define O2NET_MSG_KEEP_RESP_MAGIC	0xfa58
static const value_string o2net_magic[] = {
	{ O2NET_MSG_MAGIC,	     "Request" },
	{ O2NET_MSG_STATUS_MAGIC,    "Response" },
	{ O2NET_MSG_KEEP_REQ_MAGIC,  "Keepalive Request" },
	{ O2NET_MSG_KEEP_RESP_MAGIC, "Keepalive Response" },
	{ 0x0000,  NULL  }
};

/* DLM constants */
#define DLM_LVB_LEN  64
#define DLM_MOD_KEY (0x666c6172)

#if 0
enum dlm_query_join_response {
	JOIN_DISALLOW = 0,
	JOIN_OK,
	JOIN_OK_NO_MAP,
	JOIN_PROTOCOL_MISMATCH
};
#endif

/* DLM lock modes */
enum {
	LKM_IVMODE = -1,
	LKM_NLMODE = 0,
	LKM_CRMODE,
	LKM_CWMODE,
	LKM_PRMODE,
	LKM_PWMODE,
	LKM_EXMODE,
	LKM_MAXMODE
};

static const value_string dlm_lock_modes[] = {
	{ LKM_IVMODE, "IV" },
	{ LKM_NLMODE, "NL" },
	{ LKM_CRMODE, "CR" },
	{ LKM_CWMODE, "CW" },
	{ LKM_PRMODE, "PR" },
	{ LKM_PWMODE, "PW" },
	{ LKM_EXMODE, "EX" },
	{ 0x0000,  NULL  }
};

/* DLM message types */
enum {
	DLM_MASTER_REQUEST_MSG		= 500,
	DLM_UNUSED_MSG1			= 501,
	DLM_ASSERT_MASTER_MSG		= 502,
	DLM_CREATE_LOCK_MSG		= 503,
	DLM_CONVERT_LOCK_MSG		= 504,
	DLM_PROXY_AST_MSG		= 505,
	DLM_UNLOCK_LOCK_MSG		= 506,
	DLM_DEREF_LOCKRES_MSG		= 507,
	DLM_MIGRATE_REQUEST_MSG		= 508,
	DLM_MIG_LOCKRES_MSG		= 509,
	DLM_QUERY_JOIN_MSG		= 510,
	DLM_ASSERT_JOINED_MSG		= 511,
	DLM_CANCEL_JOIN_MSG		= 512,
	DLM_EXIT_DOMAIN_MSG		= 513,
	DLM_MASTER_REQUERY_MSG		= 514,
	DLM_LOCK_REQUEST_MSG		= 515,
	DLM_RECO_DATA_DONE_MSG		= 516,
	DLM_BEGIN_RECO_MSG		= 517,
	DLM_FINALIZE_RECO_MSG		= 518,
	DLM_QUERY_REGION_MSG		= 519,
	DLM_QUERY_NODEINFO_MSG		= 520
};

static const value_string dlm_magic[] = {
	{ DLM_MASTER_REQUEST_MSG,  "Master Request" },
	{ DLM_UNUSED_MSG1,	   "Unused 1" },
	{ DLM_ASSERT_MASTER_MSG,   "Assert Master" },
	{ DLM_CREATE_LOCK_MSG,	   "Create Lock" },
	{ DLM_CONVERT_LOCK_MSG,	   "Convert Lock" },
	{ DLM_PROXY_AST_MSG,	   "Proxy AST" },
	{ DLM_UNLOCK_LOCK_MSG,	   "Unlock Lock" },
	{ DLM_DEREF_LOCKRES_MSG,   "Deref Lockres" },
	{ DLM_MIGRATE_REQUEST_MSG, "Migrate Request" },
	{ DLM_MIG_LOCKRES_MSG,	   "Migrate Lockres" },
	{ DLM_QUERY_JOIN_MSG,	   "Query Join" },
	{ DLM_ASSERT_JOINED_MSG,   "Assert Join" },
	{ DLM_CANCEL_JOIN_MSG,	   "Cancel Join" },
	{ DLM_EXIT_DOMAIN_MSG,	   "Exit Domain" },
	{ DLM_MASTER_REQUERY_MSG,  "Master Requery" },
	{ DLM_LOCK_REQUEST_MSG,	   "Lock Request" },
	{ DLM_RECO_DATA_DONE_MSG,  "Recovery Data Done" },
	{ DLM_BEGIN_RECO_MSG,	   "Begin Recovery" },
	{ DLM_FINALIZE_RECO_MSG,   "Finalize Recovery" },
	{ DLM_QUERY_REGION_MSG,	   "Query Region" },
	{ DLM_QUERY_NODEINFO_MSG,  "Query Node Info" },
	{ 0x0000, NULL }
};

value_string_ext ext_dlm_magic = VALUE_STRING_EXT_INIT(dlm_magic);


enum {
	DLM_GRANTED_LIST = 0,
	DLM_CONVERTING_LIST,
	DLM_BLOCKED_LIST,
	DLM_MAX_LIST
};

static const value_string dlm_lockres_list[] = {
	{ DLM_GRANTED_LIST,    "Granted" },
	{ DLM_CONVERTING_LIST, "Converting" },
	{ DLM_BLOCKED_LIST,    "Blocked" },
	{ 0x0000, NULL }
};


#if 0
enum dlm_status {
	DLM_NORMAL = 0,
	DLM_GRANTED,
	DLM_DENIED,
	DLM_DENIED_NOLOCKS,
	DLM_WORKING,
	DLM_BLOCKED,
	DLM_BLOCKED_ORPHAN,
	DLM_DENIED_GRACE_PERIOD,
	DLM_SYSERR,
	DLM_NOSUPPORT,
	DLM_CANCELGRANT,
	DLM_IVLOCKID,
	DLM_SYNC,
	DLM_BADTYPE,
	DLM_BADRESOURCE,
	DLM_MAXHANDLES,
	DLM_NOCLINFO,
	DLM_NOLOCKMGR,
	DLM_NOPURGED,
	DLM_BADARGS,
	DLM_VOID,
	DLM_NOTQUEUED,
	DLM_IVBUFLEN,
	DLM_CVTUNGRANT,
	DLM_BADPARAM,
	DLM_VALNOTVALID,
	DLM_REJECTED,
	DLM_ABORT,
	DLM_CANCEL,
	DLM_IVRESHANDLE,
	DLM_DEADLOCK,
	DLM_DENIED_NOASTS,
	DLM_FORWARD,
	DLM_TIMEOUT,
	DLM_IVGROUPID,
	DLM_VERS_CONFLICT,
	DLM_BAD_DEVICE_PATH,
	DLM_NO_DEVICE_PERMISSION,
	DLM_NO_CONTROL_DEVICE,
	DLM_RECOVERING,
	DLM_MIGRATING,
	DLM_MAXSTATS
};

static const value_string dlm_errnames[] = {
	{ DLM_NORMAL,		    "DLM_NORMAL" },
	{ DLM_GRANTED,		    "DLM_GRANTED" },
	{ DLM_DENIED,		    "DLM_DENIED" },
	{ DLM_DENIED_NOLOCKS,	    "DLM_DENIED_NOLOCKS" },
	{ DLM_WORKING,		    "DLM_WORKING" },
	{ DLM_BLOCKED,		    "DLM_BLOCKED" },
	{ DLM_BLOCKED_ORPHAN,	    "DLM_BLOCKED_ORPHAN" },
	{ DLM_DENIED_GRACE_PERIOD,  "DLM_DENIED_GRACE_PERIOD" },
	{ DLM_SYSERR,		    "DLM_SYSERR" },
	{ DLM_NOSUPPORT,	    "DLM_NOSUPPORT" },
	{ DLM_CANCELGRANT,	    "DLM_CANCELGRANT" },
	{ DLM_IVLOCKID,		    "DLM_IVLOCKID" },
	{ DLM_SYNC,		    "DLM_SYNC" },
	{ DLM_BADTYPE,		    "DLM_BADTYPE" },
	{ DLM_BADRESOURCE,	    "DLM_BADRESOURCE" },
	{ DLM_MAXHANDLES,	    "DLM_MAXHANDLES" },
	{ DLM_NOCLINFO,		    "DLM_NOCLINFO" },
	{ DLM_NOLOCKMGR,	    "DLM_NOLOCKMGR" },
	{ DLM_NOPURGED,		    "DLM_NOPURGED" },
	{ DLM_BADARGS,		    "DLM_BADARGS" },
	{ DLM_VOID,		    "DLM_VOID" },
	{ DLM_NOTQUEUED,	    "DLM_NOTQUEUED" },
	{ DLM_IVBUFLEN,		    "DLM_IVBUFLEN" },
	{ DLM_CVTUNGRANT,	    "DLM_CVTUNGRANT" },
	{ DLM_BADPARAM,		    "DLM_BADPARAM" },
	{ DLM_VALNOTVALID,	    "DLM_VALNOTVALID" },
	{ DLM_REJECTED,		    "DLM_REJECTED" },
	{ DLM_ABORT,		    "DLM_ABORT" },
	{ DLM_CANCEL,		    "DLM_CANCEL" },
	{ DLM_IVRESHANDLE,	    "DLM_IVRESHANDLE" },
	{ DLM_DEADLOCK,		    "DLM_DEADLOCK" },
	{ DLM_DENIED_NOASTS,	    "DLM_DENIED_NOASTS" },
	{ DLM_FORWARD,		    "DLM_FORWARD" },
	{ DLM_TIMEOUT,		    "DLM_TIMEOUT" },
	{ DLM_IVGROUPID,	    "DLM_IVGROUPID" },
	{ DLM_VERS_CONFLICT,	    "DLM_VERS_CONFLICT" },
	{ DLM_BAD_DEVICE_PATH,	    "DLM_BAD_DEVICE_PATH" },
	{ DLM_NO_DEVICE_PERMISSION, "DLM_NO_DEVICE_PERMISSION" },
	{ DLM_NO_CONTROL_DEVICE ,   "DLM_NO_CONTROL_DEVICE " },
	{ DLM_RECOVERING,	    "DLM_RECOVERING" },
	{ DLM_MIGRATING,	    "DLM_MIGRATING" },
	{ DLM_MAXSTATS,		    "DLM_MAXSTATS" },
	{ 0x0000,  NULL }
};

value_string_ext ext_dlm_errnames = VALUE_STRING_EXT_INIT(dlm_errnames);

static const value_string dlm_errmsgs[] = {
	{ DLM_NORMAL,		    "request in progress" },
	{ DLM_GRANTED,		    "request granted" },
	{ DLM_DENIED,		    "request denied" },
	{ DLM_DENIED_NOLOCKS,	    "request denied, out of system resources" },
	{ DLM_WORKING,		    "async request in progress" },
	{ DLM_BLOCKED,		    "lock request blocked" },
	{ DLM_BLOCKED_ORPHAN,	    "lock request blocked by a orphan lock" },
	{ DLM_DENIED_GRACE_PERIOD,  "topological change in progress" },
	{ DLM_SYSERR,		    "system error" },
	{ DLM_NOSUPPORT,	    "unsupported" },
	{ DLM_CANCELGRANT,	    "can't cancel convert: already granted" },
	{ DLM_IVLOCKID,		    "bad lockid" },
	{ DLM_SYNC,		    "synchronous request granted" },
	{ DLM_BADTYPE,		    "bad resource type" },
	{ DLM_BADRESOURCE,	    "bad resource handle" },
	{ DLM_MAXHANDLES,	    "no more resource handles" },
	{ DLM_NOCLINFO,		    "can't contact cluster manager" },
	{ DLM_NOLOCKMGR,	    "can't contact lock manager" },
	{ DLM_NOPURGED,		    "can't contact purge daemon" },
	{ DLM_BADARGS,		    "bad api args" },
	{ DLM_VOID,		    "no status" },
	{ DLM_NOTQUEUED,	    "NOQUEUE was specified and request failed" },
	{ DLM_IVBUFLEN,		    "invalid resource name length" },
	{ DLM_CVTUNGRANT,	    "attempted to convert ungranted lock" },
	{ DLM_BADPARAM,		    "invalid lock mode specified" },
	{ DLM_VALNOTVALID,	    "value block has been invalidated" },
	{ DLM_REJECTED,		    "request rejected, unrecognized client" },
	{ DLM_ABORT,		    "blocked lock request cancelled" },
	{ DLM_CANCEL,		    "conversion request cancelled" },
	{ DLM_IVRESHANDLE,	    "invalid resource handle" },
	{ DLM_DEADLOCK,		    "deadlock recovery refused this request" },
	{ DLM_DENIED_NOASTS,	    "failed to allocate AST" },
	{ DLM_FORWARD,		    "request must wait for primary's response" },
	{ DLM_TIMEOUT,		    "timeout value for lock has expired" },
	{ DLM_IVGROUPID,	    "invalid group specification" },
	{ DLM_VERS_CONFLICT,	    "version conflicts prevent request handling" },
	{ DLM_BAD_DEVICE_PATH,	    "Locks device does not exist or path wrong" },
	{ DLM_NO_DEVICE_PERMISSION, "Client has insufficient perms for device" },
	{ DLM_NO_CONTROL_DEVICE,    "Cannot set options on opened device " },
	{ DLM_RECOVERING,	    "lock resource being recovered" },
	{ DLM_MIGRATING,	    "lock resource being migrated" },
	{ DLM_MAXSTATS,		    "invalid error number" },
	{ 0x0000,  NULL }
};

value_string_ext ext_dlm_errmsgs = VALUE_STRING_EXT_INIT(dlm_errmsgs);
#endif


#define DLM_ASSERT_MASTER_MLE_CLEANUP      0x00000001
#define DLM_ASSERT_MASTER_REQUERY          0x00000002
#define DLM_ASSERT_MASTER_FINISH_MIGRATION 0x00000004
static const value_string dlm_assert_master_flags[] = {
	{ DLM_ASSERT_MASTER_MLE_CLEANUP,      "cleanup" },
	{ DLM_ASSERT_MASTER_REQUERY,          "requery" },
	{ DLM_ASSERT_MASTER_FINISH_MIGRATION, "finish" },
	{ 0x0000,  NULL }
};

#define DLM_FINALIZE_STAGE2  0x01
static const value_string dlm_finalize_reco_flags[] = {
	{ DLM_FINALIZE_STAGE2, "stage2" },
	{ 0x0000,  NULL }
};

enum dlm_ast_type {
	DLM_AST = 0,
	DLM_BAST,
	DLM_ASTUNLOCK
};

static const value_string dlm_proxy_ast_types[] = {
	{ DLM_AST,       "AST" },
	{ DLM_BAST,      "BAST" },
	{ DLM_ASTUNLOCK, "Unlock AST (unused)" },
	{ 0x0000,  NULL }
};

/* DLM lock flag types */
enum {
	DLM_LOCK_FLAGS_PUT_LVB = 0x20000000,
	DLM_LOCK_FLAGS_GET_LVB = 0x40000000
};


static int dlm_cookie_handler(proto_tree *tree, tvbuff_t *tvb, guint offset, int hf_cookie)
{
	proto_item *item;
	guint64 cookie;
	guint64 seq;
	guint8 node_idx;

	item = proto_tree_add_item(tree, hf_cookie, tvb, offset, 8, ENC_BIG_ENDIAN);
	cookie = tvb_get_ntoh64(tvb, offset);

	cookie >>= 56;
	node_idx = (guint8)((cookie >> 56) & G_GINT64_CONSTANT(0xff));
	seq = cookie & G_GINT64_CONSTANT(0x00ffffffffffffff);

	proto_item_append_text(item, " (%u:%" G_GINT64_MODIFIER "u)", node_idx, seq);

	return offset + 8;
}

static int dlm_lkm_flags_handler(proto_tree *tree, tvbuff_t *tvb, guint offset,
				 guint32 *dlm_lock_flags_ptr)
{
	static int * const flags[] = {
		&hf_dlm_lock_flag_unused1,
		&hf_dlm_lock_flag_orphan,
		&hf_dlm_lock_flag_parentable,
		&hf_dlm_lock_flag_block,
		&hf_dlm_lock_flag_local,
		&hf_dlm_lock_flag_valblk,
		&hf_dlm_lock_flag_noqueue,
		&hf_dlm_lock_flag_convert,
		&hf_dlm_lock_flag_nodlckwt,
		&hf_dlm_lock_flag_unlock,
		&hf_dlm_lock_flag_cancel,
		&hf_dlm_lock_flag_deqall,
		&hf_dlm_lock_flag_invvalblk,
		&hf_dlm_lock_flag_syncsts,
		&hf_dlm_lock_flag_timeout,
		&hf_dlm_lock_flag_sngldlck,
		&hf_dlm_lock_flag_findlocal,
		&hf_dlm_lock_flag_proc_owned,
		&hf_dlm_lock_flag_xid,
		&hf_dlm_lock_flag_xid_conflict,
		&hf_dlm_lock_flag_force,
		&hf_dlm_lock_flag_revvalblk,
		&hf_dlm_lock_flag_unused2,
		&hf_dlm_lock_flag_migration,
		&hf_dlm_lock_flag_put_lvb,
		&hf_dlm_lock_flag_get_lvb,
		&hf_dlm_lock_flag_recovery,
		NULL
	};

	if(dlm_lock_flags_ptr != NULL){
		*dlm_lock_flags_ptr = tvb_get_ntohl(tvb, offset);
	}
	proto_tree_add_bitmask_with_flags(tree, tvb, offset, hf_dlm_lock_flags,
					ett_dtm_lock_flags, flags, ENC_BIG_ENDIAN, BMT_NO_INT | BMT_NO_FALSE | BMT_NO_TFS);
	return offset + 4;
}

static int dlm_name_handler(proto_tree *tree, tvbuff_t *tvb, guint offset, int namelen)
{
	guint8 lock_type;
	guint64 blkno;
	proto_item *ti;

	ti = proto_tree_add_item(tree, hf_dlm_name, tvb, offset, namelen, ENC_ASCII|ENC_NA);
	lock_type = tvb_get_guint8(tvb, offset);
	if (lock_type == 'N') {
		blkno = tvb_get_ntoh64(tvb, offset + OCFS2_DENTRY_LOCK_INO_START);
		proto_item_append_text(ti, "%08x", (unsigned int)blkno);
	}

	return offset + namelen;
}

/*
 * We would like to get one whole lockres into a single network
 * message whenever possible.  Generally speaking, there will be
 * at most one dlm_lock on a lockres for each node in the cluster,
 * plus (infrequently) any additional locks coming in from userdlm.
 *
 * struct _dlm_lockres_page
 * {
 *	dlm_migratable_lockres mres;
 *	dlm_migratable_lock ml[DLM_MAX_MIGRATABLE_LOCKS];
 *	guint8 pad[DLM_MIG_LOCKRES_RESERVED];
 * };
 *
 * from ../cluster/tcp.h
 *    NET_MAX_PAYLOAD_BYTES  (4096 - sizeof(net_msg))
 *    (roughly 4080 bytes)
 * and sizeof(dlm_migratable_lockres) = 112 bytes
 * and sizeof(dlm_migratable_lock) = 16 bytes
 *
 * Choosing DLM_MAX_MIGRATABLE_LOCKS=240 and
 * DLM_MIG_LOCKRES_RESERVED=128 means we have this:
 *
 *  (DLM_MAX_MIGRATABLE_LOCKS * sizeof(dlm_migratable_lock)) +
 *     sizeof(dlm_migratable_lockres) + DLM_MIG_LOCKRES_RESERVED =
 *        NET_MAX_PAYLOAD_BYTES
 *  (240 * 16) + 112 + 128 = 4080
 *
 * So a lockres would need more than 240 locks before it would
 * use more than one network packet to recover.  Not too bad.
 */

static void dissect_dlm_migrate_lockres(proto_tree *tree, tvbuff_t *tvb, int offset)
{
	unsigned int i;
	guint32 num_locks;

	static int * const mres_flags[] = {
		&hf_dlm_mres_flag_recovery,
		&hf_dlm_mres_flag_migration,
		&hf_dlm_mres_flag_all_done,
		NULL
	};

	/* master */
	proto_tree_add_item(tree, hf_dlm_master, tvb, offset, 1, ENC_BIG_ENDIAN);
	offset += 1;

	/* lockname_len */
	proto_tree_add_item(tree, hf_dlm_namelen, tvb, offset, 1, ENC_BIG_ENDIAN);
	offset += 1;

	/* num_locks */
	proto_tree_add_item_ret_uint(tree, hf_dlm_mres_num_locks, tvb, offset, 1, ENC_BIG_ENDIAN, &num_locks);
	offset += 1;

	/* no locks were found on this lockres! done! */
	if (num_locks == 0)
		return;

	/* flags */
	proto_tree_add_bitmask_with_flags(tree, tvb, offset, hf_dlm_mres_flags,
					ett_mres_flags, mres_flags, ENC_BIG_ENDIAN, BMT_NO_INT | BMT_NO_FALSE | BMT_NO_TFS);
	offset += 1;

	/* total_locks */
	proto_tree_add_item(tree, hf_dlm_mres_total_locks, tvb, offset, 4, ENC_BIG_ENDIAN);
	offset += 4;

	/* mig_cookie */
	offset = dlm_cookie_handler(tree, tvb, offset, hf_dlm_mres_mig_cookie);

	/* lockname */
	proto_tree_add_item(tree, hf_dlm_name, tvb, offset, 32, ENC_ASCII|ENC_NA);
	offset += 32;

	/* lvb */
	proto_tree_add_item(tree, hf_dlm_lvb1, tvb, offset, 24, ENC_NA);
	offset += 24;

	proto_tree_add_item(tree, hf_dlm_lvb2, tvb, offset, 24, ENC_NA);
	offset += 24;

	proto_tree_add_item(tree, hf_dlm_lvb3, tvb, offset, 16, ENC_NA);
	offset += 16;

	/* dlm_migratable_lock */
	for (i = 0; i < num_locks; i++) {
		proto_tree *subtree;

		subtree = proto_tree_add_subtree_format(tree, tvb, offset, 16,
					   ett_migrate_lockres_locks, NULL, "Locks%d: ", i + 1);

		/* cookie */
		offset = dlm_cookie_handler(subtree, tvb, offset, hf_dlm_mres_mig_cookie);

		proto_tree_add_item(subtree, hf_dlm_pad8, tvb, offset, 1, ENC_BIG_ENDIAN);
		offset += 1;

		/* list */
		proto_tree_add_item(subtree, hf_dlm_mres_list, tvb, offset, 1, ENC_BIG_ENDIAN);
		offset += 1;

		/* flags */
		proto_tree_add_item(subtree, hf_dlm_mres_ml_flags, tvb, offset, 1, ENC_BIG_ENDIAN);
		offset += 1;

		/* type */
		proto_tree_add_item(subtree, hf_dlm_mres_type, tvb, offset, 1, ENC_BIG_ENDIAN);
		offset += 1;

		/* convert_type */
		proto_tree_add_item(subtree, hf_dlm_mres_convert_type, tvb, offset, 1, ENC_BIG_ENDIAN);
		offset += 1;

		/* highest_blocked */
		proto_tree_add_item(subtree, hf_dlm_mres_highest_blocked, tvb, offset, 1, ENC_BIG_ENDIAN);
		offset += 1;

		proto_tree_add_item(subtree, hf_dlm_mres_node, tvb, offset, 1, ENC_BIG_ENDIAN);
		offset += 1;
	}
}

static void
dlm_fmt_revision( gchar *result, guint32 revision )
{
	g_snprintf( result, ITEM_LABEL_LENGTH, "%d.%02d", (guint8)(( revision & 0xFF00 ) >> 8), (guint8)(revision & 0xFF) );
}

#define DLM_QUERY_JOIN_REQUEST_OFF_DLMPROTO	4
#define DLM_QUERY_JOIN_REQUEST_OFF_FSPROTO	6
#define DLM_QUERY_JOIN_REQUEST_OFF_DOMAIN	8
#define DLM_QUERY_JOIN_REQUEST_OFF_NODEMAP	72
#define DLM_QUERY_JOIN_REQUEST_LEN_DLMPROTO	2
#define DLM_QUERY_JOIN_REQUEST_LEN_FSPROTO	2
#define DLM_QUERY_JOIN_REQUEST_LEN_DOMAIN	64
#define DLM_QUERY_JOIN_REQUEST_LEN_NODEMAP	32

#define DLM_QUERY_JOIN_REQUEST_OLD_LEN		100
static void dissect_dlm_query_join_request(proto_tree *tree, tvbuff_t *tvb, int offset)
{
	guint8 cc, *node_bits_array;
	guint8 *node_map;
	gint len;
	unsigned int i, j;
	gboolean oldver = FALSE;

	node_bits_array = (guint8 *)wmem_alloc0(wmem_packet_scope(), (DLM_QUERY_JOIN_REQUEST_LEN_NODEMAP*8)+1);

	len = tvb_reported_length_remaining(tvb, offset);
	if (len == DLM_QUERY_JOIN_REQUEST_OLD_LEN)
		oldver = TRUE;

	/* node_idx */
	proto_tree_add_item(tree, hf_dlm_node_idx, tvb, offset, 1, ENC_BIG_ENDIAN);
	offset += 1;

	proto_tree_add_item(tree, hf_dlm_pad16, tvb, offset, 2, ENC_BIG_ENDIAN);
	offset += 2;

	/* name_len */
	proto_tree_add_item(tree, hf_dlm_domain_name_len, tvb, offset, 1, ENC_BIG_ENDIAN);
	offset += 1;

	if (!oldver)
	{
	    /* dlm_proto */
	    proto_tree_add_item(tree, hf_dlm_proto_ver, tvb, offset, 2, ENC_BIG_ENDIAN);
	    offset += 2;

	    /* fs_proto */
	    proto_tree_add_item(tree, hf_dlm_fs_proto_ver, tvb, offset, 2, ENC_BIG_ENDIAN);
	    offset += 2;
	}

	/* domain */
	proto_tree_add_item(tree, hf_dlm_domain_name, tvb, offset, 64, ENC_ASCII|ENC_NA);
	offset += 64;

	/* node_map */
	node_map = (guint8 *)tvb_memdup(wmem_packet_scope(), tvb, offset, DLM_QUERY_JOIN_REQUEST_LEN_NODEMAP);

	for (i = 0; i < DLM_QUERY_JOIN_REQUEST_LEN_NODEMAP; i++) {
		cc = node_map[i];
		for (j = 0; j < 8; j++)
			node_bits_array[i * 8 + j] =
					(((cc >> j) & 1) ? '1' : '0');
	}

	/* NULL terminate string */
	node_bits_array[(DLM_QUERY_JOIN_REQUEST_LEN_NODEMAP*8)] = 0;
	proto_tree_add_bytes_format_value(tree, hf_dlm_node_map, tvb, offset, DLM_QUERY_JOIN_REQUEST_LEN_NODEMAP, NULL, "%s", node_bits_array);
}

#define O2HB_MAX_REGION_NAME_LEN 32

static void dissect_dlm_query_region(proto_tree *tree, tvbuff_t *tvb,
				     guint offset)
{
	guint32 i, num_regions;
	guchar *region;

	/* qr_node */
	proto_tree_add_item(tree, hf_dlm_qr_node, tvb, offset, 1, ENC_BIG_ENDIAN);
	offset += 1;

	/* qr_numregions */
	proto_tree_add_item_ret_uint(tree, hf_dlm_qr_numregions, tvb, offset, 1, ENC_BIG_ENDIAN, &num_regions);
	offset += 1;

	/* qr_namelen */
	proto_tree_add_item(tree, hf_dlm_qr_namelen, tvb, offset, 1, ENC_BIG_ENDIAN);
	offset += 1;

	proto_tree_add_item(tree, hf_dlm_pad8, tvb, offset, 1, ENC_BIG_ENDIAN);
	offset += 1;

	/* qr_domain */
	proto_tree_add_item(tree, hf_dlm_qr_domain, tvb, offset, 64, ENC_ASCII|ENC_NA);
	offset += 64;

	/* qr_regions */
	for (i = 0; i < num_regions; i++, offset += O2HB_MAX_REGION_NAME_LEN) {
		region = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, O2HB_MAX_REGION_NAME_LEN, ENC_ASCII);
		proto_tree_add_string_format(tree, hf_dlm_qr_region, tvb, offset, 1,
					   region, "Region%d: %s", i + 1, region);
	}
}

static void dissect_dlm_query_nodeinfo(proto_tree *tree, tvbuff_t *tvb, guint offset)
{
	guint32 i, num_nodes;

	/* qn_nodenum */
	proto_tree_add_item(tree, hf_dlm_qn_nodenum, tvb, offset, 1, ENC_BIG_ENDIAN);
	offset += 1;

	/* qn_numnodes */
	proto_tree_add_item_ret_uint(tree, hf_dlm_qn_numnodes, tvb, offset, 1, ENC_BIG_ENDIAN, &num_nodes);
	offset += 1;

	/* qn_namelen */
	proto_tree_add_item(tree, hf_dlm_qn_namelen, tvb, offset, 1, ENC_BIG_ENDIAN);
	offset += 1;

	/* qn_domain */
	proto_tree_add_item(tree, hf_dlm_qn_domain, tvb, offset, 64, ENC_ASCII|ENC_NA);
	offset += 64;

	/* qn_nodes */
	for (i = 0; i < num_nodes; i++) {
		proto_tree *subtree;

		/* ni_nodenum */
		subtree = proto_tree_add_subtree_format(tree, tvb, offset, 8,
					   ett_query_nodeinfo, NULL, "Node%d: ", i+1);

		proto_tree_add_item(subtree, hf_dlm_qn_node, tvb, offset, 1, ENC_BIG_ENDIAN);
		offset += 1;

		proto_tree_add_item(subtree, hf_dlm_pad8, tvb, offset, 1, ENC_BIG_ENDIAN);
		offset += 1;

		proto_tree_add_item(subtree, hf_dlm_qn_port, tvb, offset, 2, ENC_BIG_ENDIAN);
		offset += 2;

		proto_tree_add_item(subtree, hf_dlm_qn_ip, tvb, offset, 4, ENC_BIG_ENDIAN);
		offset += 4;
	}
}

static int dissect_master_msg(proto_tree *tree, tvbuff_t *tvb, int offset, int hf_flag)
{
	guint32 namelen;

	proto_tree_add_item(tree, hf_dlm_node_idx, tvb, offset, 1, ENC_NA);
	offset += 1;

	proto_tree_add_item_ret_uint(tree, hf_dlm_namelen, tvb, offset, 1, ENC_BIG_ENDIAN, &namelen);
	offset += 1;

	proto_tree_add_item(tree, hf_dlm_pad16, tvb, offset, 2, ENC_BIG_ENDIAN);
	offset += 2;

	if (hf_flag == -1)
		proto_tree_add_item(tree, hf_dlm_flags, tvb, offset, 4, ENC_BIG_ENDIAN);
	else
		proto_tree_add_item(tree, hf_flag, tvb, offset, 4, ENC_BIG_ENDIAN);
	offset += 4;

	return dlm_name_handler(tree, tvb, offset, namelen);
}

static int dissect_create_lock_msg(proto_tree *tree, tvbuff_t *tvb, int offset,
				   guint32 *dlm_lock_flags_ptr)
{
	guint32 namelen;

	offset = dlm_cookie_handler(tree, tvb, offset, hf_dlm_cookie);
	offset = dlm_lkm_flags_handler(tree, tvb, offset, dlm_lock_flags_ptr);

	proto_tree_add_item(tree, hf_dlm_pad8, tvb, offset, 1, ENC_NA);
	offset += 1;

	proto_tree_add_item(tree, hf_dlm_node_idx, tvb, offset, 1, ENC_NA);
	offset += 1;

	proto_tree_add_item(tree, hf_dlm_requested_type, tvb, offset, 1, ENC_BIG_ENDIAN);
	offset += 1;

	proto_tree_add_item_ret_uint(tree, hf_dlm_namelen, tvb, offset, 1, ENC_BIG_ENDIAN, &namelen);
	offset += 1;

	dlm_name_handler(tree, tvb, offset, namelen);
	return offset + O2NM_MAX_NAME_LEN;
}

static int dissect_convert_lock_msg(proto_tree *tree, tvbuff_t *tvb, int offset)
{
	guint32 dlm_lock_flags;
	offset = dissect_create_lock_msg(tree, tvb, offset, &dlm_lock_flags);

	if(dlm_lock_flags & DLM_LOCK_FLAGS_PUT_LVB){
		proto_tree_add_item(tree, hf_dlm_lvb1, tvb, offset, 24, ENC_NA);
		offset += 24;

		proto_tree_add_item(tree, hf_dlm_lvb2, tvb, offset, 24, ENC_NA);
		offset += 24;

		proto_tree_add_item(tree, hf_dlm_lvb3, tvb, offset, 16, ENC_NA);
		offset += 16;
	}

	return offset;
}

static int dissect_unlock_msg(proto_tree *tree, tvbuff_t *tvb, int offset)
{
	guint32 namelen;
	guint32 dlm_lock_flags;

	offset = dlm_cookie_handler(tree, tvb, offset, hf_dlm_cookie);
	offset = dlm_lkm_flags_handler(tree, tvb, offset, &dlm_lock_flags);

	proto_tree_add_item(tree, hf_dlm_pad16, tvb, offset, 2, ENC_BIG_ENDIAN);
	offset += 2;

	proto_tree_add_item(tree, hf_dlm_node_idx, tvb, offset, 1, ENC_NA);
	offset += 1;

	proto_tree_add_item_ret_uint(tree, hf_dlm_namelen, tvb, offset, 1, ENC_BIG_ENDIAN, &namelen);
	offset += 1;

	dlm_name_handler(tree, tvb, offset, namelen);
	offset += O2NM_MAX_NAME_LEN;

	if(dlm_lock_flags & DLM_LOCK_FLAGS_PUT_LVB){
		proto_tree_add_item(tree, hf_dlm_lvb1, tvb, offset, 24, ENC_NA);
		offset += 24;

		proto_tree_add_item(tree, hf_dlm_lvb2, tvb, offset, 24, ENC_NA);
		offset += 24;

		proto_tree_add_item(tree, hf_dlm_lvb3, tvb, offset, 16, ENC_NA);
		offset += 16;
	}

	return offset;
}

static int dissect_proxy_ast_msg(proto_tree *tree, tvbuff_t *tvb, int offset)
{
	guint32 namelen;
	guint32 dlm_lock_flags;

	offset = dlm_cookie_handler(tree, tvb, offset, hf_dlm_cookie);
	offset = dlm_lkm_flags_handler(tree, tvb, offset, &dlm_lock_flags);

	proto_tree_add_item(tree, hf_dlm_node_idx, tvb, offset, 1, ENC_NA);
	offset += 1;

	proto_tree_add_item(tree, hf_dlm_ast_type, tvb, offset, 1, ENC_BIG_ENDIAN);
	offset += 1;

	proto_tree_add_item(tree, hf_dlm_blocked_type, tvb, offset, 1, ENC_BIG_ENDIAN);
	offset += 1;

	proto_tree_add_item_ret_uint(tree, hf_dlm_namelen, tvb, offset, 1, ENC_BIG_ENDIAN, &namelen);
	offset += 1;

	dlm_name_handler(tree, tvb, offset, namelen);
	offset += O2NM_MAX_NAME_LEN;

	if(dlm_lock_flags & DLM_LOCK_FLAGS_GET_LVB){
		proto_tree_add_item(tree, hf_dlm_lvb1, tvb, offset, 24, ENC_NA);
		offset += 24;

		proto_tree_add_item(tree, hf_dlm_lvb2, tvb, offset, 24, ENC_NA);
		offset += 24;

		proto_tree_add_item(tree, hf_dlm_lvb3, tvb, offset, 16, ENC_NA);
		offset += 16;
	}
	return offset;
}

static int dissect_deref_lockres_msg(proto_tree *tree, tvbuff_t *tvb, int offset)
{
	guint32 namelen;

	proto_tree_add_item(tree, hf_dlm_pad32, tvb, offset, 4, ENC_BIG_ENDIAN);
	offset += 4;

	proto_tree_add_item(tree, hf_dlm_pad16, tvb, offset, 2, ENC_BIG_ENDIAN);
	offset += 2;

	proto_tree_add_item(tree, hf_dlm_node_idx, tvb, offset, 1, ENC_NA);
	offset += 1;

	proto_tree_add_item_ret_uint(tree, hf_dlm_namelen, tvb, offset, 1, ENC_BIG_ENDIAN, &namelen);
	offset += 1;

	return dlm_name_handler(tree, tvb, offset, namelen);
}

static int dissect_migrate_request_msg(proto_tree *tree, tvbuff_t *tvb, int offset)
{
	guint32 namelen;

	proto_tree_add_item(tree, hf_dlm_master, tvb, offset, 1, ENC_NA);
	offset += 1;

	proto_tree_add_item(tree, hf_dlm_new_master, tvb, offset, 1, ENC_NA);
	offset += 1;

	proto_tree_add_item_ret_uint(tree, hf_dlm_namelen, tvb, offset, 1, ENC_BIG_ENDIAN, &namelen);
	offset += 1;

	proto_tree_add_item(tree, hf_dlm_pad8, tvb, offset, 1, ENC_NA);
	offset += 1;

	proto_tree_add_item(tree, hf_dlm_pad32, tvb, offset, 4, ENC_BIG_ENDIAN);
	offset += 4;

	return dlm_name_handler(tree, tvb, offset, namelen);
}

static int dissect_dlm_joined_msg(proto_tree *tree, tvbuff_t *tvb, int offset)
{
	proto_tree_add_item(tree, hf_dlm_node_idx, tvb, offset, 1, ENC_NA);
	offset += 1;

	proto_tree_add_item(tree, hf_dlm_pad16, tvb, offset, 2, ENC_BIG_ENDIAN);
	offset += 2;

	proto_tree_add_item(tree, hf_dlm_domain_name_len, tvb, offset, 1, ENC_BIG_ENDIAN);
	offset += 1;

	proto_tree_add_item(tree, hf_dlm_domain_name, tvb, offset, 64, ENC_ASCII|ENC_NA);
	offset += 64;

	return offset;
}

static int dissect_master_requery_msg(proto_tree *tree, tvbuff_t *tvb, int offset)
{
	guint32 namelen;

	proto_tree_add_item(tree, hf_dlm_pad16, tvb, offset, 2, ENC_BIG_ENDIAN);
	offset += 2;

	proto_tree_add_item(tree, hf_dlm_node_idx, tvb, offset, 1, ENC_NA);
	offset += 1;

	proto_tree_add_item_ret_uint(tree, hf_dlm_namelen, tvb, offset, 1, ENC_BIG_ENDIAN, &namelen);
	offset += 1;

	proto_tree_add_item(tree, hf_dlm_pad32, tvb, offset, 4, ENC_BIG_ENDIAN);
	offset += 4;

	return dlm_name_handler(tree, tvb, offset, namelen);
}

static int dissect_lock_request_msg(proto_tree *tree, tvbuff_t *tvb, int offset)
{
	proto_tree_add_item(tree, hf_dlm_node_idx, tvb, offset, 1, ENC_NA);
	offset += 1;

	proto_tree_add_item(tree, hf_dlm_dead_node, tvb, offset, 1, ENC_NA);
	offset += 1;

	proto_tree_add_item(tree, hf_dlm_pad16, tvb, offset, 2, ENC_BIG_ENDIAN);
	offset += 2;

	proto_tree_add_item(tree, hf_dlm_pad32, tvb, offset, 4, ENC_BIG_ENDIAN);
	offset += 4;

	return offset;
}

static int dissect_reco_data_done_msg(proto_tree *tree, tvbuff_t *tvb, int offset)
{
	offset = dissect_lock_request_msg(tree, tvb, offset);

	proto_tree_add_item(tree, hf_dlm_reco_lvb, tvb, offset, 64, ENC_NA);
	offset += 64;

	return offset;
}

static int dissect_finalize_reco_msg(proto_tree *tree, tvbuff_t *tvb, int offset)
{
	proto_tree_add_item(tree, hf_dlm_node_idx, tvb, offset, 1, ENC_NA);
	offset += 1;

	proto_tree_add_item(tree, hf_dlm_dead_node, tvb, offset, 1, ENC_NA);
	offset += 1;

	proto_tree_add_item(tree, hf_dlm_fr_flags, tvb, offset, 1, ENC_NA);
	offset += 1;

	proto_tree_add_item(tree, hf_dlm_pad8, tvb, offset, 1, ENC_NA);
	offset += 1;

	proto_tree_add_item(tree, hf_dlm_pad32, tvb, offset, 4, ENC_BIG_ENDIAN);
	offset += 4;

	return offset;
}

static int dissect_ocfs2_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
	proto_tree *subtree;
	proto_item *ti;
	guint32 len, msg_type;
	guint32 magic;
	tvbuff_t   *next_tvb;
	int offset = 0;

	magic = tvb_get_ntohs(tvb, offset);
	if (try_val_to_str(magic, o2net_magic) == NULL)
		return 0;

	col_set_str(pinfo->cinfo, COL_PROTOCOL, "OCFS2");
	col_clear(pinfo->cinfo, COL_INFO);

	ti = proto_tree_add_item(tree, proto_ocfs2, tvb, offset, -1, ENC_NA);
	subtree = proto_item_add_subtree(ti, ett_ocfs2);

	proto_tree_add_item(subtree, hf_msg_magic, tvb, offset, 2, ENC_BIG_ENDIAN);
	offset += 2;

	proto_tree_add_item_ret_uint(subtree, hf_msg_data_len, tvb, 2, 2, ENC_BIG_ENDIAN, &len);
	offset += 2;

	proto_tree_add_item_ret_uint(subtree, hf_msg_msg_type, tvb, 4, 2, ENC_BIG_ENDIAN, &msg_type);
	offset += 2;

	switch(magic){
	case O2NET_MSG_KEEP_REQ_MAGIC:
		col_append_sep_str(pinfo->cinfo, COL_INFO, " | ", "Keepalive Request");
		break;
	case O2NET_MSG_KEEP_RESP_MAGIC:
		col_append_sep_str(pinfo->cinfo, COL_INFO, " | ", "Keepalive Response");
		break;
	default:
		col_append_sep_str(pinfo->cinfo, COL_INFO, " | ",
			val_to_str_ext(msg_type, &ext_dlm_magic, "Unknown Type (0x%02x)") );
		break;
	}

	col_set_fence(pinfo->cinfo, COL_INFO);

	proto_tree_add_item(subtree, hf_msg_pad, tvb, 4, 2, ENC_BIG_ENDIAN);
	offset += 2;

	proto_tree_add_item(subtree, hf_msg_sys_status, tvb, 8, 4, ENC_BIG_ENDIAN);
	offset += 4;

	proto_tree_add_item(subtree, hf_msg_status, tvb, 12, 4, ENC_BIG_ENDIAN);
	offset += 4;

	proto_tree_add_item(subtree, hf_msg_key, tvb, 16, 4, ENC_BIG_ENDIAN);
	offset += 4;

	proto_tree_add_item(subtree, hf_msg_msg_num, tvb, 20, 4, ENC_BIG_ENDIAN);
	offset += 4;

	if (magic == O2NET_MSG_MAGIC) {
		switch (msg_type) {
		case DLM_MASTER_REQUEST_MSG:
			dissect_master_msg(subtree, tvb, offset, -1);
			break;
		case DLM_ASSERT_MASTER_MSG:
			dissect_master_msg(subtree, tvb, offset, hf_dlm_am_flags);
			break;
		case DLM_CREATE_LOCK_MSG:
			dissect_create_lock_msg(subtree, tvb, offset, NULL);
			break;
		case DLM_CONVERT_LOCK_MSG:
			dissect_convert_lock_msg(subtree, tvb, offset);
			break;
		case DLM_PROXY_AST_MSG:
			dissect_proxy_ast_msg(subtree, tvb, offset);
			break;
		case DLM_UNLOCK_LOCK_MSG:
			dissect_unlock_msg(subtree, tvb, offset);
			break;
		case DLM_DEREF_LOCKRES_MSG:
			dissect_deref_lockres_msg(subtree, tvb, offset);
			break;
		case DLM_MIGRATE_REQUEST_MSG:
			dissect_migrate_request_msg(subtree, tvb, offset);
			break;
		case DLM_MIG_LOCKRES_MSG:
			dissect_dlm_migrate_lockres(subtree, tvb, offset);
			break;
		case DLM_QUERY_JOIN_MSG:
			dissect_dlm_query_join_request(subtree, tvb, offset);
			break;
		case DLM_ASSERT_JOINED_MSG:
		case DLM_CANCEL_JOIN_MSG:
			dissect_dlm_joined_msg(subtree, tvb, offset);
			break;
		case DLM_EXIT_DOMAIN_MSG:
			proto_tree_add_item(tree, hf_dlm_node_idx, tvb, offset, 1, ENC_NA);
			break;
		case DLM_MASTER_REQUERY_MSG:
			dissect_master_requery_msg(subtree, tvb, offset);
			break;
		case DLM_LOCK_REQUEST_MSG:
		case DLM_BEGIN_RECO_MSG:
			dissect_lock_request_msg(subtree, tvb, offset);
			break;
		case DLM_RECO_DATA_DONE_MSG:
			dissect_reco_data_done_msg(subtree, tvb, offset);
			break;
		case DLM_FINALIZE_RECO_MSG:
			dissect_finalize_reco_msg(subtree, tvb, offset);
			break;
		case DLM_QUERY_REGION_MSG:
			dissect_dlm_query_region(subtree, tvb, offset);
			break;
		case DLM_QUERY_NODEINFO_MSG:
			dissect_dlm_query_nodeinfo(subtree, tvb, offset);
			break;
		default:
			proto_tree_add_item(tree, hf_dlm_payload, tvb, offset, len, ENC_NA);
			break;
		}
	} else {
		next_tvb = tvb_new_subset_length(tvb, offset, len);
		call_data_dissector(next_tvb, pinfo, tree);
	}

	return tvb_reported_length(tvb);
}

static guint
get_ocfs2_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_)
{
	guint16 plen;

	/* Get the length of the data from header. */
	plen = tvb_get_ntohs(tvb, offset + 2);

	/* That length doesn't include the header itself, add that in. */
	return plen + 24;
}

static int dissect_ocfs2(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
{
	guint32 magic;
	int offset = 0;

	if (!tvb_bytes_exist(tvb, offset, 2))
		return 0;

	magic = tvb_get_ntohs(tvb, offset);
	if (try_val_to_str(magic, o2net_magic) == NULL)
		return 0;

	tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 4, get_ocfs2_pdu_len, dissect_ocfs2_pdu, data);
	return tvb_captured_length(tvb);
}

void proto_register_ocfs2(void)
{
	static hf_register_info hf[] = {
		/* ocfs2_msg */
		{ &hf_msg_magic,
			{ "Magic", "ocfs2.msg.magic", FT_UINT16, BASE_HEX,
			  VALS(o2net_magic), 0x0,
			  "Magic number identifier of O2NET-over-TCPmessage",
			  HFILL
			}
		},
		{ &hf_msg_data_len,
			{ "Len", "ocfs2.msg.data_len", FT_UINT16, BASE_DEC,
			  NULL, 0x0, "Data length", HFILL
			}
		},
		{ &hf_msg_msg_type,
			{ "Type", "ocfs2.msg.msg_type", FT_UINT16, BASE_DEC|BASE_EXT_STRING,
			  &ext_dlm_magic, 0x0, "Message type", HFILL
			}
		},
		{ &hf_msg_pad,
			{ "Pad", "ocfs2.msg.pad", FT_UINT16, BASE_HEX,
			  NULL, 0x0, NULL, HFILL
			}
		},
		{ &hf_msg_sys_status,
			{ "Sys Status",	"ocfs2.msg.sys_status", FT_UINT32,
			  BASE_DEC, NULL, 0x0,
			  "System level status return code", HFILL
			}
		},
		{ &hf_msg_status,
			{ "Status", "ocfs2.msg.status", FT_UINT32, BASE_DEC,
			  NULL, 0x0, "Return code", HFILL
			}
		},
		{ &hf_msg_key,
			{ "Key", "ocfs2.msg.key", FT_UINT32, BASE_HEX, NULL,
			  0x0, NULL, HFILL
			}
		},
		{ &hf_msg_msg_num,
			{ "Num", "ocfs2.msg.msg_num", FT_UINT32, BASE_DEC, NULL,
			  0x0, "Message identification number", HFILL
			}
		},
		{ &hf_dlm_node_idx,
			{ "Node", "ocfs2.dlm.node_idx", FT_UINT8, BASE_DEC,
			  NULL, 0x0, "Node index", HFILL
			}
		},
		{ &hf_dlm_lock_flags,
			{ "Flags", "ocfs2.dlm.lock.flags", FT_UINT32, BASE_HEX,
			  NULL, 0x0, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_unused1,
			{ "unused", "ocfs2.dlm.lock.flags.unused", FT_UINT32, BASE_HEX,
			  NULL, 0x0000000F, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_orphan,
			{ "orphan", "ocfs2.dlm.lock.flags.orphan", FT_BOOLEAN, 32,
			  NULL, 0x00000010, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_parentable,
			{ "parentable", "ocfs2.dlm.lock.flags.parentable", FT_BOOLEAN, 32,
			  NULL, 0x00000020, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_block,
			{ "block", "ocfs2.dlm.lock.flags.block", FT_BOOLEAN, 32,
			  NULL, 0x00000040, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_local,
			{ "local", "ocfs2.dlm.lock.flags.local", FT_BOOLEAN, 32,
			  NULL, 0x00000080, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_valblk,
			{ "valblk", "ocfs2.dlm.lock.flags.valblk", FT_BOOLEAN, 32,
			  NULL, 0x00000100, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_noqueue,
			{ "noqueue", "ocfs2.dlm.lock.flags.noqueue", FT_BOOLEAN, 32,
			  NULL, 0x00000200, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_convert,
			{ "convert", "ocfs2.dlm.lock.flags.convert", FT_BOOLEAN, 32,
			  NULL, 0x00000400, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_nodlckwt,
			{ "nodlckwt", "ocfs2.dlm.lock.flags.nodlckwt", FT_BOOLEAN, 32,
			  NULL, 0x00000800, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_unlock,
			{ "unlock", "ocfs2.dlm.lock.flags.unlock", FT_BOOLEAN, 32,
			  NULL, 0x00001000, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_cancel,
			{ "cancel", "ocfs2.dlm.lock.flags.cancel", FT_BOOLEAN, 32,
			  NULL, 0x00002000, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_deqall,
			{ "deqall", "ocfs2.dlm.lock.flags.deqall", FT_BOOLEAN, 32,
			  NULL, 0x00004000, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_invvalblk,
			{ "invvalblk", "ocfs2.dlm.lock.flags.invvalblk", FT_BOOLEAN, 32,
			  NULL, 0x00008000, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_syncsts,
			{ "syncsts", "ocfs2.dlm.lock.flags.syncsts", FT_BOOLEAN, 32,
			  NULL, 0x00010000, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_timeout,
			{ "timeout", "ocfs2.dlm.lock.flags.timeout", FT_BOOLEAN, 32,
			  NULL, 0x00020000, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_sngldlck,
			{ "sngldlck", "ocfs2.dlm.lock.flags.sngldlck", FT_BOOLEAN, 32,
			  NULL, 0x00040000, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_findlocal,
			{ "findlocal", "ocfs2.dlm.lock.flags.findlocal", FT_BOOLEAN, 32,
			  NULL, 0x00080000, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_proc_owned,
			{ "proc_owned", "ocfs2.dlm.lock.flags.proc_owned", FT_BOOLEAN, 32,
			  NULL, 0x00100000, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_xid,
			{ "xid", "ocfs2.dlm.lock.flags.xid", FT_BOOLEAN, 32,
			  NULL, 0x00200000, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_xid_conflict,
			{ "xid_conflict", "ocfs2.dlm.lock.flags.xid_conflict", FT_BOOLEAN, 32,
			  NULL, 0x00400000, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_force,
			{ "force", "ocfs2.dlm.lock.flags.force", FT_BOOLEAN, 32,
			  NULL, 0x00800000, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_revvalblk,
			{ "revvalblk", "ocfs2.dlm.lock.flags.revvalblk", FT_BOOLEAN, 32,
			  NULL, 0x01000000, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_unused2,
			{ "unused", "ocfs2.dlm.lock.flags.unused", FT_UINT32, BASE_HEX,
			  NULL, 0x0E000000, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_migration,
			{ "migration", "ocfs2.dlm.lock.flags.migration", FT_BOOLEAN, 32,
			  NULL, 0x10000000, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_put_lvb,
			{ "put_lvb", "ocfs2.dlm.lock.flags.put_lvb", FT_BOOLEAN, 32,
			  NULL, 0x20000000, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_get_lvb,
			{ "get_lvb", "ocfs2.dlm.lock.flags.get_lvb", FT_BOOLEAN, 32,
			  NULL, 0x40000000, NULL, HFILL
			}
		},
		{ &hf_dlm_lock_flag_recovery,
			{ "recovery", "ocfs2.dlm.lock.flags.recovery", FT_BOOLEAN, 32,
			  NULL, 0x80000000, NULL, HFILL
			}
		},
		{ &hf_dlm_am_flags,
			{ "Flags", "ocfs2.dlm.am_flags", FT_UINT32, BASE_HEX,
			  VALS(dlm_assert_master_flags), 0x0,
			  "Assert Master Flags", HFILL
			}
		},
		{ &hf_dlm_fr_flags,
			{ "Flags", "ocfs2.dlm.fr_flags", FT_UINT32, BASE_HEX,
			  VALS(dlm_finalize_reco_flags), 0x0,
			  "Finalize Recovery Flags", HFILL
			}
		},
		{ &hf_dlm_namelen,
			{ "Namelen", "ocfs2.dlm.namelen", FT_UINT8, BASE_DEC,
			  NULL, 0x0, NULL, HFILL
			}
		},
		{ &hf_dlm_name,
			{ "Name", "ocfs2.dlm.name", FT_STRING, BASE_NONE, NULL,
			  0x0, NULL, HFILL
			}
		},
		{ &hf_dlm_cookie,
			{ "Cookie", "ocfs2.dlm.cookie", FT_UINT64, BASE_HEX,
			  NULL, 0x0,
			  "Unique ID for a single lock on a resource", HFILL
			}
		},
		{ &hf_dlm_requested_type,
			{ "Requested", "ocfs2.dlm.requested_type", FT_UINT8,
			  BASE_DEC, VALS(dlm_lock_modes), 0x0,
			  "Requested lock level", HFILL
			}
		},
		{ &hf_dlm_blocked_type,
			{ "Blocked", "ocfs2.dlm.blocked_type", FT_UINT8,
			  BASE_DEC, VALS(dlm_lock_modes), 0x0,
			  "Blocked lock type", HFILL
			}
		},
		{ &hf_dlm_ast_type,
			{ "AST Type", "ocfs2.dlm.ast_type", FT_UINT8, BASE_DEC,
			  VALS(dlm_proxy_ast_types), 0x0, NULL, HFILL
			}
		},
		{ &hf_dlm_dead_node,
			{ "Dead Node", "ocfs2.dlm.dead_node", FT_UINT8,
			  BASE_DEC, NULL, 0x0, "Dead node index", HFILL
			}
		},
		{ &hf_dlm_lvb1,
			{ "LVB1", "ocfs2.dlm.lvb", FT_BYTES, BASE_NONE, NULL,
			  0x0, "Lock value block", HFILL
			}
		},
		{ &hf_dlm_lvb2,
			{ "LVB2", "ocfs2.dlm.lvb", FT_BYTES, BASE_NONE, NULL,
			  0x0, "Lock value block", HFILL
			}
		},
		{ &hf_dlm_lvb3,
			{ "LVB3", "ocfs2.dlm.lvb", FT_BYTES, BASE_NONE, NULL,
			  0x0, "Lock value block", HFILL
			}
		},
		{ &hf_dlm_domain_name_len,
			{ "Domain Namelen", "ocfs2.dlm.domain_namelen",
			  FT_UINT8, BASE_DEC, NULL, 0x0, NULL,
			  HFILL
			}
		},
		{ &hf_dlm_proto_ver,
			{ "DLM Protocol", "ocfs2.dlm.proto_ver",
			  FT_UINT16, BASE_CUSTOM, CF_FUNC(dlm_fmt_revision), 0x0, NULL,
			  HFILL
			}
		},
		{ &hf_dlm_fs_proto_ver,
			{ "FS Protocol", "ocfs2.dlm.fs_proto_ver",
			  FT_UINT16, BASE_CUSTOM, CF_FUNC(dlm_fmt_revision), 0x0, NULL,
			  HFILL
			}
		},
		{ &hf_dlm_node_map,
			{ "Node Map", "ocfs2.dlm.node_map", FT_BYTES,
			  BASE_NONE, NULL, 0x0, NULL, HFILL
			}
		},
		{ &hf_dlm_domain_name,
			{ "Domain Name", "ocfs2.dlm.domain_name", FT_STRING,
			  BASE_NONE, NULL, 0x0, NULL, HFILL
			}
		},
		{ &hf_dlm_master,
			{ "Master", "ocfs2.dlm.master", FT_UINT8, BASE_DEC,
			  NULL, 0x0, NULL, HFILL
			}
		},
		{ &hf_dlm_new_master,
			{ "New Master", "ocfs2.dlm.new_master", FT_UINT8,
			  BASE_DEC, NULL, 0x0, NULL, HFILL
			}
		},
		{ &hf_dlm_mres_num_locks,
			{ "Num Locks", "ocfs2.dlm.num_locks", FT_UINT8,
			  BASE_DEC, NULL, 0x0, "Migres Num Locks", HFILL
			}
		},
		{ &hf_dlm_mres_flags,
			{ "Flags", "ocfs2.dlm.mres_flags", FT_UINT8, BASE_HEX,
			  NULL, 0x01, "Migres Flags", HFILL
			}
		},
		{ &hf_dlm_mres_flag_recovery,
			{ "recovery", "ocfs2.dlm.mres_flags.recovery", FT_BOOLEAN, 8,
			  NULL, 0x02, NULL, HFILL
			}
		},
		{ &hf_dlm_mres_flag_migration,
			{ "migration", "ocfs2.dlm.mres_flags.migration", FT_BOOLEAN, 8,
			  NULL, 0x04, NULL, HFILL
			}
		},
		{ &hf_dlm_mres_flag_all_done,
			{ "all_done", "ocfs2.dlm.mres_flags.all_done", FT_BOOLEAN, 8,
			  NULL, 0x0, NULL, HFILL
			}
		},
		{ &hf_dlm_mres_total_locks,
			{ "Total Locks", "ocfs2.dlm.total_locks", FT_UINT32,
			  BASE_DEC, NULL, 0x0, "Migres Total Locks", HFILL
			}
		},
		{ &hf_dlm_mres_mig_cookie,
			{ "Cookie", "ocfs2.dlm.migratable_lock.mig_cookie",
			  FT_UINT64, BASE_DEC, NULL, 0x0, "Migres Cookie", HFILL
			}
		},
		{ &hf_dlm_mres_list,
			{ "List", "ocfs2.dlm.migratable_lock.list", FT_UINT8,
			  BASE_DEC, VALS(dlm_lockres_list), 0x0, NULL, HFILL
			}
		},
		{ &hf_dlm_mres_ml_flags,
			{ "List", "ocfs2.dlm.migratable_lock.flags", FT_UINT8,
			  BASE_HEX, NULL, 0x0, NULL, HFILL
			}
		},
		{ &hf_dlm_mres_type,
			{ "Type", "ocfs2.dlm.migratable_lock.type", FT_UINT8,
			  BASE_DEC, VALS(dlm_lock_modes), 0x0, NULL, HFILL
			}
		},
		{ &hf_dlm_mres_convert_type,
			{ "Convert type", "ocfs2.dlm.migratable_lock.convert_type", FT_UINT8,
			  BASE_DEC, NULL, 0x0, NULL, HFILL
			}
		},
		{ &hf_dlm_mres_highest_blocked,
			{ "Highest blocked", "ocfs2.dlm.migratable_lock.highest_blocked", FT_UINT8,
			  BASE_DEC, NULL, 0x0, NULL, HFILL
			}
		},
		{ &hf_dlm_mres_node,
			{ "Node", "ocfs2.dlm.migratable_lock.node", FT_UINT8,
			  BASE_DEC, NULL, 0x0, NULL, HFILL
			}
		},
		{ &hf_dlm_qr_node,
			{ "Node", "ocfs2.dlm.query_region.qr_node", FT_UINT8,
			  BASE_DEC, NULL, 0x0, "Query Region Node", HFILL
			}
		},
		{ &hf_dlm_qr_numregions,
			{ "Num Regions", "ocfs2.dlm.query_region.qr_numregions",
			  FT_UINT8, BASE_DEC, NULL, 0x0,
			  "The number of regions to compare with", HFILL
			}
		},
		{ &hf_dlm_qr_namelen,
			{ "Domain Namelen", "ocfs2.dlm.query_region.qr_namelen",
			  FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL
			}
		},
		{ &hf_dlm_qr_domain,
			{ "Domain Name", "ocfs2.dlm.query_region.qr_domain",
			  FT_STRING, BASE_NONE, NULL, 0x0, NULL,
			  HFILL
			}
		},
		{ &hf_dlm_qr_region,
			{ "Region", "ocfs2.dlm.query_region.region",
			  FT_STRING, BASE_NONE, NULL, 0x0, NULL,
			  HFILL
			}
		},
		{ &hf_dlm_qn_nodenum,
			{ "Node", "ocfs2.dlm_query_nodeinfo.qn_nodenum",
			  FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL
			}
		},
		{ &hf_dlm_qn_numnodes,
			{ "Num Nodes", "ocfs2.dlm_query_nodeinfo.qn_numnodes",
			  FT_UINT8, BASE_DEC, NULL, 0x0,
			  "The number of nodes to query", HFILL
			}
		},
		{ &hf_dlm_qn_namelen,
			{ "Domain Namelen",
			  "ocfs2.dlm_query_nodeinfo.qn_namelen", FT_UINT8,
			  BASE_DEC, NULL, 0x0, NULL, HFILL
			}
		},
		{ &hf_dlm_qn_domain,
			{ "Domain Name", "ocfs2.dlm_query_nodeinfo.qn_domain",
			  FT_UINT8, BASE_DEC, NULL, 0x0, NULL,
			  HFILL
			}
		},
		{ &hf_dlm_qn_node,
			{ "Node", "ocfs2.dlm_query_nodeinfo.node",
			  FT_UINT8, BASE_DEC, NULL, 0x0, NULL,
			  HFILL
			}
		},
		{ &hf_dlm_qn_port,
			{ "Port", "ocfs2.dlm_query_nodeinfo.port",
			  FT_UINT16, BASE_DEC, NULL, 0x0, NULL,
			  HFILL
			}
		},
		{ &hf_dlm_qn_ip,
			{ "IP Address", "ocfs2.dlm_query_nodeinfo.ip",
			  FT_IPv4, BASE_NONE, NULL, 0x0, NULL,
			  HFILL
			}
		},
		{ &hf_dlm_reco_lvb,
			{ "Recovery LVB", "ocfs2.reco_lvb",
			  FT_BYTES, BASE_NONE, NULL, 0x0, NULL,
			  HFILL
			}
		},
		{ &hf_dlm_pad8,
			{ "Pad", "ocfs2.dlm.pad",
			  FT_UINT8, BASE_DEC, NULL, 0x0, NULL,
			  HFILL
			}
		},
		{ &hf_dlm_pad16,
			{ "Pad", "ocfs2.dlm.pad",
			  FT_UINT16, BASE_HEX, NULL, 0x0, NULL,
			  HFILL
			}
		},
		{ &hf_dlm_pad32,
			{ "Pad", "ocfs2.dlm.pad",
			  FT_UINT32, BASE_HEX, NULL, 0x0, NULL,
			  HFILL
			}
		},
		{ &hf_dlm_flags,
			{ "Flags", "ocfs2.dlm.flags",
			  FT_UINT32, BASE_HEX, NULL, 0x0, NULL,
			  HFILL
			}
		},
		{ &hf_dlm_payload,
			{ "Payload", "ocfs2.dlm.payload",
			  FT_BYTES, BASE_NONE, NULL, 0x0, NULL,
			  HFILL
			}
		},
	};

	static gint *ett[] = {
		&ett_ocfs2,
		&ett_dtm_lock_flags,
		&ett_mres_flags,
		&ett_migrate_lockres_locks,
		&ett_query_nodeinfo,
	};

	proto_ocfs2 = proto_register_protocol("OCFS2 Networking", "OCFS2", "ocfs2");
	proto_register_field_array(proto_ocfs2, hf, array_length(hf));
	proto_register_subtree_array(ett, array_length(ett));
}

void proto_reg_handoff_ocfs2(void)
{
	dissector_handle_t ocfs2_handle;

	ocfs2_handle = create_dissector_handle(dissect_ocfs2, proto_ocfs2);

	dissector_add_for_decode_as_with_preference("tcp.port", ocfs2_handle);
}


/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 8
 * tab-width: 8
 * indent-tabs-mode: t
 * End:
 *
 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
 * :indentSize=8:tabSize=8:noTabs=false:
 */
