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>
6 * $Id: packet-ncp.c,v 1.64 2002/05/25 12:44:06 guy Exp $
8 * Ethereal - Network traffic analyzer
9 * By Gerald Combs <gerald@ethereal.com>
10 * Copyright 2000 Gerald Combs
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
31 #ifdef HAVE_SYS_TYPES_H
32 # include <sys/types.h>
35 #ifdef HAVE_NETINET_IN_H
36 # include <netinet/in.h>
41 #include <epan/packet.h>
42 #include <epan/conversation.h>
44 #include "packet-ipx.h"
45 #include "packet-tcp.h"
46 #include "packet-ncp-int.h"
49 static int hf_ncp_ip_ver = -1;
50 static int hf_ncp_ip_length = -1;
51 static int hf_ncp_ip_rplybufsize = -1;
52 static int hf_ncp_ip_sig = -1;
53 static int hf_ncp_ip_packetsig = -1;
54 static int hf_ncp_type = -1;
55 static int hf_ncp_seq = -1;
56 static int hf_ncp_connection = -1;
57 static int hf_ncp_task = -1;
58 static int hf_ncp_stream_type = -1;
59 static int hf_ncp_system_flags = -1;
60 static int hf_ncp_system_flags_abt = -1;
61 static int hf_ncp_system_flags_eob = -1;
62 static int hf_ncp_system_flags_sys = -1;
63 static int hf_ncp_src_connection = -1;
64 static int hf_ncp_dst_connection = -1;
65 static int hf_ncp_packet_seqno = -1;
66 static int hf_ncp_delay_time = -1;
67 static int hf_ncp_burst_seqno = -1;
68 static int hf_ncp_ack_seqno = -1;
69 static int hf_ncp_burst_len = -1;
70 static int hf_ncp_data_offset = -1;
71 static int hf_ncp_data_bytes = -1;
72 static int hf_ncp_missing_fraglist_count = -1;
73 static int hf_ncp_missing_data_offset = -1;
74 static int hf_ncp_missing_data_count = -1;
75 static int hf_ncp_completion_code = -1;
76 static int hf_ncp_connection_status = -1;
77 static int hf_ncp_slot = -1;
78 static int hf_ncp_control_code = -1;
81 static gint ett_ncp_system_flags = -1;
83 /* desegmentation of NCP over TCP */
84 static gboolean ncp_desegment = TRUE;
86 static dissector_handle_t data_handle;
88 #define TCP_PORT_NCP 524
89 #define UDP_PORT_NCP 524
91 #define NCP_RQST_HDR_LENGTH 7
92 #define NCP_RPLY_HDR_LENGTH 8
95 gint ncp_equal (gconstpointer v, gconstpointer v2);
96 guint ncp_hash (gconstpointer v);
98 /* These are the header structures to handle NCP over IP */
99 #define NCPIP_RQST 0x446d6454 /* "DmdT" */
100 #define NCPIP_RPLY 0x744e6350 /* "tNcP" */
102 struct ncp_ip_header {
107 /* This header only appears on NCP over IP request packets */
108 struct ncp_ip_rqhdr {
113 static const value_string ncp_ip_signature[] = {
114 { NCPIP_RQST, "Demand Transport (Request)" },
115 { NCPIP_RPLY, "Transport is NCP (Reply)" },
119 /* The information in this module comes from:
120 NetWare LAN Analysis, Second Edition
121 Laura A. Chappell and Dan E. Hakes
122 (c) 1994 Novell, Inc.
123 Novell Press, San Jose.
126 And from the ncpfs source code by Volker Lendecke
129 Programmer's Guide to the NetWare Core Protocol
130 Steve Conner & Diane Conner
131 (c) 1996 by Steve Conner & Diane Conner
132 Published by Annabooks, San Diego, California
138 * Every NCP packet has this common header (except for burst packets).
140 struct ncp_common_header {
145 guint8 conn_high; /* type=0x5555 doesn't have this */
149 static value_string ncp_type_vals[] = {
150 { NCP_ALLOCATE_SLOT, "Create a service connection" },
151 { NCP_SERVICE_REQUEST, "Service request" },
152 { NCP_SERVICE_REPLY, "Service reply" },
153 { NCP_WATCHDOG, "Watchdog" },
154 { NCP_DEALLOCATE_SLOT, "Destroy service connection" },
155 { NCP_BURST_MODE_XFER, "Burst mode transfer" },
156 { NCP_POSITIVE_ACK, "Request being processed" },
162 * Burst packet system flags.
164 #define ABT 0x04 /* Abort request */
165 #define EOB 0x10 /* End of burst */
166 #define SYS 0x80 /* System packet */
169 dissect_ncp_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
172 proto_tree *ncp_tree = NULL;
174 struct ncp_ip_header ncpiph;
175 struct ncp_ip_rqhdr ncpiphrq;
176 gboolean is_signed = FALSE;
177 struct ncp_common_header header;
178 guint16 nw_connection;
180 char flags_str[1+3+1+3+1+3+1+1];
182 proto_tree *flags_tree = NULL;
183 guint16 data_len = 0;
184 guint16 missing_fraglist_count = 0;
188 gint length_remaining;
191 if (check_col(pinfo->cinfo, COL_PROTOCOL))
192 col_set_str(pinfo->cinfo, COL_PROTOCOL, "NCP");
193 if (check_col(pinfo->cinfo, COL_INFO))
194 col_clear(pinfo->cinfo, COL_INFO);
197 ncpiph.signature = tvb_get_ntohl(tvb, 0);
198 ncpiph.length = tvb_get_ntohl(tvb, 4);
200 if ( ncpiph.signature == NCPIP_RQST ) {
201 ncpiphrq.version = tvb_get_ntohl(tvb, hdr_offset);
203 ncpiphrq.rplybufsize = tvb_get_ntohl(tvb, hdr_offset);
206 if (ncpiph.length & 0x80000000) {
208 * This appears to indicate that this packet
209 * is signed; the signature is 8 bytes long.
211 * XXX - that bit does *not* appear to be set
212 * in signed replies, and we can't dissect the
213 * reply enough to find the matching request
214 * without knowing whether the reply is
217 * XXX - what about NCP-over-IPX signed
222 ncpiph.length &= 0x7fffffff;
226 /* Record the offset where the NCP common header starts */
227 commhdr = hdr_offset;
229 header.type = tvb_get_ntohs(tvb, commhdr);
230 header.sequence = tvb_get_guint8(tvb, commhdr+2);
231 header.conn_low = tvb_get_guint8(tvb, commhdr+3);
232 header.conn_high = tvb_get_guint8(tvb, commhdr+5);
234 if (check_col(pinfo->cinfo, COL_INFO)) {
235 col_add_fstr(pinfo->cinfo, COL_INFO,
237 val_to_str(header.type, ncp_type_vals, "Unknown type (0x%04x)"));
240 nw_connection = (header.conn_high << 16) + header.conn_low;
243 ti = proto_tree_add_item(tree, proto_ncp, tvb, 0, -1, FALSE);
244 ncp_tree = proto_item_add_subtree(ti, ett_ncp);
247 proto_tree_add_uint(ncp_tree, hf_ncp_ip_sig, tvb, 0, 4, ncpiph.signature);
248 proto_tree_add_uint(ncp_tree, hf_ncp_ip_length, tvb, 4, 4, ncpiph.length);
249 if (ncpiph.signature == NCPIP_RQST) {
250 proto_tree_add_uint(ncp_tree, hf_ncp_ip_ver, tvb, 8, 4, ncpiphrq.version);
251 proto_tree_add_uint(ncp_tree, hf_ncp_ip_rplybufsize, tvb, 12, 4, ncpiphrq.rplybufsize);
254 proto_tree_add_item(ncp_tree, hf_ncp_ip_packetsig, tvb, 16, 8, FALSE);
256 proto_tree_add_uint(ncp_tree, hf_ncp_type, tvb, commhdr + 0, 2, header.type);
261 * Process the packet-type-specific header.
263 switch (header.type) {
265 case NCP_ALLOCATE_SLOT: /* Allocate Slot Request */
266 case NCP_SERVICE_REQUEST: /* Server NCP Request */
267 case NCP_SERVICE_REPLY: /* Server NCP Reply */
268 case NCP_WATCHDOG: /* Watchdog Packet */
269 case NCP_DEALLOCATE_SLOT: /* Deallocate Slot Request */
270 case NCP_POSITIVE_ACK: /* Positive Acknowledgement */
272 proto_tree_add_uint(ncp_tree, hf_ncp_seq, tvb, commhdr + 2, 1, header.sequence);
273 proto_tree_add_uint(ncp_tree, hf_ncp_connection,tvb, commhdr + 3, 3, nw_connection);
274 proto_tree_add_item(ncp_tree, hf_ncp_task, tvb, commhdr + 4, 1, FALSE);
277 case NCP_BURST_MODE_XFER: /* Packet Burst Packet */
279 * XXX - we should keep track of whether there's a burst
280 * outstanding on a connection and, if not, treat the
281 * beginning of the data as a burst header.
283 * The burst header contains:
285 * 4 bytes of little-endian function number:
286 * 1 = read, 2 = write;
288 * 4 bytes of file handle;
292 * 4 bytes of big-endian file offset;
294 * 4 bytes of big-endian byte count.
296 * The data follows for a burst write operation.
298 * The first packet of a burst read reply contains:
300 * 4 bytes of little-endian result code:
306 * 4 bytes of returned byte count (big-endian?).
310 * Each burst of a write request is responded to with a
311 * burst packet with a 2-byte little-endian result code:
313 * 0: Write successful
316 flags = tvb_get_guint8(tvb, commhdr + 2);
317 strcpy(flags_str, "");
320 strcat(flags_str, sep);
321 strcat(flags_str, "ABT");
325 strcat(flags_str, sep);
326 strcat(flags_str, "EOB");
330 strcat(flags_str, sep);
331 strcat(flags_str, "SYS");
333 if (flags_str[0] != '\0')
334 strcat(flags_str, ")");
335 ti = proto_tree_add_uint_format(ncp_tree, hf_ncp_system_flags,
336 tvb, commhdr + 2, 1, flags, "Flags: 0x%04x%s", flags,
338 flags_tree = proto_item_add_subtree(ti, ett_ncp_system_flags);
339 proto_tree_add_item(flags_tree, hf_ncp_system_flags_abt,
340 tvb, commhdr + 2, 1, FALSE);
341 proto_tree_add_item(flags_tree, hf_ncp_system_flags_eob,
342 tvb, commhdr + 2, 1, FALSE);
343 proto_tree_add_item(flags_tree, hf_ncp_system_flags_sys,
344 tvb, commhdr + 2, 1, FALSE);
346 proto_tree_add_item(ncp_tree, hf_ncp_stream_type,
347 tvb, commhdr + 3, 1, FALSE);
348 proto_tree_add_item(ncp_tree, hf_ncp_src_connection,
349 tvb, commhdr + 4, 4, FALSE);
350 proto_tree_add_item(ncp_tree, hf_ncp_dst_connection,
351 tvb, commhdr + 8, 4, FALSE);
352 proto_tree_add_item(ncp_tree, hf_ncp_packet_seqno,
353 tvb, commhdr + 12, 4, FALSE);
354 proto_tree_add_item(ncp_tree, hf_ncp_delay_time,
355 tvb, commhdr + 16, 4, FALSE);
356 proto_tree_add_item(ncp_tree, hf_ncp_burst_seqno,
357 tvb, commhdr + 20, 2, FALSE);
358 proto_tree_add_item(ncp_tree, hf_ncp_ack_seqno,
359 tvb, commhdr + 22, 2, FALSE);
360 proto_tree_add_item(ncp_tree, hf_ncp_burst_len,
361 tvb, commhdr + 24, 4, FALSE);
362 proto_tree_add_item(ncp_tree, hf_ncp_data_offset,
363 tvb, commhdr + 28, 4, FALSE);
364 data_len = tvb_get_ntohs(tvb, commhdr + 32);
365 proto_tree_add_uint(ncp_tree, hf_ncp_data_bytes,
366 tvb, commhdr + 32, 2, data_len);
367 missing_fraglist_count = tvb_get_ntohs(tvb, commhdr + 34);
368 proto_tree_add_item(ncp_tree, hf_ncp_missing_fraglist_count,
369 tvb, commhdr + 34, 2, FALSE);
374 * Process the packet body.
376 switch (header.type) {
378 case NCP_ALLOCATE_SLOT: /* Allocate Slot Request */
379 case NCP_SERVICE_REQUEST: /* Server NCP Request */
380 case NCP_DEALLOCATE_SLOT: /* Deallocate Slot Request */
381 next_tvb = tvb_new_subset(tvb, hdr_offset, -1, -1);
382 dissect_ncp_request(next_tvb, pinfo, nw_connection,
383 header.sequence, header.type, ncp_tree);
386 case NCP_SERVICE_REPLY: /* Server NCP Reply */
387 case NCP_POSITIVE_ACK: /* Positive Acknowledgement */
388 next_tvb = tvb_new_subset(tvb, hdr_offset, -1, -1);
389 dissect_ncp_reply(next_tvb, pinfo, nw_connection,
390 header.sequence, header.type, ncp_tree);
393 case NCP_WATCHDOG: /* Watchdog Packet */
395 * XXX - should the completion code be interpreted as
396 * it is in "packet-ncp2222.inc"? If so, this
397 * packet should be handled by "dissect_ncp_reply()".
399 proto_tree_add_item(ncp_tree, hf_ncp_completion_code,
400 tvb, commhdr + 6, 1, TRUE);
401 proto_tree_add_item(ncp_tree, hf_ncp_connection_status,
402 tvb, commhdr + 7, 1, TRUE);
403 proto_tree_add_item(ncp_tree, hf_ncp_slot,
404 tvb, commhdr + 8, 1, TRUE);
405 proto_tree_add_item(ncp_tree, hf_ncp_control_code,
406 tvb, commhdr + 9, 1, TRUE);
408 * Display the rest of the packet as data.
410 if (tvb_offset_exists(tvb, commhdr + 10)) {
411 call_dissector(data_handle,
412 tvb_new_subset(tvb, commhdr + 10, -1, -1),
417 case NCP_BURST_MODE_XFER: /* Packet Burst Packet */
420 * System packet; show missing fragments if there
423 offset = commhdr + 36;
424 while (missing_fraglist_count != 0) {
425 proto_tree_add_item(ncp_tree, hf_ncp_missing_data_offset,
426 tvb, offset, 4, FALSE);
427 proto_tree_add_item(ncp_tree, hf_ncp_missing_data_count,
428 tvb, offset, 2, FALSE);
429 missing_fraglist_count--;
433 * XXX - do this by using -1 and -1 as the length
434 * arguments to "tvb_new_subset()" and then calling
435 * "tvb_set_reported_length()"? That'll throw an
436 * exception if "data_len" goes past the reported
437 * length of the packet, but that's arguably a
438 * feature in this case.
440 length_remaining = tvb_length_remaining(tvb, commhdr + 36);
441 if (length_remaining > data_len)
442 length_remaining = data_len;
444 call_dissector(data_handle,
445 tvb_new_subset(tvb, commhdr + 36,
446 length_remaining, data_len),
454 proto_tree_add_text(ncp_tree, tvb, commhdr + 6, -1,
455 "%s packets not supported yet",
456 val_to_str(header.type, ncp_type_vals,
457 "Unknown type (0x%04x)"));
464 dissect_ncp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
466 dissect_ncp_common(tvb, pinfo, tree, FALSE);
470 get_ncp_pdu_len(tvbuff_t *tvb, int offset)
475 * Check the NCP-over-TCP header signature, to make sure it's there.
476 * If it's not there, we cannot trust the next 4 bytes to be a
477 * packet length+"has signature" flag, so we just say the length is
478 * "what remains in the packet".
480 signature = tvb_get_ntohl(tvb, offset);
481 if (signature != NCPIP_RQST && signature != NCPIP_RPLY)
482 return tvb_length_remaining(tvb, offset);
485 * Get the length of the NCP-over-TCP packet. Strip off the "has
488 return tvb_get_ntohl(tvb, offset + 4) & 0x7fffffff;
492 dissect_ncp_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
494 dissect_ncp_common(tvb, pinfo, tree, TRUE);
498 dissect_ncp_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
500 tcp_dissect_pdus(tvb, pinfo, tree, ncp_desegment, 8, get_ncp_pdu_len,
501 dissect_ncp_tcp_pdu);
505 proto_register_ncp(void)
508 static hf_register_info hf[] = {
510 { "NCP over IP signature", "ncp.ip.signature",
511 FT_UINT32, BASE_HEX, VALS(ncp_ip_signature), 0x0,
514 { "NCP over IP length", "ncp.ip.length",
515 FT_UINT32, BASE_DEC, NULL, 0x0,
518 { "NCP over IP Version", "ncp.ip.version",
519 FT_UINT32, BASE_DEC, NULL, 0x0,
521 { &hf_ncp_ip_rplybufsize,
522 { "NCP over IP Reply Buffer Size", "ncp.ip.replybufsize",
523 FT_UINT32, BASE_DEC, NULL, 0x0,
525 { &hf_ncp_ip_packetsig,
526 { "NCP over IP Packet Signature", "ncp.ip.packetsig",
527 FT_BYTES, BASE_NONE, NULL, 0x0,
530 { "Type", "ncp.type",
531 FT_UINT16, BASE_HEX, VALS(ncp_type_vals), 0x0,
532 "NCP message type", HFILL }},
534 { "Sequence Number", "ncp.seq",
535 FT_UINT8, BASE_DEC, NULL, 0x0,
537 { &hf_ncp_connection,
538 { "Connection Number", "ncp.connection",
539 FT_UINT16, BASE_DEC, NULL, 0x0,
542 { "Task Number", "ncp.task",
543 FT_UINT8, BASE_DEC, NULL, 0x0,
545 { &hf_ncp_stream_type,
546 { "Stream Type", "ncp.stream_type",
547 FT_UINT8, BASE_HEX, NULL, 0x0,
548 "Type of burst", HFILL }},
549 { &hf_ncp_system_flags,
550 { "System Flags", "ncp.system_flags",
551 FT_UINT8, BASE_HEX, NULL, 0x0,
553 { &hf_ncp_system_flags_abt,
554 { "ABT", "ncp.system_flags.abt",
555 FT_BOOLEAN, 8, NULL, ABT,
556 "Is this an abort request?", HFILL }},
557 { &hf_ncp_system_flags_eob,
558 { "EOB", "ncp.system_flags.eob",
559 FT_BOOLEAN, 8, NULL, EOB,
560 "Is this the last packet of the burst?", HFILL }},
561 { &hf_ncp_system_flags_sys,
562 { "SYS", "ncp.system_flags.sys",
563 FT_BOOLEAN, 8, NULL, SYS,
564 "Is this a system packet?", HFILL }},
565 { &hf_ncp_src_connection,
566 { "Source Connection ID", "ncp.src_connection",
567 FT_UINT32, BASE_DEC, NULL, 0x0,
568 "The workstation's connection identification number", HFILL }},
569 { &hf_ncp_dst_connection,
570 { "Destination Connection ID", "ncp.dst_connection",
571 FT_UINT32, BASE_DEC, NULL, 0x0,
572 "The server's connection identification number", HFILL }},
573 { &hf_ncp_packet_seqno,
574 { "Packet Sequence Number", "ncp.packet_seqno",
575 FT_UINT32, BASE_DEC, NULL, 0x0,
576 "Sequence number of this packet in a burst", HFILL }},
577 { &hf_ncp_delay_time,
578 { "Delay Time", "ncp.delay_time", /* in 100 us increments */
579 FT_UINT32, BASE_DEC, NULL, 0x0,
580 "Delay time between consecutive packet sends (100 us increments)", HFILL }},
581 { &hf_ncp_burst_seqno,
582 { "Burst Sequence Number", "ncp.burst_seqno",
583 FT_UINT16, BASE_DEC, NULL, 0x0,
584 "Sequence number of this packet in the burst", HFILL }},
586 { "ACK Sequence Number", "ncp.ack_seqno",
587 FT_UINT16, BASE_DEC, NULL, 0x0,
588 "Next expected burst sequence number", HFILL }},
590 { "Burst Length", "ncp.burst_len",
591 FT_UINT32, BASE_DEC, NULL, 0x0,
592 "Total length of data in this burst", HFILL }},
593 { &hf_ncp_data_offset,
594 { "Data Offset", "ncp.data_offset",
595 FT_UINT32, BASE_DEC, NULL, 0x0,
596 "Offset of this packet in the burst", HFILL }},
597 { &hf_ncp_data_bytes,
598 { "Data Bytes", "ncp.data_bytes",
599 FT_UINT16, BASE_DEC, NULL, 0x0,
600 "Number of data bytes in this packet", HFILL }},
601 { &hf_ncp_missing_fraglist_count,
602 { "Missing Fragment List Count", "ncp.missing_fraglist_count",
603 FT_UINT16, BASE_DEC, NULL, 0x0,
604 "Number of missing fragments reported", HFILL }},
605 { &hf_ncp_missing_data_offset,
606 { "Missing Data Offset", "ncp.missing_data_offset",
607 FT_UINT32, BASE_DEC, NULL, 0x0,
608 "Offset of beginning of missing data", HFILL }},
609 { &hf_ncp_missing_data_count,
610 { "Missing Data Count", "ncp.missing_data_count",
611 FT_UINT16, BASE_DEC, NULL, 0x0,
612 "Number of bytes of missing data", HFILL }},
613 { &hf_ncp_completion_code,
614 { "Completion Code", "ncp.completion_code",
615 FT_UINT8, BASE_DEC, NULL, 0x0,
617 { &hf_ncp_connection_status,
618 { "Connection Status", "ncp.connection_status",
619 FT_UINT8, BASE_DEC, NULL, 0x0,
622 { "Slot", "ncp.slot",
623 FT_UINT8, BASE_DEC, NULL, 0x0,
625 { &hf_ncp_control_code,
626 { "Control Code", "ncp.control_code",
627 FT_UINT8, BASE_DEC, NULL, 0x0,
630 static gint *ett[] = {
632 &ett_ncp_system_flags,
634 module_t *ncp_module;
636 proto_ncp = proto_register_protocol("NetWare Core Protocol", "NCP", "ncp");
637 proto_register_field_array(proto_ncp, hf, array_length(hf));
638 proto_register_subtree_array(ett, array_length(ett));
640 ncp_module = prefs_register_protocol(proto_ncp, NULL);
641 prefs_register_obsolete_preference(ncp_module, "initial_hash_size");
642 prefs_register_bool_preference(ncp_module, "desegment",
643 "Desegment all NCP-over-TCP messages spanning multiple segments",
644 "Whether the NCP dissector should desegment all messages spanning multiple TCP segments",
649 proto_reg_handoff_ncp(void)
651 dissector_handle_t ncp_handle;
652 dissector_handle_t ncp_tcp_handle;
654 ncp_handle = create_dissector_handle(dissect_ncp, proto_ncp);
655 ncp_tcp_handle = create_dissector_handle(dissect_ncp_tcp, proto_ncp);
656 dissector_add("tcp.port", TCP_PORT_NCP, ncp_tcp_handle);
657 dissector_add("udp.port", UDP_PORT_NCP, ncp_handle);
658 dissector_add("ipx.packet_type", IPX_PACKET_TYPE_NCP, ncp_handle);
659 dissector_add("ipx.socket", IPX_SOCKET_NCP, ncp_handle);
661 data_handle = find_dissector("data");