2 * Routines for NetWare Core Protocol
3 * Gilbert Ramirez <gram@alumni.rice.edu>
4 * Modified to allow NCP over TCP/IP decodes by James Coe <jammer@cin.net>
5 * Modified to decode server op-lock, packet signature,
6 * & NDS packets by Greg Morris <gmorris@novell.com>
8 * Portions Copyright (c) by Gilbert Ramirez 2000-2002
9 * Portions Copyright (c) by James Coe 2000-2002
10 * Portions Copyright (c) Novell, Inc. 2000-2003
14 * Ethereal - Network traffic analyzer
15 * By Gerald Combs <gerald@ethereal.com>
16 * Copyright 2000 Gerald Combs
18 * This program is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU General Public License
20 * as published by the Free Software Foundation; either version 2
21 * of the License, or (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
37 #ifdef HAVE_SYS_TYPES_H
38 # include <sys/types.h>
41 #ifdef HAVE_NETINET_IN_H
42 # include <netinet/in.h>
47 #include <epan/packet.h>
48 #include <epan/prefs.h>
49 #include "packet-ipx.h"
50 #include "packet-tcp.h"
51 #include "packet-ncp-int.h"
52 #include "reassemble.h"
53 #include <epan/conversation.h>
56 static int hf_ncp_ip_ver = -1;
57 static int hf_ncp_ip_length = -1;
58 static int hf_ncp_ip_rplybufsize = -1;
59 static int hf_ncp_ip_sig = -1;
60 static int hf_ncp_ip_packetsig = -1;
61 static int hf_ncp_type = -1;
62 static int hf_ncp_seq = -1;
63 static int hf_ncp_connection = -1;
64 static int hf_ncp_task = -1;
65 static int hf_ncp_stream_type = -1;
66 static int hf_ncp_system_flags = -1;
67 static int hf_ncp_system_flags_abt = -1;
68 static int hf_ncp_system_flags_eob = -1;
69 static int hf_ncp_system_flags_sys = -1;
70 static int hf_ncp_system_flags_bsy = -1;
71 static int hf_ncp_system_flags_lst = -1;
72 static int hf_ncp_src_connection = -1;
73 static int hf_ncp_dst_connection = -1;
74 static int hf_ncp_packet_seqno = -1;
75 static int hf_ncp_delay_time = -1;
76 static int hf_ncp_burst_seqno = -1;
77 static int hf_ncp_ack_seqno = -1;
78 static int hf_ncp_burst_len = -1;
79 static int hf_ncp_burst_offset = -1;
80 static int hf_ncp_data_offset = -1;
81 static int hf_ncp_data_bytes = -1;
82 static int hf_ncp_missing_fraglist_count = -1;
83 static int hf_ncp_missing_data_offset = -1;
84 static int hf_ncp_missing_data_count = -1;
85 static int hf_ncp_oplock_flag = -1;
86 static int hf_ncp_oplock_handle = -1;
87 static int hf_ncp_completion_code = -1;
88 static int hf_ncp_connection_status = -1;
89 static int hf_ncp_slot = -1;
90 static int hf_ncp_control_code = -1;
91 static int hf_ncp_fragment_handle = -1;
92 static int hf_lip_echo = -1;
93 static int hf_ncp_burst_command = -1;
94 static int hf_ncp_burst_file_handle = -1;
95 static int hf_ncp_burst_reserved = -1;
99 gint ett_nds_segments = -1;
100 gint ett_nds_segment = -1;
101 static gint ett_ncp_system_flags = -1;
104 /* Tables for reassembly of fragments. */
105 GHashTable *nds_fragment_table = NULL;
106 GHashTable *nds_reassembled_table = NULL;
107 dissector_handle_t nds_data_handle;
109 /* desegmentation of NCP over TCP */
110 static gboolean ncp_desegment = TRUE;
112 static dissector_handle_t data_handle;
114 #define TCP_PORT_NCP 524
115 #define UDP_PORT_NCP 524
117 #define NCP_RQST_HDR_LENGTH 7
118 #define NCP_RPLY_HDR_LENGTH 8
122 gint ncp_equal (gconstpointer v, gconstpointer v2);
123 guint ncp_hash (gconstpointer v);
125 /* These are the header structures to handle NCP over IP */
126 #define NCPIP_RQST 0x446d6454 /* "DmdT" */
127 #define NCPIP_RPLY 0x744e6350 /* "tNcP" */
129 struct ncp_ip_header {
135 /* This header only appears on NCP over IP request packets */
136 struct ncp_ip_rqhdr {
141 static const value_string ncp_ip_signature[] = {
142 { NCPIP_RQST, "Demand Transport (Request)" },
143 { NCPIP_RPLY, "Transport is NCP (Reply)" },
147 static const value_string burst_command[] = {
148 { 0x01000000, "Burst Read" },
149 { 0x02000000, "Burst Write" },
153 /* The information in this module comes from:
154 NetWare LAN Analysis, Second Edition
155 Laura A. Chappell and Dan E. Hakes
156 (c) 1994 Novell, Inc.
157 Novell Press, San Jose.
160 And from the ncpfs source code by Volker Lendecke
163 Programmer's Guide to the NetWare Core Protocol
164 Steve Conner & Diane Conner
165 (c) 1996 by Steve Conner & Diane Conner
166 Published by Annabooks, San Diego, California
170 http:developer.novell.com
176 * Every NCP packet has this common header (except for burst packets).
178 struct ncp_common_header {
183 guint8 conn_high; /* type=0x5555 doesn't have this */
187 static value_string ncp_type_vals[] = {
188 { NCP_ALLOCATE_SLOT, "Create a service connection" },
189 { NCP_SERVICE_REQUEST, "Service request" },
190 { NCP_SERVICE_REPLY, "Service reply" },
191 { NCP_WATCHDOG, "Watchdog" },
192 { NCP_DEALLOCATE_SLOT, "Destroy service connection" },
193 { NCP_BROADCAST_SLOT, "Server Broadcast" },
194 { NCP_BURST_MODE_XFER, "Burst mode transfer" },
195 { NCP_POSITIVE_ACK, "Request being processed" },
196 { NCP_LIP_ECHO, "Large Internet Packet Echo" },
200 /* Conversation Struct so we can store whether the conversation is using Packet Signature */
203 conversation_t *conversation;
207 gboolean packet_signature;
210 static GHashTable *mncp_rhash = NULL;
211 static GMemChunk *mncp_rhash_keys = NULL;
212 static GMemChunk *mncp_rhash_values = NULL;
216 mncp_equal(gconstpointer v, gconstpointer v2)
218 const mncp_rhash_key *val1 = (const mncp_rhash_key*)v;
219 const mncp_rhash_key *val2 = (const mncp_rhash_key*)v2;
221 if (val1->conversation == val2->conversation ) {
228 mncp_hash(gconstpointer v)
230 const mncp_rhash_key *mncp_key = (const mncp_rhash_key*)v;
231 return GPOINTER_TO_UINT(mncp_key->conversation);
234 /* Initializes the hash table and the mem_chunk area each time a new
235 * file is loaded or re-loaded in ethereal */
237 mncp_init_protocol(void)
240 g_hash_table_destroy(mncp_rhash);
242 g_mem_chunk_destroy(mncp_rhash_keys);
243 if (mncp_rhash_values)
244 g_mem_chunk_destroy(mncp_rhash_values);
246 mncp_rhash = g_hash_table_new(mncp_hash, mncp_equal);
247 mncp_rhash_keys = g_mem_chunk_new("mncp_rhash_keys",
248 sizeof(mncp_rhash_key),
249 200 * sizeof(mncp_rhash_key),
251 mncp_rhash_values = g_mem_chunk_new("mncp_rhash_values",
252 sizeof(mncp_rhash_value),
253 200 * sizeof(mncp_rhash_value),
257 /* After the sequential run, we don't need the ncp_request hash and keys
258 * anymore; the lookups have already been done and the vital info
259 * saved in the reply-packets' private_data in the frame_data struct. */
261 mncp_postseq_cleanup(void)
266 mncp_hash_insert(conversation_t *conversation)
269 mncp_rhash_value *value;
271 /* Now remember the request, so we can find it if we later
273 key = g_mem_chunk_alloc(mncp_rhash_keys);
274 key->conversation = conversation;
276 value = g_mem_chunk_alloc(mncp_rhash_values);
277 value->packet_signature = FALSE;
279 g_hash_table_insert(mncp_rhash, key, value);
284 /* Returns the ncp_rec*, or NULL if not found. */
286 mncp_hash_lookup(conversation_t *conversation)
290 key.conversation = conversation;
292 return g_hash_table_lookup(mncp_rhash, &key);
296 * Burst packet system flags.
298 #define ABT 0x04 /* Abort request */
299 #define BSY 0x08 /* Server Busy */
300 #define EOB 0x10 /* End of burst */
301 #define LST 0x40 /* Include Fragment List */
302 #define SYS 0x80 /* System packet */
305 dissect_ncp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
308 proto_tree *ncp_tree = NULL;
310 struct ncp_ip_header ncpiph;
311 struct ncp_ip_rqhdr ncpiphrq;
312 struct ncp_common_header header;
313 guint16 nw_connection, ncp_burst_seqno, ncp_ack_seqno;
315 char flags_str[1+3+1+3+1+3+1+1];
317 proto_tree *flags_tree = NULL;
321 gint length_remaining;
323 guint32 testvar = 0, ncp_burst_command, burst_len, burst_off, burst_file;
326 guint16 data_len = 0;
327 guint16 missing_fraglist_count = 0;
328 mncp_rhash_value *request_value = NULL;
329 conversation_t *conversation;
331 if (check_col(pinfo->cinfo, COL_PROTOCOL))
332 col_set_str(pinfo->cinfo, COL_PROTOCOL, "NCP");
333 if (check_col(pinfo->cinfo, COL_INFO))
334 col_clear(pinfo->cinfo, COL_INFO);
339 if (tvb_get_ntohl(tvb, hdr_offset) != NCPIP_RQST && tvb_get_ntohl(tvb, hdr_offset) != NCPIP_RPLY)
341 ncpiph.signature = tvb_get_ntohl(tvb, hdr_offset);
342 ncpiph.length = tvb_get_ntohl(tvb, hdr_offset+4);
344 if (ncpiph.signature == NCPIP_RQST) {
345 ncpiphrq.version = tvb_get_ntohl(tvb, hdr_offset);
347 ncpiphrq.rplybufsize = tvb_get_ntohl(tvb, hdr_offset);
350 /* Ok, we need to track the conversation so that we can
351 * determine if packet signature is occuring for this
352 * connection. We will store the conversation the first
353 * time and that state of packet signature will be stored
354 * later in our logic. This way when we dissect reply
355 * packets we will be able to determine if we need
356 * to also dissect with a signature.
358 conversation = find_conversation(&pinfo->src, &pinfo->dst,
359 PT_NCP, (guint32) pinfo->srcport, (guint32) pinfo->destport,
361 if ((ncpiph.length & 0x80000000) ||
362 ncpiph.signature == NCPIP_RPLY) {
363 /* First time through we will store packet signature
366 if (!pinfo->fd->flags.visited) {
367 if (conversation != NULL) {
368 /* find the record telling us the
369 * request made that caused this
373 mncp_hash_lookup(conversation);
374 /* if for some reason we have no
375 * conversation in our hash, create
377 if (request_value == NULL) {
379 mncp_hash_insert(conversation);
382 /* It's not part of any conversation
383 * - create a new one.
385 conversation = conversation_new(&pinfo->src,
387 (guint32) pinfo->srcport,
388 (guint32) pinfo->destport, 0);
390 mncp_hash_insert(conversation);
392 /* If this is a request packet then we know
393 * that we have a signature
395 if (ncpiph.signature == NCPIP_RQST) {
397 ncpiph.length &= 0x7fffffff;
398 request_value->packet_signature=TRUE;
400 /* Now on reply packets we have to
401 * use the state of the original
402 * request packet, so look up the
403 * request value and check the state
404 * of packet signature
407 mncp_hash_lookup(conversation);
408 if (request_value->packet_signature) {
410 ncpiph.length &= 0x7fffffff;
411 /* XXX - it already *is* TRUE */
412 request_value->packet_signature=TRUE;
414 /* XXX - it already *is* FALSE */
415 request_value->packet_signature=FALSE;
419 /* Get request value data */
420 request_value = mncp_hash_lookup(conversation);
421 if (request_value->packet_signature) {
423 ncpiph.length &= 0x7fffffff;
427 if (!pinfo->fd->flags.visited) {
428 if (conversation != NULL) {
429 /* find the record telling us the
430 * request made that caused this
434 mncp_hash_lookup(conversation);
435 /* if for some reason we have no
436 * conversation in our hash, create
438 if (request_value == NULL) {
440 mncp_hash_insert(conversation);
443 /* It's not part of any conversation
444 * - create a new one.
446 conversation = conversation_new(&pinfo->src,
448 (guint32) pinfo->srcport,
449 (guint32) pinfo->destport, 0);
451 mncp_hash_insert(conversation);
453 /* find the record telling us the request
454 * made that caused this reply
456 request_value->packet_signature=FALSE;
458 request_value = mncp_hash_lookup(conversation);
463 /* Record the offset where the NCP common header starts */
464 commhdr = hdr_offset;
466 header.type = tvb_get_ntohs(tvb, commhdr);
467 header.sequence = tvb_get_guint8(tvb, commhdr+2);
468 header.conn_low = tvb_get_guint8(tvb, commhdr+3);
469 header.conn_high = tvb_get_guint8(tvb, commhdr+5);
471 if (check_col(pinfo->cinfo, COL_INFO)) {
472 col_add_fstr(pinfo->cinfo, COL_INFO,
474 val_to_str(header.type, ncp_type_vals, "Unknown type (0x%04x)"));
477 nw_connection = (header.conn_high << 16) + header.conn_low;
480 ti = proto_tree_add_item(tree, proto_ncp, tvb, 0, -1, FALSE);
481 ncp_tree = proto_item_add_subtree(ti, ett_ncp);
484 proto_tree_add_uint(ncp_tree, hf_ncp_ip_sig, tvb, 0, 4, ncpiph.signature);
485 proto_tree_add_uint(ncp_tree, hf_ncp_ip_length, tvb, 4, 4, ncpiph.length);
486 if (ncpiph.signature == NCPIP_RQST) {
487 proto_tree_add_uint(ncp_tree, hf_ncp_ip_ver, tvb, 8, 4, ncpiphrq.version);
488 proto_tree_add_uint(ncp_tree, hf_ncp_ip_rplybufsize, tvb, 12, 4, ncpiphrq.rplybufsize);
489 if (request_value->packet_signature==TRUE)
490 proto_tree_add_item(ncp_tree, hf_ncp_ip_packetsig, tvb, 16, 8, FALSE);
492 if (request_value->packet_signature==TRUE)
493 proto_tree_add_item(ncp_tree, hf_ncp_ip_packetsig, tvb, 8, 8, FALSE);
496 proto_tree_add_uint(ncp_tree, hf_ncp_type, tvb, commhdr + 0, 2, header.type);
501 * Process the packet-type-specific header.
503 switch (header.type) {
505 case NCP_BROADCAST_SLOT: /* Server Broadcast */
506 proto_tree_add_uint(ncp_tree, hf_ncp_seq, tvb, commhdr + 2, 1, header.sequence);
507 proto_tree_add_uint(ncp_tree, hf_ncp_connection,tvb, commhdr + 3, 3, nw_connection);
508 proto_tree_add_item(ncp_tree, hf_ncp_task, tvb, commhdr + 4, 1, FALSE);
509 proto_tree_add_item(ncp_tree, hf_ncp_oplock_flag, tvb, commhdr + 9, 1, FALSE);
510 proto_tree_add_item(ncp_tree, hf_ncp_oplock_handle, tvb, commhdr + 10, 4, FALSE);
513 case NCP_LIP_ECHO: /* Lip Echo Packet */
514 proto_tree_add_item(ncp_tree, hf_lip_echo, tvb, commhdr, 13, FALSE);
517 case NCP_BURST_MODE_XFER: /* Packet Burst Packet */
519 * XXX - we should keep track of whether there's a burst
520 * outstanding on a connection and, if not, treat the
521 * beginning of the data as a burst header.
523 * The burst header contains:
525 * 4 bytes of little-endian function number:
526 * 1 = read, 2 = write;
528 * 4 bytes of file handle;
532 * 4 bytes of big-endian file offset;
534 * 4 bytes of big-endian byte count.
536 * The data follows for a burst write operation.
538 * The first packet of a burst read reply contains:
540 * 4 bytes of little-endian result code:
546 * 4 bytes of returned byte count (big-endian?).
550 * Each burst of a write request is responded to with a
551 * burst packet with a 2-byte little-endian result code:
553 * 0: Write successful
556 flags = tvb_get_guint8(tvb, commhdr + 2);
557 strcpy(flags_str, "");
560 strcat(flags_str, sep);
561 strcat(flags_str, "ABT");
565 strcat(flags_str, sep);
566 strcat(flags_str, "BSY");
570 strcat(flags_str, sep);
571 strcat(flags_str, "EOB");
575 strcat(flags_str, sep);
576 strcat(flags_str, "LST");
580 strcat(flags_str, sep);
581 strcat(flags_str, "SYS");
583 if (flags_str[0] != '\0')
584 strcat(flags_str, ")");
585 ti = proto_tree_add_uint_format(ncp_tree, hf_ncp_system_flags,
586 tvb, commhdr + 2, 1, flags, "Flags: 0x%04x%s", flags,
588 flags_tree = proto_item_add_subtree(ti, ett_ncp_system_flags);
589 proto_tree_add_item(flags_tree, hf_ncp_system_flags_abt,
590 tvb, commhdr + 2, 1, FALSE);
591 proto_tree_add_item(flags_tree, hf_ncp_system_flags_bsy,
592 tvb, commhdr + 2, 1, FALSE);
593 proto_tree_add_item(flags_tree, hf_ncp_system_flags_eob,
594 tvb, commhdr + 2, 1, FALSE);
595 proto_tree_add_item(flags_tree, hf_ncp_system_flags_lst,
596 tvb, commhdr + 2, 1, FALSE);
597 proto_tree_add_item(flags_tree, hf_ncp_system_flags_sys,
598 tvb, commhdr + 2, 1, FALSE);
600 proto_tree_add_item(ncp_tree, hf_ncp_stream_type,
601 tvb, commhdr + 3, 1, FALSE);
602 proto_tree_add_item(ncp_tree, hf_ncp_src_connection,
603 tvb, commhdr + 4, 4, FALSE);
604 proto_tree_add_item(ncp_tree, hf_ncp_dst_connection,
605 tvb, commhdr + 8, 4, FALSE);
606 proto_tree_add_item(ncp_tree, hf_ncp_packet_seqno,
607 tvb, commhdr + 12, 4, FALSE);
608 proto_tree_add_item(ncp_tree, hf_ncp_delay_time,
609 tvb, commhdr + 16, 4, FALSE);
610 ncp_burst_seqno = tvb_get_ntohs(tvb, commhdr+20);
611 proto_tree_add_item(ncp_tree, hf_ncp_burst_seqno,
612 tvb, commhdr + 20, 2, FALSE);
613 ncp_ack_seqno = tvb_get_ntohs(tvb, commhdr+22);
614 proto_tree_add_item(ncp_tree, hf_ncp_ack_seqno,
615 tvb, commhdr + 22, 2, FALSE);
616 proto_tree_add_item(ncp_tree, hf_ncp_burst_len,
617 tvb, commhdr + 24, 4, FALSE);
618 data_offset = tvb_get_ntohl(tvb, commhdr + 28);
619 proto_tree_add_uint(ncp_tree, hf_ncp_data_offset,
620 tvb, commhdr + 28, 4, data_offset);
621 data_len = tvb_get_ntohs(tvb, commhdr + 32);
622 proto_tree_add_uint(ncp_tree, hf_ncp_data_bytes,
623 tvb, commhdr + 32, 2, data_len);
624 missing_fraglist_count = tvb_get_ntohs(tvb, commhdr + 34);
625 proto_tree_add_item(ncp_tree, hf_ncp_missing_fraglist_count,
626 tvb, commhdr + 34, 2, FALSE);
627 offset = commhdr + 36;
628 if (!(flags & SYS) && ncp_burst_seqno == ncp_ack_seqno &&
631 * This is either a Burst Read or Burst Write
632 * command. The data length includes the burst
633 * mode header, plus any data in the command
634 * (there shouldn't be any in a read, but there
635 * might be some in a write).
639 ncp_burst_command = tvb_get_ntohl(tvb, offset);
640 proto_tree_add_item(ncp_tree, hf_ncp_burst_command,
641 tvb, offset, 4, FALSE);
647 burst_file = tvb_get_ntohl(tvb, offset);
648 proto_tree_add_item(ncp_tree, hf_ncp_burst_file_handle,
649 tvb, offset, 4, FALSE);
655 proto_tree_add_item(ncp_tree, hf_ncp_burst_reserved,
656 tvb, offset, 8, FALSE);
662 burst_off = tvb_get_ntohl(tvb, offset);
663 proto_tree_add_uint(ncp_tree, hf_ncp_burst_offset,
664 tvb, offset, 4, burst_off);
670 burst_len = tvb_get_ntohl(tvb, offset);
671 proto_tree_add_uint(ncp_tree, hf_ncp_burst_len,
672 tvb, offset, 4, burst_len);
676 if (check_col(pinfo->cinfo, COL_INFO)) {
677 col_add_fstr(pinfo->cinfo, COL_INFO,
678 "%s %d bytes starting at offset %d in file 0x%08x",
679 val_to_str(ncp_burst_command,
680 burst_command, "Unknown (0x%08x)"),
681 burst_len, burst_off, burst_file);
685 if (tvb_get_guint8(tvb, commhdr + 2) & 0x10) {
686 if (check_col(pinfo->cinfo, COL_INFO)) {
687 col_set_str(pinfo->cinfo, COL_INFO,
694 case NCP_ALLOCATE_SLOT: /* Allocate Slot Request */
695 length_remaining = tvb_length_remaining(tvb, commhdr + 4);
696 if (length_remaining > 4) {
697 testvar = tvb_get_ntohl(tvb, commhdr+4);
698 if (testvar == 0x4c495020) {
699 proto_tree_add_item(ncp_tree, hf_lip_echo, tvb, commhdr+4, 13, FALSE);
703 /* otherwise fall through */
705 case NCP_POSITIVE_ACK: /* Positive Acknowledgement */
706 case NCP_SERVICE_REQUEST: /* Server NCP Request */
707 case NCP_SERVICE_REPLY: /* Server NCP Reply */
708 case NCP_WATCHDOG: /* Watchdog Packet */
709 case NCP_DEALLOCATE_SLOT: /* Deallocate Slot Request */
711 proto_tree_add_uint(ncp_tree, hf_ncp_seq, tvb, commhdr + 2, 1, header.sequence);
712 proto_tree_add_uint(ncp_tree, hf_ncp_connection,tvb, commhdr + 3, 3, nw_connection);
713 proto_tree_add_item(ncp_tree, hf_ncp_task, tvb, commhdr + 4, 1, FALSE);
718 * Process the packet body.
720 switch (header.type) {
722 case NCP_ALLOCATE_SLOT: /* Allocate Slot Request */
723 length_remaining = tvb_length_remaining(tvb, commhdr + 4);
724 if (length_remaining > 4) {
725 testvar = tvb_get_ntohl(tvb, commhdr+4);
726 if (testvar == 0x4c495020) {
727 proto_tree_add_text(ncp_tree, tvb, commhdr, -1,
733 case NCP_SERVICE_REQUEST: /* Server NCP Request */
734 case NCP_DEALLOCATE_SLOT: /* Deallocate Slot Request */
735 case NCP_BROADCAST_SLOT: /* Server Broadcast Packet */
736 next_tvb = tvb_new_subset(tvb, hdr_offset, -1, -1);
737 if (tvb_get_guint8(tvb, commhdr+6) == 0x68) {
738 subfunction = tvb_get_guint8(tvb, commhdr+7);
739 switch (subfunction) {
741 case 0x02: /* NDS Frag Packet to decode */
742 dissect_nds_request(next_tvb, pinfo,
743 nw_connection, header.sequence,
744 header.type, ncp_tree);
747 case 0x01: /* NDS Ping */
748 dissect_ping_req(next_tvb, pinfo,
749 nw_connection, header.sequence,
750 header.type, ncp_tree);
754 dissect_ncp_request(next_tvb, pinfo,
755 nw_connection, header.sequence,
756 header.type, ncp_tree);
760 dissect_ncp_request(next_tvb, pinfo, nw_connection,
761 header.sequence, header.type, ncp_tree);
765 case NCP_SERVICE_REPLY: /* Server NCP Reply */
766 next_tvb = tvb_new_subset(tvb, hdr_offset, -1, -1);
767 nds_defrag(next_tvb, pinfo, nw_connection, header.sequence,
768 header.type, ncp_tree);
771 case NCP_POSITIVE_ACK: /* Positive Acknowledgement */
773 * XXX - this used to call "nds_defrag()", which would
774 * clear out "frags". Was that the right thing to
777 next_tvb = tvb_new_subset(tvb, hdr_offset, -1, -1);
778 dissect_ncp_reply(next_tvb, pinfo, nw_connection,
779 header.sequence, header.type, ncp_tree);
782 case NCP_WATCHDOG: /* Watchdog Packet */
784 * XXX - should the completion code be interpreted as
785 * it is in "packet-ncp2222.inc"? If so, this
786 * packet should be handled by "dissect_ncp_reply()".
788 proto_tree_add_item(ncp_tree, hf_ncp_completion_code,
789 tvb, commhdr + 6, 1, TRUE);
790 proto_tree_add_item(ncp_tree, hf_ncp_connection_status,
791 tvb, commhdr + 7, 1, TRUE);
792 proto_tree_add_item(ncp_tree, hf_ncp_slot,
793 tvb, commhdr + 8, 1, TRUE);
794 proto_tree_add_item(ncp_tree, hf_ncp_control_code,
795 tvb, commhdr + 9, 1, TRUE);
797 * Display the rest of the packet as data.
799 if (tvb_offset_exists(tvb, commhdr + 10)) {
800 call_dissector(data_handle,
801 tvb_new_subset(tvb, commhdr + 10, -1, -1),
806 case NCP_BURST_MODE_XFER: /* Packet Burst Packet */
809 * System packet; show missing fragments if there
812 while (missing_fraglist_count != 0) {
813 proto_tree_add_item(ncp_tree, hf_ncp_missing_data_offset,
814 tvb, offset, 4, FALSE);
816 proto_tree_add_item(ncp_tree, hf_ncp_missing_data_count,
817 tvb, offset, 2, FALSE);
819 missing_fraglist_count--;
823 * XXX - do this by using -1 and -1 as the length
824 * arguments to "tvb_new_subset()" and then calling
825 * "tvb_set_reported_length()"? That'll throw an
826 * exception if "data_len" goes past the reported
827 * length of the packet, but that's arguably a
828 * feature in this case.
830 length_remaining = tvb_length_remaining(tvb, offset);
831 if (length_remaining > data_len)
832 length_remaining = data_len;
834 call_dissector(data_handle,
835 tvb_new_subset(tvb, offset,
836 length_remaining, data_len),
842 case NCP_LIP_ECHO: /* LIP Echo Packet */
843 proto_tree_add_text(ncp_tree, tvb, commhdr, -1,
849 proto_tree_add_text(ncp_tree, tvb, commhdr + 6, -1,
850 "%s packets not supported yet",
851 val_to_str(header.type, ncp_type_vals,
852 "Unknown type (0x%04x)"));
859 dissect_ncp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
861 dissect_ncp_common(tvb, pinfo, tree, FALSE);
865 get_ncp_pdu_len(tvbuff_t *tvb, int offset)
870 * Check the NCP-over-TCP header signature, to make sure it's there.
871 * If it's not there, we cannot trust the next 4 bytes to be a
872 * packet length+"has signature" flag, so we just say the length is
873 * "what remains in the packet".
875 /*if (tvb_get_guint8(tvb, offset)==0xff)
879 signature = tvb_get_ntohl(tvb, offset);
880 if (signature != NCPIP_RQST && signature != NCPIP_RPLY)
881 return tvb_length_remaining(tvb, offset);
884 * Get the length of the NCP-over-TCP packet. Strip off the "has
888 return tvb_get_ntohl(tvb, offset + 4) & 0x7fffffff;
892 dissect_ncp_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
894 dissect_ncp_common(tvb, pinfo, tree, TRUE);
898 dissect_ncp_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
900 tcp_dissect_pdus(tvb, pinfo, tree, ncp_desegment, 8, get_ncp_pdu_len,
901 dissect_ncp_tcp_pdu);
905 proto_register_ncp(void)
908 static hf_register_info hf[] = {
910 { "NCP over IP signature", "ncp.ip.signature",
911 FT_UINT32, BASE_HEX, VALS(ncp_ip_signature), 0x0,
914 { "NCP over IP length", "ncp.ip.length",
915 FT_UINT32, BASE_DEC, NULL, 0x0,
918 { "NCP over IP Version", "ncp.ip.version",
919 FT_UINT32, BASE_DEC, NULL, 0x0,
921 { &hf_ncp_ip_rplybufsize,
922 { "NCP over IP Reply Buffer Size", "ncp.ip.replybufsize",
923 FT_UINT32, BASE_DEC, NULL, 0x0,
925 { &hf_ncp_ip_packetsig,
926 { "NCP over IP Packet Signature", "ncp.ip.packetsig",
927 FT_BYTES, BASE_NONE, NULL, 0x0,
930 { "Type", "ncp.type",
931 FT_UINT16, BASE_HEX, VALS(ncp_type_vals), 0x0,
932 "NCP message type", HFILL }},
934 { "Sequence Number", "ncp.seq",
935 FT_UINT8, BASE_DEC, NULL, 0x0,
937 { &hf_ncp_connection,
938 { "Connection Number", "ncp.connection",
939 FT_UINT16, BASE_DEC, NULL, 0x0,
942 { "Task Number", "ncp.task",
943 FT_UINT8, BASE_DEC, NULL, 0x0,
945 { &hf_ncp_oplock_flag,
946 { "Oplock Flag", "ncp.oplock_flag",
947 FT_UINT8, BASE_HEX, NULL, 0x0,
949 { &hf_ncp_oplock_handle,
950 { "File Handle", "ncp.oplock_handle",
951 FT_UINT16, BASE_HEX, NULL, 0x0,
953 { &hf_ncp_stream_type,
954 { "Stream Type", "ncp.stream_type",
955 FT_UINT8, BASE_HEX, NULL, 0x0,
956 "Type of burst", HFILL }},
957 { &hf_ncp_system_flags,
958 { "System Flags", "ncp.system_flags",
959 FT_UINT8, BASE_HEX, NULL, 0x0,
961 { &hf_ncp_system_flags_abt,
962 { "ABT", "ncp.system_flags.abt",
963 FT_BOOLEAN, 8, NULL, ABT,
964 "Is this an abort request?", HFILL }},
965 { &hf_ncp_system_flags_eob,
966 { "EOB", "ncp.system_flags.eob",
967 FT_BOOLEAN, 8, NULL, EOB,
968 "Is this the last packet of the burst?", HFILL }},
969 { &hf_ncp_system_flags_sys,
970 { "SYS", "ncp.system_flags.sys",
971 FT_BOOLEAN, 8, NULL, SYS,
972 "Is this a system packet?", HFILL }},
973 { &hf_ncp_system_flags_bsy,
974 { "BSY", "ncp.system_flags.bsy",
975 FT_BOOLEAN, 8, NULL, BSY,
976 "Is the server busy?", HFILL }},
977 { &hf_ncp_system_flags_lst,
978 { "LST", "ncp.system_flags.lst",
979 FT_BOOLEAN, 8, NULL, LST,
980 "Return Fragment List?", HFILL }},
981 { &hf_ncp_src_connection,
982 { "Source Connection ID", "ncp.src_connection",
983 FT_UINT32, BASE_DEC, NULL, 0x0,
984 "The workstation's connection identification number", HFILL }},
985 { &hf_ncp_dst_connection,
986 { "Destination Connection ID", "ncp.dst_connection",
987 FT_UINT32, BASE_DEC, NULL, 0x0,
988 "The server's connection identification number", HFILL }},
989 { &hf_ncp_packet_seqno,
990 { "Packet Sequence Number", "ncp.packet_seqno",
991 FT_UINT32, BASE_DEC, NULL, 0x0,
992 "Sequence number of this packet in a burst", HFILL }},
993 { &hf_ncp_delay_time,
994 { "Delay Time", "ncp.delay_time", /* in 100 us increments */
995 FT_UINT32, BASE_DEC, NULL, 0x0,
996 "Delay time between consecutive packet sends (100 us increments)", HFILL }},
997 { &hf_ncp_burst_seqno,
998 { "Burst Sequence Number", "ncp.burst_seqno",
999 FT_UINT16, BASE_DEC, NULL, 0x0,
1000 "Sequence number of this packet in the burst", HFILL }},
1001 { &hf_ncp_ack_seqno,
1002 { "ACK Sequence Number", "ncp.ack_seqno",
1003 FT_UINT16, BASE_DEC, NULL, 0x0,
1004 "Next expected burst sequence number", HFILL }},
1005 { &hf_ncp_burst_len,
1006 { "Burst Length", "ncp.burst_len",
1007 FT_UINT32, BASE_DEC, NULL, 0x0,
1008 "Total length of data in this burst", HFILL }},
1009 { &hf_ncp_burst_offset,
1010 { "Burst Offset", "ncp.burst_offset",
1011 FT_UINT32, BASE_DEC, NULL, 0x0,
1012 "Offset of data in the burst", HFILL }},
1013 { &hf_ncp_data_offset,
1014 { "Data Offset", "ncp.data_offset",
1015 FT_UINT32, BASE_DEC, NULL, 0x0,
1016 "Offset of this packet", HFILL }},
1017 { &hf_ncp_data_bytes,
1018 { "Data Bytes", "ncp.data_bytes",
1019 FT_UINT16, BASE_DEC, NULL, 0x0,
1020 "Number of data bytes in this packet", HFILL }},
1021 { &hf_ncp_missing_fraglist_count,
1022 { "Missing Fragment List Count", "ncp.missing_fraglist_count",
1023 FT_UINT16, BASE_DEC, NULL, 0x0,
1024 "Number of missing fragments reported", HFILL }},
1025 { &hf_ncp_missing_data_offset,
1026 { "Missing Data Offset", "ncp.missing_data_offset",
1027 FT_UINT32, BASE_DEC, NULL, 0x0,
1028 "Offset of beginning of missing data", HFILL }},
1029 { &hf_ncp_missing_data_count,
1030 { "Missing Data Count", "ncp.missing_data_count",
1031 FT_UINT16, BASE_DEC, NULL, 0x0,
1032 "Number of bytes of missing data", HFILL }},
1033 { &hf_ncp_completion_code,
1034 { "Completion Code", "ncp.completion_code",
1035 FT_UINT8, BASE_DEC, NULL, 0x0,
1037 { &hf_ncp_connection_status,
1038 { "Connection Status", "ncp.connection_status",
1039 FT_UINT8, BASE_DEC, NULL, 0x0,
1042 { "Slot", "ncp.slot",
1043 FT_UINT8, BASE_DEC, NULL, 0x0,
1045 { &hf_ncp_control_code,
1046 { "Control Code", "ncp.control_code",
1047 FT_UINT8, BASE_DEC, NULL, 0x0,
1049 { &hf_ncp_fragment_handle,
1050 { "Fragment Handle", "ncp.fragger_hndl",
1051 FT_UINT16, BASE_HEX, NULL, 0x0,
1054 { "Large Internet Packet Echo", "ncp.lip_echo",
1055 FT_STRING, BASE_NONE, NULL, 0x0,
1057 { &hf_ncp_burst_command,
1058 { "Burst Command", "ncp.burst_command",
1059 FT_UINT32, BASE_HEX, VALS(burst_command), 0x0,
1060 "Packet Burst Command", HFILL }},
1061 { &hf_ncp_burst_file_handle,
1062 { "Burst File Handle", "ncp.file_handle",
1063 FT_UINT32, BASE_HEX, NULL, 0x0,
1064 "Packet Burst File Handle", HFILL }},
1065 { &hf_ncp_burst_reserved,
1066 { "Reserved", "ncp.burst_reserved",
1067 FT_BYTES, BASE_HEX, NULL, 0x0, "", HFILL }},
1070 static gint *ett[] = {
1072 &ett_ncp_system_flags,
1077 module_t *ncp_module;
1079 proto_ncp = proto_register_protocol("NetWare Core Protocol", "NCP", "ncp");
1080 proto_register_field_array(proto_ncp, hf, array_length(hf));
1081 proto_register_subtree_array(ett, array_length(ett));
1083 ncp_module = prefs_register_protocol(proto_ncp, NULL);
1084 prefs_register_obsolete_preference(ncp_module, "initial_hash_size");
1085 prefs_register_bool_preference(ncp_module, "desegment",
1086 "Reassemble NCP-over-TCP messages spanning multiple TCP segments",
1087 "Whether the NCP dissector should reassemble messages spanning multiple TCP segments."
1088 " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
1090 prefs_register_bool_preference(ncp_module, "defragment_nds",
1091 "Reassemble fragmented NDS messages spanning multiple packets",
1092 "Whether the NCP dissector should defragment NDS messages spanning multiple packets.",
1094 register_init_routine(&mncp_init_protocol);
1095 register_postseq_cleanup_routine(&mncp_postseq_cleanup);
1099 proto_reg_handoff_ncp(void)
1101 dissector_handle_t ncp_handle;
1102 dissector_handle_t ncp_tcp_handle;
1104 ncp_handle = create_dissector_handle(dissect_ncp, proto_ncp);
1105 ncp_tcp_handle = create_dissector_handle(dissect_ncp_tcp, proto_ncp);
1106 dissector_add("tcp.port", TCP_PORT_NCP, ncp_tcp_handle);
1107 dissector_add("udp.port", UDP_PORT_NCP, ncp_handle);
1108 dissector_add("ipx.packet_type", IPX_PACKET_TYPE_NCP, ncp_handle);
1109 dissector_add("ipx.socket", IPX_SOCKET_NCP, ncp_handle);
1111 data_handle = find_dissector("data");