/* packet-ncp.c * Routines for NetWare Core Protocol * Gilbert Ramirez * Modified to allow NCP over TCP/IP decodes by James Coe * * $Id: packet-ncp.c,v 1.40 2000/08/13 14:08:31 deniel Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs * 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. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_NETINET_IN_H # include #endif #include #include "packet.h" #include "conversation.h" #include "prefs.h" #include "packet-ipx.h" #include "packet-ncp-int.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_type = -1; static int hf_ncp_seq = -1; static int hf_ncp_connection = -1; static int hf_ncp_task = -1; static gint ett_ncp = -1; #define TCP_PORT_NCP 524 #define UDP_PORT_NCP 524 #define NCP_RQST_HDR_LENGTH 7 #define NCP_RPLY_HDR_LENGTH 8 /* Hash functions */ gint ncp_equal (gconstpointer v, gconstpointer v2); guint ncp_hash (gconstpointer v); static guint ncp_packet_init_count = 200; /* These are the header structures to handle NCP over IP */ #define NCPIP_RQST 0x446d6454 /* "DmdT" */ #define NCPIP_RPLY 0x744e6350 /* "tNcP" */ struct ncp_ip_header { guint32 signature; guint32 length; }; /* This header only appears on NCP over IP request packets */ struct ncp_ip_rqhdr { guint32 version; guint32 rplybufsize; }; static const value_string ncp_ip_signature[] = { { NCPIP_RQST, "Demand Transport (Request)" }, { NCPIP_RPLY, "Transport is NCP (Reply)" }, }; /* The information in this module comes from: NetWare LAN Analysis, Second Edition Laura A. Chappell and Dan E. Hakes (c) 1994 Novell, Inc. Novell Press, San Jose. ISBN: 0-7821-1362-1 And from the ncpfs source code by Volker Lendecke And: Programmer's Guide to the NetWare Core Protocol Steve Conner & Diane Conner (c) 1996 by Steve Conner & Diane Conner Published by Annabooks, San Diego, California ISBN: 0-929392-31-0 */ /* Every NCP packet has this common header */ struct ncp_common_header { guint16 type; guint8 sequence; guint8 conn_low; guint8 task; guint8 conn_high; /* type=0x5555 doesn't have this */ }; static value_string ncp_type_vals[] = { { 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 } }; /* 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. * * We construct a conversation specified by the client and server * addresses and the connection number; the key representing the unique * NCP request then is composed of the pointer to the conversation * structure, cast to a "guint" (which may throw away the upper 32 * bits of the pointer on a P64 platform, but the low-order 32 bits * are more likely to differ between conversations than the upper 32 bits), * and the sequence number. * * 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. */ typedef struct { conversation_t *conversation; guint8 nw_sequence; } ncp_request_key; typedef struct { guint16 ncp_type; const ncp_record *ncp_record; } ncp_request_val; static GHashTable *ncp_request_hash = NULL; static GMemChunk *ncp_request_keys = NULL; static GMemChunk *ncp_request_records = NULL; /* Hash Functions */ gint ncp_equal (gconstpointer v, gconstpointer v2) { ncp_request_key *val1 = (ncp_request_key*)v; ncp_request_key *val2 = (ncp_request_key*)v2; if (val1->conversation == val2->conversation && val1->nw_sequence == val2->nw_sequence ) { return 1; } return 0; } guint ncp_hash (gconstpointer v) { ncp_request_key *ncp_key = (ncp_request_key*)v; return GPOINTER_TO_UINT(ncp_key->conversation) + ncp_key->nw_sequence; } /* Initializes the hash table and the mem_chunk area each time a new * file is loaded or re-loaded in ethereal */ static void ncp_init_protocol(void) { if (ncp_request_hash) g_hash_table_destroy(ncp_request_hash); if (ncp_request_keys) g_mem_chunk_destroy(ncp_request_keys); if (ncp_request_records) g_mem_chunk_destroy(ncp_request_records); ncp_request_hash = g_hash_table_new(ncp_hash, ncp_equal); ncp_request_keys = g_mem_chunk_new("ncp_request_keys", sizeof(ncp_request_key), ncp_packet_init_count * sizeof(ncp_request_key), G_ALLOC_AND_FREE); ncp_request_records = g_mem_chunk_new("ncp_request_records", sizeof(ncp_request_val), ncp_packet_init_count * sizeof(ncp_request_val), G_ALLOC_AND_FREE); } void ncp_hash_insert(conversation_t *conversation, guint8 nw_sequence, guint16 ncp_type, const ncp_record *ncp_rec) { ncp_request_val *request_val; ncp_request_key *request_key; /* Now remember the request, so we can find it if we later a reply to it. */ request_key = g_mem_chunk_alloc(ncp_request_keys); request_key->conversation = conversation; request_key->nw_sequence = nw_sequence; request_val = g_mem_chunk_alloc(ncp_request_records); request_val->ncp_type = ncp_type; request_val->ncp_record = ncp_rec; g_hash_table_insert(ncp_request_hash, request_key, request_val); } /* Returns TRUE or FALSE. If TRUE, the record was found and * ncp_type and ncp_rec are set. */ gboolean ncp_hash_lookup(conversation_t *conversation, guint8 nw_sequence, guint16 *ncp_type, const ncp_record **ncp_rec) { ncp_request_val *request_val; ncp_request_key request_key; request_key.conversation = conversation; request_key.nw_sequence = nw_sequence; request_val = (ncp_request_val*) g_hash_table_lookup(ncp_request_hash, &request_key); if (request_val) { *ncp_type = request_val->ncp_type; *ncp_rec = request_val->ncp_record; return TRUE; } else { return FALSE; } } static void dissect_ncp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { proto_tree *ncp_tree = NULL; proto_item *ti; struct ncp_ip_header ncpiph; struct ncp_ip_rqhdr ncpiphrq; struct ncp_common_header header; guint16 nw_connection; int hdr_offset = 0; int commhdr; CHECK_DISPLAY_AS_DATA(proto_ncp, tvb, pinfo, tree); pinfo->current_proto = "NCP"; if (check_col(pinfo->fd, COL_PROTOCOL)) col_add_str(pinfo->fd, COL_PROTOCOL, "NCP"); if ( pi.ptype == PT_TCP || pi.ptype == PT_UDP ) { ncpiph.signature = tvb_get_ntohl(tvb, 0); ncpiph.length = tvb_get_ntohl(tvb, 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; }; }; /* Record the offset where the NCP common header starts */ commhdr = hdr_offset; 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); nw_connection = (header.conn_high << 16) + header.conn_low; if (tree) { ti = proto_tree_add_item(tree, proto_ncp, tvb, 0, tvb_length(tvb), FALSE); ncp_tree = proto_item_add_subtree(ti, ett_ncp); if ( pi.ptype == PT_TCP || pi.ptype == PT_UDP ) { 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); }; }; proto_tree_add_uint(ncp_tree, hf_ncp_type, tvb, commhdr + 0, 2, header.type); 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); } if (header.type == 0x1111 || header.type == 0x2222) { dissect_ncp_request(tvb, pinfo, nw_connection, header.sequence, header.type, ncp_tree, tree); } else if (header.type == 0x3333) { dissect_ncp_reply(tvb, pinfo, nw_connection, header.sequence, ncp_tree, tree); } else if ( header.type == 0x5555 || header.type == 0x7777 || header.type == 0x9999 ) { if (check_col(pinfo->fd, COL_INFO)) { col_add_fstr(pinfo->fd, COL_INFO, "Type 0x%04x", header.type); } if (tree) { proto_tree_add_text(ncp_tree, tvb, commhdr + 0, 2, "Type 0x%04x not supported yet", header.type); } return; } else { /* The value_string for hf_ncp_type already indicates that this type is unknown. * Just return and do no more parsing. */ return; } } void proto_register_ncp(void) { 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, "" }}, { &hf_ncp_ip_length, { "NCP over IP length", "ncp.ip.length", FT_UINT32, BASE_HEX, NULL, 0x0, "" }}, { &hf_ncp_ip_ver, { "NCP over IP Version", "ncp.ip.version", FT_UINT32, BASE_DEC, NULL, 0x0, "" }}, { &hf_ncp_ip_rplybufsize, { "NCP over IP Reply Buffer Size", "ncp.ip.replybufsize", FT_UINT32, BASE_DEC, NULL, 0x0, "" }}, { &hf_ncp_type, { "Type", "ncp.type", FT_UINT16, BASE_HEX, VALS(ncp_type_vals), 0x0, "NCP message type" }}, { &hf_ncp_seq, { "Sequence Number", "ncp.seq", FT_UINT8, BASE_DEC, NULL, 0x0, "" }}, { &hf_ncp_connection, { "Connection Number", "ncp.connection", FT_UINT16, BASE_DEC, NULL, 0x0, "" }}, { &hf_ncp_task, { "Task Number", "ncp.task", FT_UINT8, BASE_DEC, NULL, 0x0, "" }} }; static gint *ett[] = { &ett_ncp, }; module_t *ncp_module; proto_ncp = proto_register_protocol("NetWare Core Protocol", "ncp"); proto_register_field_array(proto_ncp, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); register_init_routine(&ncp_init_protocol); /* Register a configuration option for initial size of NCP hash */ ncp_module = prefs_register_module("ncp", "NCP", NULL); prefs_register_uint_preference(ncp_module, "initial_hash_size", "Initial Hash Size", "Number of entries initially allocated for NCP hash", 10, &ncp_packet_init_count); } void proto_reg_handoff_ncp(void) { dissector_add("tcp.port", TCP_PORT_NCP, dissect_ncp); dissector_add("udp.port", UDP_PORT_NCP, dissect_ncp); dissector_add("ipx.packet_type", IPX_PACKET_TYPE_NCP, dissect_ncp); dissector_add("ipx.socket", IPX_SOCKET_NCP, dissect_ncp); }