2 * Routines for NetWare's IPX
3 * Gilbert Ramirez <gram@verdict.uthscsa.edu>
5 * $Id: packet-ipx.c,v 1.28 1999/10/12 06:20:10 gram Exp $
7 * Ethereal - Network traffic analyzer
8 * By Gerald Combs <gerald@unicom.net>
9 * Copyright 1998 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>
38 #include "packet-ipx.h"
39 #include "packet-ncp.h"
41 /* The information in this module (IPX, SPX, NCP) comes from:
42 NetWare LAN Analysis, Second Edition
43 Laura A. Chappell and Dan E. Hakes
45 Novell Press, San Jose.
48 And from the ncpfs source code by Volker Lendecke
52 static int proto_ipx = -1;
53 static int hf_ipx_checksum = -1;
54 static int hf_ipx_len = -1;
55 static int hf_ipx_hops = -1;
56 static int hf_ipx_packet_type = -1;
57 static int hf_ipx_dnet = -1;
58 static int hf_ipx_dnode = -1;
59 static int hf_ipx_dsocket = -1;
60 static int hf_ipx_snet = -1;
61 static int hf_ipx_snode = -1;
62 static int hf_ipx_ssocket = -1;
64 static int proto_spx = -1;
65 static int proto_ipxrip = -1;
66 static int proto_sap = -1;
69 dissect_spx(const u_char *pd, int offset, frame_data *fd, proto_tree *tree);
72 dissect_ipxrip(const u_char *pd, int offset, frame_data *fd, proto_tree *tree);
75 dissect_sap(const u_char *pd, int offset, frame_data *fd, proto_tree *tree);
77 typedef void (dissect_func_t)(const u_char *, int, frame_data *, proto_tree *);
95 /* ================================================================= */
97 /* ================================================================= */
99 #define IPX_SOCKET_NCP 0x0451
100 #define IPX_SOCKET_SAP 0x0452
101 #define IPX_SOCKET_IPXRIP 0x0453
102 #define IPX_SOCKET_NETBIOS 0x0455
103 #define IPX_SOCKET_DIAGNOSTIC 0x0456
104 #define IPX_SOCKET_SERIALIZATION 0x0457
105 #define IPX_SOCKET_NWLINK_SMB_NAMEQUERY 0x0551
106 #define IPX_SOCKET_NWLINK_SMB_DGRAM 0x0553
107 #define IPX_SOCKET_ATTACHMATE_GW 0x055d
108 #define IPX_SOCKET_IPX_MESSAGE 0x4001
110 static struct port_info ports[] = {
111 { IPX_SOCKET_NCP, dissect_ncp,
113 { IPX_SOCKET_SAP, dissect_sap,
115 { IPX_SOCKET_IPXRIP, dissect_ipxrip,
117 { IPX_SOCKET_NETBIOS, NULL,
119 { IPX_SOCKET_DIAGNOSTIC, NULL,
121 { IPX_SOCKET_SERIALIZATION, NULL,
123 { IPX_SOCKET_NWLINK_SMB_NAMEQUERY, NULL,
124 "NWLink SMB Name Query" },
125 { IPX_SOCKET_NWLINK_SMB_DGRAM, dissect_nwlink_dg,
126 "NWLink SMB Datagram" },
127 { IPX_SOCKET_ATTACHMATE_GW, NULL,
128 "Attachmate Gateway" },
129 { IPX_SOCKET_IPX_MESSAGE, NULL,
136 port_text(guint16 port) {
139 while (ports[i].text != NULL) {
140 if (ports[i].port == port) {
141 return ports[i].text;
148 static dissect_func_t*
149 port_func(guint16 port) {
152 while (ports[i].text != NULL) {
153 if (ports[i].port == port) {
154 return ports[i].func;
161 #define IPX_PACKET_TYPE_IPX 0
162 #define IPX_PACKET_TYPE_IPX_4 4
163 #define IPX_PACKET_TYPE_SPX 5
164 #define IPX_PACKET_TYPE_NCP 17
165 #define IPX_PACKET_TYPE_WANBCAST 20
167 static const value_string ipx_packet_type_vals[] = {
168 { IPX_PACKET_TYPE_IPX, "IPX" },
169 { IPX_PACKET_TYPE_IPX_4, "IPX" },
170 /* NetMon calls it "IPX" */
171 { IPX_PACKET_TYPE_SPX, "SPX" },
172 { 16, "Experimental Protocol" },
173 { IPX_PACKET_TYPE_NCP, "NCP" },
174 { 18, "Experimental Protocol" },
175 { 19, "Experimental Protocol" },
176 { IPX_PACKET_TYPE_WANBCAST, "NetBIOS Broadcast" },
177 /* NetMon calls it "WAN Broadcast" */
178 { 21, "Experimental Protocol" },
179 { 22, "Experimental Protocol" },
180 { 23, "Experimental Protocol" },
181 { 24, "Experimental Protocol" },
182 { 25, "Experimental Protocol" },
183 { 26, "Experimental Protocol" },
184 { 27, "Experimental Protocol" },
185 { 28, "Experimental Protocol" },
186 { 29, "Experimental Protocol" },
187 { 30, "Experimental Protocol" },
188 { 31, "Experimental Protocol" },
193 ipxnet_to_string(const guint8 *ad)
195 static gchar str[3][12];
198 if (cur == &str[0][0]) {
200 } else if (cur == &str[1][0]) {
206 sprintf(cur, "%02X %02X %02X %02X", ad[0], ad[1], ad[2], ad[3]);
211 ipx_addr_to_str(guint32 net, const guint8 *ad)
213 static gchar str[3][22];
216 if (cur == &str[0][0]) {
218 } else if (cur == &str[1][0]) {
224 sprintf(cur, "%X.%02x%02x%02x%02x%02x%02x", net,
225 ad[0], ad[1], ad[2], ad[3], ad[4], ad[5]);
230 dissect_ipx(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
232 proto_tree *ipx_tree;
234 guint8 ipx_type, ipx_hops;
235 guint16 ipx_checksum, ipx_length;
237 guint8 *ipx_snode, *ipx_dnode, *ipx_snet, *ipx_dnet;
239 gchar *str_dnet, *str_snet;
240 guint16 ipx_dsocket, ipx_ssocket;
241 dissect_func_t *dissect;
242 guint32 ipx_dnet_val, ipx_snet_val;
244 /* Calculate here for use in pinfo and in tree */
245 ipx_dnet = (guint8*)&pd[offset+6];
246 ipx_snet = (guint8*)&pd[offset+18];
247 str_dnet = ipxnet_to_string(ipx_dnet);
248 str_snet = ipxnet_to_string(ipx_snet);
249 ipx_dnet_val = pntohl(ipx_dnet);
250 ipx_snet_val = pntohl(ipx_snet);
251 ipx_dsocket = pntohs(&pd[offset+16]);
252 ipx_ssocket = pntohs(&pd[offset+28]);
253 ipx_dnode = (guint8*)&pd[offset+10];
254 ipx_snode = (guint8*)&pd[offset+22];
255 ipx_type = pd[offset+5];
256 ipx_length = pntohs(&pd[offset+2]);
258 /* Length of IPX datagram plus headers above it. */
259 len = ipx_length + offset;
261 /* Set the payload and captured-payload lengths to the minima of
262 (the IPX length plus the length of the headers above it) and
263 the frame lengths. */
266 if (pi.captured_len > len)
267 pi.captured_len = len;
269 if (check_col(fd, COL_RES_DL_DST))
270 col_add_str(fd, COL_RES_DL_DST,
271 ipx_addr_to_str(pntohl(ipx_dnet), ipx_dnode));
272 if (check_col(fd, COL_RES_DL_SRC))
273 col_add_str(fd, COL_RES_DL_SRC,
274 ipx_addr_to_str(pntohl(ipx_snet), ipx_snode));
276 if (check_col(fd, COL_PROTOCOL))
277 col_add_str(fd, COL_PROTOCOL, "IPX");
278 if (check_col(fd, COL_INFO))
279 col_add_fstr(fd, COL_INFO, "%s (0x%04X)", port_text(ipx_dsocket),
283 ipx_checksum = pntohs(&pd[offset]);
284 ipx_hops = pd[offset+4];
286 ti = proto_tree_add_item(tree, proto_ipx, offset, 30, NULL);
287 ipx_tree = proto_item_add_subtree(ti, ETT_IPX);
288 proto_tree_add_item(ipx_tree, hf_ipx_checksum, offset, 2, ipx_checksum);
289 proto_tree_add_item_format(ipx_tree, hf_ipx_len, offset+2, 2, ipx_length,
290 "Length: %d bytes", ipx_length);
291 proto_tree_add_item_format(ipx_tree, hf_ipx_hops, offset+4, 1, ipx_hops,
292 "Transport Control: %d hops", ipx_hops);
293 proto_tree_add_item(ipx_tree, hf_ipx_packet_type, offset+5, 1, ipx_type);
294 proto_tree_add_item(ipx_tree, hf_ipx_dnet, offset+6, 4, ipx_dnet_val);
295 proto_tree_add_item(ipx_tree, hf_ipx_dnode, offset+10, 6, ipx_dnode);
296 proto_tree_add_item_format(ipx_tree, hf_ipx_dsocket, offset+16, 2,
297 ipx_dsocket, "Destination Socket: %s (0x%04X)",
298 port_text(ipx_dsocket), ipx_dsocket);
299 proto_tree_add_item(ipx_tree, hf_ipx_snet, offset+18, 4, ipx_snet_val);
300 proto_tree_add_item(ipx_tree, hf_ipx_snode, offset+22, 6, ipx_snode);
301 proto_tree_add_item_format(ipx_tree, hf_ipx_ssocket, offset+28, 2,
302 ipx_ssocket, "Source Socket: %s (0x%04X)", port_text(ipx_ssocket),
308 case IPX_PACKET_TYPE_SPX:
309 dissect_spx(pd, offset, fd, tree);
312 case IPX_PACKET_TYPE_NCP:
313 /* Is the destination node 00:00:00:00:00:01 ? */
314 if (pntohl(ipx_dnode) == 0 && pntohs(ipx_dnode + 4) == 1)
315 nw_server_address = pntohl(ipx_dnet);
317 /* Is the source node 00:00:00:00:00:01 ? */
318 else if (pntohl(ipx_snode) == 0 && pntohs(ipx_snode + 4) == 1)
319 nw_server_address = pntohl(ipx_snet);
321 nw_server_address = 0;
323 dissect_ncp(pd, offset, fd, tree);
326 case IPX_PACKET_TYPE_WANBCAST:
327 case IPX_PACKET_TYPE_IPX_4:
328 if (ipx_dsocket == IPX_SOCKET_NETBIOS) {
329 dissect_nbipx(pd, offset, fd, tree);
332 /* else fall through */
334 case 0: /* IPX, fall through to default */
335 /* XXX - should type 0's be dissected as NBIPX
336 if they're aimed at the NetBIOS socket? */
338 dissect = port_func(ipx_dsocket);
340 dissect(pd, offset, fd, tree);
343 dissect = port_func(ipx_ssocket);
345 dissect(pd, offset, fd, tree);
348 dissect_data(pd, offset, fd, tree);
356 /* ================================================================= */
358 /* ================================================================= */
360 spx_conn_ctrl(u_char ctrl)
364 static struct conn_info conns[] = {
365 { 0x10, "End-of-Message" },
366 { 0x20, "Attention" },
367 { 0x40, "Acknowledgment Required"},
368 { 0x80, "System Packet"}
371 while (conns[i].text != NULL) {
372 if (conns[i].ctrl == ctrl) {
373 return conns[i].text;
381 spx_datastream(u_char type)
385 return "End-of-Connection";
387 return "End-of-Connection Acknowledgment";
389 return "Client-Defined";
394 dissect_spx(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
395 proto_tree *spx_tree;
398 if (check_col(fd, COL_PROTOCOL))
399 col_add_str(fd, COL_PROTOCOL, "SPX");
400 if (check_col(fd, COL_INFO))
401 col_add_str(fd, COL_INFO, "SPX");
404 ti = proto_tree_add_item(tree, proto_spx, offset, 12, NULL);
405 spx_tree = proto_item_add_subtree(ti, ETT_SPX);
407 proto_tree_add_text(spx_tree, offset, 1,
408 "Connection Control: %s (0x%02X)",
409 spx_conn_ctrl(pd[offset]), pd[offset]);
411 proto_tree_add_text(spx_tree, offset+1, 1,
412 "Datastream Type: %s (0x%02X)",
413 spx_datastream(pd[offset+1]), pd[offset+1]);
415 proto_tree_add_text(spx_tree, offset+2, 2,
416 "Source Connection ID: %d", pntohs( &pd[offset+2] ) );
418 proto_tree_add_text(spx_tree, offset+4, 2,
419 "Destination Connection ID: %d", pntohs( &pd[offset+4] ) );
421 proto_tree_add_text(spx_tree, offset+6, 2,
422 "Sequence Number: %d", pntohs( &pd[offset+6] ) );
424 proto_tree_add_text(spx_tree, offset+8, 2,
425 "Acknowledgment Number: %d", pntohs( &pd[offset+8] ) );
427 proto_tree_add_text(spx_tree, offset+10, 2,
428 "Allocation Number: %d", pntohs( &pd[offset+10] ) );
431 dissect_data(pd, offset, fd, tree);
435 /* ================================================================= */
437 /* ================================================================= */
439 dissect_ipxrip(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
440 proto_tree *rip_tree;
443 struct ipx_rt_def route;
446 char *rip_type[2] = { "Request", "Response" };
448 operation = pntohs(&pd[offset]) - 1;
450 if (check_col(fd, COL_PROTOCOL))
451 col_add_str(fd, COL_PROTOCOL, "IPX RIP");
452 if (check_col(fd, COL_PROTOCOL)) {
454 col_add_str(fd, COL_INFO, rip_type[operation]);
457 col_add_str(fd, COL_INFO, "Unknown Packet Type");
462 ti = proto_tree_add_item(tree, proto_ipxrip, offset, END_OF_FRAME, NULL);
463 rip_tree = proto_item_add_subtree(ti, ETT_IPXRIP);
466 proto_tree_add_text(rip_tree, offset, 2,
467 "RIP packet type: %s", rip_type[operation]);
470 proto_tree_add_text(rip_tree, offset, 2, "Unknown RIP packet type");
473 for (cursor = offset + 2; cursor < fd->cap_len; cursor += 8) {
474 memcpy(&route.network, &pd[cursor], 4);
475 route.hops = pntohs(&pd[cursor+4]);
476 route.ticks = pntohs(&pd[cursor+6]);
478 if (operation == IPX_RIP_REQUEST - 1) {
479 proto_tree_add_text(rip_tree, cursor, 8,
480 "Route Vector: %s, %d hop%s, %d tick%s",
481 ipxnet_to_string((guint8*)&route.network),
482 route.hops, route.hops == 1 ? "" : "s",
483 route.ticks, route.ticks == 1 ? "" : "s");
486 proto_tree_add_text(rip_tree, cursor, 8,
487 "Route Vector: %s, %d hop%s, %d tick%s (%d ms)",
488 ipxnet_to_string((guint8*)&route.network),
489 route.hops, route.hops == 1 ? "" : "s",
490 route.ticks, route.ticks == 1 ? "" : "s",
491 route.ticks * 1000 / 18);
499 /* ================================================================= */
501 /* ================================================================= */
503 server_type(guint16 type)
507 /* some of these are from ncpfs, others are from the book */
508 static struct server_info servers[] = {
510 { 0x0002, "User Group" },
511 { 0x0003, "Print Queue" },
512 { 0x0004, "File server" },
513 { 0x0005, "Job server" },
514 { 0x0007, "Print server" },
515 { 0x0008, "Archive server" },
516 { 0x0009, "Archive server" },
517 { 0x000a, "Job queue" },
518 { 0x000b, "Administration" },
519 { 0x0021, "NAS SNA gateway" },
520 { 0x0024, "Remote bridge" },
521 { 0x0026, "Bridge server" },
522 { 0x0027, "TCP/IP gateway" },
523 { 0x002d, "Time Synchronization VAP" },
524 { 0x002e, "Archive Server Dynamic SAP" },
525 { 0x0047, "Advertising print server" },
526 { 0x004b, "Btrieve VAP 5.0" },
527 { 0x004c, "SQL VAP" },
528 { 0x0050, "Btrieve VAP" },
529 { 0x0053, "Print Queue VAP" },
530 { 0x007a, "TES NetWare for VMS" },
531 { 0x0098, "NetWare access server" },
532 { 0x009a, "Named Pipes server" },
533 { 0x009e, "Portable NetWare Unix" },
534 { 0x0107, "NetWare 386" },
535 { 0x0111, "Test server" },
536 { 0x0133, "NetWare Name Service" },
537 { 0x0166, "NetWare management" },
538 { 0x026a, "NetWare management" },
539 { 0x026b, "Time synchronization" },
540 { 0x0278, "NetWare Directory server" },
541 { 0x055d, "Attachmate SNA gateway" },
545 while (servers[i].text != NULL) {
546 if (servers[i].type == type) {
547 return servers[i].text;
555 dissect_sap(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
557 proto_tree *sap_tree, *s_tree;
560 struct sap_query query;
561 struct sap_server_ident server;
563 char *sap_type[4] = { "General Query", "General Response",
564 "Nearest Query", "Nearest Response" };
566 query.query_type = pntohs(&pd[offset]);
567 query.server_type = pntohs(&pd[offset+2]);
569 if (check_col(fd, COL_PROTOCOL))
570 col_add_str(fd, COL_PROTOCOL, "SAP");
571 if (check_col(fd, COL_INFO)) {
572 if (query.query_type >= 1 && query.query_type <= 4) {
573 col_add_str(fd, COL_INFO, sap_type[query.query_type - 1]);
576 col_add_str(fd, COL_INFO, "Unknown Packet Type");
581 ti = proto_tree_add_item(tree, proto_sap, offset, END_OF_FRAME, NULL);
582 sap_tree = proto_item_add_subtree(ti, ETT_IPXSAP);
584 if (query.query_type >= 1 && query.query_type <= 4) {
585 proto_tree_add_text(sap_tree, offset, 2, sap_type[query.query_type - 1]);
588 proto_tree_add_text(sap_tree, offset, 2,
589 "Unknown SAP Packet Type %d", query.query_type);
592 if (query.query_type == IPX_SAP_GENERAL_RESPONSE ||
593 query.query_type == IPX_SAP_NEAREST_RESPONSE) { /* responses */
595 for (cursor = offset + 2; (cursor + 64) <= fd->cap_len; cursor += 64) {
596 server.server_type = pntohs(&pd[cursor]);
597 memcpy(server.server_name, &pd[cursor+2], 48);
598 memcpy(&server.server_network, &pd[cursor+50], 4);
599 memcpy(&server.server_node, &pd[cursor+54], 6);
600 server.server_port = pntohs(&pd[cursor+60]);
601 server.intermediate_network = pntohs(&pd[cursor+62]);
603 ti = proto_tree_add_text(sap_tree, cursor+2, 48,
604 "Server Name: %s", server.server_name);
605 s_tree = proto_item_add_subtree(ti, ETT_IPXSAP_SERVER);
607 proto_tree_add_text(s_tree, cursor, 2, "Server Type: %s (0x%04X)",
608 server_type(server.server_type), server.server_type);
609 proto_tree_add_text(s_tree, cursor+50, 4, "Network: %s",
610 ipxnet_to_string((guint8*)&pd[cursor+50]));
611 proto_tree_add_text(s_tree, cursor+54, 6, "Node: %s",
612 ether_to_str((guint8*)&pd[cursor+54]));
613 proto_tree_add_text(s_tree, cursor+60, 2, "Socket: %s (0x%04X)",
614 port_text(server.server_port), server.server_port);
615 proto_tree_add_text(s_tree, cursor+62, 2,
616 "Intermediate Networks: %d",
617 server.intermediate_network);
621 proto_tree_add_text(sap_tree, offset+2, 2, "Server Type: %s (0x%04X)",
622 server_type(query.server_type), query.server_type);
629 proto_register_ipx(void)
631 static hf_register_info hf_ipx[] = {
633 { "Checksum", "ipx.checksum", FT_UINT16, BASE_HEX, NULL, 0x0,
637 { "Length", "ipx.len", FT_UINT16, BASE_DEC, NULL, 0x0,
641 { "Transport Control (Hops)", "ipx.hops", FT_UINT8, BASE_DEC, NULL, 0x0,
644 { &hf_ipx_packet_type,
645 { "Packet Type", "ipx.packet_type", FT_UINT8, BASE_HEX, VALS(ipx_packet_type_vals),
650 { "Destination Network","ipx.dst.net", FT_IPXNET, BASE_NONE, NULL, 0x0,
654 { "Destination Node", "ipx.dst.node", FT_ETHER, BASE_NONE, NULL, 0x0,
658 { "Destination Socket", "ipx.dst.socket", FT_UINT16, BASE_HEX, NULL, 0x0,
662 { "Source Network","ipx.src.net", FT_IPXNET, BASE_NONE, NULL, 0x0,
666 { "Source Node", "ipx.src.node", FT_ETHER, BASE_NONE, NULL, 0x0,
670 { "Source Socket", "ipx.src.socket", FT_UINT16, BASE_HEX, NULL, 0x0,
674 proto_ipx = proto_register_protocol ("Internetwork Packet eXchange", "ipx");
675 proto_register_field_array(proto_ipx, hf_ipx, array_length(hf_ipx));
677 proto_spx = proto_register_protocol ("Sequenced Packet eXchange", "spx");
679 proto_ipxrip = proto_register_protocol ("IPX Routing Information Protocol", "ipxrip");
681 proto_sap = proto_register_protocol ("Service Advertisement Protocol", "sap");