/* packet-ncp.c
* Routines for NetWare Core Protocol
- * Gilbert Ramirez <gram@verdict.uthscsa.edu>
+ * Gilbert Ramirez <gram@alumni.rice.edu>
+ * Modified to allow NCP over TCP/IP decodes by James Coe <jammer@cin.net>
+ * Modified to decode server op-lock, packet signature,
+ * & NDS packets by Greg Morris <gmorris@novell.com>
*
- * $Id: packet-ncp.c,v 1.11 1999/05/10 20:51:36 gram Exp $
+ * Portions Copyright (c) by Gilbert Ramirez 2000-2002
+ * Portions Copyright (c) by James Coe 2000-2002
+ * Portions Copyright (c) Novell, Inc. 2000-2003
+ *
+ * $Id$
*
* Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@unicom.net>
- * Copyright 1998 Gerald Combs
+ * By Gerald Combs <gerald@ethereal.com>
+ * Copyright 2000 Gerald Combs
*
- *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
# include <netinet/in.h>
#endif
+#include <string.h>
#include <glib.h>
-#include "packet.h"
+#include <epan/packet.h>
+#include "prefs.h"
#include "packet-ipx.h"
-#include "packet-ncp.h"
+#include "packet-tcp.h"
+#include "packet-ncp-int.h"
+#include "reassemble.h"
+#include <epan/conversation.h>
+
+int proto_ncp = -1;
+static int hf_ncp_ip_ver = -1;
+static int hf_ncp_ip_length = -1;
+static int hf_ncp_ip_rplybufsize = -1;
+static int hf_ncp_ip_sig = -1;
+static int hf_ncp_ip_packetsig = -1;
+static int hf_ncp_type = -1;
+static int hf_ncp_seq = -1;
+static int hf_ncp_connection = -1;
+static int hf_ncp_task = -1;
+static int hf_ncp_stream_type = -1;
+static int hf_ncp_system_flags = -1;
+static int hf_ncp_system_flags_abt = -1;
+static int hf_ncp_system_flags_eob = -1;
+static int hf_ncp_system_flags_sys = -1;
+static int hf_ncp_system_flags_bsy = -1;
+static int hf_ncp_system_flags_lst = -1;
+static int hf_ncp_src_connection = -1;
+static int hf_ncp_dst_connection = -1;
+static int hf_ncp_packet_seqno = -1;
+static int hf_ncp_delay_time = -1;
+static int hf_ncp_burst_seqno = -1;
+static int hf_ncp_ack_seqno = -1;
+static int hf_ncp_burst_len = -1;
+static int hf_ncp_data_offset = -1;
+static int hf_ncp_data_bytes = -1;
+static int hf_ncp_missing_fraglist_count = -1;
+static int hf_ncp_missing_data_offset = -1;
+static int hf_ncp_missing_data_count = -1;
+static int hf_ncp_oplock_flag = -1;
+static int hf_ncp_oplock_handle = -1;
+static int hf_ncp_completion_code = -1;
+static int hf_ncp_connection_status = -1;
+static int hf_ncp_slot = -1;
+static int hf_ncp_control_code = -1;
+static int hf_ncp_fragment_handle = -1;
+static int hf_lip_echo = -1;
+static int hf_ncp_burst_command = -1;
+static int hf_ncp_burst_file_handle = -1;
+static int hf_ncp_burst_reserved = -1;
+
+gint ett_ncp = -1;
+gint ett_nds = -1;
+gint ett_nds_segments = -1;
+gint ett_nds_segment = -1;
+static gint ett_ncp_system_flags = -1;
+
+
+/* Tables for reassembly of fragments. */
+GHashTable *nds_fragment_table = NULL;
+GHashTable *nds_reassembled_table = NULL;
+dissector_handle_t nds_data_handle;
+
+/* desegmentation of NCP over TCP */
+static gboolean ncp_desegment = TRUE;
+
+static dissector_handle_t data_handle;
+
+#define TCP_PORT_NCP 524
+#define UDP_PORT_NCP 524
+
+#define NCP_RQST_HDR_LENGTH 7
+#define NCP_RPLY_HDR_LENGTH 8
-static void
-dissect_ncp_request(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree);
-static void
-dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree);
+/* Hash functions */
+gint ncp_equal (gconstpointer v, gconstpointer v2);
+guint ncp_hash (gconstpointer v);
+
+/* These are the header structures to handle NCP over IP */
+#define NCPIP_RQST 0x446d6454 /* "DmdT" */
+#define NCPIP_RPLY 0x744e6350 /* "tNcP" */
-static struct ncp2222_record *
-ncp2222_find(guint8 func, guint8 subfunc);
+struct ncp_ip_header {
+ guint32 signature;
+ guint32 length;
+};
-struct svc_record;
-static int
-svc_record_byte_count(struct svc_record *sr);
+/* This header only appears on NCP over IP request packets */
+struct ncp_ip_rqhdr {
+ guint32 version;
+ guint32 rplybufsize;
+};
-/* Hash functions */
-gint ncp_equal (const gpointer v, const gpointer v2);
-guint ncp_hash (const gpointer v);
+static const value_string ncp_ip_signature[] = {
+ { NCPIP_RQST, "Demand Transport (Request)" },
+ { NCPIP_RPLY, "Transport is NCP (Reply)" },
+ { 0, NULL },
+};
+static const value_string burst_command[] = {
+ { 0x01000000, "Burst Read" },
+ { 0x02000000, "Burst Write" },
+ { 0, NULL },
+};
/* The information in this module comes from:
NetWare LAN Analysis, Second Edition
(c) 1996 by Steve Conner & Diane Conner
Published by Annabooks, San Diego, California
ISBN: 0-929392-31-0
+
+ And:
+ http:developer.novell.com
+ NCP documentation
*/
+/*
+ * Every NCP packet has this common header (except for burst packets).
+ */
struct ncp_common_header {
guint16 type;
guint8 sequence;
guint8 conn_low;
guint8 task;
- guint8 conn_high;
-};
-struct ncp_request_header {
- guint16 type;
- guint8 sequence;
- guint8 conn_low;
- guint8 task;
- guint8 conn_high;
- guint8 function;
- guint16 length;
- guint8 subfunc;
-};
-
-struct ncp_reply_header {
- guint16 type;
- guint8 sequence;
- guint8 conn_low;
- guint8 task;
- guint8 conn_high;
- guint8 completion_code;
- guint8 connection_state;
+ guint8 conn_high; /* type=0x5555 doesn't have this */
};
-static value_string request_reply_values[] = {
- { 0x1111, "Create a service connection" },
- { 0x2222, "Service request" },
- { 0x3333, "Service reply" },
- { 0x5555, "Destroy service connection" },
- { 0x7777, "Burst mode transfer" },
- { 0x9999, "Request being processed" },
- { 0x0000, NULL }
+static value_string ncp_type_vals[] = {
+ { NCP_ALLOCATE_SLOT, "Create a service connection" },
+ { NCP_SERVICE_REQUEST, "Service request" },
+ { NCP_SERVICE_REPLY, "Service reply" },
+ { NCP_WATCHDOG, "Watchdog" },
+ { NCP_DEALLOCATE_SLOT, "Destroy service connection" },
+ { NCP_BROADCAST_SLOT, "Server Broadcast" },
+ { NCP_BURST_MODE_XFER, "Burst mode transfer" },
+ { NCP_POSITIVE_ACK, "Request being processed" },
+ { NCP_LIP_ECHO, "Large Internet Packet Echo" },
+ { 0, NULL }
};
-enum ntype { nend, nbyte, nhex, nbelong, nbeshort, ndata, nbytevar,
- ndatetime, nasciiz };
-
-typedef struct svc_record {
- enum ntype type;
- guint8 length;
- gchar *description;
-} svc_record;
-
-typedef struct ncp2222_record {
- guint8 func;
- guint8 subfunc;
- guint8 submask;
- gchar *funcname;
-
- svc_record *req;
- gchar *req_summ;
- guint8 req_summ_var1;
- guint8 req_summ_var2;
- guint8 req_summ_var3;
-
- svc_record *rep;
- gchar *rep_summ;
- guint8 rep_summ_var1;
- guint8 rep_summ_var2;
- guint8 rep_summ_var3;
-} ncp2222_record;
-
-/* Service Queue Job REQUEST */
-static svc_record ncp_17_7C_C[] = {
- { nbelong, 4, "The queue the job resides in" },
- { nbeshort, 2, "Job Type" },
- { nend, 0, NULL }
-};
-/* Service Queue Job REPLY */
-static svc_record ncp_17_7C_R[] = {
- { nbelong, 4, "Client station number" },
- { nbelong, 4, "Task Number" },
- { nbelong, 4, "User" },
- { nbelong, 4, "Server specifed to service queue entry" },
- { ndatetime,6, "Earliest time to execute" },
- { ndatetime,6, "When job entered queue" },
- { nbelong, 4, "Job Number" },
- { nbeshort, 2, "Job Type" },
- { nbeshort, 2, "Job Position" },
- { nbeshort, 2, "Current status of job" },
- { nasciiz, 14, "Name of file" },
- { nbelong, 4, "File handle" },
- { nbelong, 4, "Client station number" },
- { nbelong, 4, "Task number" },
- { nbelong, 4, "Job server" },
- { nend, 0, NULL }
-};
+/* Conversation Struct so we can store whether the conversation is using Packet Signature */
-/* Read from a file REQUEST */
-static svc_record ncp_48_00_C[] = {
- { nbyte, 1, "Unknown" },
- { nhex, 6, "File Handle" },
- { nbelong, 4, "Byte offset within file" },
- { nbeshort, 2, "Maximum data bytes to return" },
- { nend, 0, NULL }
-};
-/* RESPONSE */
-static svc_record ncp_48_00_R[] = {
- { nbeshort, 2, "Data bytes returned" },
- { nbytevar, 1, "Padding" },
- { ndata, 0, NULL }
-};
-
-#define SUBFUNC 0xff
-#define NOSUB 0x00
+typedef struct {
+ conversation_t *conversation;
+} mncp_rhash_key;
-static ncp2222_record ncp2222[] = {
+typedef struct {
+ gboolean packet_signature;
+} mncp_rhash_value;
-{ 0x17, 0x7C, SUBFUNC, "Service Queue Job",
- ncp_17_7C_C, "", -1, -1, -1,
- ncp_17_7C_R, "", -1, -1, -1
-},
+static GHashTable *mncp_rhash = NULL;
+static GMemChunk *mncp_rhash_keys = NULL;
+static GMemChunk *mncp_rhash_values = NULL;
-{ 0x48, 0x00, NOSUB, "Read from a file",
- ncp_48_00_C, "F=%s Read %d at %d", 1, 2, 3,
- ncp_48_00_R, "%d bytes read", 0, -1, -1
-},
+/* Hash Functions */
+gint
+mncp_equal(gconstpointer v, gconstpointer v2)
+{
+ const mncp_rhash_key *val1 = (const mncp_rhash_key*)v;
+ const mncp_rhash_key *val2 = (const mncp_rhash_key*)v2;
-{ 0x00, 0x00, NOSUB, NULL,
- NULL, NULL, -1, -1, -1,
- NULL, NULL, -1, -1, -1
+ if (val1->conversation == val2->conversation ) {
+ return 1;
+ }
+ return 0;
}
-};
-
-/* NCP packets come in request/reply pairs. The request packets tell the type
- * of NCP request and give a sequence ID. The response, unfortunately, only
- * identifies itself via the sequence ID; you have to know what type of NCP
- * request the request packet contained in order to successfully parse the NCP
- * response. A global method for doing this does not exist in ethereal yet
- * (NFS also requires it), so for now the NCP section will keep its own hash
- * table keeping track of NCP packet types.
- *
- * The key representing the unique NCP request is composed of 3 variables:
- *
- * ServerIPXNetwork.Connection.SequenceNumber
- * 4 bytes 2 bytes 1 byte
- * guint32 guint16 guint8 (all are host order)
- *
- * This assumes that all NCP connection is between a client and server.
- * Servers can be identified by having a 00:00:00:00:00:01 IPX Node address.
- * We have to let the IPX layer pass us the ServerIPXNetwork via a global
- * variable (nw_server_address). In the future, if we decode NCP over TCP/UDP,
- * then nw_server_address will represent the IP address of the server, which
- * conveniently, is also 4 bytes long.
- *
- * The value stored in the hash table is the ncp_request_val pointer. This
- * struct tells us the NCP type and gives the ncp2222_record pointer, if
- * ncp_type == 0x2222.
- */
-guint32 nw_server_address = 0; /* set by IPX layer */
-guint16 nw_connection = 0; /* set by dissect_ncp */
-guint8 nw_sequence = 0; /* set by dissect_ncp */
-guint16 nw_ncp_type = 0; /* set by dissect_ncp */
-
-struct ncp_request_key {
- guint32 nw_server_address;
- guint16 nw_connection;
- guint8 nw_sequence;
-};
-
-struct ncp_request_val {
- guint32 ncp_type;
- struct ncp2222_record* ncp_record;
-};
-
-GHashTable *ncp_request_hash = NULL;
-GMemChunk *ncp_request_records = NULL;
-
-/* Hash Functions */
-gint ncp_equal (const gpointer v, const gpointer v2)
+guint
+mncp_hash(gconstpointer v)
{
- return memcmp(v, v2, 7);
+ const mncp_rhash_key *mncp_key = (const mncp_rhash_key*)v;
+ return GPOINTER_TO_UINT(mncp_key->conversation);
}
-guint ncp_hash (const gpointer v)
+/* Initializes the hash table and the mem_chunk area each time a new
+ * file is loaded or re-loaded in ethereal */
+static void
+mncp_init_protocol(void)
{
- struct ncp_request_key *ncp_key = (struct ncp_request_key*)v;
-
- return ncp_key->nw_server_address +
- ((guint32) ncp_key->nw_connection << 16) +
- ncp_key->nw_sequence;
+ if (mncp_rhash)
+ g_hash_table_destroy(mncp_rhash);
+ if (mncp_rhash_keys)
+ g_mem_chunk_destroy(mncp_rhash_keys);
+ if (mncp_rhash_values)
+ g_mem_chunk_destroy(mncp_rhash_values);
+
+ mncp_rhash = g_hash_table_new(mncp_hash, mncp_equal);
+ mncp_rhash_keys = g_mem_chunk_new("mncp_rhash_keys",
+ sizeof(mncp_rhash_key),
+ 200 * sizeof(mncp_rhash_key),
+ G_ALLOC_ONLY);
+ mncp_rhash_values = g_mem_chunk_new("mncp_rhash_values",
+ sizeof(mncp_rhash_value),
+ 200 * sizeof(mncp_rhash_value),
+ G_ALLOC_ONLY);
}
-void
-ncp_init_protocol(void)
+/* After the sequential run, we don't need the ncp_request hash and keys
+ * anymore; the lookups have already been done and the vital info
+ * saved in the reply-packets' private_data in the frame_data struct. */
+static void
+mncp_postseq_cleanup(void)
{
- if (ncp_request_hash)
- g_hash_table_destroy(ncp_request_hash);
- if (ncp_request_records)
- g_mem_chunk_destroy(ncp_request_records);
-
- ncp_request_hash = g_hash_table_new(ncp_hash, ncp_equal);
- ncp_request_records = g_mem_chunk_new("ncp_request_records",
- sizeof(struct ncp_request_val), 50 * sizeof(struct ncp_request_val),
- G_ALLOC_AND_FREE);
}
-static struct ncp2222_record *
-ncp2222_find(guint8 func, guint8 subfunc)
+mncp_rhash_value*
+mncp_hash_insert(conversation_t *conversation)
{
- struct ncp2222_record *ncp_record, *retval = NULL;
+ mncp_rhash_key *key;
+ mncp_rhash_value *value;
- ncp_record = ncp2222;
+ /* Now remember the request, so we can find it if we later
+ a reply to it. */
+ key = g_mem_chunk_alloc(mncp_rhash_keys);
+ key->conversation = conversation;
- while(ncp_record->func != 0) {
- if (ncp_record->func == func &&
- ncp_record->subfunc == (subfunc & ncp_record->submask)) {
- retval = ncp_record;
- break;
- }
- ncp_record++;
- }
+ value = g_mem_chunk_alloc(mncp_rhash_values);
+ value->packet_signature = FALSE;
+
+ g_hash_table_insert(mncp_rhash, key, value);
- return retval;
+ return value;
}
-static int
-svc_record_byte_count(svc_record *sr)
+/* Returns the ncp_rec*, or NULL if not found. */
+mncp_rhash_value*
+mncp_hash_lookup(conversation_t *conversation)
{
- svc_record *rec = sr;
- int byte_count = 0;
+ mncp_rhash_key key;
- while (rec->type != nend && rec->type != ndata) {
- byte_count += rec->length;
- rec++;
- }
+ key.conversation = conversation;
- return byte_count;
+ return g_hash_table_lookup(mncp_rhash, &key);
}
-void
-dissect_ncp(const u_char *pd, int offset, frame_data *fd, proto_tree *tree,
- int max_data) {
+/*
+ * Burst packet system flags.
+ */
+#define ABT 0x04 /* Abort request */
+#define BSY 0x08 /* Server Busy */
+#define EOB 0x10 /* End of burst */
+#define LST 0x40 /* Include Fragment List */
+#define SYS 0x80 /* System packet */
- proto_tree *ncp_tree = NULL;
- proto_item *ti;
- int ncp_hdr_length = 0;
+static void
+dissect_ncp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ gboolean is_tcp)
+{
+ proto_tree *ncp_tree = NULL;
+ proto_item *ti;
+ struct ncp_ip_header ncpiph;
+ struct ncp_ip_rqhdr ncpiphrq;
struct ncp_common_header header;
-
- memcpy(&header, &pd[offset], sizeof(header));
- header.type = ntohs(header.type);
-
- if (header.type == 0x1111 ||
- header.type == 0x2222 ||
- header.type == 0x5555 ||
- header.type == 0x7777) {
- ncp_hdr_length = 7;
- }
- else if (header.type == 0x3333 || header.type == 0x9999) {
- ncp_hdr_length = 8;
+ guint16 nw_connection, ncp_burst_seqno, ncp_ack_seqno;
+ guint16 flags = 0;
+ char flags_str[1+3+1+3+1+3+1+1];
+ char *sep;
+ proto_tree *flags_tree = NULL;
+ guint16 data_len = 0;
+ guint16 missing_fraglist_count = 0;
+ int hdr_offset = 0;
+ int commhdr;
+ int offset;
+ gint length_remaining;
+ tvbuff_t *next_tvb;
+ guint32 testvar = 0, ncp_burst_command, burst_len, burst_off, burst_file;
+ guint8 subfunction;
+ mncp_rhash_value *request_value = NULL;
+ conversation_t *conversation;
+
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, "NCP");
+ if (check_col(pinfo->cinfo, COL_INFO))
+ col_clear(pinfo->cinfo, COL_INFO);
+
+ hdr_offset = 0;
+
+ if (is_tcp)
+ {
+ if (tvb_get_ntohl(tvb, hdr_offset) != NCPIP_RQST && tvb_get_ntohl(tvb, hdr_offset) != NCPIP_RPLY)
+ hdr_offset += 1;
+ ncpiph.signature = tvb_get_ntohl(tvb, hdr_offset);
+ ncpiph.length = tvb_get_ntohl(tvb, hdr_offset+4);
+ hdr_offset += 8;
+ if (ncpiph.signature == NCPIP_RQST)
+ {
+ ncpiphrq.version = tvb_get_ntohl(tvb, hdr_offset);
+ hdr_offset += 4;
+ ncpiphrq.rplybufsize = tvb_get_ntohl(tvb, hdr_offset);
+ hdr_offset += 4;
+ }
+ /* Ok, we need to track the conversation so that we can determine
+ * if packet signature is occuring for this connection. We will
+ * store the conversation the first time and that state of packet
+ * signature will be stored later in our logic. This way when we
+ * dissect reply packets we will be able to determine if we need
+ * to also dissect with a signature.
+ */
+ conversation = find_conversation(&pinfo->src, &pinfo->dst,
+ PT_NCP, (guint32) pinfo->srcport, (guint32) pinfo->destport, 0);
+ if (ncpiph.length & 0x80000000 || ncpiph.signature == NCPIP_RPLY)
+ {
+ /* First time through we will store packet signature state */
+ if (!pinfo->fd->flags.visited)
+ {
+ if (conversation != NULL)
+ {
+ /* find the record telling us the request made that caused
+ this reply */
+ request_value = mncp_hash_lookup(conversation);
+ /* if for some reason we have no conversation in our hash, create one */
+ if (request_value==NULL)
+ {
+ request_value = mncp_hash_insert(conversation);
+ }
+ }
+ else
+ {
+ /* It's not part of any conversation - create a new one. */
+ conversation = conversation_new(&pinfo->src, &pinfo->dst,
+ PT_NCP, (guint32) pinfo->srcport, (guint32) pinfo->destport, 0);
+ request_value = mncp_hash_insert(conversation);
+ }
+ /* If this is a request packet then we know that we have a signature */
+ if (ncpiph.signature == NCPIP_RQST)
+ {
+ hdr_offset += 8;
+ ncpiph.length &= 0x7fffffff;
+ request_value->packet_signature=TRUE;
+ }
+ else
+ {
+ /* Now on reply packets we have to use the state of the original request packet */
+ /* So look up the request value and check the state of packet signature */
+ request_value = mncp_hash_lookup(conversation);
+ if (request_value->packet_signature==TRUE)
+ {
+ hdr_offset += 8;
+ ncpiph.length &= 0x7fffffff;
+ request_value->packet_signature=TRUE;
+ }
+ else
+ {
+ request_value->packet_signature=FALSE;
+ }
+ }
+ }
+ else
+ {
+ /* Get request value data */
+ request_value = mncp_hash_lookup(conversation);
+ if (request_value->packet_signature==TRUE)
+ {
+ hdr_offset += 8;
+ ncpiph.length &= 0x7fffffff;
+ }
+ }
+ }
+ else
+ {
+ if (!pinfo->fd->flags.visited)
+ {
+ if (conversation != NULL)
+ {
+ /* find the record telling us the request made that caused
+ this reply */
+ request_value = mncp_hash_lookup(conversation);
+ /* if for some reason we have no conversation in our hash, create one */
+ if (request_value==NULL)
+ {
+ request_value = mncp_hash_insert(conversation);
+ }
+ }
+ else
+ {
+ /* It's not part of any conversation - create a new one. */
+ conversation = conversation_new(&pinfo->src, &pinfo->dst,
+ PT_NCP, (guint32) pinfo->srcport, (guint32) pinfo->destport, 0);
+ request_value = mncp_hash_insert(conversation);
+ }
+ /* find the record telling us the request made that caused
+ this reply */
+ request_value->packet_signature=FALSE;
+ }
+ else
+ {
+ request_value = mncp_hash_lookup(conversation);
+ }
+ }
}
- if (check_col(fd, COL_PROTOCOL))
- col_add_str(fd, COL_PROTOCOL, "NCP");
- nw_connection = (header.conn_high << 16) + header.conn_low;
- nw_sequence = header.sequence;
- nw_ncp_type = header.type;
-
- if (tree) {
- ti = proto_tree_add_item(tree, offset, END_OF_FRAME,
- "NetWare Core Protocol");
- ncp_tree = proto_tree_new();
- proto_item_add_subtree(ti, ncp_tree, ETT_NCP);
+ /* Record the offset where the NCP common header starts */
+ commhdr = hdr_offset;
- proto_tree_add_item(ncp_tree, offset, 2,
- "Type: %s", val_to_str( header.type,
- request_reply_values, "Unknown (%04X)"));
+ header.type = tvb_get_ntohs(tvb, commhdr);
+ header.sequence = tvb_get_guint8(tvb, commhdr+2);
+ header.conn_low = tvb_get_guint8(tvb, commhdr+3);
+ header.conn_high = tvb_get_guint8(tvb, commhdr+5);
- proto_tree_add_item(ncp_tree, offset+2, 1,
- "Sequence Number: %d", header.sequence);
+ if (check_col(pinfo->cinfo, COL_INFO)) {
+ col_add_fstr(pinfo->cinfo, COL_INFO,
+ "%s",
+ val_to_str(header.type, ncp_type_vals, "Unknown type (0x%04x)"));
- proto_tree_add_item(ncp_tree, offset+3, 3,
- "Connection Number: %d", nw_connection);
-
- proto_tree_add_item(ncp_tree, offset+4, 1,
- "Task Number: %d", header.task);
}
- /* Note how I use ncp_tree *and* tree in my args for ncp request/reply */
- if (ncp_hdr_length == 7)
- dissect_ncp_request(pd, offset, fd, ncp_tree, tree);
- else if (ncp_hdr_length == 8)
- dissect_ncp_reply(pd, offset, fd, ncp_tree, tree);
- else
- dissect_data(pd, offset, fd, tree);
-}
-
-void
-dissect_ncp_request(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree) {
-
- struct ncp_request_header request;
- struct ncp2222_record *ncp_request;
- gchar *description = "Unknown";
- struct ncp_request_val *request_val;
- struct ncp_request_key request_key;
+ nw_connection = (header.conn_high << 16) + header.conn_low;
- /*memcpy(&request, &pd[offset], sizeof(request));*/
- request.function = pd[offset+6];
- request.subfunc = pd[offset+9];
+ if (tree) {
+ ti = proto_tree_add_item(tree, proto_ncp, tvb, 0, -1, FALSE);
+ ncp_tree = proto_item_add_subtree(ti, ett_ncp);
+
+ if (is_tcp) {
+ proto_tree_add_uint(ncp_tree, hf_ncp_ip_sig, tvb, 0, 4, ncpiph.signature);
+ proto_tree_add_uint(ncp_tree, hf_ncp_ip_length, tvb, 4, 4, ncpiph.length);
+ if (ncpiph.signature == NCPIP_RQST) {
+ proto_tree_add_uint(ncp_tree, hf_ncp_ip_ver, tvb, 8, 4, ncpiphrq.version);
+ proto_tree_add_uint(ncp_tree, hf_ncp_ip_rplybufsize, tvb, 12, 4, ncpiphrq.rplybufsize);
+ if (request_value->packet_signature==TRUE)
+ proto_tree_add_item(ncp_tree, hf_ncp_ip_packetsig, tvb, 16, 8, FALSE);
+ } else {
+ if (request_value->packet_signature==TRUE)
+ proto_tree_add_item(ncp_tree, hf_ncp_ip_packetsig, tvb, 8, 8, FALSE);
+ }
+ }
+ proto_tree_add_uint(ncp_tree, hf_ncp_type, tvb, commhdr + 0, 2, header.type);
+ }
- ncp_request = ncp2222_find(request.function, request.subfunc);
- if (ncp_request)
- description = ncp_request->funcname;
+ /*
+ * Process the packet-type-specific header.
+ */
+ switch (header.type) {
+
+ case NCP_BROADCAST_SLOT: /* Server Broadcast */
+ proto_tree_add_uint(ncp_tree, hf_ncp_seq, tvb, commhdr + 2, 1, header.sequence);
+ proto_tree_add_uint(ncp_tree, hf_ncp_connection,tvb, commhdr + 3, 3, nw_connection);
+ proto_tree_add_item(ncp_tree, hf_ncp_task, tvb, commhdr + 4, 1, FALSE);
+ proto_tree_add_item(ncp_tree, hf_ncp_oplock_flag, tvb, commhdr + 9, 1, FALSE);
+ proto_tree_add_item(ncp_tree, hf_ncp_oplock_handle, tvb, commhdr + 10, 4, FALSE);
+ break;
+
+ case NCP_LIP_ECHO: /* Lip Echo Packet */
+ proto_tree_add_item(ncp_tree, hf_lip_echo, tvb, commhdr, 13, FALSE);
+ break;
+
+ case NCP_BURST_MODE_XFER: /* Packet Burst Packet */
+ /*
+ * XXX - we should keep track of whether there's a burst
+ * outstanding on a connection and, if not, treat the
+ * beginning of the data as a burst header.
+ *
+ * The burst header contains:
+ *
+ * 4 bytes of little-endian function number:
+ * 1 = read, 2 = write;
+ *
+ * 4 bytes of file handle;
+ *
+ * 8 reserved bytes;
+ *
+ * 4 bytes of big-endian file offset;
+ *
+ * 4 bytes of big-endian byte count.
+ *
+ * The data follows for a burst write operation.
+ *
+ * The first packet of a burst read reply contains:
+ *
+ * 4 bytes of little-endian result code:
+ * 0: No error
+ * 1: Initial error
+ * 2: I/O error
+ * 3: No data read;
+ *
+ * 4 bytes of returned byte count (big-endian?).
+ *
+ * The data follows.
+ *
+ * Each burst of a write request is responded to with a
+ * burst packet with a 2-byte little-endian result code:
+ *
+ * 0: Write successful
+ * 4: Write error
+ */
+ flags = tvb_get_guint8(tvb, commhdr + 2);
+ strcpy(flags_str, "");
+ sep = " (";
+ if (flags & ABT) {
+ strcat(flags_str, sep);
+ strcat(flags_str, "ABT");
+ sep = ",";
+ }
+ if (flags & BSY) {
+ strcat(flags_str, sep);
+ strcat(flags_str, "BSY");
+ sep = ",";
+ }
+ if (flags & EOB) {
+ strcat(flags_str, sep);
+ strcat(flags_str, "EOB");
+ sep = ",";
+ }
+ if (flags & LST) {
+ strcat(flags_str, sep);
+ strcat(flags_str, "LST");
+ sep = ",";
+ }
+ if (flags & SYS) {
+ strcat(flags_str, sep);
+ strcat(flags_str, "SYS");
+ }
+ if (flags_str[0] != '\0')
+ strcat(flags_str, ")");
+ ti = proto_tree_add_uint_format(ncp_tree, hf_ncp_system_flags,
+ tvb, commhdr + 2, 1, flags, "Flags: 0x%04x%s", flags,
+ flags_str);
+ flags_tree = proto_item_add_subtree(ti, ett_ncp_system_flags);
+ proto_tree_add_item(flags_tree, hf_ncp_system_flags_abt,
+ tvb, commhdr + 2, 1, FALSE);
+ proto_tree_add_item(flags_tree, hf_ncp_system_flags_bsy,
+ tvb, commhdr + 2, 1, FALSE);
+ proto_tree_add_item(flags_tree, hf_ncp_system_flags_eob,
+ tvb, commhdr + 2, 1, FALSE);
+ proto_tree_add_item(flags_tree, hf_ncp_system_flags_lst,
+ tvb, commhdr + 2, 1, FALSE);
+ proto_tree_add_item(flags_tree, hf_ncp_system_flags_sys,
+ tvb, commhdr + 2, 1, FALSE);
+
+ proto_tree_add_item(ncp_tree, hf_ncp_stream_type,
+ tvb, commhdr + 3, 1, FALSE);
+ proto_tree_add_item(ncp_tree, hf_ncp_src_connection,
+ tvb, commhdr + 4, 4, FALSE);
+ proto_tree_add_item(ncp_tree, hf_ncp_dst_connection,
+ tvb, commhdr + 8, 4, FALSE);
+ proto_tree_add_item(ncp_tree, hf_ncp_packet_seqno,
+ tvb, commhdr + 12, 4, FALSE);
+ proto_tree_add_item(ncp_tree, hf_ncp_delay_time,
+ tvb, commhdr + 16, 4, FALSE);
+ ncp_burst_seqno = tvb_get_ntohs(tvb, commhdr+20);
+ proto_tree_add_item(ncp_tree, hf_ncp_burst_seqno,
+ tvb, commhdr + 20, 2, FALSE);
+ ncp_ack_seqno = tvb_get_ntohs(tvb, commhdr+22);
+ proto_tree_add_item(ncp_tree, hf_ncp_ack_seqno,
+ tvb, commhdr + 22, 2, FALSE);
+ proto_tree_add_item(ncp_tree, hf_ncp_burst_len,
+ tvb, commhdr + 24, 4, FALSE);
+ proto_tree_add_item(ncp_tree, hf_ncp_data_offset,
+ tvb, commhdr + 28, 4, FALSE);
+ data_len = tvb_get_ntohs(tvb, commhdr + 32);
+ proto_tree_add_uint(ncp_tree, hf_ncp_data_bytes,
+ tvb, commhdr + 32, 2, data_len);
+ missing_fraglist_count = tvb_get_ntohs(tvb, commhdr + 34);
+ proto_tree_add_item(ncp_tree, hf_ncp_missing_fraglist_count,
+ tvb, commhdr + 34, 2, FALSE);
+ if (ncp_burst_seqno==ncp_ack_seqno)
+ {
+ ncp_burst_command = tvb_get_ntohl(tvb, commhdr+36);
+ proto_tree_add_item(ncp_tree, hf_ncp_burst_command,
+ tvb, commhdr + 36, 4, FALSE);
+ burst_file = tvb_get_ntohl(tvb, commhdr+40);
+ proto_tree_add_item(ncp_tree, hf_ncp_burst_file_handle,
+ tvb, commhdr + 40, 4, FALSE);
+ proto_tree_add_item(ncp_tree, hf_ncp_burst_reserved,
+ tvb, commhdr + 44, 8, FALSE);
+ burst_off = tvb_get_ntohl(tvb, commhdr+52);
+ proto_tree_add_item(ncp_tree, hf_ncp_data_offset,
+ tvb, commhdr + 52, 4, FALSE);
+ burst_len = tvb_get_ntohl(tvb, commhdr+56);
+ proto_tree_add_item(ncp_tree, hf_ncp_burst_len,
+ tvb, commhdr + 56, 4, FALSE);
+ if (check_col(pinfo->cinfo, COL_INFO)) {
+ col_set_str(pinfo->cinfo, COL_INFO, match_strval(ncp_burst_command, burst_command));
+ col_append_fstr(pinfo->cinfo, COL_INFO, " %d bytes starting at offset %d in file 0x%08x", burst_len, burst_off, burst_file);
+ }
+ return;
+ }
+ break;
+
+ case NCP_ALLOCATE_SLOT: /* Allocate Slot Request */
+ length_remaining = tvb_length_remaining(tvb, commhdr + 4);
+ if (length_remaining > 4) {
+ testvar = tvb_get_ntohl(tvb, commhdr+4);
+ if (testvar == 0x4c495020) {
+ proto_tree_add_item(ncp_tree, hf_lip_echo, tvb, commhdr+4, 13, FALSE);
+ break;
+ }
+ }
+ /* otherwise fall through */
+
+ case NCP_POSITIVE_ACK: /* Positive Acknowledgement */
+ case NCP_SERVICE_REQUEST: /* Server NCP Request */
+ case NCP_SERVICE_REPLY: /* Server NCP Reply */
+ case NCP_WATCHDOG: /* Watchdog Packet */
+ case NCP_DEALLOCATE_SLOT: /* Deallocate Slot Request */
+ default:
+ proto_tree_add_uint(ncp_tree, hf_ncp_seq, tvb, commhdr + 2, 1, header.sequence);
+ proto_tree_add_uint(ncp_tree, hf_ncp_connection,tvb, commhdr + 3, 3, nw_connection);
+ proto_tree_add_item(ncp_tree, hf_ncp_task, tvb, commhdr + 4, 1, FALSE);
+ break;
+ }
- if (check_col(fd, COL_INFO))
- col_add_fstr(fd, COL_INFO, "C %s", description);
+ /*
+ * Process the packet body.
+ */
+ switch (header.type) {
+
+ case NCP_ALLOCATE_SLOT: /* Allocate Slot Request */
+ length_remaining = tvb_length_remaining(tvb, commhdr + 4);
+ if (length_remaining > 4) {
+ testvar = tvb_get_ntohl(tvb, commhdr+4);
+ if (testvar == 0x4c495020) {
+ proto_tree_add_text(ncp_tree, tvb, commhdr, -1,
+ "Lip Echo Packet");
+ /*break;*/
+ }
+ }
- if (ncp_tree) {
- proto_tree_add_item(ncp_tree, offset+6, 1, "Function Code: 0x%02X (%s)",
- request.function, description);
- if (ncp_request) {
- offset += 10 + svc_record_byte_count(ncp_request->req);
- dissect_data(pd, offset, fd, tree);
+ case NCP_SERVICE_REQUEST: /* Server NCP Request */
+ case NCP_DEALLOCATE_SLOT: /* Deallocate Slot Request */
+ case NCP_BROADCAST_SLOT: /* Server Broadcast Packet */
+ next_tvb = tvb_new_subset(tvb, hdr_offset, -1, -1);
+ if (tvb_get_guint8(tvb, commhdr+6) == 0x68) {
+ subfunction = tvb_get_guint8(tvb, commhdr+7);
+ switch (subfunction) {
+
+ case 0x02: /* NDS Frag Packet to decode */
+ dissect_nds_request(next_tvb, pinfo,
+ nw_connection, header.sequence,
+ header.type, ncp_tree);
+ break;
+
+ case 0x01: /* NDS Ping */
+ dissect_ping_req(next_tvb, pinfo,
+ nw_connection, header.sequence,
+ header.type, ncp_tree);
+ break;
+
+ default:
+ dissect_ncp_request(next_tvb, pinfo,
+ nw_connection, header.sequence,
+ header.type, ncp_tree);
+ break;
+ }
+ } else {
+ dissect_ncp_request(next_tvb, pinfo, nw_connection,
+ header.sequence, header.type, ncp_tree);
}
- }
- else { /* ! tree */
- request_val = g_mem_chunk_alloc(ncp_request_records);
- request_val->ncp_type = nw_ncp_type;
- request_val->ncp_record = ncp2222_find(request.function, request.subfunc);
- request_key.nw_server_address = nw_server_address;
- request_key.nw_connection = nw_connection;
- request_key.nw_sequence = nw_sequence;
-
- g_hash_table_insert(ncp_request_hash, &request_key, request_val);
- }
+ break;
+
+ case NCP_SERVICE_REPLY: /* Server NCP Reply */
+ next_tvb = tvb_new_subset(tvb, hdr_offset, -1, -1);
+ nds_defrag(next_tvb, pinfo, nw_connection, header.sequence,
+ header.type, ncp_tree);
+ break;
+
+ case NCP_POSITIVE_ACK: /* Positive Acknowledgement */
+ /*
+ * XXX - this used to call "nds_defrag()", which would
+ * clear out "frags". Was that the right thing to
+ * do?
+ */
+ next_tvb = tvb_new_subset(tvb, hdr_offset, -1, -1);
+ dissect_ncp_reply(next_tvb, pinfo, nw_connection,
+ header.sequence, header.type, ncp_tree);
+ break;
+
+ case NCP_WATCHDOG: /* Watchdog Packet */
+ /*
+ * XXX - should the completion code be interpreted as
+ * it is in "packet-ncp2222.inc"? If so, this
+ * packet should be handled by "dissect_ncp_reply()".
+ */
+ proto_tree_add_item(ncp_tree, hf_ncp_completion_code,
+ tvb, commhdr + 6, 1, TRUE);
+ proto_tree_add_item(ncp_tree, hf_ncp_connection_status,
+ tvb, commhdr + 7, 1, TRUE);
+ proto_tree_add_item(ncp_tree, hf_ncp_slot,
+ tvb, commhdr + 8, 1, TRUE);
+ proto_tree_add_item(ncp_tree, hf_ncp_control_code,
+ tvb, commhdr + 9, 1, TRUE);
+ /*
+ * Display the rest of the packet as data.
+ */
+ if (tvb_offset_exists(tvb, commhdr + 10)) {
+ call_dissector(data_handle,
+ tvb_new_subset(tvb, commhdr + 10, -1, -1),
+ pinfo, ncp_tree);
+ }
+ break;
+
+ case NCP_BURST_MODE_XFER: /* Packet Burst Packet */
+ if (flags & SYS) {
+ /*
+ * System packet; show missing fragments if there
+ * are any.
+ */
+ offset = commhdr + 36;
+ while (missing_fraglist_count != 0) {
+ proto_tree_add_item(ncp_tree, hf_ncp_missing_data_offset,
+ tvb, offset, 4, FALSE);
+ proto_tree_add_item(ncp_tree, hf_ncp_missing_data_count,
+ tvb, offset, 2, FALSE);
+ missing_fraglist_count--;
+ }
+ } else {
+ /*
+ * XXX - do this by using -1 and -1 as the length
+ * arguments to "tvb_new_subset()" and then calling
+ * "tvb_set_reported_length()"? That'll throw an
+ * exception if "data_len" goes past the reported
+ * length of the packet, but that's arguably a
+ * feature in this case.
+ */
+ length_remaining = tvb_length_remaining(tvb, commhdr + 36);
+ if (length_remaining > data_len)
+ length_remaining = data_len;
+ if (data_len != 0) {
+ call_dissector(data_handle,
+ tvb_new_subset(tvb, commhdr + 36,
+ length_remaining, data_len),
+ pinfo, ncp_tree);
+ }
+ }
+ break;
+
+ case NCP_LIP_ECHO: /* LIP Echo Packet */
+ proto_tree_add_text(ncp_tree, tvb, commhdr, -1,
+ "Lip Echo Packet");
+ break;
+
+ default:
+ if (tree) {
+ proto_tree_add_text(ncp_tree, tvb, commhdr + 6, -1,
+ "%s packets not supported yet",
+ val_to_str(header.type, ncp_type_vals,
+ "Unknown type (0x%04x)"));
+ }
+ break;
+ }
+}
+static void
+dissect_ncp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ dissect_ncp_common(tvb, pinfo, tree, FALSE);
}
-void
-dissect_ncp_reply(const u_char *pd, int offset, frame_data *fd, proto_tree *ncp_tree, proto_tree *tree) {
- struct ncp_reply_header reply;
- struct ncp2222_record *ncp_request = NULL;
- struct ncp_request_val *request_val;
- struct ncp_request_key request_key;
- gchar *description = "Unknown";
+static guint
+get_ncp_pdu_len(tvbuff_t *tvb, int offset)
+{
+ guint32 signature;
+
+ /*
+ * Check the NCP-over-TCP header signature, to make sure it's there.
+ * If it's not there, we cannot trust the next 4 bytes to be a
+ * packet length+"has signature" flag, so we just say the length is
+ * "what remains in the packet".
+ */
+ /*if (tvb_get_guint8(tvb, offset)==0xff)
+ {
+ offset += 1;
+ }*/
+ signature = tvb_get_ntohl(tvb, offset);
+ if (signature != NCPIP_RQST && signature != NCPIP_RPLY)
+ return tvb_length_remaining(tvb, offset);
+
+ /*
+ * Get the length of the NCP-over-TCP packet. Strip off the "has
+ * signature" flag.
+ */
+
+ return tvb_get_ntohl(tvb, offset + 4) & 0x7fffffff;
+}
- memcpy(&reply, &pd[offset], sizeof(reply));
+static void
+dissect_ncp_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ dissect_ncp_common(tvb, pinfo, tree, TRUE);
+}
- /* find the record telling us the request made that caused this reply */
- request_key.nw_server_address = nw_server_address;
- request_key.nw_connection = nw_connection;
- request_key.nw_sequence = nw_sequence;
+static void
+dissect_ncp_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ tcp_dissect_pdus(tvb, pinfo, tree, ncp_desegment, 8, get_ncp_pdu_len,
+ dissect_ncp_tcp_pdu);
+}
- request_val = (struct ncp_request_val*)
- g_hash_table_lookup(ncp_request_hash, &request_key);
+void
+proto_register_ncp(void)
+{
- if (request_val)
- ncp_request = request_val->ncp_record;
+ static hf_register_info hf[] = {
+ { &hf_ncp_ip_sig,
+ { "NCP over IP signature", "ncp.ip.signature",
+ FT_UINT32, BASE_HEX, VALS(ncp_ip_signature), 0x0,
+ "", HFILL }},
+ { &hf_ncp_ip_length,
+ { "NCP over IP length", "ncp.ip.length",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "", HFILL }},
+ { &hf_ncp_ip_ver,
+ { "NCP over IP Version", "ncp.ip.version",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "", HFILL }},
+ { &hf_ncp_ip_rplybufsize,
+ { "NCP over IP Reply Buffer Size", "ncp.ip.replybufsize",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "", HFILL }},
+ { &hf_ncp_ip_packetsig,
+ { "NCP over IP Packet Signature", "ncp.ip.packetsig",
+ FT_BYTES, BASE_NONE, NULL, 0x0,
+ "", HFILL }},
+ { &hf_ncp_type,
+ { "Type", "ncp.type",
+ FT_UINT16, BASE_HEX, VALS(ncp_type_vals), 0x0,
+ "NCP message type", HFILL }},
+ { &hf_ncp_seq,
+ { "Sequence Number", "ncp.seq",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "", HFILL }},
+ { &hf_ncp_connection,
+ { "Connection Number", "ncp.connection",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "", HFILL }},
+ { &hf_ncp_task,
+ { "Task Number", "ncp.task",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "", HFILL }},
+ { &hf_ncp_oplock_flag,
+ { "Oplock Flag", "ncp.oplock_flag",
+ FT_UINT8, BASE_HEX, NULL, 0x0,
+ "", HFILL }},
+ { &hf_ncp_oplock_handle,
+ { "File Handle", "ncp.oplock_handle",
+ FT_UINT16, BASE_HEX, NULL, 0x0,
+ "", HFILL }},
+ { &hf_ncp_stream_type,
+ { "Stream Type", "ncp.stream_type",
+ FT_UINT8, BASE_HEX, NULL, 0x0,
+ "Type of burst", HFILL }},
+ { &hf_ncp_system_flags,
+ { "System Flags", "ncp.system_flags",
+ FT_UINT8, BASE_HEX, NULL, 0x0,
+ "", HFILL }},
+ { &hf_ncp_system_flags_abt,
+ { "ABT", "ncp.system_flags.abt",
+ FT_BOOLEAN, 8, NULL, ABT,
+ "Is this an abort request?", HFILL }},
+ { &hf_ncp_system_flags_eob,
+ { "EOB", "ncp.system_flags.eob",
+ FT_BOOLEAN, 8, NULL, EOB,
+ "Is this the last packet of the burst?", HFILL }},
+ { &hf_ncp_system_flags_sys,
+ { "SYS", "ncp.system_flags.sys",
+ FT_BOOLEAN, 8, NULL, SYS,
+ "Is this a system packet?", HFILL }},
+ { &hf_ncp_system_flags_bsy,
+ { "BSY", "ncp.system_flags.bsy",
+ FT_BOOLEAN, 8, NULL, BSY,
+ "Is the server busy?", HFILL }},
+ { &hf_ncp_system_flags_lst,
+ { "LST", "ncp.system_flags.lst",
+ FT_BOOLEAN, 8, NULL, LST,
+ "Return Fragment List?", HFILL }},
+ { &hf_ncp_src_connection,
+ { "Source Connection ID", "ncp.src_connection",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "The workstation's connection identification number", HFILL }},
+ { &hf_ncp_dst_connection,
+ { "Destination Connection ID", "ncp.dst_connection",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "The server's connection identification number", HFILL }},
+ { &hf_ncp_packet_seqno,
+ { "Packet Sequence Number", "ncp.packet_seqno",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Sequence number of this packet in a burst", HFILL }},
+ { &hf_ncp_delay_time,
+ { "Delay Time", "ncp.delay_time", /* in 100 us increments */
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Delay time between consecutive packet sends (100 us increments)", HFILL }},
+ { &hf_ncp_burst_seqno,
+ { "Burst Sequence Number", "ncp.burst_seqno",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "Sequence number of this packet in the burst", HFILL }},
+ { &hf_ncp_ack_seqno,
+ { "ACK Sequence Number", "ncp.ack_seqno",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "Next expected burst sequence number", HFILL }},
+ { &hf_ncp_burst_len,
+ { "Burst Length", "ncp.burst_len",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Total length of data in this burst", HFILL }},
+ { &hf_ncp_data_offset,
+ { "Data Offset", "ncp.data_offset",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Offset of this packet in the burst", HFILL }},
+ { &hf_ncp_data_bytes,
+ { "Data Bytes", "ncp.data_bytes",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "Number of data bytes in this packet", HFILL }},
+ { &hf_ncp_missing_fraglist_count,
+ { "Missing Fragment List Count", "ncp.missing_fraglist_count",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "Number of missing fragments reported", HFILL }},
+ { &hf_ncp_missing_data_offset,
+ { "Missing Data Offset", "ncp.missing_data_offset",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ "Offset of beginning of missing data", HFILL }},
+ { &hf_ncp_missing_data_count,
+ { "Missing Data Count", "ncp.missing_data_count",
+ FT_UINT16, BASE_DEC, NULL, 0x0,
+ "Number of bytes of missing data", HFILL }},
+ { &hf_ncp_completion_code,
+ { "Completion Code", "ncp.completion_code",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "", HFILL }},
+ { &hf_ncp_connection_status,
+ { "Connection Status", "ncp.connection_status",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "", HFILL }},
+ { &hf_ncp_slot,
+ { "Slot", "ncp.slot",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "", HFILL }},
+ { &hf_ncp_control_code,
+ { "Control Code", "ncp.control_code",
+ FT_UINT8, BASE_DEC, NULL, 0x0,
+ "", HFILL }},
+ { &hf_ncp_fragment_handle,
+ { "Fragment Handle", "ncp.fragger_hndl",
+ FT_UINT16, BASE_HEX, NULL, 0x0,
+ "", HFILL }},
+ { &hf_lip_echo,
+ { "Large Internet Packet Echo", "ncp.lip_echo",
+ FT_STRING, BASE_NONE, NULL, 0x0,
+ "", HFILL }},
+ { &hf_ncp_burst_command,
+ { "Burst Command", "ncp.burst_command",
+ FT_UINT32, BASE_HEX, VALS(burst_command), 0x0,
+ "Packet Burst Command", HFILL }},
+ { &hf_ncp_burst_file_handle,
+ { "Burst File Handle", "ncp.file_handle",
+ FT_UINT32, BASE_HEX, NULL, 0x0,
+ "Packet Burst File Handle", HFILL }},
+ { &hf_ncp_burst_reserved,
+ { "Reserved", "ncp.burst_reserved",
+ FT_BYTES, BASE_HEX, NULL, 0x0, "", HFILL }},
+
+ };
+ static gint *ett[] = {
+ &ett_ncp,
+ &ett_ncp_system_flags,
+ &ett_nds,
+ &ett_nds_segments,
+ &ett_nds_segment,
+ };
+ module_t *ncp_module;
+
+ proto_ncp = proto_register_protocol("NetWare Core Protocol", "NCP", "ncp");
+ proto_register_field_array(proto_ncp, hf, array_length(hf));
+ proto_register_subtree_array(ett, array_length(ett));
+
+ ncp_module = prefs_register_protocol(proto_ncp, NULL);
+ prefs_register_obsolete_preference(ncp_module, "initial_hash_size");
+ prefs_register_bool_preference(ncp_module, "desegment",
+ "Desegment all NCP-over-TCP messages spanning multiple segments",
+ "Whether the NCP dissector should desegment all messages spanning multiple TCP segments",
+ &ncp_desegment);
+ prefs_register_bool_preference(ncp_module, "defragment_nds",
+ "Defragment all NDS messages spanning multiple packets",
+ "Whether the NCP dissector should defragment all NDS messages spanning multiple packets",
+ &nds_defragment);
+ register_init_routine(&mncp_init_protocol);
+ register_postseq_cleanup_routine(&mncp_postseq_cleanup);
+}
- if (ncp_request)
- description = ncp_request->funcname;
+void
+proto_reg_handoff_ncp(void)
+{
+ dissector_handle_t ncp_handle;
+ dissector_handle_t ncp_tcp_handle;
- if (check_col(fd, COL_INFO))
- col_add_fstr(fd, COL_INFO, "R %s", description);
+ ncp_handle = create_dissector_handle(dissect_ncp, proto_ncp);
+ ncp_tcp_handle = create_dissector_handle(dissect_ncp_tcp, proto_ncp);
+ dissector_add("tcp.port", TCP_PORT_NCP, ncp_tcp_handle);
+ dissector_add("udp.port", UDP_PORT_NCP, ncp_handle);
+ dissector_add("ipx.packet_type", IPX_PACKET_TYPE_NCP, ncp_handle);
+ dissector_add("ipx.socket", IPX_SOCKET_NCP, ncp_handle);
- if (ncp_tree) {
- proto_tree_add_item(ncp_tree, offset+6, 1,
- "Completion Code: %d", reply.completion_code);
+ data_handle = find_dissector("data");
+}
- proto_tree_add_item(ncp_tree, offset+7, 1,
- "Connection Status: %d", reply.connection_state);
- offset += 8;
- dissect_data(pd, offset, fd, tree);
- }
-}