Added support for HPUX11 NETTL captures for the NS_LS_DRIVER type.
[obnox/wireshark/wip.git] / packet-ndmp.c
index ecee3927d5825ebd3880c956e9051178ecc789fb..82d52303106f5b7f044ff01c649cda408374f17e 100644 (file)
@@ -1,8 +1,8 @@
 /* packet-ndmp.c
- * Routines for NDMP
- * Ronnie Sahlberg (see AUTHORS for email)
+ * Routines for NDMP dissection
+ * 2001 Ronnie Sahlberg (see AUTHORS for email)
  *
- * $Id: packet-ndmp.c,v 1.1 2001/12/23 21:36:57 guy Exp $
+ * $Id: packet-ndmp.c,v 1.19 2002/05/01 07:50:38 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
 #include <string.h>
 #include <glib.h>
 
-#include "packet.h"
+#include <epan/packet.h>
+#include <epan/conversation.h>
+#include "packet-rpc.h"
+#include "packet-scsi.h"
+#include "packet-frame.h"
 #include "prefs.h"
+#include "reassemble.h"
+#include "rpc_defrag.h"
 
 #define TCP_PORT_NDMP 10000
 
 static int proto_ndmp = -1;
 static int hf_ndmp_version = -1;
-static int hf_ndmp_size = -1;
 static int hf_ndmp_header = -1;
 static int hf_ndmp_sequence = -1;
 static int hf_ndmp_reply_sequence = -1;
@@ -54,13 +59,192 @@ static int hf_ndmp_timestamp = -1;
 static int hf_ndmp_msgtype = -1;
 static int hf_ndmp_msg = -1;
 static int hf_ndmp_error = -1;
+static int hf_ndmp_hostname = -1;
+static int hf_ndmp_os_type = -1;
+static int hf_ndmp_os_vers = -1;
+static int hf_ndmp_hostid = -1;
+static int hf_ndmp_addr_types = -1;
+static int hf_ndmp_addr_type = -1;
+static int hf_ndmp_auth_type = -1;
+static int hf_ndmp_auth_types = -1;
+static int hf_ndmp_auth_challenge = -1;
+static int hf_ndmp_auth_digest = -1;
+static int hf_ndmp_auth_id = -1;
+static int hf_ndmp_auth_password = -1;
+static int hf_ndmp_butype_info = -1;
+static int hf_ndmp_butype_name = -1;
+static int hf_ndmp_butype_default_env = -1;
+static int hf_ndmp_butype_attr_backup_file_history = -1;
+static int hf_ndmp_butype_attr_backup_filelist = -1;
+static int hf_ndmp_butype_attr_recover_filelist = -1;
+static int hf_ndmp_butype_attr_backup_direct = -1;
+static int hf_ndmp_butype_attr_recover_direct = -1;
+static int hf_ndmp_butype_attr_backup_incremental = -1;
+static int hf_ndmp_butype_attr_recover_incremental = -1;
+static int hf_ndmp_butype_attr_backup_utf8 = -1;
+static int hf_ndmp_butype_attr_recover_utf8 = -1;
+static int hf_ndmp_butype_env_name = -1;
+static int hf_ndmp_butype_env_value = -1;
+static int hf_ndmp_fs_info = -1;
+static int hf_ndmp_fs_invalid_total_size = -1;
+static int hf_ndmp_fs_invalid_used_size = -1;
+static int hf_ndmp_fs_invalid_avail_size = -1;
+static int hf_ndmp_fs_invalid_total_inodes = -1;
+static int hf_ndmp_fs_invalid_used_inodes = -1;
+static int hf_ndmp_fs_fs_type = -1;
+static int hf_ndmp_fs_logical_device = -1;
+static int hf_ndmp_fs_physical_device = -1;
+static int hf_ndmp_fs_total_size = -1;
+static int hf_ndmp_fs_used_size = -1;
+static int hf_ndmp_fs_avail_size = -1;
+static int hf_ndmp_fs_total_inodes = -1;
+static int hf_ndmp_fs_used_inodes = -1;
+static int hf_ndmp_fs_env = -1;
+static int hf_ndmp_fs_env_name = -1;
+static int hf_ndmp_fs_env_value = -1;
+static int hf_ndmp_fs_status = -1;
+static int hf_ndmp_tape_info = -1;
+static int hf_ndmp_tape_model = -1;
+static int hf_ndmp_tape_dev_cap = -1;
+static int hf_ndmp_tape_device = -1;
+static int hf_ndmp_tape_open_mode = -1;
+static int hf_ndmp_tape_attr_rewind = -1;
+static int hf_ndmp_tape_attr_unload = -1;
+static int hf_ndmp_tape_capability = -1;
+static int hf_ndmp_tape_capability_name = -1;
+static int hf_ndmp_tape_capability_value = -1;
+static int hf_ndmp_scsi_info = -1;
+static int hf_ndmp_scsi_model = -1;
+static int hf_ndmp_server_vendor = -1;
+static int hf_ndmp_server_product = -1;
+static int hf_ndmp_server_revision = -1;
+static int hf_ndmp_scsi_device = -1;
+static int hf_ndmp_scsi_controller = -1;
+static int hf_ndmp_scsi_id = -1;
+static int hf_ndmp_scsi_lun = -1;
+static int hf_ndmp_execute_cdb_flags_data_in = -1;
+static int hf_ndmp_execute_cdb_flags_data_out = -1;
+static int hf_ndmp_execute_cdb_timeout = -1;
+static int hf_ndmp_execute_cdb_datain_len = -1;
+static int hf_ndmp_execute_cdb_cdb_len = -1;
+static int hf_ndmp_execute_cdb_dataout = -1;
+static int hf_ndmp_execute_cdb_status = -1;
+static int hf_ndmp_execute_cdb_dataout_len = -1;
+static int hf_ndmp_execute_cdb_datain = -1;
+static int hf_ndmp_execute_cdb_sns_len = -1;
+static int hf_ndmp_tape_invalid_file_num = -1;
+static int hf_ndmp_tape_invalid_soft_errors = -1;
+static int hf_ndmp_tape_invalid_block_size = -1;
+static int hf_ndmp_tape_invalid_block_no = -1;
+static int hf_ndmp_tape_invalid_total_space = -1;
+static int hf_ndmp_tape_invalid_space_remain = -1;
+static int hf_ndmp_tape_invalid_partition = -1;
+static int hf_ndmp_tape_flags_no_rewind = -1;
+static int hf_ndmp_tape_flags_write_protect = -1;
+static int hf_ndmp_tape_flags_error = -1;
+static int hf_ndmp_tape_flags_unload = -1;
+static int hf_ndmp_tape_file_num = -1;
+static int hf_ndmp_tape_soft_errors = -1;
+static int hf_ndmp_tape_block_size = -1;
+static int hf_ndmp_tape_block_no = -1;
+static int hf_ndmp_tape_total_space = -1;
+static int hf_ndmp_tape_space_remain = -1;
+static int hf_ndmp_tape_partition = -1;
+static int hf_ndmp_tape_mtio_op = -1;
+static int hf_ndmp_count = -1;
+static int hf_ndmp_resid_count = -1;
+static int hf_ndmp_mover_state = -1;
+static int hf_ndmp_mover_pause = -1;
+static int hf_ndmp_halt = -1;
+static int hf_ndmp_halt_reason = -1;
+static int hf_ndmp_record_size = -1;
+static int hf_ndmp_record_num = -1;
+static int hf_ndmp_data_written = -1;
+static int hf_ndmp_seek_position = -1;
+static int hf_ndmp_bytes_left_to_read = -1;
+static int hf_ndmp_window_offset = -1;
+static int hf_ndmp_window_length = -1;
+static int hf_ndmp_addr_ip = -1;
+static int hf_ndmp_addr_tcp = -1;
+static int hf_ndmp_addr_fcal_loop_id = -1;
+static int hf_ndmp_addr_ipc = -1;
+static int hf_ndmp_mover_mode = -1;
+static int hf_ndmp_file_name = -1;
+static int hf_ndmp_nt_file_name = -1;
+static int hf_ndmp_dos_file_name = -1;
+static int hf_ndmp_log_type = -1;
+static int hf_ndmp_log_message_id = -1;
+static int hf_ndmp_log_message = -1;
+static int hf_ndmp_connected = -1;
+static int hf_ndmp_connected_reason = -1;
+static int hf_ndmp_data = -1;
+static int hf_ndmp_files = -1;
+static int hf_ndmp_file_fs_type = -1;
+static int hf_ndmp_file_names = -1;
+static int hf_ndmp_file_stats = -1;
+static int hf_ndmp_file_node = -1;
+static int hf_ndmp_file_parent = -1;
+static int hf_ndmp_file_fh_info = -1;
+static int hf_ndmp_file_invalid_atime = -1;
+static int hf_ndmp_file_invalid_ctime = -1;
+static int hf_ndmp_file_invalid_group = -1;
+static int hf_ndmp_file_type = -1;
+static int hf_ndmp_file_mtime = -1;
+static int hf_ndmp_file_atime = -1;
+static int hf_ndmp_file_ctime = -1;
+static int hf_ndmp_file_owner = -1;
+static int hf_ndmp_file_group = -1;
+static int hf_ndmp_file_fattr = -1;
+static int hf_ndmp_file_size = -1;
+static int hf_ndmp_file_links = -1;
+static int hf_ndmp_dirs = -1;
+static int hf_ndmp_nodes = -1;
+static int hf_ndmp_nlist = -1;
+static int hf_ndmp_bu_original_path = -1;
+static int hf_ndmp_bu_destination_dir = -1;
+static int hf_ndmp_bu_new_name = -1;
+static int hf_ndmp_bu_other_name = -1;
+static int hf_ndmp_state_invalid_ebr = -1;
+static int hf_ndmp_state_invalid_etr = -1;
+static int hf_ndmp_bu_operation = -1;
+static int hf_ndmp_data_state = -1;
+static int hf_ndmp_data_halted = -1;
+static int hf_ndmp_data_bytes_processed = -1;
+static int hf_ndmp_data_est_bytes_remain = -1;
+static int hf_ndmp_data_est_time_remain = -1;
 
 static gint ett_ndmp = -1;
 static gint ett_ndmp_header = -1;
+static gint ett_ndmp_butype_attrs = -1;
+static gint ett_ndmp_fs_invalid = -1;
+static gint ett_ndmp_tape_attr = -1;
+static gint ett_ndmp_execute_cdb_flags = -1;
+static gint ett_ndmp_execute_cdb_cdb = -1;
+static gint ett_ndmp_execute_cdb_sns = -1;
+static gint ett_ndmp_execute_cdb_payload = -1;
+static gint ett_ndmp_tape_invalid = -1;
+static gint ett_ndmp_tape_flags = -1;
+static gint ett_ndmp_addr = -1;
+static gint ett_ndmp_file = -1;
+static gint ett_ndmp_file_name = -1;
+static gint ett_ndmp_file_stats = -1;
+static gint ett_ndmp_file_invalids = -1;
+static gint ett_ndmp_state_invalids = -1;
+
+struct ndmp_header {
+       guint32 seq;
+       guint32 time;
+       guint32 type;
+       guint32 msg;
+       guint32 rep_seq;
+       guint32 err;
+};
 
 /* desegmentation of NDMP packets */
-static gboolean ndmp_desegment = FALSE;
+static gboolean ndmp_desegment = TRUE;
 
+/* defragmentation of fragmented NDMP records */
+static gboolean ndmp_defragment = FALSE;
 
 #define NDMP_MESSAGE_REQUEST   0x00
 #define NDMP_MESSAGE_REPLY     0x01
@@ -243,7 +427,8 @@ static const value_string msg_vals[] = {
 
 
 static int
-dissect_connect_open_request(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
+dissect_connect_open_request(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree, guint32 seq _U_)
 {
        /* version number */
        proto_tree_add_item(tree, hf_ndmp_version, tvb, offset, 4, FALSE);
@@ -253,7 +438,8 @@ dissect_connect_open_request(tvbuff_t *tvb, int offset, packet_info *pinfo, prot
 }
 
 static int
-dissect_error(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
+dissect_error(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree, guint32 seq _U_)
 {
        /* error */
        proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
@@ -262,254 +448,2973 @@ dissect_error(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
        return offset;
 }
 
+static int
+dissect_ndmp_get_host_info_reply(tvbuff_t *tvb, int offset,
+    packet_info *pinfo _U_, proto_tree *tree, guint32 seq _U_)
+{
+       /* error */
+       proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
+       offset += 4;
 
-typedef struct _ndmp_command {
-       guint32 cmd;
-       int (*request) (tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree);
-       int (*response)(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree);
-} ndmp_command;
+       /* hostname */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_hostname, offset, NULL);
 
-static const ndmp_command ndmp_commands[] = {
-       {NDMP_CONFIG_GET_HOST_INFO,     NULL,NULL},
-       {NDMP_CONFIG_GET_CONNECTION_TYPE, NULL,NULL},
-       {NDMP_CONFIG_GET_AUTH_ATTR,     NULL,NULL},
-       {NDMP_CONFIG_GET_BUTYPE_INFO,   NULL,NULL},
-       {NDMP_CONFIG_GET_FS_INFO,       NULL,NULL},
-       {NDMP_CONFIG_GET_TAPE_INFO,     NULL,NULL},
-       {NDMP_CONFIG_GET_SCSI_INFO,     NULL,NULL},
-       {NDMP_CONFIG_GET_SERVER_INFO,   NULL,NULL},
-       {NDMP_SCSI_OPEN,                NULL,NULL},
-       {NDMP_SCSI_CLOSE,               NULL,NULL},
-       {NDMP_SCSI_GET_STATE,           NULL,NULL},
-       {NDMP_SCSI_SET_TARGET,          NULL,NULL},
-       {NDMP_SCSI_RESET_DEVICE,        NULL,NULL},
-       {NDMP_SCSI_RESET_BUS,           NULL,NULL},
-       {NDMP_SCSI_EXECUTE_CDB,         NULL,NULL},
-       {NDMP_TAPE_OPEN,                NULL,NULL},
-       {NDMP_TAPE_CLOSE,               NULL,NULL},
-       {NDMP_TAPE_GET_STATE,           NULL,NULL},
-       {NDMP_TAPE_MTIO,                NULL,NULL},
-       {NDMP_TAPE_WRITE,               NULL,NULL},
-       {NDMP_TAPE_READ,                NULL,NULL},
-       {NDMP_TAPE_EXECUTE_CDB,         NULL,NULL},
-       {NDMP_DATA_GET_STATE,           NULL,NULL},
-       {NDMP_DATA_START_BACKUP,        NULL,NULL},
-       {NDMP_DATA_START_RECOVER,       NULL,NULL},
-       {NDMP_DATA_ABORT,               NULL,NULL},
-       {NDMP_DATA_GET_ENV,             NULL,NULL},
-       {NDMP_DATA_STOP,                NULL,NULL},
-       {NDMP_DATA_LISTEN,              NULL,NULL},
-       {NDMP_DATA_CONNECT,             NULL,NULL},
-       {NDMP_NOTIFY_DATA_HALTED,       NULL,NULL},
-       {NDMP_NOTIFY_CONNECTED,         NULL,NULL},
-       {NDMP_NOTIFY_MOVER_HALTED,      NULL,NULL},
-       {NDMP_NOTIFY_MOVER_PAUSED,      NULL,NULL},
-       {NDMP_NOTIFY_DATA_READ,         NULL,NULL},
-       {NDMP_LOG_FILE,                 NULL,NULL},
-       {NDMP_LOG_MESSAGE,              NULL,NULL},
-       {NDMP_FH_ADD_FILE,              NULL,NULL},
-       {NDMP_FH_ADD_DIR,               NULL,NULL},
-       {NDMP_FH_ADD_NODE,              NULL,NULL},
-       {NDMP_CONNECT_OPEN,             dissect_connect_open_request, dissect_error},
-       {NDMP_CONNECT_CLIENT_AUTH,      NULL,NULL},
-       {NDMP_CONNECT_CLOSE,            NULL,NULL},
-       {NDMP_CONNECT_SERVER_AUTH,      NULL,NULL},
-       {NDMP_MOVER_GET_STATE,          NULL,NULL},
-       {NDMP_MOVER_LISTEN,             NULL,NULL},
-       {NDMP_MOVER_CONTINUE,           NULL,NULL},
-       {NDMP_MOVER_ABORT,              NULL,NULL},
-       {NDMP_MOVER_STOP,               NULL,NULL},
-       {NDMP_MOVER_SET_WINDOW,         NULL,NULL},
-       {NDMP_MOVER_READ,               NULL,NULL},
-       {NDMP_MOVER_CLOSE,              NULL,NULL},
-       {NDMP_MOVER_SET_RECORD_SIZE,    NULL,NULL},
-       {NDMP_MOVER_CONNECT,            NULL,NULL},
-       {0, NULL,NULL}
+       /* os type */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_os_type, offset, NULL);
+
+       /* os version */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_os_vers, offset, NULL);
+
+       /* hostid */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_hostid, offset, NULL);
+
+       return offset;
+}
+
+#define NDMP_ADDR_LOCAL                0
+#define NDMP_ADDR_TCP          1
+#define NDMP_ADDR_FC           2
+#define NDMP_ADDR_IPC          3
+static const value_string addr_type_vals[] = {
+       {NDMP_ADDR_LOCAL,       "Local"},
+       {NDMP_ADDR_TCP,         "TCP"},
+       {NDMP_ADDR_FC,          "FC"},
+       {NDMP_ADDR_IPC,         "IPC"},
+       {0,NULL}
+};
+
+static int
+dissect_ndmp_addr_type(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree)
+{
+       proto_tree_add_item(tree, hf_ndmp_addr_type, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       return offset;
+}
+
+static int
+dissect_ndmp_addr_msg(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /*address type*/
+       return dissect_ndmp_addr_type(tvb, offset, pinfo, tree);
+}
+
+static int
+dissect_ndmp_config_get_connection_type_reply(tvbuff_t *tvb, int offset,
+    packet_info *pinfo, proto_tree *tree, guint32 seq _U_)
+{
+       /* error */
+       proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* addr types */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_ndmp_addr_type, hf_ndmp_addr_types);
+
+       return offset;
+}
+
+#define NDMP_AUTH_NONE         0
+#define NDMP_AUTH_TEXT         1
+#define NDMP_AUTH_MD5          2
+static const value_string auth_type_vals[] = {
+       {NDMP_AUTH_NONE,        "None"},
+       {NDMP_AUTH_TEXT,        "Text"},
+       {NDMP_AUTH_MD5,         "MD5"},
+       {0,NULL}
 };
+static int
+dissect_auth_type(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree)
+{
+       proto_tree_add_item(tree, hf_ndmp_auth_type, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       return offset;
+}
+
+static int
+dissect_get_auth_type_request(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* auth type */
+       return dissect_auth_type(tvb, offset, pinfo, tree);
+}
+
+static int
+dissect_auth_attr_msg(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree, guint32 seq _U_)
+{
+       guint type;
+       
+       type=tvb_get_ntohl(tvb,offset);
+
+       /* auth type */
+       proto_tree_add_item(tree, hf_ndmp_auth_type, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       switch(type){
+       case NDMP_AUTH_NONE:
+               break;
+       case NDMP_AUTH_TEXT:
+               break;
+       case NDMP_AUTH_MD5:
+               proto_tree_add_item(tree, hf_ndmp_auth_challenge, 
+                       tvb, offset, 64, FALSE);
+               offset+=64;
+       }
+
+       return offset;
+}
+
+static int
+dissect_default_env(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree)
+{
+       /* name */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_butype_env_name, offset, NULL);
+
+       /* value */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_butype_env_value, offset, NULL);
 
+       return offset;
+}
 
+
+static const true_false_string tfs_butype_attr_backup_file_history = {
+       "Backup FILE HISTORY",
+       "Do NOT backup file history"
+};
+static const true_false_string tfs_butype_attr_backup_filelist = {
+       "Backup FILELIST",
+       "Do NOT backup filelist"
+};
+static const true_false_string tfs_butype_attr_recover_filelist = {
+       "Recover FILELIST",
+       "Do NOT recover filelist"
+};
+static const true_false_string tfs_butype_attr_backup_direct = {
+       "Perform DIRECT backup",
+       "Do NOT perform direct backup"
+};
+static const true_false_string tfs_butype_attr_recover_direct = {
+       "Perform DIRECT recovery",
+       "Do NOT perform direct recovery"
+};
+static const true_false_string tfs_butype_attr_backup_incremental = {
+       "Perform INCREMENTAL backup",
+       "Perform FULL backup"
+};
+static const true_false_string tfs_butype_attr_recover_incremental = {
+       "Perform INCREMENTAL revocery",
+       "Perform FULL recovery"
+};
+static const true_false_string tfs_butype_attr_backup_utf8 = {
+       "Backup using UTF8",
+       "Normal backup. Do NOT use utf8"
+};
+static const true_false_string tfs_butype_attr_recover_utf8 = {
+       "Recover using UTF8",
+       "Normal recover. Do NOT use utf8"
+};
 static int
-dissect_ndmp_header(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *parent_tree)
+dissect_butype_attrs(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *parent_tree)
 {
        proto_item* item = NULL;
        proto_tree* tree = NULL;
-       guint32 msgtype, msg;
+       guint32 flags;
 
+       flags=tvb_get_ntohl(tvb, offset);
        if (parent_tree) {
-               item = proto_tree_add_item(tree, hf_ndmp_header, tvb,
-                               offset, 24, FALSE);
-               tree = proto_item_add_subtree(item, ett_ndmp_header);
+               item = proto_tree_add_text(parent_tree, tvb, offset, 4,
+                               "Attributes: 0x%08x", flags);
+               tree = proto_item_add_subtree(item, ett_ndmp_butype_attrs);
        }
 
-       /* sequence number */
-       proto_tree_add_item(tree, hf_ndmp_sequence, tvb, offset, 4, FALSE);
-       offset += 4;
+       proto_tree_add_boolean(tree, hf_ndmp_butype_attr_recover_utf8,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_butype_attr_backup_utf8,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_butype_attr_recover_incremental,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_butype_attr_backup_incremental,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_butype_attr_recover_direct,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_butype_attr_backup_direct,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_butype_attr_recover_filelist,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_butype_attr_backup_filelist,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_butype_attr_backup_file_history,
+                               tvb, offset, 4, flags);
 
-       /* timestamp */
-       proto_tree_add_item(tree, hf_ndmp_timestamp, tvb, offset, 4, FALSE);
        offset += 4;
+       return offset;
+}
 
-       /* Message Type */
-       msgtype = tvb_get_ntohl(tvb, offset);
-       proto_tree_add_item(tree, hf_ndmp_msgtype, tvb, offset, 4, FALSE);
-       offset += 4;
+static int
+dissect_butype_info(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
+{
+       /*butype name*/
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_butype_name, offset, NULL);
 
-       /* Message */
-       msg = tvb_get_ntohl(tvb, offset);
-       proto_tree_add_item(tree, hf_ndmp_msg, tvb, offset, 4, FALSE);
-       offset += 4;
+       /* default env */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_default_env, hf_ndmp_butype_default_env);
 
-       /* Reply sequence number */
-       proto_tree_add_item(tree, hf_ndmp_reply_sequence, tvb, offset, 4, FALSE);
-       offset += 4;
+       /* attrs */
+       offset = dissect_butype_attrs(tvb, offset, pinfo, tree);
+
+       return offset;
+}
 
+static int
+dissect_get_butype_info_reply(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq _U_)
+{
        /* error */
        proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
        offset += 4;
 
-       if (check_col(pinfo->cinfo, COL_INFO)){
-               col_clear(pinfo->cinfo, COL_INFO);
-               col_append_fstr(pinfo->cinfo, COL_INFO, "%s %s",
-                       val_to_str(msg, msg_vals, "Unknown Message (0x%02x)"),
-                       val_to_str(msgtype, msg_type_vals, "Unknown Type (0x%02x)")
-                       );
+       /* butype */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_butype_info, hf_ndmp_butype_info);
+
+       return offset;
+}
+
+static const true_false_string tfs_fs_invalid_total_size = {
+       "Total size is INVALID",
+       "Total size is VALID"
+};
+static const true_false_string tfs_fs_invalid_used_size = {
+       "Used size is INVALID",
+       "Used size is VALID"
+};
+static const true_false_string tfs_fs_invalid_avail_size = {
+       "Available size is INVALID",
+       "Available size is VALID"
+};
+static const true_false_string tfs_fs_invalid_total_inodes = {
+       "Total inode count is INVALID",
+       "Total inode count is VALID"
+};
+static const true_false_string tfs_fs_invalid_used_inodes = {
+       "Used inode count is INVALID",
+       "Used inode count is VALID"
+};
+static int
+dissect_fs_invalid(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *parent_tree)
+{
+       proto_item* item = NULL;
+       proto_tree* tree = NULL;
+       guint32 flags;
+
+       flags=tvb_get_ntohl(tvb, offset);
+       if (parent_tree) {
+               item = proto_tree_add_text(parent_tree, tvb, offset, 4,
+                               "Invalids: 0x%08x", flags);
+               tree = proto_item_add_subtree(item, ett_ndmp_fs_invalid);
        }
 
+       proto_tree_add_boolean(tree, hf_ndmp_fs_invalid_used_inodes,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_fs_invalid_total_inodes,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_fs_invalid_avail_size,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_fs_invalid_used_size,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_fs_invalid_total_size,
+                               tvb, offset, 4, flags);
+
+       offset+=4;
        return offset;
 }
 
+static int
+dissect_fs_env(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree)
+{
+       /* name */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_fs_env_name, offset, NULL);
+
+       /* value */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_fs_env_value, offset, NULL);
+
+       return offset;
+}
 
 static int
-dissect_ndmp_cmd(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
+dissect_fs_info(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
 {
-       int i;
-       guint32 msg, msgtype;
+       /* invalid bits */
+       offset=dissect_fs_invalid(tvb, offset, pinfo, tree);
 
-       msg=tvb_get_ntohl(tvb, offset+12);
-       msgtype=tvb_get_ntohl(tvb, offset+8);
+       /* fs type */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_fs_fs_type, offset, NULL);
 
-       offset=dissect_ndmp_header(tvb, offset, pinfo, tree);
+       /* fs logical device */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_fs_logical_device, offset, NULL);
 
+       /* fs physical device */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_fs_physical_device, offset, NULL);
 
-       for(i=0;ndmp_commands[i].cmd!=0;i++){
-               if(ndmp_commands[i].cmd==msg){
-                       break;
-               }
-       }
+       /*total_size*/
+       offset = dissect_rpc_uint64(tvb, tree, hf_ndmp_fs_total_size,
+                       offset);
 
+       /*used_size*/
+       offset = dissect_rpc_uint64(tvb, tree, hf_ndmp_fs_used_size,
+                       offset);
 
-       if(ndmp_commands[i].cmd==0){
-               /* we do not know this message */
-               proto_tree_add_text(tree, tvb, offset, tvb_length_remaining(tvb, offset), "Unknown type of NDMP message: 0x%02x", msg);
-               offset+=tvb_length_remaining(tvb, offset);
-               return offset;
-       }
+       /*avail_size*/
+       offset = dissect_rpc_uint64(tvb, tree, hf_ndmp_fs_avail_size,
+                       offset);
 
+       /*total_inodes*/
+       offset = dissect_rpc_uint64(tvb, tree, hf_ndmp_fs_total_inodes,
+                       offset);
 
-       if(msgtype==NDMP_MESSAGE_REQUEST){
-               offset=ndmp_commands[i].request(tvb, offset, pinfo, tree);
-       } else {
-               offset=ndmp_commands[i].response(tvb, offset, pinfo, tree);
+       /*used_inodes*/
+       offset = dissect_rpc_uint64(tvb, tree, hf_ndmp_fs_used_inodes,
+                       offset);
+
+       /* env */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_fs_env, hf_ndmp_fs_env);
+
+       /* status */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_fs_status, offset, NULL);
+
+       return offset;
+}
+
+static int
+dissect_get_fs_info_reply(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* error */
+       proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* fs */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_fs_info, hf_ndmp_fs_info);
+
+       return offset;
+}
+
+static const true_false_string tfs_tape_attr_rewind = {
+       "Device supports REWIND",
+       "Device does NOT support rewind"
+};
+static const true_false_string tfs_tape_attr_unload = {
+       "Device supports UNLOAD",
+       "Device does NOT support unload"
+};
+static int
+dissect_tape_attr(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *parent_tree)
+{
+       proto_item* item = NULL;
+       proto_tree* tree = NULL;
+       guint32 flags;
+
+       flags=tvb_get_ntohl(tvb, offset);
+       if (parent_tree) {
+               item = proto_tree_add_text(parent_tree, tvb, offset, 4,
+                               "Attributes: 0x%08x", flags);
+               tree = proto_item_add_subtree(item, ett_ndmp_tape_attr);
        }
 
+       proto_tree_add_boolean(tree, hf_ndmp_tape_attr_unload,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_tape_attr_rewind,
+                               tvb, offset, 4, flags);
+
+       offset+=4;
        return offset;
 }
 
-static void
-dissect_ndmp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+static int
+dissect_tape_capability(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree)
 {
-       int offset = 0;
-       guint32 size, available_bytes;
-
-       /* loop through the packet, dissecting multiple NDMP pdus*/
-       do {
-               available_bytes = tvb_length_remaining(tvb, offset);
-
-               /* size of this NDMP PDU */
-               size = tvb_get_ntohl(tvb, offset);      
-
-               /* desegmentation */
-               if(ndmp_desegment){
-                       if(pinfo->can_desegment
-                       && size>available_bytes) {
-                               pinfo->desegment_offset = offset;
-                               pinfo->desegment_len = size-available_bytes;
-                               return;
-                       }
-               }
+       /* name */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_tape_capability_name, offset, NULL);
+
+       /* value */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_tape_capability_value, offset, NULL);
+
+       return offset;
+}
+
+static int
+dissect_tape_dev_cap(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
+{
+       /* device */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_tape_device, offset, NULL);
 
-               /* the size of the current PDU */
-               proto_tree_add_item(tree, hf_ndmp_size, tvb, offset, 4, size);
-               offset += 4;
+       /* tape attributes */
+       offset = dissect_tape_attr(tvb, offset, pinfo, tree);
 
-               offset = dissect_ndmp_cmd(tvb, offset, pinfo, tree);
-       } while(offset<(int)tvb_reported_length(tvb));
+       /* capability */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_tape_capability, hf_ndmp_tape_capability);
 
+       return offset;
 }
 
+static int
+dissect_tape_info(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
+{
+       /* model */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_tape_model, offset, NULL);
 
+       /* device capabilites */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_tape_dev_cap, hf_ndmp_tape_dev_cap);
 
+       return offset;
+}
 
-void
-proto_register_ndmp(void)
+static int
+dissect_get_tape_info_reply(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq _U_)
 {
+       /* error */
+       proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
+       offset += 4;
 
-  static hf_register_info hf_ndmp[] = {
-       { &hf_ndmp_size, {
-               "Size", "ndmp.size", FT_UINT32, BASE_DEC,
-               NULL, 0, "Size of this NDMP PDU", HFILL }},
+       /* tape */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_tape_info, hf_ndmp_tape_info);
 
-       { &hf_ndmp_header, {
-               "NDMP Header", "ndmp.header", FT_NONE, 0,
-               NULL, 0, "NDMP Header", HFILL }},
+       return offset;
+}
 
-       { &hf_ndmp_sequence, {
-               "Sequence", "ndmp.sequence", FT_UINT32, BASE_DEC,
-               NULL, 0, "Sequence number for NDMP PDU", HFILL }},
+static int
+dissect_scsi_info(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
+{
+       /* model */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_scsi_model, offset, NULL);
 
-       { &hf_ndmp_reply_sequence, {
-               "Reply Sequence", "ndmp.reply_sequence", FT_UINT32, BASE_DEC,
-               NULL, 0, "Reply Sequence number for NDMP PDU", HFILL }},
+       /* device capabilites */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_tape_dev_cap, hf_ndmp_tape_dev_cap);
 
-       { &hf_ndmp_timestamp, {
-               "Time", "ndmp.timestamp", FT_ABSOLUTE_TIME, BASE_NONE,
-               NULL, 0, "Timestamp for this NDMP PDU", HFILL }},
+       return offset;
+}
 
-       { &hf_ndmp_msgtype, {
-               "Type", "ndmp.msg_type", FT_UINT32, BASE_DEC,
-               VALS(msg_type_vals), 0, "Is this a Request or Response?", HFILL }},
+static int
+dissect_get_scsi_info_reply(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* error */
+       proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
+       offset += 4;
 
-       { &hf_ndmp_msg, {
-               "Message", "ndmp.msg", FT_UINT32, BASE_DEC,
-               VALS(msg_vals), 0, "Type of NDMP PDU", HFILL }},
+       /* scsi */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_scsi_info, hf_ndmp_scsi_info);
 
-       { &hf_ndmp_error, {
-               "Error", "ndmp.error", FT_UINT32, BASE_DEC,
-               VALS(error_vals), 0, "Error code for this NDMP PDU", HFILL }},
+       return offset;
+}
 
-       { &hf_ndmp_version, {
-               "Version", "ndmp.version", FT_UINT32, BASE_DEC,
-               NULL, 0, "Version of NDMP protocol", HFILL }},
+static int
+dissect_get_server_info_reply(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* error */
+       proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
+       offset += 4;
 
+       /* vendor */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_server_vendor, offset, NULL);
 
-  };
+       /* product */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_server_product, offset, NULL);
 
-  static gint *ett[] = {
-    &ett_ndmp,
-    &ett_ndmp_header,
-  };
+       /* revision */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_server_revision, offset, NULL);
 
-  module_t *ndmp_module;
 
-  proto_ndmp = proto_register_protocol("Network Data Management Protocol", "NDMP", "ndmp");
-  proto_register_field_array(proto_ndmp, hf_ndmp, array_length(hf_ndmp));
-  
-  proto_register_subtree_array(ett, array_length(ett));
+       /* server */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_auth_type, hf_ndmp_auth_types);
 
-  /* desegmentation */
-  ndmp_module = prefs_register_protocol(proto_ndmp, NULL);
-  prefs_register_bool_preference(ndmp_module, "ndmp.desegment", "Desegment all NDMP messages spanning multiple TCP segments", "Whether the dissector should desegment NDMP over TCP PDUs or not", &ndmp_desegment);
+       return offset;
+}
+
+static int
+dissect_scsi_open_request(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* device */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_scsi_device, offset, NULL);
+
+       return offset;
+}
+
+static int
+dissect_scsi_get_state_reply(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* error */
+       proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* controller */
+       proto_tree_add_item(tree, hf_ndmp_scsi_controller, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* id */
+       proto_tree_add_item(tree, hf_ndmp_scsi_id, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* lun */
+       proto_tree_add_item(tree, hf_ndmp_scsi_lun, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       return offset;
+}
 
+static int
+dissect_scsi_set_state_request(tvbuff_t *tvb, int offset,
+    packet_info *pinfo _U_, proto_tree *tree, guint32 seq _U_)
+{
+       /* device */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_scsi_device, offset, NULL);
+
+       /* controller */
+       proto_tree_add_item(tree, hf_ndmp_scsi_controller, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* id */
+       proto_tree_add_item(tree, hf_ndmp_scsi_id, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* lun */
+       proto_tree_add_item(tree, hf_ndmp_scsi_lun, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       return offset;
+}
+
+static int
+dissect_execute_cdb_flags(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *parent_tree)
+{
+       proto_item* item = NULL;
+       proto_tree* tree = NULL;
+       guint32 flags;
+
+       flags = tvb_get_ntohl(tvb, offset);
+       if (parent_tree) {
+               item = proto_tree_add_text(parent_tree, tvb, offset, 4,
+                               "Flags: 0x%08x", flags);
+               tree = proto_item_add_subtree(item, ett_ndmp_execute_cdb_flags);
+       }
+       
+       proto_tree_add_boolean(tree, hf_ndmp_execute_cdb_flags_data_in,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_execute_cdb_flags_data_out,
+                               tvb, offset, 4, flags);
+       offset += 4;
+       return offset;
+}
+
+static int
+dissect_execute_cdb_cdb(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *parent_tree)
+{
+       proto_item* item = NULL;
+       proto_tree* tree = NULL;
+       guint32 cdb_len;
+       guint32 cdb_len_full;
+
+       cdb_len = tvb_get_ntohl(tvb, offset);
+       cdb_len_full = rpc_roundup(cdb_len);
+       
+       if (parent_tree) {
+               item = proto_tree_add_text(parent_tree, tvb, offset,
+                               4+cdb_len_full, "CDB");
+               tree = proto_item_add_subtree(item, ett_ndmp_execute_cdb_cdb);
+       }
+
+       proto_tree_add_uint(tree, hf_ndmp_execute_cdb_cdb_len, tvb, offset, 4,
+                       cdb_len);
+       offset += 4;
+
+       if (cdb_len != 0) {
+               dissect_scsi_cdb(tvb, pinfo, tree, offset, cdb_len);
+               offset += cdb_len_full;
+       }
+
+       return offset;
+}
+
+
+static int
+dissect_execute_cdb_payload(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *parent_tree,
+    char *name, int hf_len, gboolean isreq)
+{
+       proto_item* item = NULL;
+       proto_tree* tree = NULL;
+       guint32 payload_len;
+       guint32 payload_len_full;
+
+       payload_len = tvb_get_ntohl(tvb, offset);
+       payload_len_full = rpc_roundup(payload_len);
+       
+       if (parent_tree) {
+               item = proto_tree_add_text(parent_tree, tvb, offset,
+                               4+payload_len_full, "%s", name);
+               tree = proto_item_add_subtree(item,
+                   ett_ndmp_execute_cdb_payload);
+       }
+
+       proto_tree_add_uint(tree, hf_len, tvb, offset, 4, payload_len);
+       offset += 4;
+
+       if (payload_len != 0) {
+               dissect_scsi_payload(tvb, pinfo, tree, offset, isreq,
+                   payload_len);
+               offset += payload_len_full;
+       }
+
+       return offset;
+}
+
+static int
+dissect_execute_cdb_request(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq)
+{
+       conversation_t *conversation;
+       scsi_task_id_t task_key;
+
+       /*
+        * We need to provide SCSI task information to the SCSI
+        * dissection routines.  We use a conversation plus the
+        * sequence number in requests and the reply sequence
+        * number in replies to identify SCSI tasks.
+        */
+       conversation = find_conversation(&pinfo->src, &pinfo->dst,
+           pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
+       if (conversation == NULL) {
+               conversation = conversation_new(&pinfo->src, &pinfo->dst,
+                   pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
+       }
+       task_key.conv_id = conversation->index;
+       task_key.task_id = seq;
+        pinfo->private_data = &task_key;
+        
+       /* flags */
+       offset = dissect_execute_cdb_flags(tvb, offset, pinfo, tree);
+
+       /* timeout */
+       proto_tree_add_item(tree, hf_ndmp_execute_cdb_timeout, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* datain_len */
+       proto_tree_add_item(tree, hf_ndmp_execute_cdb_datain_len, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* CDB */
+       offset = dissect_execute_cdb_cdb(tvb, offset, pinfo, tree);
+
+       /* dataout */
+       offset = dissect_execute_cdb_payload(tvb, offset, pinfo, tree,
+           "Data out", hf_ndmp_execute_cdb_dataout_len, TRUE);
+
+       return offset;
+}
+
+static int
+dissect_execute_cdb_sns(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *parent_tree)
+{
+       proto_item* item = NULL;
+       proto_tree* tree = NULL;
+       guint32 sns_len;
+       guint32 sns_len_full;
+
+       sns_len = tvb_get_ntohl(tvb, offset);
+       sns_len_full = rpc_roundup(sns_len);
+       
+       if (parent_tree) {
+               item = proto_tree_add_text(parent_tree, tvb, offset,
+                               4+sns_len_full, "Sense data");
+               tree = proto_item_add_subtree(item, ett_ndmp_execute_cdb_sns);
+       }
+
+       proto_tree_add_uint(tree, hf_ndmp_execute_cdb_sns_len, tvb, offset, 4,
+                       sns_len);
+       offset += 4;
+
+       if (sns_len != 0) {
+               dissect_scsi_snsinfo(tvb, pinfo, tree, offset, sns_len);
+               offset += sns_len_full;
+       }
+
+       return offset;
+}
+
+static int
+dissect_execute_cdb_reply(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq)
+{
+       conversation_t *conversation;
+       scsi_task_id_t task_key;
+
+       /*
+        * We need to provide SCSI task information to the SCSI
+        * dissection routines.  We use a conversation plus the
+        * sequence number in requests and the reply sequence
+        * number in replies to identify SCSI tasks.
+        */
+       conversation = find_conversation(&pinfo->src, &pinfo->dst,
+           pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
+       if (conversation != NULL) {
+               task_key.conv_id = conversation->index;
+               task_key.task_id = seq;
+               pinfo->private_data = &task_key;
+       } else {
+               /* no conversation, meaning we didn't see the request */
+               pinfo->private_data = NULL;
+       }
+        
+       /* error */
+       proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* status */
+       proto_tree_add_item(tree, hf_ndmp_execute_cdb_status, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* dataout_len */
+       proto_tree_add_item(tree, hf_ndmp_execute_cdb_dataout_len, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* datain */
+       offset = dissect_execute_cdb_payload(tvb, offset, pinfo, tree,
+           "Data in", hf_ndmp_execute_cdb_datain_len, FALSE);
+
+       /* ext_sense */
+       offset = dissect_execute_cdb_sns(tvb, offset, pinfo, tree);
+
+       return offset;
+}
+
+#define NDMP_TAPE_OPEN_MODE_READ       0
+#define NDMP_TAPE_OPEN_MODE_RDWR       1
+static const value_string tape_open_mode_vals[] = {
+       {NDMP_TAPE_OPEN_MODE_READ,      "Read"},
+       {NDMP_TAPE_OPEN_MODE_RDWR,      "Read/Write"},
+       {0, NULL}
+};
+
+static int
+dissect_tape_open_request(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* device */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_tape_device, offset, NULL);
+
+       /* open mode */
+       proto_tree_add_item(tree, hf_ndmp_tape_open_mode, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       return offset;
+}
+
+
+static const true_false_string tfs_ndmp_tape_invalid_file_num = {
+       "File num is valid",
+       "File num is INVALID"
+};
+static const true_false_string tfs_ndmp_tape_invalid_soft_errors = {
+       "Soft errors is valid",
+       "Soft errors is INVALID"
+};
+static const true_false_string tfs_ndmp_tape_invalid_block_size = {
+       "Block size is valid",
+       "Block size is INVALID"
+};
+static const true_false_string tfs_ndmp_tape_invalid_block_no = {
+       "Block no is valid",
+       "Block no is INVALID"
+};
+static const true_false_string tfs_ndmp_tape_invalid_total_space = {
+       "Total space is valid",
+       "Total space is INVALID"
+};
+static const true_false_string tfs_ndmp_tape_invalid_space_remain = {
+       "Space remaining is INVALID",
+       "Space remaining is valid"
+};
+static const true_false_string tfs_ndmp_tape_invalid_partition = {
+       "Partition is INVALID",
+       "Partition is valid"
+};
+static int
+dissect_tape_invalid(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *parent_tree)
+{
+       proto_item* item = NULL;
+       proto_tree* tree = NULL;
+       guint32 flags;
+
+       flags=tvb_get_ntohl(tvb, offset);
+       if (parent_tree) {
+               item = proto_tree_add_text(parent_tree, tvb, offset, 4,
+                               "Invalids: 0x%08x", flags);
+               tree = proto_item_add_subtree(item, ett_ndmp_tape_invalid);
+       }
+
+       proto_tree_add_boolean(tree, hf_ndmp_tape_invalid_partition,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_tape_invalid_space_remain,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_tape_invalid_total_space,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_tape_invalid_block_no,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_tape_invalid_block_size,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_tape_invalid_soft_errors,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_tape_invalid_file_num,
+                               tvb, offset, 4, flags);
+
+       offset+=4;
+       return offset;
+}
+
+static const true_false_string tfs_ndmp_tape_flags_no_rewind = {
+       "This is a NON-REWINDING device",
+       "This device supports rewind"
+};
+static const true_false_string tfs_ndmp_tape_flags_write_protect = {
+       "This device is WRITE-PROTECTED",
+       "This device is NOT write-protected"
+};
+static const true_false_string tfs_ndmp_tape_flags_error = {
+       "This device shows ERROR",
+       "This device shows NO errors"
+};
+static const true_false_string tfs_ndmp_tape_flags_unload = {
+       "This device supports UNLOAD",
+       "This device does NOT support unload"
+};
+static int
+dissect_tape_flags(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *parent_tree)
+{
+       proto_item* item = NULL;
+       proto_tree* tree = NULL;
+       guint32 flags;
+
+       flags=tvb_get_ntohl(tvb, offset);
+       if (parent_tree) {
+               item = proto_tree_add_text(parent_tree, tvb, offset, 4,
+                               "Flags: 0x%08x", flags);
+               tree = proto_item_add_subtree(item, ett_ndmp_tape_flags);
+       }
+
+
+       proto_tree_add_boolean(tree, hf_ndmp_tape_flags_unload,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_tape_flags_error,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_tape_flags_write_protect,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_tape_flags_no_rewind,
+                               tvb, offset, 4, flags);
+
+       offset+=4;
+       return offset;
+}
+
+static int
+dissect_tape_get_state_reply(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* invalid bits */
+       offset=dissect_tape_invalid(tvb, offset, pinfo, tree);
+
+       /* error */
+       proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* flags */
+       offset=dissect_tape_flags(tvb, offset, pinfo, tree);
+
+       /* file_num */
+       proto_tree_add_item(tree, hf_ndmp_tape_file_num, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* soft_errors */
+       proto_tree_add_item(tree, hf_ndmp_tape_soft_errors, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* block_size */
+       proto_tree_add_item(tree, hf_ndmp_tape_block_size, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* block_no */
+       proto_tree_add_item(tree, hf_ndmp_tape_block_no, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* total_space */
+       offset = dissect_rpc_uint64(tvb, tree,hf_ndmp_tape_total_space,
+                       offset);
+
+       /* space_remain */
+       offset = dissect_rpc_uint64(tvb, tree,hf_ndmp_tape_space_remain,
+                       offset);
+
+       /* partition */
+       proto_tree_add_item(tree, hf_ndmp_tape_partition, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       return offset;
+}
+
+#define NDMP_TAPE_MTIO_FSF     0
+#define NDMP_TAPE_MTIO_BSF     1
+#define NDMP_TAPE_MTIO_FSR     2
+#define NDMP_TAPE_MTIO_BSR     3
+#define NDMP_TAPE_MTIO_REW     4
+#define NDMP_TAPE_MTIO_EOF     5
+#define NDMP_TAPE_MTIO_OFF     6
+static const value_string tape_mtio_vals[] = {
+       {NDMP_TAPE_MTIO_FSF,    "FSF"},
+       {NDMP_TAPE_MTIO_BSF,    "BSF"},
+       {NDMP_TAPE_MTIO_FSR,    "FSR"},
+       {NDMP_TAPE_MTIO_BSR,    "BSR"},
+       {NDMP_TAPE_MTIO_REW,    "REW"},
+       {NDMP_TAPE_MTIO_EOF,    "EOF"},
+       {NDMP_TAPE_MTIO_OFF,    "OFF"},
+       {0, NULL}
+};
+
+static int
+dissect_tape_mtio_request(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* op */
+       proto_tree_add_item(tree, hf_ndmp_tape_mtio_op, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* count */
+       proto_tree_add_item(tree, hf_ndmp_count, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       return offset;
+}
+
+static int
+dissect_tape_mtio_reply(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* error */
+       proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* resid count */
+       proto_tree_add_item(tree, hf_ndmp_resid_count, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       return offset;
+}
+
+#define NDMP_MOVER_STATE_IDLE          0
+#define NDMP_MOVER_STATE_LISTEN                1
+#define NDMP_MOVER_STATE_ACTIVE                2
+#define NDMP_MOVER_STATE_PAUSED                3
+#define NDMP_MOVER_STATE_HALTED                4
+static const value_string mover_state_vals[] = {
+       {NDMP_MOVER_STATE_IDLE, "MOVER_STATE_IDLE"},
+       {NDMP_MOVER_STATE_LISTEN,       "MOVER_STATE_LISTEN"},
+       {NDMP_MOVER_STATE_ACTIVE,       "MOVER_STATE_ACTIVE"},
+       {NDMP_MOVER_STATE_PAUSED,       "MOVER_STATE_PAUSED"},
+       {NDMP_MOVER_STATE_HALTED,       "MOVER_STATE_HALTED"},
+       {0, NULL}
+};
+
+#define NDMP_MOVER_PAUSE_NA            0
+#define NDMP_MOVER_PAUSE_EOM           1
+#define NDMP_MOVER_PAUSE_EOF           2
+#define NDMP_MOVER_PAUSE_SEEK          3
+#define NDMP_MOVER_PAUSE_MEDIA_ERROR   4
+#define NDMP_MOVER_PAUSE_EOW           5
+static const value_string mover_pause_vals[] = {
+       {NDMP_MOVER_PAUSE_NA,           "MOVER_PAUSE_NA"},
+       {NDMP_MOVER_PAUSE_EOM,          "MOVER_PAUSE_EOM"},
+       {NDMP_MOVER_PAUSE_EOF,          "MOVER_PAUSE_EOF"},
+       {NDMP_MOVER_PAUSE_SEEK,         "MOVER_PAUSE_SEEK"},
+       {NDMP_MOVER_PAUSE_MEDIA_ERROR,  "MOVER_PAUSE_MEDIA_ERROR"},
+       {NDMP_MOVER_PAUSE_EOW,          "MOVER_PAUSE_EOW"},
+       {0, NULL}
+};
+
+#define NDMP_HALT_NA           0
+#define NDMP_HALT_CONNECT_CLOSE        1
+#define NDMP_HALT_ABORTED              2
+#define NDMP_HALT_INTERNAL_ERROR       3
+#define NDMP_HALT_CONNECT_ERROR        4
+static const value_string halt_vals[] = {
+       {NDMP_HALT_NA,                  "HALT_NA"},
+       {NDMP_HALT_CONNECT_CLOSE,       "HALT_CONNECT_CLOSE"},
+       {NDMP_HALT_ABORTED,             "HALT_ABORTED"},
+       {NDMP_HALT_INTERNAL_ERROR,      "HALT_INTERNAL_ERROR"},
+       {NDMP_HALT_CONNECT_ERROR,       "HALT_CONNECT_ERROR"},
+       {0, NULL}
+};
+
+static int
+dissect_ndmp_addr(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *parent_tree)
+{
+       proto_item* item = NULL;
+       proto_tree* tree = NULL;
+       guint32 type;
+
+       type=tvb_get_ntohl(tvb, offset);
+       if (parent_tree) {
+               item = proto_tree_add_text(parent_tree, tvb, offset, 4,
+                               "Type: %s ", val_to_str(type, addr_type_vals,"Unknown addr type (0x%02x)") );
+               tree = proto_item_add_subtree(item, ett_ndmp_addr);
+       }
+
+       /*address type*/
+       proto_tree_add_item(tree, hf_ndmp_addr_type, tvb, offset, 4, FALSE);
+       offset += 4;
+
+
+       switch(type){
+       case NDMP_ADDR_LOCAL:
+               break;
+       case NDMP_ADDR_TCP:
+               /* IP addr */
+               proto_tree_add_item(tree, hf_ndmp_addr_ip, tvb, offset, 4, FALSE);
+               offset+=4;
+               
+               /* TCP port */
+               proto_tree_add_item(tree, hf_ndmp_addr_tcp, tvb, offset, 4, FALSE);
+               offset+=4;
+               
+               break;
+       case NDMP_ADDR_FC:
+               /* FCAL loop id */
+               proto_tree_add_item(tree, hf_ndmp_addr_fcal_loop_id, tvb, offset, 4, FALSE);
+               offset+=4;
+
+               break;
+       case NDMP_ADDR_IPC:
+               /* IPC address */
+               offset = dissect_rpc_data(tvb, tree, hf_ndmp_addr_ipc, offset);
+               break;
+       }
+
+       return offset;
+}
+               
+static int
+dissect_mover_get_state_reply(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* error */
+       proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* mover state */
+       proto_tree_add_item(tree, hf_ndmp_mover_state, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* mover pause */
+       proto_tree_add_item(tree, hf_ndmp_mover_pause, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* halt */
+       proto_tree_add_item(tree, hf_ndmp_halt, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* record size */
+       proto_tree_add_item(tree, hf_ndmp_record_size, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* record num */
+       proto_tree_add_item(tree, hf_ndmp_record_num, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* data written */
+       proto_tree_add_item(tree, hf_ndmp_data_written, tvb, offset, 8, FALSE);
+       offset += 8;
+
+       /* seek position */
+       proto_tree_add_item(tree, hf_ndmp_seek_position, tvb, offset, 8, FALSE);
+       offset += 8;
+
+       /* bytes left to read */
+       proto_tree_add_item(tree, hf_ndmp_bytes_left_to_read, tvb, offset, 8, FALSE);
+       offset += 8;
+
+       /* window offset */
+       proto_tree_add_item(tree, hf_ndmp_window_offset, tvb, offset, 8, FALSE);
+       offset += 8;
+
+       /* window length */
+       proto_tree_add_item(tree, hf_ndmp_window_length, tvb, offset, 8, FALSE);
+       offset += 8;
+
+       /* ndmp addr */
+       offset=dissect_ndmp_addr(tvb, offset, pinfo, tree);
+
+       return offset;
+}
+
+#define NDMP_MOVER_MODE_READ   0
+#define NDMP_MOVER_MODE_WRITE  1
+static const value_string mover_mode_vals[] = {
+       {NDMP_MOVER_MODE_READ,  "MODE_READ"},
+       {NDMP_MOVER_MODE_WRITE, "MOVER_MODE_WRITE"},
+       {0, NULL}
+};
+
+static int
+dissect_mover_listen_request(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* mode */
+       proto_tree_add_item(tree, hf_ndmp_mover_mode, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /*address type*/
+       proto_tree_add_item(tree, hf_ndmp_addr_type, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       return offset;
+}
+
+static int
+dissect_mover_listen_reply(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* error */
+       proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* ndmp addr */
+       offset=dissect_ndmp_addr(tvb, offset, pinfo, tree);
+
+       return offset;
+}
+
+static int
+dissect_mover_set_window_request(tvbuff_t *tvb, int offset,
+    packet_info *pinfo _U_, proto_tree *tree, guint32 seq _U_)
+{
+       /* window offset */
+       proto_tree_add_item(tree, hf_ndmp_window_offset, tvb, offset, 8, FALSE);
+       offset += 8;
+
+       /* window length */
+       proto_tree_add_item(tree, hf_ndmp_window_length, tvb, offset, 8, FALSE);
+       offset += 8;
+
+       return offset;
+}
+
+static int
+dissect_mover_set_record_size_request(tvbuff_t *tvb, int offset,
+    packet_info *pinfo _U_, proto_tree *tree, guint32 seq _U_)
+{
+       /* record size */
+       proto_tree_add_item(tree, hf_ndmp_record_size, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       return offset;
+}
+
+static int
+dissect_mover_connect_request(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* mode */
+       proto_tree_add_item(tree, hf_ndmp_mover_mode, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* ndmp addr */
+       offset=dissect_ndmp_addr(tvb, offset, pinfo, tree);
+
+       return offset;
+}
+
+static int
+dissect_log_file_request(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* file */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_file_name, offset, NULL);
+
+       /* error */
+       proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       return offset;
+}
+
+#define NDMP_LOG_TYPE_NORMAL   0
+#define NDMP_LOG_TYPE_DEBUG    1
+#define NDMP_LOG_TYPE_ERROR    2
+#define NDMP_LOG_TYPE_WARNING  3
+static const value_string log_type_vals[] = {
+       {NDMP_LOG_TYPE_NORMAL,  "NORMAL"},
+       {NDMP_LOG_TYPE_DEBUG,   "DEBUG"},
+       {NDMP_LOG_TYPE_ERROR,   "ERROR"},
+       {NDMP_LOG_TYPE_WARNING, "WARNING"},
+       {0, NULL}
+};
+
+static int
+dissect_log_message_request(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* type */
+       proto_tree_add_item(tree, hf_ndmp_log_type, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* message id */
+       proto_tree_add_item(tree, hf_ndmp_log_message_id, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* message */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_log_message, offset, NULL);
+
+       return offset;
+}
+
+static int
+dissect_notify_data_halted_request(tvbuff_t *tvb, int offset,
+    packet_info *pinfo _U_, proto_tree *tree, guint32 seq _U_)
+{
+       /* halt */
+       proto_tree_add_item(tree, hf_ndmp_halt, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* reason */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_halt_reason, offset, NULL);
+
+       return offset;
+}
+
+#define NDMP_CONNECTED_CONNECTED       0
+#define NDMP_CONNECTED_SHUTDOWN                1
+#define NDMP_CONNECTED_REFUSED         2
+static const value_string connected_vals[] = {
+       {NDMP_CONNECTED_CONNECTED,      "CONNECTED"},
+       {NDMP_CONNECTED_SHUTDOWN,       "SHUTDOWN"},
+       {NDMP_CONNECTED_REFUSED,        "REFUSED"},
+       {0, NULL}
+};
+
+static int
+dissect_notify_connected_request(tvbuff_t *tvb, int offset,
+    packet_info *pinfo _U_, proto_tree *tree, guint32 seq _U_)
+{
+       /* connected */
+       proto_tree_add_item(tree, hf_ndmp_connected, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* version number */
+       proto_tree_add_item(tree, hf_ndmp_version, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* reason */
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_connected_reason, offset, NULL);
+
+       return offset;
+}
+
+
+static int
+dissect_notify_mover_paused_request(tvbuff_t *tvb, int offset,
+    packet_info *pinfo _U_, proto_tree *tree, guint32 seq _U_)
+{
+       /* mover pause */
+       proto_tree_add_item(tree, hf_ndmp_mover_pause, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* seek position */
+       proto_tree_add_item(tree, hf_ndmp_seek_position, tvb, offset, 8, FALSE);
+       offset += 8;
+
+       return offset;
+}
+
+static int
+dissect_auth_data(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree)
+{
+       guint type;
+       
+       type=tvb_get_ntohl(tvb,offset);
+
+       /* auth type */
+       proto_tree_add_item(tree, hf_ndmp_auth_type, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       switch(type){
+       case NDMP_AUTH_NONE:
+               break;
+       case NDMP_AUTH_TEXT:
+               /* auth id */
+               offset = dissect_rpc_string(tvb, tree,
+                               hf_ndmp_auth_id, offset, NULL);
+
+               /* auth password */
+               offset = dissect_rpc_string(tvb, tree,
+                               hf_ndmp_auth_password, offset, NULL);
+
+               
+               break;
+       case NDMP_AUTH_MD5:
+               /* auth id */
+               offset = dissect_rpc_string(tvb, tree,
+                               hf_ndmp_auth_id, offset, NULL);
+
+               /* digest */
+               proto_tree_add_item(tree, hf_ndmp_auth_digest, 
+                       tvb, offset, 16, FALSE);
+               offset+=16;
+       }
+
+       return offset;
+}
+
+static int
+dissect_connect_client_auth_request(tvbuff_t *tvb, int offset,
+    packet_info *pinfo, proto_tree *tree, guint32 seq _U_)
+{
+       return dissect_auth_data(tvb, offset, pinfo, tree);
+}
+
+static int
+dissect_connect_server_auth_reply(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* error */
+       proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* auth data */
+       offset = dissect_auth_data(tvb, offset, pinfo, tree);
+
+       return offset;
+}
+
+static int
+dissect_tape_write_request(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* data */
+       offset = dissect_rpc_data(tvb, tree, hf_ndmp_data, offset);
+
+       return offset;
+}
+
+static int
+dissect_tape_write_reply(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* error */
+       proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* count */
+       proto_tree_add_item(tree, hf_ndmp_count, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       return offset;
+}
+
+static int
+dissect_tape_read_request(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* count */
+       proto_tree_add_item(tree, hf_ndmp_count, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       return offset;
+}
+
+static int
+dissect_tape_read_reply(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* error */
+       proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* data */
+       offset = dissect_rpc_data(tvb, tree, hf_ndmp_data, offset);
+
+       return offset;
+}
+
+#define NDMP_FS_UNIX   0
+#define NDMP_FS_NT     1
+#define NDMP_FS_OTHER  2
+static const value_string file_fs_type_vals[] = {
+       {NDMP_FS_UNIX,  "UNIX"},
+       {NDMP_FS_NT,    "NT"},
+       {NDMP_FS_OTHER, "OTHER"},
+       {0, NULL}
+};
+
+static int
+dissect_file_name(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *parent_tree)
+{
+       proto_item* item = NULL;
+       proto_tree* tree = NULL;
+       int old_offset=offset;
+       guint32 type;
+       char *name;
+
+       if (parent_tree) {
+               item = proto_tree_add_text(parent_tree, tvb, offset, -1,
+                               "File");
+               tree = proto_item_add_subtree(item, ett_ndmp_file_name);
+       }
+
+       /* file type */
+       type=tvb_get_ntohl(tvb, offset);
+       proto_tree_add_item(tree, hf_ndmp_file_fs_type, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       switch(type){
+       case NDMP_FS_UNIX:
+               /* file */
+               offset = dissect_rpc_string(tvb, tree,
+                               hf_ndmp_file_name, offset, &name);
+               if (check_col(pinfo->cinfo, COL_INFO)){
+                       col_append_fstr(pinfo->cinfo, COL_INFO, " %s ", name);
+               }
+               break;
+       case NDMP_FS_NT:
+               /* nt file */
+               offset = dissect_rpc_string(tvb, tree,
+                               hf_ndmp_nt_file_name, offset, &name);
+               if (check_col(pinfo->cinfo, COL_INFO)){
+                       col_append_fstr(pinfo->cinfo, COL_INFO, " %s ", name);
+               }
+
+               /* dos file */
+               offset = dissect_rpc_string(tvb, tree,
+                               hf_ndmp_dos_file_name, offset, NULL);
+               break;
+       default:
+               /* file */
+               offset = dissect_rpc_string(tvb, tree,
+                               hf_ndmp_file_name, offset, &name);
+               if (check_col(pinfo->cinfo, COL_INFO)){
+                       col_append_fstr(pinfo->cinfo, COL_INFO, " %s ", name);
+               }
+       }
+
+       if (check_col(pinfo->cinfo, COL_INFO)){
+               col_append_fstr(pinfo->cinfo, COL_INFO, "(%s)",
+                       val_to_str(type, file_fs_type_vals, "Unknown type") );
+       }
+
+       proto_item_set_len(item, offset-old_offset);    
+       return offset;
+}
+
+
+static const true_false_string tfs_ndmp_file_invalid_atime = {
+       "Atime is INVALID",
+       "Atime is valid"
+};
+static const true_false_string tfs_ndmp_file_invalid_ctime = {
+       "Ctime is INVALID",
+       "Ctime is valid"
+};
+static const true_false_string tfs_ndmp_file_invalid_group = {
+       "Group is INVALID",
+       "Group is valid"
+};
+static int
+dissect_file_invalids(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *parent_tree)
+{
+       proto_item* item = NULL;
+       proto_tree* tree = NULL;
+       guint32 flags;
+
+       flags=tvb_get_ntohl(tvb, offset);
+       if (parent_tree) {
+               item = proto_tree_add_text(parent_tree, tvb, offset, 4,
+                               "Invalids: 0x%08x", flags);
+               tree = proto_item_add_subtree(item, ett_ndmp_file_invalids);
+       }
+
+       proto_tree_add_boolean(tree, hf_ndmp_file_invalid_group,
+                       tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_file_invalid_ctime,
+                       tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_file_invalid_atime,
+                       tvb, offset, 4, flags);
+
+       offset+=4;
+       return offset;
+}
+
+#define NDMP_FILE_TYPE_DIR     0
+#define NDMP_FILE_TYPE_FIFO    1
+#define NDMP_FILE_TYPE_CSPEC   2
+#define NDMP_FILE_TYPE_BSPEC   3
+#define NDMP_FILE_TYPE_REG     4
+#define NDMP_FILE_TYPE_SLINK   5
+#define NDMP_FILE_TYPE_SOCK    6
+#define NDMP_FILE_TYPE_REGISTRY        7
+#define NDMP_FILE_TYPE_OTHER   8
+static const value_string file_type_vals[] = {
+       {NDMP_FILE_TYPE_DIR,    "DIR"},
+       {NDMP_FILE_TYPE_FIFO,   "FIFO"},
+       {NDMP_FILE_TYPE_CSPEC,  "CSPEC"},
+       {NDMP_FILE_TYPE_BSPEC,  "BSPEC"},
+       {NDMP_FILE_TYPE_REG,    "REG"},
+       {NDMP_FILE_TYPE_SLINK,  "SLINK"},
+       {NDMP_FILE_TYPE_SOCK,   "SOCK"},
+       {NDMP_FILE_TYPE_REGISTRY,       "REGISTRY"},
+       {NDMP_FILE_TYPE_OTHER,  "OTHER"},
+       {0, NULL}
+};
+
+static int
+dissect_file_stats(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *parent_tree)
+{
+       proto_item* item = NULL;
+       proto_tree* tree = NULL;
+       int old_offset=offset;
+       nstime_t ns;
+
+       if (parent_tree) {
+               item = proto_tree_add_text(parent_tree, tvb, offset, -1,
+                               "Stats:");
+               tree = proto_item_add_subtree(item, ett_ndmp_file_stats);
+       }
+
+       /* invalids */
+       offset = dissect_file_invalids(tvb, offset, pinfo, tree);
+
+       /* file fs type */
+       proto_tree_add_item(tree, hf_ndmp_file_fs_type, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* file type */
+       proto_tree_add_item(tree, hf_ndmp_file_type, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* mtime */
+       ns.secs=tvb_get_ntohl(tvb, offset);
+       ns.nsecs=0;
+       proto_tree_add_time(tree, hf_ndmp_file_mtime, tvb, offset, 4, &ns);
+       offset += 4;
+
+       /* atime */
+       ns.secs=tvb_get_ntohl(tvb, offset);
+       ns.nsecs=0;
+       proto_tree_add_time(tree, hf_ndmp_file_atime, tvb, offset, 4, &ns);
+       offset += 4;
+
+       /* ctime */
+       ns.secs=tvb_get_ntohl(tvb, offset);
+       ns.nsecs=0;
+       proto_tree_add_time(tree, hf_ndmp_file_ctime, tvb, offset, 4, &ns);
+       offset += 4;
+
+       /* owner */
+       proto_tree_add_item(tree, hf_ndmp_file_owner, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* group */
+       proto_tree_add_item(tree, hf_ndmp_file_group, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /*XXX here we should do proper dissection of mode for unix or
+             fattr for nt, call appropriate functions in nfs/smb*/
+       /* fattr */
+       proto_tree_add_item(tree, hf_ndmp_file_fattr, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /*file size*/
+       offset = dissect_rpc_uint64(tvb, tree, hf_ndmp_file_size,
+                       offset);
+
+       /* links */
+       proto_tree_add_item(tree, hf_ndmp_file_links, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       proto_item_set_len(item, offset-old_offset);    
+       return offset;
+}
+
+
+static int
+dissect_file(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *parent_tree)
+{
+       proto_item* item = NULL;
+       proto_tree* tree = NULL;
+       int old_offset=offset;
+
+       if (parent_tree) {
+               item = proto_tree_add_text(parent_tree, tvb, offset, -1,
+                               "File:");
+               tree = proto_item_add_subtree(item, ett_ndmp_file);
+       }
+
+       /* file names */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_file_name, hf_ndmp_file_names);
+
+       /* file stats */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_file_stats, hf_ndmp_file_stats);
+
+       /* node */
+       proto_tree_add_item(tree, hf_ndmp_file_node, tvb, offset, 8, FALSE);
+       offset += 8;
+
+       /* fh_info */
+       proto_tree_add_item(tree, hf_ndmp_file_fh_info, tvb, offset, 8, FALSE);
+       offset += 8;
+
+       proto_item_set_len(item, offset-old_offset);    
+       return offset;
+}
+
+static int
+dissect_fh_add_file_request(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* files */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_file, hf_ndmp_files);
+
+       return offset;
+}
+
+static int
+dissect_dir(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
+{
+       /* file names */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_file_name, hf_ndmp_file_names);
+
+       /* node */
+       proto_tree_add_item(tree, hf_ndmp_file_node, tvb, offset, 8, FALSE);
+       offset += 8;
+
+       /* parent */
+       proto_tree_add_item(tree, hf_ndmp_file_parent, tvb, offset, 8, FALSE);
+       offset += 8;
+
+       return offset;
+}
+
+static int
+dissect_fh_add_dir_request(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* dirs */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_dir, hf_ndmp_dirs);
+
+       return offset;
+}
+
+static int
+dissect_node(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
+{
+       /* file stats */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_file_stats, hf_ndmp_file_stats);
+
+       /* node */
+       proto_tree_add_item(tree, hf_ndmp_file_node, tvb, offset, 8, FALSE);
+       offset += 8;
+
+       /* fh_info */
+       proto_tree_add_item(tree, hf_ndmp_file_fh_info, tvb, offset, 8, FALSE);
+       offset += 8;
+
+       return offset;
+}
+
+
+static int
+dissect_fh_add_node_request(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* node */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_node, hf_ndmp_nodes);
+
+       return offset;
+}
+
+static int
+dissect_data_start_backup_request(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /*butype name*/
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_butype_name, offset, NULL);
+
+       /* default env */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_default_env, hf_ndmp_butype_default_env);
+
+       return offset;
+}
+
+static int
+dissect_nlist(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *tree)
+{
+       /*original path*/
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_bu_original_path, offset, NULL);
+
+       /*destination dir*/
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_bu_destination_dir, offset, NULL);
+
+       /*new name*/
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_bu_new_name, offset, NULL);
+
+       /*other name*/
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_bu_other_name, offset, NULL);
+
+       /* node */
+       proto_tree_add_item(tree, hf_ndmp_file_node, tvb, offset, 8, FALSE);
+       offset += 8;
+
+       /* fh_info */
+       proto_tree_add_item(tree, hf_ndmp_file_fh_info, tvb, offset, 8, FALSE);
+       offset += 8;
+
+       return offset;
+}
+
+
+static int
+dissect_data_start_recover_request(tvbuff_t *tvb, int offset,
+    packet_info *pinfo, proto_tree *tree, guint32 seq _U_)
+{
+       /* default env */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_default_env, hf_ndmp_butype_default_env);
+
+       /* nlist */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_nlist, hf_ndmp_nlist);
+
+       /*butype name*/
+       offset = dissect_rpc_string(tvb, tree,
+                       hf_ndmp_butype_name, offset, NULL);
+
+       return offset;
+}
+
+static int
+dissect_data_get_env_reply(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq _U_)
+{
+       /* error */
+       proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* default env */
+       offset = dissect_rpc_array(tvb, pinfo, tree, offset,
+                       dissect_default_env, hf_ndmp_butype_default_env);
+
+       return offset;
+}
+
+
+static const true_false_string tfs_ndmp_state_invalid_ebr = {
+       "Estimated Bytes Remaining is INVALID",
+       "Estimated Bytes Remaining is valid"
+};
+static const true_false_string tfs_ndmp_state_invalid_etr = {
+       "Estimated Time Remaining is INVALID",
+       "Estimated Time Remaining is valid"
+};
+static int
+dissect_state_invalids(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
+    proto_tree *parent_tree)
+{
+       proto_item* item = NULL;
+       proto_tree* tree = NULL;
+       guint32 flags;
+
+       flags=tvb_get_ntohl(tvb, offset);
+       if (parent_tree) {
+               item = proto_tree_add_text(parent_tree, tvb, offset, 4,
+                               "Invalids: 0x%08x", flags);
+               tree = proto_item_add_subtree(item, ett_ndmp_state_invalids);
+       }
+
+       proto_tree_add_boolean(tree, hf_ndmp_state_invalid_etr,
+                               tvb, offset, 4, flags);
+       proto_tree_add_boolean(tree, hf_ndmp_state_invalid_ebr,
+                               tvb, offset, 4, flags);
+
+       offset+=4;
+       return offset;
+}
+
+#define NDMP_DATA_OP_NOACTION  0
+#define NDMP_DATA_OP_BACKUP    1
+#define NDMP_DATA_OP_RESTORE   2
+static const value_string bu_operation_vals[] = {
+       {NDMP_DATA_OP_NOACTION, "NOACTION"},
+       {NDMP_DATA_OP_BACKUP,   "BACKUP"},
+       {NDMP_DATA_OP_RESTORE,  "RESTORE"},
+       {0, NULL}
+};
+
+#define NDMP_DATA_STATE_IDLE           0
+#define NDMP_DATA_STATE_ACTIVE         1
+#define NDMP_DATA_STATE_HALTED         2
+#define NDMP_DATA_STATE_LISTEN         3
+#define NDMP_DATA_STATE_CONNECTED      4
+static const value_string data_state_vals[] = {
+       {NDMP_DATA_STATE_IDLE,          "IDLE"},
+       {NDMP_DATA_STATE_ACTIVE,        "ACTIVE"},
+       {NDMP_DATA_STATE_HALTED,        "HALTED"},
+       {NDMP_DATA_STATE_LISTEN,        "LISTEN"},
+       {NDMP_DATA_STATE_CONNECTED,     "CONNECTED"},
+       {0, NULL}
+};
+
+#define NDMP_DATA_HALTED_NA            0
+#define NDMP_DATA_HALTED_SUCCESSFUL    1
+#define NDMP_DATA_HALTED_ABORTED       2
+#define NDMP_DATA_HALTED_INTERNAL_ERROR        3
+#define NDMP_DATA_HALTED_CONNECT_ERROR 4
+static const value_string data_halted_vals[] = {
+       {NDMP_DATA_HALTED_NA,                   "HALTED_NA"},
+       {NDMP_DATA_HALTED_SUCCESSFUL,           "HALTED_SUCCESSFUL"},
+       {NDMP_DATA_HALTED_ABORTED,              "HALTED_ABORTED"},
+       {NDMP_DATA_HALTED_INTERNAL_ERROR,       "HALTED_INTERNAL_ERROR"},
+       {NDMP_DATA_HALTED_CONNECT_ERROR,        "HALTED_CONNECT_ERROR"},
+       {0, NULL}
+};
+
+static int
+dissect_data_get_state_reply(tvbuff_t *tvb, int offset, packet_info *pinfo,
+    proto_tree *tree, guint32 seq _U_)
+{
+       nstime_t ns;
+
+       /* invalids */
+       offset = dissect_state_invalids(tvb, offset, pinfo, tree);
+
+       /* error */
+       proto_tree_add_item(tree, hf_ndmp_error, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* operation */
+       proto_tree_add_item(tree, hf_ndmp_bu_operation, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* state */
+       proto_tree_add_item(tree, hf_ndmp_data_state, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /* halted reason */
+       proto_tree_add_item(tree, hf_ndmp_data_halted, tvb, offset, 4, FALSE);
+       offset += 4;
+
+       /*bytes processed*/
+       offset = dissect_rpc_uint64(tvb, tree, hf_ndmp_data_bytes_processed,
+                       offset);
+
+       /*est bytes remain*/
+       offset = dissect_rpc_uint64(tvb, tree, hf_ndmp_data_est_bytes_remain,
+                       offset);
+
+       /* est time remain */
+       ns.secs=tvb_get_ntohl(tvb, offset);
+       ns.nsecs=0;
+       proto_tree_add_time(tree, hf_ndmp_data_est_time_remain, tvb, offset, 4, &ns);
+       offset += 4;
+
+       /* ndmp addr */
+       offset=dissect_ndmp_addr(tvb, offset, pinfo, tree);
+
+       /* window offset */
+       proto_tree_add_item(tree, hf_ndmp_window_offset, tvb, offset, 8, FALSE);
+       offset += 8;
+
+       /* window length */
+       proto_tree_add_item(tree, hf_ndmp_window_length, tvb, offset, 8, FALSE);
+       offset += 8;
+
+       return offset;
+}
+
+
+typedef struct _ndmp_command {
+       guint32 cmd;
+       int (*request) (tvbuff_t *tvb, int offset, packet_info *pinfo,
+           proto_tree *tree, guint32 seq);
+       int (*response)(tvbuff_t *tvb, int offset, packet_info *pinfo,
+           proto_tree *tree, guint32 seq);
+} ndmp_command;
+
+static const ndmp_command ndmp_commands[] = {
+       {NDMP_CONFIG_GET_HOST_INFO,    
+               NULL, dissect_ndmp_get_host_info_reply},
+       {NDMP_CONFIG_GET_CONNECTION_TYPE, 
+               NULL, dissect_ndmp_config_get_connection_type_reply},
+       {NDMP_CONFIG_GET_AUTH_ATTR, 
+               dissect_get_auth_type_request, dissect_auth_attr_msg},
+       {NDMP_CONFIG_GET_BUTYPE_INFO,   
+               NULL, dissect_get_butype_info_reply},
+       {NDMP_CONFIG_GET_FS_INFO,       
+               NULL, dissect_get_fs_info_reply},
+       {NDMP_CONFIG_GET_TAPE_INFO,     
+               NULL, dissect_get_tape_info_reply},
+       {NDMP_CONFIG_GET_SCSI_INFO,     
+               NULL, dissect_get_scsi_info_reply},
+       {NDMP_CONFIG_GET_SERVER_INFO,   
+               NULL, dissect_get_server_info_reply},
+       {NDMP_SCSI_OPEN,                
+               dissect_scsi_open_request, dissect_error},
+       {NDMP_SCSI_CLOSE,               
+               NULL, dissect_error},
+       {NDMP_SCSI_GET_STATE,           
+               NULL, dissect_scsi_get_state_reply},
+       {NDMP_SCSI_SET_TARGET,          
+               dissect_scsi_set_state_request, dissect_error},
+       {NDMP_SCSI_RESET_DEVICE,        
+               NULL, dissect_error},
+       {NDMP_SCSI_RESET_BUS,           
+               NULL, dissect_error},
+       {NDMP_SCSI_EXECUTE_CDB,
+               dissect_execute_cdb_request, dissect_execute_cdb_reply},
+       {NDMP_TAPE_OPEN,                
+               dissect_tape_open_request, dissect_error},
+       {NDMP_TAPE_CLOSE,               
+               NULL, dissect_error},
+       {NDMP_TAPE_GET_STATE,           
+               NULL, dissect_tape_get_state_reply},
+       {NDMP_TAPE_MTIO,                
+               dissect_tape_mtio_request, dissect_tape_mtio_reply},
+       {NDMP_TAPE_WRITE,               
+               dissect_tape_write_request, dissect_tape_write_reply},
+       {NDMP_TAPE_READ,        
+               dissect_tape_read_request, dissect_tape_read_reply},
+       {NDMP_TAPE_EXECUTE_CDB,
+               dissect_execute_cdb_request, dissect_execute_cdb_reply},
+       {NDMP_DATA_GET_STATE,           
+               NULL, dissect_data_get_state_reply},
+       {NDMP_DATA_START_BACKUP,
+               dissect_data_start_backup_request, dissect_error },
+       {NDMP_DATA_START_RECOVER,
+               dissect_data_start_recover_request, dissect_error },
+       {NDMP_DATA_ABORT,
+               NULL, dissect_error},
+       {NDMP_DATA_GET_ENV, 
+               NULL, dissect_data_get_env_reply},
+       {NDMP_DATA_STOP, 
+               NULL, dissect_error},
+       {NDMP_DATA_LISTEN, 
+               dissect_ndmp_addr_msg, dissect_mover_listen_reply},
+       {NDMP_DATA_CONNECT,             
+               dissect_ndmp_addr_msg, dissect_error},
+       {NDMP_NOTIFY_DATA_HALTED,       
+               dissect_notify_data_halted_request, NULL},
+       {NDMP_NOTIFY_CONNECTED,         
+               dissect_notify_connected_request, NULL},
+       {NDMP_NOTIFY_MOVER_HALTED, 
+               dissect_notify_data_halted_request, NULL},
+       {NDMP_NOTIFY_MOVER_PAUSED, 
+               dissect_notify_mover_paused_request, NULL},
+       {NDMP_NOTIFY_DATA_READ,         
+               dissect_mover_set_window_request, NULL},
+       {NDMP_LOG_FILE,                 
+               dissect_log_file_request, NULL},
+       {NDMP_LOG_MESSAGE,      
+               dissect_log_message_request, NULL},
+       {NDMP_FH_ADD_FILE, 
+               dissect_fh_add_file_request, NULL},
+       {NDMP_FH_ADD_DIR,               
+               dissect_fh_add_dir_request, NULL},
+       {NDMP_FH_ADD_NODE, 
+               dissect_fh_add_node_request, NULL},
+       {NDMP_CONNECT_OPEN,             
+               dissect_connect_open_request, dissect_error},
+       {NDMP_CONNECT_CLIENT_AUTH, 
+               dissect_connect_client_auth_request, dissect_error},
+       {NDMP_CONNECT_CLOSE,            
+               NULL,NULL},
+       {NDMP_CONNECT_SERVER_AUTH, 
+               dissect_auth_attr_msg, dissect_connect_server_auth_reply},
+       {NDMP_MOVER_GET_STATE,          
+               NULL, dissect_mover_get_state_reply},
+       {NDMP_MOVER_LISTEN,             
+               dissect_mover_listen_request, dissect_mover_listen_reply},
+       {NDMP_MOVER_CONTINUE,           
+               NULL, dissect_error},
+       {NDMP_MOVER_ABORT,              
+               NULL, dissect_error},
+       {NDMP_MOVER_STOP,               
+               NULL, dissect_error},
+       {NDMP_MOVER_SET_WINDOW,         
+               dissect_mover_set_window_request, dissect_error},
+       {NDMP_MOVER_READ, 
+               dissect_mover_set_window_request, dissect_error},
+       {NDMP_MOVER_CLOSE,              
+               NULL, dissect_error},
+       {NDMP_MOVER_SET_RECORD_SIZE, 
+               dissect_mover_set_record_size_request, dissect_error},
+       {NDMP_MOVER_CONNECT,    
+               dissect_mover_connect_request, dissect_error},
+       {0, NULL,NULL}
+};
+
+
+static int
+dissect_ndmp_header(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *parent_tree, struct ndmp_header *nh)
+{
+       proto_item* item = NULL;
+       proto_tree* tree = NULL;
+       nstime_t ns;
+
+       if (parent_tree) {
+               item = proto_tree_add_item(parent_tree, hf_ndmp_header, tvb,
+                               offset, 24, FALSE);
+               tree = proto_item_add_subtree(item, ett_ndmp_header);
+       }
+
+       /* sequence number */
+       proto_tree_add_uint(tree, hf_ndmp_sequence, tvb, offset, 4, nh->seq);
+       offset += 4;
+
+       /* timestamp */
+       ns.secs=nh->time;
+       ns.nsecs=0;
+       proto_tree_add_time(tree, hf_ndmp_timestamp, tvb, offset, 4, &ns);
+       offset += 4;
+
+       /* Message Type */
+       proto_tree_add_uint(tree, hf_ndmp_msgtype, tvb, offset, 4, nh->type);
+       offset += 4;
+
+       /* Message */
+       proto_tree_add_uint(tree, hf_ndmp_msg, tvb, offset, 4, nh->msg);
+       offset += 4;
+
+       /* Reply sequence number */
+       proto_tree_add_uint(tree, hf_ndmp_reply_sequence, tvb, offset, 4, nh->rep_seq);
+       offset += 4;
+
+       /* error */
+       proto_tree_add_uint(tree, hf_ndmp_error, tvb, offset, 4, nh->err);
+       offset += 4;
+
+       if (check_col(pinfo->cinfo, COL_INFO)){
+               col_append_fstr(pinfo->cinfo, COL_INFO, "%s %s  ",
+                       val_to_str(nh->msg, msg_vals, "Unknown Message (0x%02x)"),
+                       val_to_str(nh->type, msg_type_vals, "Unknown Type (0x%02x)")
+                       );
+       }
+
+       return offset;
+}
+
+
+static int
+dissect_ndmp_cmd(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, struct ndmp_header *nh)
+{
+       int i;
+       proto_item *cmd_item=NULL;
+       proto_tree *cmd_tree=NULL;
+
+       offset=dissect_ndmp_header(tvb, offset, pinfo, tree, nh);
+
+       for(i=0;ndmp_commands[i].cmd!=0;i++){
+               if(ndmp_commands[i].cmd==nh->msg){
+                       break;
+               }
+       }
+
+
+       if(ndmp_commands[i].cmd==0){
+               /* we do not know this message */
+               proto_tree_add_text(tree, tvb, offset, -1, "Unknown type of NDMP message: 0x%02x", nh->msg);
+               offset+=tvb_length_remaining(tvb, offset);
+               return offset;
+       }
+
+       if(tree){
+               cmd_item = proto_tree_add_text(tree, tvb, offset, -1, 
+                       msg_vals[i].strptr);
+               cmd_tree = proto_item_add_subtree(cmd_item, ett_ndmp);
+       }
+
+       if(nh->type==NDMP_MESSAGE_REQUEST){
+               if(ndmp_commands[i].request){
+                       offset=ndmp_commands[i].request(tvb, offset, pinfo, cmd_tree,
+                           nh->seq);
+               }
+       } else {
+               if(ndmp_commands[i].response){
+                       offset=ndmp_commands[i].response(tvb, offset, pinfo, cmd_tree,
+                           nh->rep_seq);
+               }
+       }
+
+       return offset;
+}
+
+static gboolean
+dissect_ndmp_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+    tvbuff_t *frag_tvb, fragment_data *ipfd_head, gboolean is_tcp,
+    guint32 rpc_rm)
+{
+       int offset = (is_tcp && tvb == frag_tvb) ? 4 : 0;
+       guint32 size;
+       struct ndmp_header nh;
+       proto_item *ndmp_item = NULL;
+       proto_tree *ndmp_tree = NULL;
+
+       /* size of this NDMP PDU */
+       size = tvb_length_remaining(tvb, offset);
+       if (size < 24) {
+               /* too short to be NDMP */
+               return FALSE;
+       }
+
+       /*
+        * Check the NDMP header, if we have it.
+        */
+       nh.seq = tvb_get_ntohl(tvb, offset);
+       nh.time = tvb_get_ntohl(tvb, offset+4);
+       nh.type = tvb_get_ntohl(tvb, offset+8);
+       nh.msg = tvb_get_ntohl(tvb, offset+12);
+       nh.rep_seq = tvb_get_ntohl(tvb, offset+16);
+       nh.err = tvb_get_ntohl(tvb, offset+20);
+
+       if (nh.type > 1)
+               return FALSE;
+       if (nh.msg > 0xa09 || nh.msg == 0)
+               return FALSE;
+       if (nh.err > 0x17)
+               return FALSE;
+
+       /*
+        * Check if this is the last fragment.
+        */
+       if (!(rpc_rm & RPC_RM_LASTFRAG)) {
+               /*
+                * This isn't the last fragment.
+                * If we're doing reassembly, just return
+                * TRUE to indicate that this looks like
+                * the beginning of an NDMP message,
+                * and let them do reassembly.
+                */
+               if (ndmp_defragment)
+                       return TRUE;
+       }
+
+       if (check_col(pinfo->cinfo, COL_PROTOCOL)) 
+               col_set_str(pinfo->cinfo, COL_PROTOCOL, "NDMP");
+       if (check_col(pinfo->cinfo, COL_INFO)) 
+               col_clear(pinfo->cinfo, COL_INFO);
+
+       if (tree) {
+               ndmp_item = proto_tree_add_item(tree, proto_ndmp,
+                   tvb, 0, -1, FALSE);
+               ndmp_tree = proto_item_add_subtree(ndmp_item, ett_ndmp);
+
+               if (is_tcp) {
+                       show_rpc_fraginfo(tvb, frag_tvb, ndmp_tree, rpc_rm,
+                           ipfd_head);
+               }
+       }
+
+       /*
+        * We cannot trust what dissect_ndmp_cmd() tells us, as there
+        * are implementations which pad some additional data after
+        * the PDU.  We MUST use size.
+        */
+       dissect_ndmp_cmd(tvb, offset, pinfo, ndmp_tree, &nh);
+       return TRUE;
+}
+
+static void
+dissect_ndmp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+       int offset = 0;
+       int len;
+
+       while (tvb_reported_length_remaining(tvb, offset) != 0) {
+               /*
+                * Process this fragment.
+                */
+               len = dissect_rpc_fragment(tvb, offset, pinfo, tree,
+                   dissect_ndmp_message, FALSE, proto_ndmp, ett_ndmp,
+                   ndmp_defragment);
+               if (len < 0) {
+                       /*
+                        * We need more data from the TCP stream for
+                        * this fragment.
+                        */
+                       return;
+               }
+               if (len == 0) {
+                       /*
+                        * It's not NDMP.  Stop processing.
+                        */
+                       break;
+               }
+
+               offset += len;
+       }
+}
+
+void
+proto_register_ndmp(void)
+{
+
+  static hf_register_info hf_ndmp[] = {
+       { &hf_ndmp_header, {
+               "NDMP Header", "ndmp.header", FT_NONE, 0,
+               NULL, 0, "NDMP Header", HFILL }},
+
+       { &hf_ndmp_sequence, {
+               "Sequence", "ndmp.sequence", FT_UINT32, BASE_DEC,
+               NULL, 0, "Sequence number for NDMP PDU", HFILL }},
+
+       { &hf_ndmp_reply_sequence, {
+               "Reply Sequence", "ndmp.reply_sequence", FT_UINT32, BASE_DEC,
+               NULL, 0, "Reply Sequence number for NDMP PDU", HFILL }},
+
+       { &hf_ndmp_timestamp, {
+               "Time", "ndmp.timestamp", FT_ABSOLUTE_TIME, BASE_NONE,
+               NULL, 0, "Timestamp for this NDMP PDU", HFILL }},
+
+       { &hf_ndmp_msgtype, {
+               "Type", "ndmp.msg_type", FT_UINT32, BASE_DEC,
+               VALS(msg_type_vals), 0, "Is this a Request or Response?", HFILL }},
+
+       { &hf_ndmp_msg, {
+               "Message", "ndmp.msg", FT_UINT32, BASE_HEX,
+               VALS(msg_vals), 0, "Type of NDMP PDU", HFILL }},
+
+       { &hf_ndmp_error, {
+               "Error", "ndmp.error", FT_UINT32, BASE_DEC,
+               VALS(error_vals), 0, "Error code for this NDMP PDU", HFILL }},
+
+       { &hf_ndmp_version, {
+               "Version", "ndmp.version", FT_UINT32, BASE_DEC,
+               NULL, 0, "Version of NDMP protocol", HFILL }},
+
+       { &hf_ndmp_hostname, {
+               "Hostname", "ndmp.hostname", FT_STRING, BASE_NONE,
+               NULL, 0, "Hostname", HFILL }},
+
+       { &hf_ndmp_hostid, {
+               "HostID", "ndmp.hostid", FT_STRING, BASE_NONE,
+               NULL, 0, "HostID", HFILL }},
+
+       { &hf_ndmp_os_type, {
+               "OS Type", "ndmp.os.type", FT_STRING, BASE_NONE,
+               NULL, 0, "OS Type", HFILL }},
+
+       { &hf_ndmp_os_vers, {
+               "OS Version", "ndmp.os.version", FT_STRING, BASE_NONE,
+               NULL, 0, "OS Version", HFILL }},
+
+       { &hf_ndmp_addr_types, {
+               "Addr Types", "ndmp.addr_types", FT_NONE, BASE_NONE,
+               NULL, 0, "List Of Address Types", HFILL }},
+
+       { &hf_ndmp_addr_type, {
+               "Addr Type", "ndmp.addr_type", FT_UINT32, BASE_DEC,
+               VALS(addr_type_vals), 0, "Address Type", HFILL }},
+
+       { &hf_ndmp_auth_type, {
+               "Auth Type", "ndmp.auth_type", FT_UINT32, BASE_DEC,
+               VALS(auth_type_vals), 0, "Authentication Type", HFILL }},
+
+       { &hf_ndmp_auth_challenge, {
+               "Challenge", "ndmp.auth.challenge", FT_BYTES, BASE_HEX,
+               NULL, 0, "Authentication Challenge", HFILL }},
+
+       { &hf_ndmp_auth_digest, {
+               "Digest", "ndmp.auth.digest", FT_BYTES, BASE_HEX,
+               NULL, 0, "Authentication Digest", HFILL }},
+
+       { &hf_ndmp_butype_info, {
+               "Butype Info", "ndmp.butype.info", FT_NONE, BASE_NONE,
+               NULL, 0, "Butype Info", HFILL }},
+
+       { &hf_ndmp_butype_name, {
+               "Butype Name", "ndmp.butype.name", FT_STRING, BASE_NONE,
+               NULL, 0, "Name of Butype", HFILL }},
+
+       { &hf_ndmp_butype_default_env, {
+               "Default Env", "ndmp.butype.default_env", FT_NONE, BASE_NONE,
+               NULL, 0, "Default Env's for this Butype Info", HFILL }},
+
+       { &hf_ndmp_butype_attr_backup_file_history, {
+               "", "ndmp.butype.attr.backup_file_history", FT_BOOLEAN, 32,
+               TFS(&tfs_butype_attr_backup_file_history), 0x00000001, "backup_file_history", HFILL }},
+
+       { &hf_ndmp_butype_attr_backup_filelist, {
+               "", "ndmp.butype.attr.backup_filelist", FT_BOOLEAN, 32,
+               TFS(&tfs_butype_attr_backup_filelist), 0x00000002, "backup_filelist", HFILL }},
+
+       { &hf_ndmp_butype_attr_recover_filelist, {
+               "", "ndmp.butype.attr.recover_filelist", FT_BOOLEAN, 32,
+               TFS(&tfs_butype_attr_recover_filelist), 0x00000004, "recover_filelist", HFILL }},
+
+       { &hf_ndmp_butype_attr_backup_direct, {
+               "", "ndmp.butype.attr.backup_direct", FT_BOOLEAN, 32,
+               TFS(&tfs_butype_attr_backup_direct), 0x00000008, "backup_direct", HFILL }},
+
+       { &hf_ndmp_butype_attr_recover_direct, {
+               "", "ndmp.butype.attr.recover_direct", FT_BOOLEAN, 32,
+               TFS(&tfs_butype_attr_recover_direct), 0x00000010, "recover_direct", HFILL }},
+
+       { &hf_ndmp_butype_attr_backup_incremental, {
+               "", "ndmp.butype.attr.backup_incremental", FT_BOOLEAN, 32,
+               TFS(&tfs_butype_attr_backup_incremental), 0x00000020, "backup_incremental", HFILL }},
+
+       { &hf_ndmp_butype_attr_recover_incremental, {
+               "", "ndmp.butype.attr.recover_incremental", FT_BOOLEAN, 32,
+               TFS(&tfs_butype_attr_recover_incremental), 0x00000040, "recover_incremental", HFILL }},
+
+       { &hf_ndmp_butype_attr_backup_utf8, {
+               "", "ndmp.butype.attr.backup_utf8", FT_BOOLEAN, 32,
+               TFS(&tfs_butype_attr_backup_utf8), 0x00000080, "backup_utf8", HFILL }},
+
+       { &hf_ndmp_butype_attr_recover_utf8, {
+               "", "ndmp.butype.attr.recover_utf8", FT_BOOLEAN, 32,
+               TFS(&tfs_butype_attr_recover_utf8), 0x00000100, "recover_utf8", HFILL }},
+
+       { &hf_ndmp_butype_env_name, {
+               "Name", "ndmp.butype.env.name", FT_STRING, BASE_NONE,
+               NULL, 0, "Name for this env-variable", HFILL }},
+
+       { &hf_ndmp_butype_env_value, {
+               "Value", "ndmp.butype.env.value", FT_STRING, BASE_NONE,
+               NULL, 0, "Value for this env-variable", HFILL }},
+
+       { &hf_ndmp_fs_info, {
+               "FS Info", "ndmp.fs.info", FT_NONE, BASE_NONE,
+               NULL, 0, "FS Info", HFILL }},
+
+       { &hf_ndmp_fs_invalid_total_size, {
+               "", "ndmp.fs.invalid.total_size", FT_BOOLEAN, 32,
+               TFS(&tfs_fs_invalid_total_size), 0x00000001, "If total size is invalid", HFILL }},
+
+       { &hf_ndmp_fs_invalid_used_size, {
+               "", "ndmp.fs.invalid.used_size", FT_BOOLEAN, 32,
+               TFS(&tfs_fs_invalid_used_size), 0x00000002, "If used size is invalid", HFILL }},
+
+       { &hf_ndmp_fs_invalid_avail_size, {
+               "", "ndmp.fs.invalid.avail_size", FT_BOOLEAN, 32,
+               TFS(&tfs_fs_invalid_avail_size), 0x00000004, "If available size is invalid", HFILL }},
+
+       { &hf_ndmp_fs_invalid_total_inodes, {
+               "", "ndmp.fs.invalid.total_inodes", FT_BOOLEAN, 32,
+               TFS(&tfs_fs_invalid_total_inodes), 0x00000008, "If total number of inodes is invalid", HFILL }},
+
+       { &hf_ndmp_fs_invalid_used_inodes, {
+               "", "ndmp.fs.invalid.used_inodes", FT_BOOLEAN, 32,
+               TFS(&tfs_fs_invalid_used_inodes), 0x00000010, "If used number of inodes is invalid", HFILL }},
+
+       { &hf_ndmp_fs_fs_type, {
+               "Type", "ndmp.fs.type", FT_STRING, BASE_NONE,
+               NULL, 0, "Type of FS", HFILL }},
+
+       { &hf_ndmp_fs_logical_device, {
+               "Logical Device", "ndmp.fs.logical_device", FT_STRING, BASE_NONE,
+               NULL, 0, "Name of logical device", HFILL }},
+
+       { &hf_ndmp_fs_physical_device, {
+               "Physical Device", "ndmp.fs.physical_device", FT_STRING, BASE_NONE,
+               NULL, 0, "Name of physical device", HFILL }},
+
+       { &hf_ndmp_fs_total_size, {
+               "Total Size", "ndmp.fs.total_size", FT_UINT64, BASE_DEC,
+               NULL, 0, "Total size of FS", HFILL }},
+
+       { &hf_ndmp_fs_used_size, {
+               "Used Size", "ndmp.fs.used_size", FT_UINT64, BASE_DEC,
+               NULL, 0, "Total used size of FS", HFILL }},
+
+       { &hf_ndmp_fs_avail_size, {
+               "Avail Size", "ndmp.fs.avail_size", FT_UINT64, BASE_DEC,
+               NULL, 0, "Total available size on FS", HFILL }},
+
+       { &hf_ndmp_fs_total_inodes, {
+               "Total Inodes", "ndmp.fs.total_inodes", FT_UINT64, BASE_DEC,
+               NULL, 0, "Total number of inodes on FS", HFILL }},
+
+       { &hf_ndmp_fs_used_inodes, {
+               "Used Inodes", "ndmp.fs.used_inodes", FT_UINT64, BASE_DEC,
+               NULL, 0, "Number of used inodes on FS", HFILL }},
+
+       { &hf_ndmp_fs_env, {
+               "Env variables", "ndmp.fs.env", FT_NONE, BASE_NONE,
+               NULL, 0, "Environment variables for FS", HFILL }},
+
+       { &hf_ndmp_fs_env_name, {
+               "Name", "ndmp.fs.env.name", FT_STRING, BASE_NONE,
+               NULL, 0, "Name for this env-variable", HFILL }},
+
+       { &hf_ndmp_fs_env_value, {
+               "Value", "ndmp.fs.env.value", FT_STRING, BASE_NONE,
+               NULL, 0, "Value for this env-variable", HFILL }},
+
+       { &hf_ndmp_fs_status, {
+               "Status", "ndmp.fs.status", FT_STRING, BASE_NONE,
+               NULL, 0, "Status for this FS", HFILL }},
+
+       { &hf_ndmp_tape_info, {
+               "Tape Info", "ndmp.tape.info", FT_NONE, BASE_NONE,
+               NULL, 0, "Tape Info", HFILL }},
+
+       { &hf_ndmp_tape_model, {
+               "Model", "ndmp.tape.model", FT_STRING, BASE_NONE,
+               NULL, 0, "Model of the TAPE drive", HFILL }},
+
+       { &hf_ndmp_tape_dev_cap, {
+               "Device Capability", "ndmp.tape.dev_cap", FT_NONE, BASE_NONE,
+               NULL, 0, "Tape Device Capability", HFILL }},
+
+       { &hf_ndmp_tape_device, {
+               "Device", "ndmp.tape.device", FT_STRING, BASE_NONE,
+               NULL, 0, "Name of TAPE Device", HFILL }},
+
+       { &hf_ndmp_tape_attr_rewind, {
+               "", "ndmp.tape.attr.rewind", FT_BOOLEAN, 32,
+               TFS(&tfs_tape_attr_rewind), 0x00000001, "If this device supports rewind", HFILL }},
+
+       { &hf_ndmp_tape_attr_unload, {
+               "", "ndmp.tape.attr.unload", FT_BOOLEAN, 32,
+               TFS(&tfs_tape_attr_unload), 0x00000002, "If this device supports unload", HFILL }},
+
+       { &hf_ndmp_tape_capability, {
+               "Tape Capabilities", "ndmp.tape.capability", FT_NONE, BASE_NONE,
+               NULL, 0, "Tape Capabilities", HFILL }},
+
+       { &hf_ndmp_tape_capability_name, {
+               "Name", "ndmp.tape.cap.name", FT_STRING, BASE_NONE,
+               NULL, 0, "Name for this env-variable", HFILL }},
+
+       { &hf_ndmp_tape_capability_value, {
+               "Value", "ndmp.tape.cap.value", FT_STRING, BASE_NONE,
+               NULL, 0, "Value for this env-variable", HFILL }},
+
+       { &hf_ndmp_scsi_info, {
+               "SCSI Info", "ndmp.scsi.info", FT_NONE, BASE_NONE,
+               NULL, 0, "SCSI Info", HFILL }},
+
+       { &hf_ndmp_scsi_model, {
+               "Model", "ndmp.scsi.model", FT_STRING, BASE_NONE,
+               NULL, 0, "Model of the SCSI device", HFILL }},
+
+       { &hf_ndmp_server_vendor, {
+               "Vendor", "ndmp.server.vendor", FT_STRING, BASE_NONE,
+               NULL, 0, "Name of vendor", HFILL }},
+
+       { &hf_ndmp_server_product, {
+               "Product", "ndmp.server.product", FT_STRING, BASE_NONE,
+               NULL, 0, "Name of product", HFILL }},
+
+       { &hf_ndmp_server_revision, {
+               "Revision", "ndmp.server.revision", FT_STRING, BASE_NONE,
+               NULL, 0, "Revision of this product", HFILL }},
+
+       { &hf_ndmp_auth_types, {
+               "Auth types", "ndmp.auth.types", FT_NONE, BASE_NONE,
+               NULL, 0, "Auth types", HFILL }},
+
+       { &hf_ndmp_scsi_device, {
+               "Device", "ndmp.scsi.device", FT_STRING, BASE_NONE,
+               NULL, 0, "Name of SCSI Device", HFILL }},
+
+       { &hf_ndmp_scsi_controller, {
+               "Controller", "ndmp.scsi.controller", FT_UINT32, BASE_DEC,
+               NULL, 0, "Target Controller", HFILL }},
+
+       { &hf_ndmp_scsi_id, {
+               "ID", "ndmp.scsi.id", FT_UINT32, BASE_DEC,
+               NULL, 0, "Target ID", HFILL }},
+
+       { &hf_ndmp_scsi_lun, {
+               "LUN", "ndmp.scsi.lun", FT_UINT32, BASE_DEC,
+               NULL, 0, "Target LUN", HFILL }},
+
+       { &hf_ndmp_execute_cdb_flags_data_in, {
+               "DATA_IN", "ndmp.execute_cdb.flags.data_in", FT_BOOLEAN, 32,
+               NULL, 0x00000001, "DATA_IN", HFILL }},
+
+       { &hf_ndmp_execute_cdb_flags_data_out, {
+               "DATA_OUT", "ndmp.execute_cdb.flags.data_out", FT_BOOLEAN, 32,
+               NULL, 0x00000002, "DATA_OUT", HFILL }},
+
+       { &hf_ndmp_execute_cdb_timeout, {
+               "Timeout", "ndmp.execute_cdb.timeout", FT_UINT32, BASE_DEC,
+               NULL, 0, "Reselect timeout, in milliseconds", HFILL }},
+
+       { &hf_ndmp_execute_cdb_datain_len, {
+               "Data in length", "ndmp.execute_cdb.datain_len", FT_UINT32, BASE_DEC,
+               NULL, 0, "Expected length of data bytes to read", HFILL }},
+
+       { &hf_ndmp_execute_cdb_cdb_len, {
+               "CDB length", "ndmp.execute_cdb.cdb_len", FT_UINT32, BASE_DEC,
+               NULL, 0, "Length of CDB", HFILL }},
+
+       { &hf_ndmp_execute_cdb_dataout, {
+               "Data out", "ndmp.execute_cdb.dataout", FT_BYTES, BASE_NONE,
+               NULL, 0, "Data to be transferred to the SCSI device", HFILL }},
+
+       { &hf_ndmp_execute_cdb_status, {
+               "Status", "ndmp.execute_cdb.status", FT_UINT8, BASE_DEC,
+               VALS(scsi_status_val), 0, "SCSI status", HFILL }},
+
+       { &hf_ndmp_execute_cdb_dataout_len, {
+               "Data out length", "ndmp.execute_cdb.dataout_len", FT_UINT32, BASE_DEC,
+               NULL, 0, "Number of bytes transferred to the device", HFILL }},
+
+       { &hf_ndmp_execute_cdb_datain, {
+               "Data in", "ndmp.execute_cdb.datain", FT_BYTES, BASE_NONE,
+               NULL, 0, "Data transferred from the SCSI device", HFILL }},
+
+       { &hf_ndmp_execute_cdb_sns_len, {
+               "Sense data length", "ndmp.execute_cdb.sns_len", FT_UINT32, BASE_DEC,
+               NULL, 0, "Length of sense data", HFILL }},
+
+       { &hf_ndmp_tape_open_mode, {
+               "Mode", "ndmp.tape.open_mode", FT_UINT32, BASE_DEC,
+               VALS(tape_open_mode_vals), 0, "Mode to open tape in", HFILL }},
+
+       { &hf_ndmp_tape_invalid_file_num, {
+               "", "ndmp.tape.invalid.file_num", FT_BOOLEAN, 32,
+               TFS(&tfs_ndmp_tape_invalid_file_num), 0x00000001, "invalid_file_num", HFILL }},
+
+       { &hf_ndmp_tape_invalid_soft_errors, {
+               "", "ndmp.tape.invalid.soft_errors", FT_BOOLEAN, 32,
+               TFS(&tfs_ndmp_tape_invalid_soft_errors), 0x00000002, "soft_errors", HFILL }},
+
+       { &hf_ndmp_tape_invalid_block_size, {
+               "", "ndmp.tape.invalid.block_size", FT_BOOLEAN, 32,
+               TFS(&tfs_ndmp_tape_invalid_block_size), 0x00000004, "block_size", HFILL }},
+
+       { &hf_ndmp_tape_invalid_block_no, {
+               "", "ndmp.tape.invalid.block_no", FT_BOOLEAN, 32,
+               TFS(&tfs_ndmp_tape_invalid_block_no), 0x00000008, "block_no", HFILL }},
+
+       { &hf_ndmp_tape_invalid_total_space, {
+               "", "ndmp.tape.invalid.total_space", FT_BOOLEAN, 32,
+               TFS(&tfs_ndmp_tape_invalid_total_space), 0x00000010, "total_space", HFILL }},
+
+       { &hf_ndmp_tape_invalid_space_remain, {
+               "", "ndmp.tape.invalid.space_remain", FT_BOOLEAN, 32,
+               TFS(&tfs_ndmp_tape_invalid_space_remain), 0x00000020, "space_remain", HFILL }},
+
+       { &hf_ndmp_tape_invalid_partition, {
+               "", "ndmp.tape.invalid.partition", FT_BOOLEAN, 32,
+               TFS(&tfs_ndmp_tape_invalid_partition), 0x00000040, "partition", HFILL }},
+
+       { &hf_ndmp_tape_flags_no_rewind, {
+               "", "ndmp.tape.flags.no_rewind", FT_BOOLEAN, 32,
+               TFS(&tfs_ndmp_tape_flags_no_rewind), 0x00000008, "no_rewind", HFILL, }},
+
+       { &hf_ndmp_tape_flags_write_protect, {
+               "", "ndmp.tape.flags.write_protect", FT_BOOLEAN, 32,
+               TFS(&tfs_ndmp_tape_flags_write_protect), 0x00000010, "write_protect", HFILL, }},
+
+       { &hf_ndmp_tape_flags_error, {
+               "", "ndmp.tape.flags.error", FT_BOOLEAN, 32,
+               TFS(&tfs_ndmp_tape_flags_error), 0x00000020, "error", HFILL, }},
+
+       { &hf_ndmp_tape_flags_unload, {
+               "", "ndmp.tape.flags.unload", FT_BOOLEAN, 32,
+               TFS(&tfs_ndmp_tape_flags_unload), 0x00000040, "unload", HFILL, }},
+
+       { &hf_ndmp_tape_file_num, {
+               "file_num", "ndmp.tape.status.file_num", FT_UINT32, BASE_DEC,
+               NULL, 0, "file_num", HFILL }},
+
+       { &hf_ndmp_tape_soft_errors, {
+               "soft_errors", "ndmp.tape.status.soft_errors", FT_UINT32, BASE_DEC,
+               NULL, 0, "soft_errors", HFILL }},
+
+       { &hf_ndmp_tape_block_size, {
+               "block_size", "ndmp.tape.status.block_size", FT_UINT32, BASE_DEC,
+               NULL, 0, "block_size", HFILL }},
+
+       { &hf_ndmp_tape_block_no, {
+               "block_no", "ndmp.tape.status.block_no", FT_UINT32, BASE_DEC,
+               NULL, 0, "block_no", HFILL }},
+
+       { &hf_ndmp_tape_total_space, {
+               "total_space", "ndmp.tape.status.total_space", FT_UINT64, BASE_DEC,
+               NULL, 0, "total_space", HFILL }},
+
+       { &hf_ndmp_tape_space_remain, {
+               "space_remain", "ndmp.tape.status.space_remain", FT_UINT64, BASE_DEC,
+               NULL, 0, "space_remain", HFILL }},
+
+       { &hf_ndmp_tape_partition, {
+               "partition", "ndmp.tape.status.partition", FT_UINT32, BASE_DEC,
+               NULL, 0, "partition", HFILL }},
+
+       { &hf_ndmp_tape_mtio_op, {
+               "Operation", "ndmp.tape.mtio.op", FT_UINT32, BASE_DEC,
+               VALS(tape_mtio_vals), 0, "MTIO Operation", HFILL }},
+
+       { &hf_ndmp_count, {
+               "Count", "ndmp.count", FT_UINT32, BASE_DEC,
+               NULL, 0, "Number of bytes/objects/operations", HFILL }},
+
+       { &hf_ndmp_resid_count, {
+               "Resid Count", "ndmp.resid_count", FT_UINT32, BASE_DEC,
+               NULL, 0, "Number of remaining bytes/objects/operations", HFILL }},
+
+       { &hf_ndmp_mover_state, {
+               "State", "ndmp.mover.state", FT_UINT32, BASE_DEC,
+               VALS(mover_state_vals), 0, "State of the selected mover", HFILL }},
+
+       { &hf_ndmp_mover_pause, {
+               "Pause", "ndmp.mover.pause", FT_UINT32, BASE_DEC,
+               VALS(mover_pause_vals), 0, "Reason why the mover paused", HFILL }},
+
+       { &hf_ndmp_halt, {
+               "Halt", "ndmp.halt", FT_UINT32, BASE_DEC,
+               VALS(halt_vals), 0, "Reason why it halted", HFILL }},
+
+       { &hf_ndmp_record_size, {
+               "Record Size", "ndmp.record.size", FT_UINT32, BASE_DEC,
+               NULL, 0, "Record size in bytes", HFILL }},
+
+       { &hf_ndmp_record_num, {
+               "Record Num", "ndmp.record.num", FT_UINT32, BASE_DEC,
+               NULL, 0, "Number of records", HFILL }},
+
+       { &hf_ndmp_data_written, {
+               "Data Written", "ndmp.data.written", FT_UINT64, BASE_DEC,
+               NULL, 0, "Number of data bytes written", HFILL }},
+
+       { &hf_ndmp_seek_position, {
+               "Seek Position", "ndmp.seek.position", FT_UINT64, BASE_DEC,
+               NULL, 0, "Current seek position on device", HFILL }},
+
+       { &hf_ndmp_bytes_left_to_read, {
+               "Bytes left to read", "ndmp.bytes_left_to_read", FT_UINT64, BASE_DEC,
+               NULL, 0, "Number of bytes left to be read from the device", HFILL }},
+
+       { &hf_ndmp_window_offset, {
+               "Window Offset", "ndmp.window.offset", FT_UINT64, BASE_DEC,
+               NULL, 0, "Offset to window in bytes", HFILL }},
+
+       { &hf_ndmp_window_length, {
+               "Window Length", "ndmp.window.length", FT_UINT64, BASE_DEC,
+               NULL, 0, "Size of window in bytes", HFILL }},
+
+       { &hf_ndmp_addr_ip, {
+               "IP Address", "ndmp.addr.ip", FT_IPv4, BASE_DEC,
+               NULL, 0, "IP Address", HFILL }},
+
+       { &hf_ndmp_addr_tcp, {
+               "TCP Port", "ndmp.addr.tcp_port", FT_UINT32, BASE_DEC,
+               NULL, 0, "TCP Port", HFILL }},
+
+       { &hf_ndmp_addr_fcal_loop_id, {
+               "Loop ID", "ndmp.addr.loop_id", FT_UINT32, BASE_HEX,
+               NULL, 0, "FCAL Loop ID", HFILL }},
+
+       { &hf_ndmp_addr_ipc, {
+               "IPC", "ndmp.addr.ipc", FT_BYTES, BASE_HEX,
+               NULL, 0, "IPC identifier", HFILL }},
+
+       { &hf_ndmp_mover_mode, {
+               "Mode", "ndmp.mover.mode", FT_UINT32, BASE_HEX,
+               VALS(mover_mode_vals), 0, "Mover Mode", HFILL }},
+
+       { &hf_ndmp_file_name, {
+               "File", "ndmp.file", FT_STRING, BASE_NONE,
+               NULL, 0, "Name of File", HFILL }},
+
+       { &hf_ndmp_nt_file_name, {
+               "NT File", "ndmp.file", FT_STRING, BASE_NONE,
+               NULL, 0, "NT Name of File", HFILL }},
+
+       { &hf_ndmp_dos_file_name, {
+               "DOS File", "ndmp.file", FT_STRING, BASE_NONE,
+               NULL, 0, "DOS Name of File", HFILL }},
+
+       { &hf_ndmp_log_type, {
+               "Type", "ndmp.log.type", FT_UINT32, BASE_HEX,
+               VALS(log_type_vals), 0, "Type of log entry", HFILL }},
+
+       { &hf_ndmp_log_message_id, {
+               "Message ID", "ndmp.log.message.id", FT_UINT32, BASE_DEC,
+               NULL, 0, "ID of this log entry", HFILL }},
+
+       { &hf_ndmp_log_message, {
+               "Message", "ndmp.log.message", FT_STRING, BASE_NONE,
+               NULL, 0, "Log entry", HFILL }},
+
+       { &hf_ndmp_halt_reason, {
+               "Reason", "ndmp.halt.reason", FT_STRING, BASE_NONE,
+               NULL, 0, "Textual reason for why it halted", HFILL }},
+
+       { &hf_ndmp_connected, {
+               "Connected", "ndmp.connected", FT_UINT32, BASE_DEC,
+               VALS(connected_vals), 0, "Status of connection", HFILL }},
+
+       { &hf_ndmp_connected_reason, {
+               "Reason", "ndmp.connected.reason", FT_STRING, BASE_NONE,
+               NULL, 0, "Textual description of the connection status", HFILL }},
+
+       { &hf_ndmp_auth_id, {
+               "ID", "ndmp.auth.id", FT_STRING, BASE_NONE,
+               NULL, 0, "ID of client authenticating", HFILL }},
+
+       { &hf_ndmp_auth_password, {
+               "Password", "ndmp.auth.password", FT_STRING, BASE_NONE,
+               NULL, 0, "Password of client authenticating", HFILL }},
+
+       { &hf_ndmp_data, {
+               "Data", "ndmp.data", FT_BYTES, BASE_HEX,
+               NULL, 0, "Data written/read", HFILL }},
+
+       { &hf_ndmp_files, {
+               "Files", "ndmp.files", FT_NONE, 0,
+               NULL, 0, "List of files", HFILL }},
+
+       { &hf_ndmp_file_names, {
+               "File Names", "ndmp.file.names", FT_NONE, 0,
+               NULL, 0, "List of file names", HFILL }},
+
+       { &hf_ndmp_file_fs_type, {
+               "File FS Type", "ndmp.file.fs_type", FT_UINT32, BASE_DEC,
+               VALS(file_fs_type_vals), 0, "Type of file permissions (UNIX or NT)", HFILL }},
+
+       { &hf_ndmp_file_type, {
+               "File Type", "ndmp.file.type", FT_UINT32, BASE_DEC,
+               VALS(file_type_vals), 0, "Type of file", HFILL }},
+
+       { &hf_ndmp_file_stats, {
+               "File Stats", "ndmp.file.stats", FT_NONE, 0,
+               NULL, 0, "List of file stats", HFILL }},
+
+       { &hf_ndmp_file_node, {
+               "Node", "ndmp.file.node", FT_UINT64, BASE_DEC,
+               NULL, 0, "Node used for direct access", HFILL }},
+
+       { &hf_ndmp_file_parent, {
+               "Parent", "ndmp.file.parent", FT_UINT64, BASE_DEC,
+               NULL, 0, "Parent node(directory) for this node", HFILL }},
+
+       { &hf_ndmp_file_fh_info, {
+               "FH Info", "ndmp.file.fh_info", FT_UINT64, BASE_DEC,
+               NULL, 0, "FH Info used for direct access", HFILL }},
+
+       { &hf_ndmp_file_invalid_atime, {
+               "", "ndmp.file.invalid_atime", FT_BOOLEAN, 32,
+               TFS(&tfs_ndmp_file_invalid_atime), 0x00000001, "invalid_atime", HFILL, }},
+
+       { &hf_ndmp_file_invalid_ctime, {
+               "", "ndmp.file.invalid_ctime", FT_BOOLEAN, 32,
+               TFS(&tfs_ndmp_file_invalid_ctime), 0x00000002, "invalid_ctime", HFILL, }},
+
+       { &hf_ndmp_file_invalid_group, {
+               "", "ndmp.file.invalid_group", FT_BOOLEAN, 32,
+               TFS(&tfs_ndmp_file_invalid_group), 0x00000004, "invalid_group", HFILL, }},
+
+       { &hf_ndmp_file_mtime, {
+               "mtime", "ndmp.file.mtime", FT_ABSOLUTE_TIME, BASE_NONE,
+               NULL, 0, "Timestamp for mtime for this file", HFILL }},
+
+       { &hf_ndmp_file_atime, {
+               "atime", "ndmp.file.atime", FT_ABSOLUTE_TIME, BASE_NONE,
+               NULL, 0, "Timestamp for atime for this file", HFILL }},
+
+       { &hf_ndmp_file_ctime, {
+               "ctime", "ndmp.file.ctime", FT_ABSOLUTE_TIME, BASE_NONE,
+               NULL, 0, "Timestamp for ctime for this file", HFILL }},
+
+       { &hf_ndmp_file_owner, {
+               "Owner", "ndmp.file.owner", FT_UINT32, BASE_DEC,
+               NULL, 0, "UID for UNIX, owner for NT", HFILL }},
+
+       { &hf_ndmp_file_group, {
+               "Group", "ndmp.file.group", FT_UINT32, BASE_DEC,
+               NULL, 0, "GID for UNIX, NA for NT", HFILL }},
+
+       { &hf_ndmp_file_fattr, {
+               "Fattr", "ndmp.file.fattr", FT_UINT32, BASE_HEX,
+               NULL, 0, "Mode for UNIX, fattr for NT", HFILL }},
+
+       { &hf_ndmp_file_size, {
+               "Size", "ndmp.file.size", FT_UINT64, BASE_DEC,
+               NULL, 0, "File Size", HFILL }},
+
+       { &hf_ndmp_file_links, {
+               "Links", "ndmp.file.links", FT_UINT32, BASE_DEC,
+               NULL, 0, "Number of links to this file", HFILL }},
+
+       { &hf_ndmp_dirs, {
+               "Dirs", "ndmp.dirs", FT_NONE, 0,
+               NULL, 0, "List of directories", HFILL }},
+
+       { &hf_ndmp_nodes, {
+               "Nodes", "ndmp.nodes", FT_NONE, 0,
+               NULL, 0, "List of nodes", HFILL }},
+
+       { &hf_ndmp_nlist, {
+               "Nlist", "ndmp.nlist", FT_NONE, 0,
+               NULL, 0, "List of names", HFILL }},
+
+       { &hf_ndmp_bu_original_path, {
+               "Original Path", "ndmp.bu.original_path", FT_STRING, BASE_NONE,
+               NULL, 0, "Original path where backup was created", HFILL }},
+
+       { &hf_ndmp_bu_destination_dir, {
+               "Destination Dir", "ndmp.bu.destination_dir", FT_STRING, BASE_NONE,
+               NULL, 0, "Destination directory to restore backup to", HFILL }},
+
+       { &hf_ndmp_bu_new_name, {
+               "New Name", "ndmp.bu.new_name", FT_STRING, BASE_NONE,
+               NULL, 0, "New Name", HFILL }},
+
+       { &hf_ndmp_bu_other_name, {
+               "Other Name", "ndmp.bu.other_name", FT_STRING, BASE_NONE,
+               NULL, 0, "Other Name", HFILL }},
+
+       { &hf_ndmp_state_invalid_ebr, {
+               "", "ndmp.bu.state.invalid_ebr", FT_BOOLEAN, 32,
+               TFS(&tfs_ndmp_state_invalid_ebr), 0x00000001, "Whether EstimatedBytesLeft is valid or not", HFILL, }},
+
+       { &hf_ndmp_state_invalid_etr, {
+               "", "ndmp.bu.state.invalid_etr", FT_BOOLEAN, 32,
+               TFS(&tfs_ndmp_state_invalid_etr), 0x00000002, "Whether EstimatedTimeLeft is valid or not", HFILL, }},
+
+       { &hf_ndmp_bu_operation, {
+               "Operation", "ndmp.bu.operation", FT_UINT32, BASE_DEC,
+               VALS(bu_operation_vals), 0, "BU Operation", HFILL, }},
+
+       { &hf_ndmp_data_state, {
+               "State", "ndmp.data.state", FT_UINT32, BASE_DEC,
+               VALS(data_state_vals), 0, "Data state", HFILL, }},
+
+       { &hf_ndmp_data_halted, {
+               "Halted Reason", "ndmp.data.halted", FT_UINT32, BASE_DEC,
+               VALS(data_halted_vals), 0, "Data halted reason", HFILL, }},
+
+       { &hf_ndmp_data_bytes_processed, {
+               "Bytes Processed", "ndmp.data.bytes_processed", FT_UINT64, BASE_DEC,
+               NULL, 0, "Number of bytes processed", HFILL }},
+
+       { &hf_ndmp_data_est_bytes_remain, {
+               "Est Bytes Remain", "ndmp.data.est_bytes_remain", FT_UINT64, BASE_DEC,
+               NULL, 0, "Estimated number of bytes remaining", HFILL }},
+
+       { &hf_ndmp_data_est_time_remain, {
+               "Est Time Remain", "ndmp.data.est_time_remain", FT_RELATIVE_TIME, BASE_DEC,
+               NULL, 0, "Estimated time remaining", HFILL }},
+  };
+
+  static gint *ett[] = {
+    &ett_ndmp,
+    &ett_ndmp_header,
+    &ett_ndmp_butype_attrs,
+    &ett_ndmp_fs_invalid,
+    &ett_ndmp_tape_attr,
+    &ett_ndmp_execute_cdb_flags,
+    &ett_ndmp_execute_cdb_cdb,
+    &ett_ndmp_execute_cdb_sns,
+    &ett_ndmp_execute_cdb_payload,
+    &ett_ndmp_tape_invalid,
+    &ett_ndmp_tape_flags,
+    &ett_ndmp_addr,
+    &ett_ndmp_file,
+    &ett_ndmp_file_name,
+    &ett_ndmp_file_stats,
+    &ett_ndmp_file_invalids,
+    &ett_ndmp_state_invalids,
+  };
+
+  module_t *ndmp_module;
+
+  proto_ndmp = proto_register_protocol("Network Data Management Protocol", "NDMP", "ndmp");
+  proto_register_field_array(proto_ndmp, hf_ndmp, array_length(hf_ndmp));
+  
+  proto_register_subtree_array(ett, array_length(ett));
+
+  /* desegmentation */
+  ndmp_module = prefs_register_protocol(proto_ndmp, NULL);
+  prefs_register_bool_preference(ndmp_module, "desegment",
+       "Desegment all NDMP messages spanning multiple TCP segments",
+       "Whether the dissector should desegment NDMP messages",
+       &ndmp_desegment);
+  prefs_register_bool_preference(ndmp_module, "defragment",
+       "Defragment all multi-fragment NDMP messages",
+       "Whether the dissector should defragment multi-fragment NDMP messages",
+       &ndmp_defragment);
 }
 
 void