wireshark/packet-ipx.c

790 lines
21 KiB
C
Raw Normal View History

/* packet-ipx.c
* Routines for NetWare's IPX
* Gilbert Ramirez <gram@verdict.uthscsa.edu>
*
Generalize the "ip_src" and "ip_dst" members of the "packet_info" structure to "dl_src"/"dl_dst", "net_src"/"net_dst", and "src"/"dst" addresses, where an address is an address type, an address length in bytes, and a pointer to that many bytes. "dl_{src,dst}" are the link-layer source/destination; "net_{src,dst}" are the network-layer source/destination; "{src,dst}" are the source/destination from the highest of those two layers that we have in the packet. Add a port type to "packet_info" as well, specifying whether it's a TCP or UDP port. Don't set the address and port columns in the dissector functions; just set the address and port members of the "packet_info" structure. Set the columns in "fill_in_columns()"; this means that if we're showing COL_{DEF,RES,UNRES}_SRC" or "COL_{DEF,RES,UNRES}_DST", we only generate the string from "src" or "dst", we don't generate a string for the link-layer address and then overwrite it with a string for the network-layer address (generating those strings costs CPU). Add support for "conversations", where a "conversation" is (at present) a source and destination address and a source and destination port. (In the future, we may support "conversations" above the transport layer, e.g. a TFTP conversation, where the first packet goes from the client to the TFTP server port, but the reply comes back from a different port, and all subsequent packets go between the client address/port and the server address/new port, or an NFS conversation, which might include lock manager, status monitor, and mount packets, as well as NFS packets.) Currently, all we support is a call that takes the source and destination address/port pairs, looks them up in a hash table, and: if nothing is found, creates a new entry in the hash table, and assigns it a unique 32-bit conversation ID, and returns that conversation ID; if an entry is found, returns its conversation ID. Use that in the SMB and AFS code to keep track of individual SMB or AFS conversations. We need to match up requests and replies, as, for certain replies, the operation code for the request to which it's a reply doesn't show up in the reply - you have to find the request with a matching transaction ID. Transaction IDs are per-conversation, so the hash table for requests should include a conversation ID and transaction ID as the key. This allows SMB and AFS decoders to handle IPv4 or IPv6 addresses transparently (and should allow the SMB decoder to handle NetBIOS atop other protocols as well, if the source and destination address and port values in the "packet_info" structure are set appropriately). In the "Follow TCP Connection" code, check to make sure that the addresses are IPv4 addressses; ultimately, that code should be changed to use the conversation code instead, which will let it handle IPv6 transparently. svn path=/trunk/; revision=909
1999-10-22 07:18:23 +00:00
* $Id: packet-ipx.c,v 1.30 1999/10/22 07:17:34 guy Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@unicom.net>
* Copyright 1998 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 <sys/types.h>
#endif
#include <stdio.h>
#include <glib.h>
#include "packet.h"
#include "packet-ipx.h"
#include "packet-ncp.h"
/* The information in this module (IPX, SPX, NCP) 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
*/
static int proto_ipx = -1;
static int hf_ipx_checksum = -1;
static int hf_ipx_len = -1;
static int hf_ipx_hops = -1;
static int hf_ipx_packet_type = -1;
static int hf_ipx_dnet = -1;
static int hf_ipx_dnode = -1;
static int hf_ipx_dsocket = -1;
static int hf_ipx_snet = -1;
static int hf_ipx_snode = -1;
static int hf_ipx_ssocket = -1;
static int proto_spx = -1;
static int hf_spx_connection_control = -1;
static int hf_spx_datastream_type = -1;
static int hf_spx_src_id = -1;
static int hf_spx_dst_id = -1;
static int hf_spx_seq_nr = -1;
static int hf_spx_ack_nr = -1;
static int hf_spx_all_nr = -1;
static int proto_ipxrip = -1;
static int hf_ipxrip_request = -1;
static int hf_ipxrip_response = -1;
static int proto_sap = -1;
static int hf_sap_request = -1;
static int hf_sap_response = -1;
static void
dissect_spx(const u_char *pd, int offset, frame_data *fd, proto_tree *tree);
static void
dissect_ipxrip(const u_char *pd, int offset, frame_data *fd, proto_tree *tree);
static void
dissect_sap(const u_char *pd, int offset, frame_data *fd, proto_tree *tree);
typedef void (dissect_func_t)(const u_char *, int, frame_data *, proto_tree *);
struct port_info {
guint16 port;
dissect_func_t *func;
char *text;
};
struct conn_info {
guint8 ctrl;
char *text;
};
struct server_info {
guint16 type;
char *text;
};
/* ================================================================= */
/* IPX */
/* ================================================================= */
#define IPX_SOCKET_NCP 0x0451
#define IPX_SOCKET_SAP 0x0452
#define IPX_SOCKET_IPXRIP 0x0453
#define IPX_SOCKET_NETBIOS 0x0455
#define IPX_SOCKET_DIAGNOSTIC 0x0456
#define IPX_SOCKET_SERIALIZATION 0x0457
#define IPX_SOCKET_NWLINK_SMB_NAMEQUERY 0x0551
#define IPX_SOCKET_NWLINK_SMB_DGRAM 0x0553
#define IPX_SOCKET_ATTACHMATE_GW 0x055d
#define IPX_SOCKET_IPX_MESSAGE 0x4001
static struct port_info ports[] = {
{ IPX_SOCKET_NCP, dissect_ncp,
"NCP" },
{ IPX_SOCKET_SAP, dissect_sap,
"SAP" },
{ IPX_SOCKET_IPXRIP, dissect_ipxrip,
"RIP" },
{ IPX_SOCKET_NETBIOS, NULL,
"NetBIOS" },
{ IPX_SOCKET_DIAGNOSTIC, NULL,
"Diagnostic" },
{ IPX_SOCKET_SERIALIZATION, NULL,
"Serialization" },
{ IPX_SOCKET_NWLINK_SMB_NAMEQUERY, NULL,
"NWLink SMB Name Query" },
{ IPX_SOCKET_NWLINK_SMB_DGRAM, dissect_nwlink_dg,
"NWLink SMB Datagram" },
{ IPX_SOCKET_ATTACHMATE_GW, NULL,
"Attachmate Gateway" },
{ IPX_SOCKET_IPX_MESSAGE, NULL,
"IPX Message" },
{ 0x0000, NULL,
NULL }
};
static char*
port_text(guint16 port) {
int i=0;
while (ports[i].text != NULL) {
if (ports[i].port == port) {
return ports[i].text;
}
i++;
}
return "Unknown";
}
static dissect_func_t*
port_func(guint16 port) {
int i=0;
while (ports[i].text != NULL) {
if (ports[i].port == port) {
return ports[i].func;
}
i++;
}
return NULL;
}
#define IPX_PACKET_TYPE_IPX 0
#define IPX_PACKET_TYPE_IPX_4 4
#define IPX_PACKET_TYPE_SPX 5
#define IPX_PACKET_TYPE_NCP 17
#define IPX_PACKET_TYPE_WANBCAST 20
static const value_string ipx_packet_type_vals[] = {
{ IPX_PACKET_TYPE_IPX, "IPX" },
{ IPX_PACKET_TYPE_IPX_4, "IPX" },
/* NetMon calls it "IPX" */
{ IPX_PACKET_TYPE_SPX, "SPX" },
{ 16, "Experimental Protocol" },
{ IPX_PACKET_TYPE_NCP, "NCP" },
{ 18, "Experimental Protocol" },
{ 19, "Experimental Protocol" },
{ IPX_PACKET_TYPE_WANBCAST, "NetBIOS Broadcast" },
/* NetMon calls it "WAN Broadcast" */
{ 21, "Experimental Protocol" },
{ 22, "Experimental Protocol" },
{ 23, "Experimental Protocol" },
{ 24, "Experimental Protocol" },
{ 25, "Experimental Protocol" },
{ 26, "Experimental Protocol" },
{ 27, "Experimental Protocol" },
{ 28, "Experimental Protocol" },
{ 29, "Experimental Protocol" },
{ 30, "Experimental Protocol" },
{ 31, "Experimental Protocol" },
{ 0, NULL }
};
gchar*
ipxnet_to_string(const guint8 *ad)
{
static gchar str[3][12];
static gchar *cur;
if (cur == &str[0][0]) {
cur = &str[1][0];
} else if (cur == &str[1][0]) {
cur = &str[2][0];
} else {
cur = &str[0][0];
}
sprintf(cur, "%02X %02X %02X %02X", ad[0], ad[1], ad[2], ad[3]);
return cur;
}
gchar*
ipx_addr_to_str(guint32 net, const guint8 *ad)
{
static gchar str[3][22];
static gchar *cur;
if (cur == &str[0][0]) {
cur = &str[1][0];
} else if (cur == &str[1][0]) {
cur = &str[2][0];
} else {
cur = &str[0][0];
}
sprintf(cur, "%X.%02x%02x%02x%02x%02x%02x", net,
ad[0], ad[1], ad[2], ad[3], ad[4], ad[5]);
return cur;
}
void
dissect_ipx(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
proto_tree *ipx_tree;
proto_item *ti;
guint8 ipx_type, ipx_hops;
guint16 ipx_checksum, ipx_length;
int len;
guint8 *ipx_snode, *ipx_dnode, *ipx_snet, *ipx_dnet;
gchar *str_dnet, *str_snet;
guint16 ipx_dsocket, ipx_ssocket;
dissect_func_t *dissect;
guint32 ipx_dnet_val, ipx_snet_val;
/* Calculate here for use in pinfo and in tree */
ipx_dnet = (guint8*)&pd[offset+6];
ipx_snet = (guint8*)&pd[offset+18];
str_dnet = ipxnet_to_string(ipx_dnet);
str_snet = ipxnet_to_string(ipx_snet);
ipx_dnet_val = pntohl(ipx_dnet);
ipx_snet_val = pntohl(ipx_snet);
ipx_dsocket = pntohs(&pd[offset+16]);
ipx_ssocket = pntohs(&pd[offset+28]);
ipx_dnode = (guint8*)&pd[offset+10];
ipx_snode = (guint8*)&pd[offset+22];
ipx_type = pd[offset+5];
ipx_length = pntohs(&pd[offset+2]);
/* Length of IPX datagram plus headers above it. */
Generalize the "ip_src" and "ip_dst" members of the "packet_info" structure to "dl_src"/"dl_dst", "net_src"/"net_dst", and "src"/"dst" addresses, where an address is an address type, an address length in bytes, and a pointer to that many bytes. "dl_{src,dst}" are the link-layer source/destination; "net_{src,dst}" are the network-layer source/destination; "{src,dst}" are the source/destination from the highest of those two layers that we have in the packet. Add a port type to "packet_info" as well, specifying whether it's a TCP or UDP port. Don't set the address and port columns in the dissector functions; just set the address and port members of the "packet_info" structure. Set the columns in "fill_in_columns()"; this means that if we're showing COL_{DEF,RES,UNRES}_SRC" or "COL_{DEF,RES,UNRES}_DST", we only generate the string from "src" or "dst", we don't generate a string for the link-layer address and then overwrite it with a string for the network-layer address (generating those strings costs CPU). Add support for "conversations", where a "conversation" is (at present) a source and destination address and a source and destination port. (In the future, we may support "conversations" above the transport layer, e.g. a TFTP conversation, where the first packet goes from the client to the TFTP server port, but the reply comes back from a different port, and all subsequent packets go between the client address/port and the server address/new port, or an NFS conversation, which might include lock manager, status monitor, and mount packets, as well as NFS packets.) Currently, all we support is a call that takes the source and destination address/port pairs, looks them up in a hash table, and: if nothing is found, creates a new entry in the hash table, and assigns it a unique 32-bit conversation ID, and returns that conversation ID; if an entry is found, returns its conversation ID. Use that in the SMB and AFS code to keep track of individual SMB or AFS conversations. We need to match up requests and replies, as, for certain replies, the operation code for the request to which it's a reply doesn't show up in the reply - you have to find the request with a matching transaction ID. Transaction IDs are per-conversation, so the hash table for requests should include a conversation ID and transaction ID as the key. This allows SMB and AFS decoders to handle IPv4 or IPv6 addresses transparently (and should allow the SMB decoder to handle NetBIOS atop other protocols as well, if the source and destination address and port values in the "packet_info" structure are set appropriately). In the "Follow TCP Connection" code, check to make sure that the addresses are IPv4 addressses; ultimately, that code should be changed to use the conversation code instead, which will let it handle IPv6 transparently. svn path=/trunk/; revision=909
1999-10-22 07:18:23 +00:00
len = ipx_length + offset;
/* Set the payload and captured-payload lengths to the minima of
(the IPX length plus the length of the headers above it) and
the frame lengths. */
if (pi.len > len)
pi.len = len;
if (pi.captured_len > len)
pi.captured_len = len;
Generalize the "ip_src" and "ip_dst" members of the "packet_info" structure to "dl_src"/"dl_dst", "net_src"/"net_dst", and "src"/"dst" addresses, where an address is an address type, an address length in bytes, and a pointer to that many bytes. "dl_{src,dst}" are the link-layer source/destination; "net_{src,dst}" are the network-layer source/destination; "{src,dst}" are the source/destination from the highest of those two layers that we have in the packet. Add a port type to "packet_info" as well, specifying whether it's a TCP or UDP port. Don't set the address and port columns in the dissector functions; just set the address and port members of the "packet_info" structure. Set the columns in "fill_in_columns()"; this means that if we're showing COL_{DEF,RES,UNRES}_SRC" or "COL_{DEF,RES,UNRES}_DST", we only generate the string from "src" or "dst", we don't generate a string for the link-layer address and then overwrite it with a string for the network-layer address (generating those strings costs CPU). Add support for "conversations", where a "conversation" is (at present) a source and destination address and a source and destination port. (In the future, we may support "conversations" above the transport layer, e.g. a TFTP conversation, where the first packet goes from the client to the TFTP server port, but the reply comes back from a different port, and all subsequent packets go between the client address/port and the server address/new port, or an NFS conversation, which might include lock manager, status monitor, and mount packets, as well as NFS packets.) Currently, all we support is a call that takes the source and destination address/port pairs, looks them up in a hash table, and: if nothing is found, creates a new entry in the hash table, and assigns it a unique 32-bit conversation ID, and returns that conversation ID; if an entry is found, returns its conversation ID. Use that in the SMB and AFS code to keep track of individual SMB or AFS conversations. We need to match up requests and replies, as, for certain replies, the operation code for the request to which it's a reply doesn't show up in the reply - you have to find the request with a matching transaction ID. Transaction IDs are per-conversation, so the hash table for requests should include a conversation ID and transaction ID as the key. This allows SMB and AFS decoders to handle IPv4 or IPv6 addresses transparently (and should allow the SMB decoder to handle NetBIOS atop other protocols as well, if the source and destination address and port values in the "packet_info" structure are set appropriately). In the "Follow TCP Connection" code, check to make sure that the addresses are IPv4 addressses; ultimately, that code should be changed to use the conversation code instead, which will let it handle IPv6 transparently. svn path=/trunk/; revision=909
1999-10-22 07:18:23 +00:00
SET_ADDRESS(&pi.net_src, AT_IPX, 10, &pd[offset+18]);
SET_ADDRESS(&pi.src, AT_IPX, 10, &pd[offset+18]);
SET_ADDRESS(&pi.net_dst, AT_IPX, 10, &pd[offset+6]);
SET_ADDRESS(&pi.dst, AT_IPX, 10, &pd[offset+6]);
if (check_col(fd, COL_PROTOCOL))
col_add_str(fd, COL_PROTOCOL, "IPX");
if (check_col(fd, COL_INFO))
col_add_fstr(fd, COL_INFO, "%s (0x%04X)", port_text(ipx_dsocket),
ipx_dsocket);
if (tree) {
ipx_checksum = pntohs(&pd[offset]);
ipx_hops = pd[offset+4];
ti = proto_tree_add_item(tree, proto_ipx, offset, 30, NULL);
ipx_tree = proto_item_add_subtree(ti, ETT_IPX);
proto_tree_add_item(ipx_tree, hf_ipx_checksum, offset, 2, ipx_checksum);
proto_tree_add_item_format(ipx_tree, hf_ipx_len, offset+2, 2, ipx_length,
"Length: %d bytes", ipx_length);
proto_tree_add_item_format(ipx_tree, hf_ipx_hops, offset+4, 1, ipx_hops,
"Transport Control: %d hops", ipx_hops);
proto_tree_add_item(ipx_tree, hf_ipx_packet_type, offset+5, 1, ipx_type);
proto_tree_add_item(ipx_tree, hf_ipx_dnet, offset+6, 4, ipx_dnet_val);
proto_tree_add_item(ipx_tree, hf_ipx_dnode, offset+10, 6, ipx_dnode);
proto_tree_add_item_format(ipx_tree, hf_ipx_dsocket, offset+16, 2,
ipx_dsocket, "Destination Socket: %s (0x%04X)",
port_text(ipx_dsocket), ipx_dsocket);
proto_tree_add_item(ipx_tree, hf_ipx_snet, offset+18, 4, ipx_snet_val);
proto_tree_add_item(ipx_tree, hf_ipx_snode, offset+22, 6, ipx_snode);
proto_tree_add_item_format(ipx_tree, hf_ipx_ssocket, offset+28, 2,
ipx_ssocket, "Source Socket: %s (0x%04X)", port_text(ipx_ssocket),
ipx_ssocket);
}
offset += 30;
switch (ipx_type) {
case IPX_PACKET_TYPE_SPX:
dissect_spx(pd, offset, fd, tree);
break;
case IPX_PACKET_TYPE_NCP:
/* Is the destination node 00:00:00:00:00:01 ? */
if (pntohl(ipx_dnode) == 0 && pntohs(ipx_dnode + 4) == 1)
nw_server_address = pntohl(ipx_dnet);
/* Is the source node 00:00:00:00:00:01 ? */
else if (pntohl(ipx_snode) == 0 && pntohs(ipx_snode + 4) == 1)
nw_server_address = pntohl(ipx_snet);
else
nw_server_address = 0;
dissect_ncp(pd, offset, fd, tree);
break;
case IPX_PACKET_TYPE_WANBCAST:
case IPX_PACKET_TYPE_IPX_4:
if (ipx_dsocket == IPX_SOCKET_NETBIOS) {
dissect_nbipx(pd, offset, fd, tree);
break;
}
/* else fall through */
case 0: /* IPX, fall through to default */
/* XXX - should type 0's be dissected as NBIPX
if they're aimed at the NetBIOS socket? */
default:
dissect = port_func(ipx_dsocket);
if (dissect) {
dissect(pd, offset, fd, tree);
}
else {
dissect = port_func(ipx_ssocket);
if (dissect) {
dissect(pd, offset, fd, tree);
}
else {
dissect_data(pd, offset, fd, tree);
}
}
break;
}
}
/* ================================================================= */
/* SPX */
/* ================================================================= */
static char*
spx_conn_ctrl(u_char ctrl)
{
int i=0;
static struct conn_info conns[] = {
{ 0x10, "End-of-Message" },
{ 0x20, "Attention" },
{ 0x40, "Acknowledgment Required"},
{ 0x80, "System Packet"},
{ 0x00, NULL }
};
while (conns[i].text != NULL) {
if (conns[i].ctrl == ctrl) {
return conns[i].text;
}
i++;
}
return "Unknown";
}
static char*
spx_datastream(u_char type)
{
switch (type) {
case 0xfe:
return "End-of-Connection";
case 0xff:
return "End-of-Connection Acknowledgment";
default:
return "Client-Defined";
}
}
static void
dissect_spx(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
proto_tree *spx_tree;
proto_item *ti;
if (check_col(fd, COL_PROTOCOL))
col_add_str(fd, COL_PROTOCOL, "SPX");
if (check_col(fd, COL_INFO))
col_add_str(fd, COL_INFO, "SPX");
if (tree) {
ti = proto_tree_add_item(tree, proto_spx, offset, 12, NULL);
spx_tree = proto_item_add_subtree(ti, ETT_SPX);
proto_tree_add_item_format(spx_tree, hf_spx_connection_control,
offset, 1,
pd[offset],
"Connection Control: %s (0x%02X)",
spx_conn_ctrl(pd[offset]),
pd[offset]);
proto_tree_add_item_format(spx_tree, hf_spx_datastream_type,
offset+1, 1,
pd[offset+1],
"Datastream Type: %s (0x%02X)",
spx_datastream(pd[offset+1]),
pd[offset+1]);
proto_tree_add_item(spx_tree, hf_spx_src_id,
offset+2, 2,
pntohs( &pd[offset+2] ));
proto_tree_add_item(spx_tree, hf_spx_dst_id,
offset+4, 2,
pntohs( &pd[offset+4] ));
proto_tree_add_item(spx_tree, hf_spx_seq_nr,
offset+6, 2,
pntohs( &pd[offset+6] ) );
proto_tree_add_item(spx_tree, hf_spx_ack_nr,
offset+8, 2,
pntohs( &pd[offset+8] ) );
proto_tree_add_item(spx_tree, hf_spx_all_nr,
offset+10, 2,
pntohs( &pd[offset+10] ) );
offset += 12;
dissect_data(pd, offset, fd, tree);
}
}
/* ================================================================= */
/* IPX RIP */
/* ================================================================= */
static void
dissect_ipxrip(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
proto_tree *rip_tree;
proto_item *ti;
guint16 operation;
struct ipx_rt_def route;
int cursor;
char *rip_type[2] = { "Request", "Response" };
operation = pntohs(&pd[offset]) - 1;
if (check_col(fd, COL_PROTOCOL))
col_add_str(fd, COL_PROTOCOL, "IPX RIP");
if (check_col(fd, COL_PROTOCOL)) {
if (operation < 2) {
col_add_str(fd, COL_INFO, rip_type[operation]);
}
else {
col_add_str(fd, COL_INFO, "Unknown Packet Type");
}
}
if (tree) {
ti = proto_tree_add_item(tree, proto_ipxrip, offset, END_OF_FRAME, NULL);
rip_tree = proto_item_add_subtree(ti, ETT_IPXRIP);
if (operation < 2) {
proto_tree_add_text(rip_tree, offset, 2,
"RIP packet type: %s", rip_type[operation]);
if (operation == 0) {
proto_tree_add_item_hidden(rip_tree,
hf_ipxrip_request,
offset, 2, 1);
} else {
proto_tree_add_item_hidden(rip_tree,
hf_ipxrip_response,
offset, 2, 1);
}
}
else {
proto_tree_add_text(rip_tree, offset, 2, "Unknown RIP packet type");
}
for (cursor = offset + 2; cursor < fd->cap_len; cursor += 8) {
memcpy(&route.network, &pd[cursor], 4);
route.hops = pntohs(&pd[cursor+4]);
route.ticks = pntohs(&pd[cursor+6]);
if (operation == IPX_RIP_REQUEST - 1) {
proto_tree_add_text(rip_tree, cursor, 8,
"Route Vector: %s, %d hop%s, %d tick%s",
ipxnet_to_string((guint8*)&route.network),
route.hops, route.hops == 1 ? "" : "s",
route.ticks, route.ticks == 1 ? "" : "s");
}
else {
proto_tree_add_text(rip_tree, cursor, 8,
"Route Vector: %s, %d hop%s, %d tick%s (%d ms)",
ipxnet_to_string((guint8*)&route.network),
route.hops, route.hops == 1 ? "" : "s",
route.ticks, route.ticks == 1 ? "" : "s",
route.ticks * 1000 / 18);
}
}
}
}
/* ================================================================= */
/* SAP */
/* ================================================================= */
static char*
server_type(guint16 type)
{
int i=0;
/* some of these are from ncpfs, others are from the book */
static struct server_info servers[] = {
{ 0x0001, "User" },
{ 0x0002, "User Group" },
{ 0x0003, "Print Queue" },
{ 0x0004, "File server" },
{ 0x0005, "Job server" },
{ 0x0007, "Print server" },
{ 0x0008, "Archive server" },
{ 0x0009, "Archive server" },
{ 0x000a, "Job queue" },
{ 0x000b, "Administration" },
{ 0x0021, "NAS SNA gateway" },
{ 0x0024, "Remote bridge" },
{ 0x0026, "Bridge server" },
{ 0x0027, "TCP/IP gateway" },
{ 0x002d, "Time Synchronization VAP" },
{ 0x002e, "Archive Server Dynamic SAP" },
{ 0x0047, "Advertising print server" },
{ 0x004b, "Btrieve VAP 5.0" },
{ 0x004c, "SQL VAP" },
{ 0x0050, "Btrieve VAP" },
{ 0x0053, "Print Queue VAP" },
{ 0x007a, "TES NetWare for VMS" },
{ 0x0098, "NetWare access server" },
{ 0x009a, "Named Pipes server" },
{ 0x009e, "Portable NetWare Unix" },
{ 0x0107, "NetWare 386" },
{ 0x0111, "Test server" },
{ 0x0133, "NetWare Name Service" },
{ 0x0166, "NetWare management" },
{ 0x026a, "NetWare management" },
{ 0x026b, "Time synchronization" },
{ 0x0278, "NetWare Directory server" },
{ 0x055d, "Attachmate SNA gateway" },
{ 0x0000, NULL }
};
while (servers[i].text != NULL) {
if (servers[i].type == type) {
return servers[i].text;
}
i++;
}
return "Unknown";
}
static void
dissect_sap(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
proto_tree *sap_tree, *s_tree;
proto_item *ti;
int cursor;
struct sap_query query;
struct sap_server_ident server;
char *sap_type[4] = { "General Query", "General Response",
"Nearest Query", "Nearest Response" };
query.query_type = pntohs(&pd[offset]);
query.server_type = pntohs(&pd[offset+2]);
if (check_col(fd, COL_PROTOCOL))
col_add_str(fd, COL_PROTOCOL, "SAP");
if (check_col(fd, COL_INFO)) {
if (query.query_type >= 1 && query.query_type <= 4) {
col_add_str(fd, COL_INFO, sap_type[query.query_type - 1]);
}
else {
col_add_str(fd, COL_INFO, "Unknown Packet Type");
}
}
if (tree) {
ti = proto_tree_add_item(tree, proto_sap, offset, END_OF_FRAME, NULL);
sap_tree = proto_item_add_subtree(ti, ETT_IPXSAP);
if (query.query_type >= 1 && query.query_type <= 4) {
proto_tree_add_text(sap_tree, offset, 2, sap_type[query.query_type - 1]);
if ((query.query_type - 1) % 2) {
proto_tree_add_item_hidden(sap_tree,
hf_sap_response,
offset, 2, 1);
} else {
proto_tree_add_item_hidden(sap_tree,
hf_sap_request,
offset, 2, 1);
}
}
else {
proto_tree_add_text(sap_tree, offset, 2,
"Unknown SAP Packet Type %d", query.query_type);
}
if (query.query_type == IPX_SAP_GENERAL_RESPONSE ||
query.query_type == IPX_SAP_NEAREST_RESPONSE) { /* responses */
for (cursor = offset + 2; (cursor + 64) <= fd->cap_len; cursor += 64) {
server.server_type = pntohs(&pd[cursor]);
memcpy(server.server_name, &pd[cursor+2], 48);
memcpy(&server.server_network, &pd[cursor+50], 4);
memcpy(&server.server_node, &pd[cursor+54], 6);
server.server_port = pntohs(&pd[cursor+60]);
server.intermediate_network = pntohs(&pd[cursor+62]);
ti = proto_tree_add_text(sap_tree, cursor+2, 48,
"Server Name: %s", server.server_name);
s_tree = proto_item_add_subtree(ti, ETT_IPXSAP_SERVER);
proto_tree_add_text(s_tree, cursor, 2, "Server Type: %s (0x%04X)",
server_type(server.server_type), server.server_type);
proto_tree_add_text(s_tree, cursor+50, 4, "Network: %s",
ipxnet_to_string((guint8*)&pd[cursor+50]));
proto_tree_add_text(s_tree, cursor+54, 6, "Node: %s",
ether_to_str((guint8*)&pd[cursor+54]));
proto_tree_add_text(s_tree, cursor+60, 2, "Socket: %s (0x%04X)",
port_text(server.server_port), server.server_port);
proto_tree_add_text(s_tree, cursor+62, 2,
"Intermediate Networks: %d",
server.intermediate_network);
}
}
else { /* queries */
proto_tree_add_text(sap_tree, offset+2, 2, "Server Type: %s (0x%04X)",
server_type(query.server_type), query.server_type);
}
}
}
void
proto_register_ipx(void)
{
static hf_register_info hf_ipx[] = {
{ &hf_ipx_checksum,
{ "Checksum", "ipx.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
"" }},
{ &hf_ipx_len,
{ "Length", "ipx.len", FT_UINT16, BASE_DEC, NULL, 0x0,
"" }},
{ &hf_ipx_hops,
{ "Transport Control (Hops)", "ipx.hops", FT_UINT8, BASE_DEC, NULL, 0x0,
"" }},
{ &hf_ipx_packet_type,
{ "Packet Type", "ipx.packet_type", FT_UINT8, BASE_HEX, VALS(ipx_packet_type_vals),
0x0,
"" }},
{ &hf_ipx_dnet,
{ "Destination Network","ipx.dst.net", FT_IPXNET, BASE_NONE, NULL, 0x0,
"" }},
{ &hf_ipx_dnode,
{ "Destination Node", "ipx.dst.node", FT_ETHER, BASE_NONE, NULL, 0x0,
"" }},
{ &hf_ipx_dsocket,
{ "Destination Socket", "ipx.dst.socket", FT_UINT16, BASE_HEX, NULL, 0x0,
"" }},
{ &hf_ipx_snet,
{ "Source Network","ipx.src.net", FT_IPXNET, BASE_NONE, NULL, 0x0,
"" }},
{ &hf_ipx_snode,
{ "Source Node", "ipx.src.node", FT_ETHER, BASE_NONE, NULL, 0x0,
"" }},
{ &hf_ipx_ssocket,
{ "Source Socket", "ipx.src.socket", FT_UINT16, BASE_HEX, NULL, 0x0,
"" }},
};
static hf_register_info hf_spx[] = {
{ &hf_spx_connection_control,
{ "Connection Control", "spx.ctl",
FT_UINT8, BASE_HEX, NULL, 0x0,
"" }},
{ &hf_spx_datastream_type,
{ "Datastream type", "spx.type",
FT_UINT8, BASE_HEX, NULL, 0x0,
"" }},
{ &hf_spx_src_id,
{ "Source Connection ID", "spx.src",
FT_UINT16, BASE_DEC, NULL, 0x0,
"" }},
{ &hf_spx_dst_id,
{ "Destination Connection ID", "spx.dst",
FT_UINT16, BASE_DEC, NULL, 0x0,
"" }},
{ &hf_spx_seq_nr,
{ "Sequence Number", "spx.seq",
FT_UINT16, BASE_DEC, NULL, 0x0,
"" }},
{ &hf_spx_ack_nr,
{ "Acknowledgment Number", "spx.ack",
FT_UINT16, BASE_DEC, NULL, 0x0,
"" }},
{ &hf_spx_all_nr,
{ "Allocation Number", "spx.alloc",
FT_UINT16, BASE_DEC, NULL, 0x0,
"" }}
};
static hf_register_info hf_ipxrip[] = {
{ &hf_ipxrip_request,
{ "Request", "ipxrip.request",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"TRUE if IPX RIP request" }},
{ &hf_ipxrip_response,
{ "Response", "ipxrip.response",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"TRUE if IPX RIP response" }}
};
static hf_register_info hf_sap[] = {
{ &hf_sap_request,
{ "Request", "sap.request",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"TRUE if SAP request" }},
{ &hf_sap_response,
{ "Response", "sap.response",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"TRUE if SAP response" }}
};
proto_ipx = proto_register_protocol ("Internetwork Packet eXchange", "ipx");
proto_register_field_array(proto_ipx, hf_ipx, array_length(hf_ipx));
proto_spx = proto_register_protocol ("Sequenced Packet eXchange", "spx");
proto_register_field_array(proto_spx, hf_spx, array_length(hf_spx));
proto_ipxrip = proto_register_protocol ("IPX Routing Information Protocol", "ipxrip");
proto_register_field_array(proto_ipxrip, hf_ipxrip, array_length(hf_ipxrip));
proto_sap = proto_register_protocol ("Service Advertisement Protocol", "sap");
proto_register_field_array(proto_sap, hf_sap, array_length(hf_sap));
}