diff options
-rw-r--r-- | Makefile.am | 13 | ||||
-rw-r--r-- | Makefile.nmake | 8 | ||||
-rw-r--r-- | column.c | 38 | ||||
-rw-r--r-- | epan/column-utils.c | 25 | ||||
-rw-r--r-- | epan/column_info.h | 4 | ||||
-rw-r--r-- | epan/conversation.c | 50 | ||||
-rw-r--r-- | epan/packet.c | 5 | ||||
-rw-r--r-- | epan/packet_info.h | 10 | ||||
-rw-r--r-- | epan/to_str.c | 114 | ||||
-rw-r--r-- | epan/to_str.h | 15 | ||||
-rw-r--r-- | packet-fc.c | 786 | ||||
-rw-r--r-- | packet-fc.h | 153 | ||||
-rw-r--r-- | packet-fcbls.h | 87 | ||||
-rw-r--r-- | packet-fcels.c | 1978 | ||||
-rw-r--r-- | packet-fcels.h | 321 | ||||
-rw-r--r-- | packet-fcip.c | 619 | ||||
-rw-r--r-- | packet-fclctl.c | 102 | ||||
-rw-r--r-- | packet-fclctl.h | 174 | ||||
-rw-r--r-- | packet-fcp.c | 690 | ||||
-rw-r--r-- | packet-fcp.h | 69 | ||||
-rw-r--r-- | packet-fcswils.c | 1727 | ||||
-rw-r--r-- | packet-fcswils.h | 389 | ||||
-rw-r--r-- | packet-ipfc.c | 155 | ||||
-rw-r--r-- | packet-llc.c | 3 |
24 files changed, 7501 insertions, 34 deletions
diff --git a/Makefile.am b/Makefile.am index 8e6aff81f7..e9c73d4e3d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,7 +1,7 @@ # Makefile.am # Automake file for Ethereal # -# $Id: Makefile.am,v 1.524 2002/12/07 21:43:27 gerald Exp $ +# $Id: Makefile.am,v 1.525 2002/12/08 02:32:17 gerald Exp $ # # Ethereal - Network traffic analyzer # By Gerald Combs <gerald@ethereal.com> @@ -179,6 +179,12 @@ DISSECTOR_SRC = \ packet-esis.c \ packet-eth.c \ packet-ethertype.c \ + packet-fc.c \ + packet-fcels.c \ + packet-fcip.c \ + packet-fclctl.c \ + packet-fcp.c \ + packet-fcswils.c \ packet-fddi.c \ packet-fix.c \ packet-fr.c \ @@ -505,6 +511,11 @@ noinst_HEADERS = \ packet-dvmrp.h \ packet-esis.h \ packet-eth.h \ + packet-fc.h \ + packet-fcels.h \ + packet-fclctl.h \ + packet-fcp.h \ + packet-fcswils.h \ packet-fddi.h \ packet-frame.h \ packet-giop.h \ diff --git a/Makefile.nmake b/Makefile.nmake index 68e738a897..f75d32c10c 100644 --- a/Makefile.nmake +++ b/Makefile.nmake @@ -1,7 +1,7 @@ ## Makefile for building ethereal.exe with Microsoft C and nmake ## Use: $(MAKE) /$(MAKEFLAGS) -f makefile.nmake # -# $Id: Makefile.nmake,v 1.259 2002/12/03 00:37:27 guy Exp $ +# $Id: Makefile.nmake,v 1.260 2002/12/08 02:32:17 gerald Exp $ include config.nmake include <win32.mak> @@ -122,6 +122,12 @@ DISSECTOR_SRC = \ packet-esis.c \ packet-eth.c \ packet-ethertype.c \ + packet-fc.c \ + packet-fcels.c \ + packet-fcip.c \ + packet-fclctl.c \ + packet-fcp.c \ + packet-fcswils.c \ packet-fddi.c \ packet-fix.c \ packet-fr.c \ @@ -1,7 +1,7 @@ /* column.c * Routines for handling column preferences * - * $Id: column.c,v 1.37 2002/08/28 21:00:06 jmayer Exp $ + * $Id: column.c,v 1.38 2002/12/08 02:32:17 gerald Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -52,8 +52,8 @@ col_format_to_string(gint fmt) { "%us","%hs", "%rhs", "%uhs", "%ns", "%rns", "%uns", "%d", "%rd", "%ud", "%hd", "%rhd", "%uhd", "%nd", "%rnd", "%und", "%S", "%rS", "%uS", "%D", "%rD", "%uD", "%p", - "%i", "%L" }; - + "%i", "%L", "%XO", "%XR" }; + if (fmt < 0 || fmt > NUM_COL_FMTS) return NULL; @@ -79,10 +79,8 @@ col_format_desc(gint fmt) { "Source port", "Src port (resolved)", "Src port (unresolved)", "Destination port", "Dest port (resolved)", "Dest port (unresolved)", - "Protocol", "Information", "Packet length (bytes)" }; - - if (fmt < 0 || fmt > NUM_COL_FMTS) - return NULL; + "Protocol", "Information", "Packet length (bytes)" , + "OXID", "RXID", }; return(dlist[fmt]); } @@ -140,6 +138,12 @@ get_column_format_matches(gboolean *fmt_list, gint format) { case COL_DEF_DST_PORT: fmt_list[COL_RES_DST_PORT] = TRUE; break; + case COL_OXID: + fmt_list[COL_OXID] = TRUE; + break; + case COL_RXID: + fmt_list[COL_RXID] = TRUE; + break; default: break; } @@ -216,6 +220,10 @@ get_column_longest_string(gint format) case COL_PACKET_LENGTH: return "000000"; break; + case COL_RXID: + case COL_OXID: + return "000000"; + break; default: /* COL_INFO */ return "Source port: kerberos-master Destination port: kerberos-master"; break; @@ -270,6 +278,8 @@ get_column_resize_type(gint format) { case COL_DEF_NET_DST: case COL_RES_NET_DST: case COL_UNRES_NET_DST: + case COL_OXID: + case COL_RXID: /* We don't want these to resize dynamically; if they get resolved to names, those names could be very long, and auto-resizing columns showing those names may leave too little room for @@ -313,6 +323,7 @@ gint get_column_format_from_str(gchar *str) { gchar *cptr = str; gint res_off = RES_DEF, addr_off = ADDR_DEF, time_off = TIME_DEF; + gint prev_code = -1; /* To do: Make this parse %-formatted strings "for real" */ while (*cptr != '\0') { @@ -354,7 +365,12 @@ get_column_format_from_str(gchar *str) { addr_off = ADDR_NET; break; case 'R': - time_off = TIME_REL; + if (prev_code == COL_OXID) { + return COL_RXID; + } + else { + time_off = TIME_REL; + } break; case 'A': time_off = TIME_ABS; @@ -368,6 +384,12 @@ get_column_format_from_str(gchar *str) { case 'L': return COL_PACKET_LENGTH; break; + case 'X': + prev_code = COL_OXID; + break; + case 'O': + return COL_OXID; + break; } cptr++; } diff --git a/epan/column-utils.c b/epan/column-utils.c index a4aad6d790..6238ab74af 100644 --- a/epan/column-utils.c +++ b/epan/column-utils.c @@ -1,7 +1,7 @@ /* column-utils.c * Routines for column utilities. * - * $Id: column-utils.c,v 1.27 2002/12/03 02:38:39 guy Exp $ + * $Id: column-utils.c,v 1.28 2002/12/08 02:32:35 gerald Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -364,6 +364,8 @@ col_set_addr(packet_info *pinfo, int col, address *addr, gboolean is_res, struct e_in6_addr ipv6_addr; struct atalk_ddp_addr ddp_addr; struct sna_fid_type_4_addr sna_fid_type_4_addr; + gchar *fcid; + guint32 tmpfc; pinfo->cinfo->col_expr[col][0] = '\0'; pinfo->cinfo->col_expr_val[col][0] = '\0'; @@ -484,6 +486,15 @@ col_set_addr(packet_info *pinfo, int col, address *addr, gboolean is_res, strcpy(pinfo->cinfo->col_expr_val[col],pinfo->cinfo->col_buf[col]); break; + case AT_FC: + tmpfc = *((guint32 *)addr->data); + fcid = fc_to_str ((const guint8 *)&tmpfc); + + strncpy (pinfo->cinfo->col_buf[col], fcid, COL_MAX_LEN); + pinfo->cinfo->col_buf[col][COL_MAX_LEN - 1] = '\0'; + pinfo->cinfo->col_data[col] = pinfo->cinfo->col_buf[col]; + break; + default: break; } @@ -676,6 +687,18 @@ fill_in_columns(packet_info *pinfo) strcpy(pinfo->cinfo->col_expr_val[i], pinfo->cinfo->col_buf[i]); break; + case COL_OXID: + snprintf (pinfo->cinfo->col_buf[i], COL_MAX_LEN, "0x%x", pinfo->oxid); + pinfo->cinfo->col_buf[i][COL_MAX_LEN - 1] = '\0'; + pinfo->cinfo->col_data[i] = pinfo->cinfo->col_buf[i]; + break; + + case COL_RXID: + snprintf (pinfo->cinfo->col_buf[i], COL_MAX_LEN, "0x%x", pinfo->rxid); + pinfo->cinfo->col_buf[i][COL_MAX_LEN - 1] = '\0'; + pinfo->cinfo->col_data[i] = pinfo->cinfo->col_buf[i]; + break; + case NUM_COL_FMTS: /* keep compiler happy - shouldn't get here */ g_assert_not_reached(); break; diff --git a/epan/column_info.h b/epan/column_info.h index e645fbb2fd..6aa86964a5 100644 --- a/epan/column_info.h +++ b/epan/column_info.h @@ -1,7 +1,7 @@ /* column.h * Definitions for column structures and routines * - * $Id: column_info.h,v 1.3 2002/08/28 20:40:44 jmayer Exp $ + * $Id: column_info.h,v 1.4 2002/12/08 02:32:36 gerald Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@zing.org> @@ -85,6 +85,8 @@ enum { COL_PROTOCOL, /* Protocol */ COL_INFO, /* Description */ COL_PACKET_LENGTH, /* Packet length in bytes */ + COL_OXID, /* Fibre Channel OXID */ + COL_RXID, /* Fibre Channel RXID */ NUM_COL_FMTS /* Should always be last */ }; diff --git a/epan/conversation.c b/epan/conversation.c index 09a5f5c240..95e6536999 100644 --- a/epan/conversation.c +++ b/epan/conversation.c @@ -1,7 +1,7 @@ /* conversation.c * Routines for building lists of packets that are part of a "conversation" * - * $Id: conversation.c,v 1.22 2002/11/27 22:44:41 guy Exp $ + * $Id: conversation.c,v 1.23 2002/12/08 02:32:36 gerald Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -621,7 +621,17 @@ find_conversation(address *addr_a, address *addr_b, port_type ptype, */ conversation = conversation_lookup_hashtable(conversation_hashtable_exact, - addr_a, addr_b, ptype, port_a, port_b); + addr_a, addr_b, ptype, + port_a, port_b); + if ((conversation == NULL) && (addr_a->type == AT_FC)) { + /* In Fibre channel, OXID & RXID are never swapped as + * TCP/UDP ports are in TCP/IP. + */ + conversation = + conversation_lookup_hashtable(conversation_hashtable_exact, + addr_b, addr_a, ptype, + port_a, port_b); + } if (conversation != NULL) return conversation; } @@ -643,6 +653,15 @@ find_conversation(address *addr_a, address *addr_b, port_type ptype, conversation = conversation_lookup_hashtable(conversation_hashtable_no_addr2, addr_a, addr_b, ptype, port_a, port_b); + if ((conversation == NULL) && (addr_a->type == AT_FC)) { + /* In Fibre channel, OXID & RXID are never swapped as + * TCP/UDP ports are in TCP/IP. + */ + conversation = + conversation_lookup_hashtable(conversation_hashtable_no_addr2, + addr_b, addr_a, ptype, + port_a, port_b); + } if (conversation != NULL) { /* * If search address B isn't wildcarded, and this @@ -673,9 +692,9 @@ find_conversation(address *addr_a, address *addr_b, port_type ptype, * ("addr_a" doesn't take part in this lookup.) */ if (!(options & NO_ADDR_B)) { - conversation = - conversation_lookup_hashtable(conversation_hashtable_no_addr2, - addr_b, addr_a, ptype, port_b, port_a); + conversation = + conversation_lookup_hashtable(conversation_hashtable_no_addr2, + addr_b, addr_a, ptype, port_b, port_a); if (conversation != NULL) { /* * If this is for a connection-oriented @@ -711,6 +730,14 @@ find_conversation(address *addr_a, address *addr_b, port_type ptype, conversation = conversation_lookup_hashtable(conversation_hashtable_no_port2, addr_a, addr_b, ptype, port_a, port_b); + if ((conversation == NULL) && (addr_a->type == AT_FC)) { + /* In Fibre channel, OXID & RXID are never swapped as + * TCP/UDP ports are in TCP/IP + */ + conversation = + conversation_lookup_hashtable(conversation_hashtable_no_port2, + addr_b, addr_a, ptype, port_a, port_b); + } if (conversation != NULL) { /* * If search port B isn't wildcarded, and this is @@ -805,9 +832,16 @@ find_conversation(address *addr_a, address *addr_b, port_type ptype, * first packet in the conversation). * (Neither "addr_a" nor "port_a" take part in this lookup.) */ - conversation = - conversation_lookup_hashtable(conversation_hashtable_no_addr2_or_port2, - addr_b, addr_a, ptype, port_b, port_a); + if (addr_a->type == AT_FC) + conversation = + conversation_lookup_hashtable(conversation_hashtable_no_addr2_or_port2, + addr_b, addr_a, ptype, port_a, + port_b); + else + conversation = + conversation_lookup_hashtable(conversation_hashtable_no_addr2_or_port2, + addr_b, addr_a, ptype, port_b, + port_a); if (conversation != NULL) { /* * If this is for a connection-oriented protocol, set the diff --git a/epan/packet.c b/epan/packet.c index 52bda772ed..ab50d0a614 100644 --- a/epan/packet.c +++ b/epan/packet.c @@ -1,7 +1,7 @@ /* packet.c * Routines for packet disassembly * - * $Id: packet.c,v 1.83 2002/11/16 21:36:39 guy Exp $ + * $Id: packet.c,v 1.84 2002/12/08 02:32:36 gerald Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -286,6 +286,9 @@ dissect_packet(epan_dissect_t *edt, union wtap_pseudo_header *pseudo_header, edt->pi.can_desegment = 0; edt->pi.p2p_dir = P2P_DIR_UNKNOWN; edt->pi.private_data = NULL; + edt->pi.oxid = 0; + edt->pi.rxid = 0; + edt->pi.r_ctl = 0; TRY { edt->tvb = tvb_new_real_data(pd, fd->cap_len, fd->pkt_len); diff --git a/epan/packet_info.h b/epan/packet_info.h index c7bcd95c7e..99b93f55e4 100644 --- a/epan/packet_info.h +++ b/epan/packet_info.h @@ -1,7 +1,7 @@ /* packet_info.h * Definitions for packet info structures and routines * - * $Id: packet_info.h,v 1.25 2002/11/08 01:00:07 guy Exp $ + * $Id: packet_info.h,v 1.26 2002/12/08 02:32:36 gerald Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -39,7 +39,8 @@ typedef enum { AT_ATALK, /* Appletalk DDP */ AT_VINES, /* Banyan Vines */ AT_OSI, /* OSI NSAP */ - AT_ARCNET /* ARCNET */ + AT_ARCNET, /* ARCNET */ + AT_FC /* Fibre Channel */ } address_type; typedef struct _address { @@ -96,6 +97,7 @@ typedef enum { PT_UDP, /* UDP */ PT_IPX, /* IPX sockets */ PT_NCP, /* NCP connection */ + PT_EXCHG, /* Fibre Channel exchange */ PT_DDP /* DDP AppleTalk connection */ } port_type; @@ -146,6 +148,10 @@ typedef struct _packet_info { int iplen; int iphdrlen; int p2p_dir; + guint16 oxid; /* next 2 fields reqd to identify fibre */ + guint16 rxid; /* channel conversations */ + guint8 r_ctl; /* R_CTL field in Fibre Channel Protocol */ + guint8 pad; void *private_data; /* pointer to data passed from one dissector to another */ } packet_info; diff --git a/epan/to_str.c b/epan/to_str.c index c8b8f83224..fbd579d9c2 100644 --- a/epan/to_str.c +++ b/epan/to_str.c @@ -1,7 +1,7 @@ /* to_str.c * Routines for utilities to convert various other types to strings. * - * $Id: to_str.c,v 1.19 2002/11/28 03:54:50 guy Exp $ + * $Id: to_str.c,v 1.20 2002/12/08 02:32:36 gerald Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -71,7 +71,7 @@ gchar * ether_to_str(const guint8 *ad) { - return ether_to_str_punct(ad, ':'); + return ether_to_str_punct(ad, ':', 5); } /* Places char punct in the string as the hex-digit separator. @@ -79,7 +79,7 @@ ether_to_str(const guint8 *ad) * the resulting string is 5 bytes shorter) */ gchar * -ether_to_str_punct(const guint8 *ad, char punct) { +ether_to_str_punct(const guint8 *ad, char punct, guint32 len) { static gchar str[3][18]; static gchar *cur; gchar *p; @@ -96,7 +96,7 @@ ether_to_str_punct(const guint8 *ad, char punct) { } p = &cur[18]; *--p = '\0'; - i = 5; + i = len; for (;;) { octet = ad[i]; *--p = hex_digits[octet&0xF]; @@ -194,7 +194,7 @@ ipx_addr_to_str(guint32 net, const guint8 *ad) sprintf(cur, "%s.%s", get_ipxnet_name(net), name); } else { - sprintf(cur, "%s.%s", get_ipxnet_name(net), ether_to_str_punct(ad, '\0')); + sprintf(cur, "%s.%s", get_ipxnet_name(net), ether_to_str_punct(ad, '\0', 5)); } return cur; } @@ -522,6 +522,110 @@ rel_time_to_secs_str(nstime_t *rel_time) return cur; } +gchar * +fc_to_str(const guint8 *ad) { + return ether_to_str_punct (ad, '.', 2); +} + +gchar * +fcwwn_to_str (const guint8 *ad) +{ + int fmt; + guint8 oui[6]; + static gchar ethstr[512]; + + if (ad == NULL) return NULL; + + fmt = (ad[0] & 0xF0) >> 4; + + if ((fmt == 1) || (fmt == 2)) { + memcpy (oui, &ad[2], 6); + sprintf (ethstr, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x (%s)", ad[0], + ad[1], ad[2], ad[3], ad[4], ad[5], ad[6], ad[7], + get_manuf_name (oui)); + } + else if (fmt == 5) { + oui[0] = ((ad[0] & 0x0F) << 4) | ((ad[1] & 0xF0) >> 4); + oui[1] = ((ad[1] & 0x0F) << 4) | ((ad[2] & 0xF0) >> 4); + oui[2] = ((ad[2] & 0x0F) << 4) | ((ad[3] & 0xF0) >> 4); + oui[3] = ((ad[3] & 0x0F) << 4) | ((ad[4] & 0xF0) >> 4); + oui[4] = ((ad[4] & 0x0F) << 4) | ((ad[5] & 0xF0) >> 4); + oui[5] = ((ad[5] & 0x0F) << 4) | ((ad[6] & 0xF0) >> 4); + + sprintf (ethstr, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x (%s)", ad[0], + ad[1], ad[2], ad[3], ad[4], ad[5], ad[6], ad[7], + get_manuf_name (oui)); + } + else { + sprintf (ethstr, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", ad[0], + ad[1], ad[2], ad[3], ad[4], ad[5], ad[6], ad[7]); + } + return (ethstr); +} + +gchar * +fc_to_str_buf(const guint8 *ad) +{ + static gchar str[3][18]; + static gchar *cur; + gchar *p; + int i; + guint32 octet; + static const gchar hex_digits[16] = "0123456789abcdef"; + + if (cur == &str[0][0]) { + cur = &str[1][0]; + } else if (cur == &str[1][0]) { + cur = &str[2][0]; + } else { + cur = &str[0][0]; + } + p = &cur[18]; + *--p = '\0'; + i = 0; + for (;;) { + octet = ad[i]; + *--p = hex_digits[octet&0xF]; + octet >>= 4; + *--p = hex_digits[octet&0xF]; + if (i == 2) + break; + *--p = '.'; + i++; + } + return p; +/* + gchar *p; + int i; + guint32 octet; + guint32 digit; + gboolean saw_nonzero; + + p = buf; + i = 0; + for (;;) { + saw_nonzero = FALSE; + octet = ad[i]; + digit = octet/100; + if (digit != 0) { + *p++ = digit + '0'; + saw_nonzero = TRUE; + } + octet %= 100; + digit = octet/10; + if (saw_nonzero || digit != 0) + *p++ = digit + '0'; + digit = octet%10; + *p++ = digit + '0'; + if (i == 2) + break; + *p++ = '.'; + i++; + } + *p = '\0'; + */ +} + /* Generate, into "buf", a string showing the bits of a bitfield. Return a pointer to the character after that string. */ char * diff --git a/epan/to_str.h b/epan/to_str.h index 2d6668fade..2fa748af6d 100644 --- a/epan/to_str.h +++ b/epan/to_str.h @@ -1,7 +1,7 @@ /* to_str.h * Definitions for utilities to convert various other types to strings. * - * $Id: to_str.h,v 1.9 2002/11/28 03:54:50 guy Exp $ + * $Id: to_str.h,v 1.10 2002/12/08 02:32:36 gerald Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -43,11 +43,16 @@ typedef enum { * but for which no more specific module applies. */ -extern gchar* ether_to_str(const guint8 *); -extern gchar* ether_to_str_punct(const guint8 *, char); -extern gchar* ip_to_str(const guint8 *); -extern void ip_to_str_buf(const guint8 *, gchar *); +gchar* ether_to_str(const guint8 *); +gchar* ether_to_str_punct(const guint8 *, char, guint32); +gchar* ip_to_str(const guint8 *); +void ip_to_str_buf(const guint8 *, gchar *); + struct e_in6_addr; + +gchar* fc_to_str(const guint8 *); +gchar* fc_to_str_buf(const guint8 *); +gchar* fcwwn_to_str (const guint8 *); extern char* ip6_to_str(const struct e_in6_addr *); extern gchar* ipx_addr_to_str(guint32, const guint8 *); extern gchar* ipxnet_to_string(const guint8 *ad); diff --git a/packet-fc.c b/packet-fc.c new file mode 100644 index 0000000000..9cc0a9c71b --- /dev/null +++ b/packet-fc.c @@ -0,0 +1,786 @@ +/* packet-fc.c + * Routines for Fibre Channel Decoding (FC Header, Link Ctl & Basic Link Svc) + * Copyright 2001, Dinesh G Dutt <ddutt@cisco.com> + * + * $Id: packet-fc.c,v 1.1 2002/12/08 02:32:17 gerald Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * Copyright 1998 Gerald Combs + * + * Copied from WHATEVER_FILE_YOU_USED (where "WHATEVER_FILE_YOU_USED" + * is a dissector file; if you just copied this from README.developer, + * don't bother with the "Copied from" - you don't even need to put + * in a "Copied from" if you copied an existing dissector, especially + * if the bulk of the code in the new dissector is your code) + * + * 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 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif + +#include <glib.h> + +#ifdef NEED_SNPRINTF_H +# include "snprintf.h" +#endif + +#include <epan/packet.h> +#include "prefs.h" +#include "reassemble.h" +#include "etypes.h" +#include "packet-fc.h" +#include "packet-fclctl.h" +#include "packet-fcbls.h" + +#define FC_HEADER_SIZE 24 +#define FC_RCTL_EISL 0x50 + +/* Size of various fields in FC header in bytes */ +#define FC_RCTL_SIZE 1 +#define FC_DID_SIZE 3 +#define FC_CSCTL_SIZE 1 +#define FC_SID_SIZE 3 +#define FC_TYPE_SIZE 1 +#define FC_FCTL_SIZE 3 +#define FC_SEQID_SIZE 1 +#define FC_DFCTL_SIZE 1 +#define FC_SEQCNT_SIZE 2 +#define FC_OXID_SIZE 2 +#define FC_RXID_SIZE 2 +#define FC_PARAM_SIZE 4 + +/* Initialize the protocol and registered fields */ +static int proto_fc = -1; +static int hf_fc_rctl = -1; +static int hf_fc_did = -1; +static int hf_fc_csctl = -1; +static int hf_fc_sid = -1; +static int hf_fc_type = -1; +static int hf_fc_fctl = -1; +static int hf_fc_seqid = -1; +static int hf_fc_dfctl = -1; +static int hf_fc_seqcnt = -1; +static int hf_fc_oxid = -1; +static int hf_fc_rxid = -1; +static int hf_fc_param = -1; +static int hf_fc_ftype = -1; /* Derived field, non-existent in FC hdr */ +static int hf_fc_exchg_orig = -1; +static int hf_fc_exchg_resp = -1; +static int hf_fc_reassembled = -1; + +/* For Basic Link Svc */ +static int hf_fc_bls_seqid_vld = -1; +static int hf_fc_bls_lastvld_seqid = -1; +static int hf_fc_bls_oxid = -1; +static int hf_fc_bls_rxid = -1; +static int hf_fc_bls_lowseqcnt = -1; +static int hf_fc_bls_hiseqcnt = -1; +static int hf_fc_bls_rjtcode = -1; +static int hf_fc_bls_rjtdetail = -1; +static int hf_fc_bls_vendor = -1; + +/* Initialize the subtree pointers */ +static gint ett_fc = -1; +static gint ett_fcbls = -1; + +static dissector_table_t fcftype_dissector_table; +static dissector_handle_t data_handle; + +/* Reassembly stuff */ +static gboolean fc_reassemble = TRUE; +static guint32 fc_max_frame_size = 1024; +static GHashTable *fc_fragment_table = NULL; + +static void fc_defragment_init(void) +{ + fragment_table_init(&fc_fragment_table); +} + + +static gchar * +fctl_to_str (const guint8 *fctl, gchar *str, gboolean is_ack) +{ + int stroff = 0; + guint8 tmp = 0; + + if (str == NULL) + return (str); + + if (fctl[0] & 0x80) { + strcpy (str, "Exchange Responder, "); + stroff += 20; + } + else { + strcpy (str, "Exchange Originator, "); + stroff += 21; + } + + if (fctl[0] & 0x40) { + strcpy (&str[stroff], "Seq Recipient, "); + stroff += 15; + } + else { + strcpy (&str[stroff], "Seq Initiator, "); + stroff += 15; + } + + if (fctl[0] & 0x20) { + strcpy (&str[stroff], "Exchg First, "); + stroff += 13; + } + + if (fctl[0] & 0x10) { + strcpy (&str[stroff], "Exchg Last, "); + stroff += 12; + } + + if (fctl[0] & 0x8) { + strcpy (&str[stroff], "Seq Last, "); + stroff += 10; + } + + if (fctl[0] & 0x2) { + strcpy (&str[stroff], "Priority, "); + stroff += 10; + } + else { + strcpy (&str[stroff], "CS_CTL, "); + stroff += 8; + } + + if (fctl[0] & 0x1) { + strcpy (&str[stroff], "Transfer Seq Initiative, "); + stroff += 25; + } + + if (fctl[1] & 0x30) { + strcpy (&str[stroff], "ACK_0 Reqd, "); + stroff += 12; + } + else if (fctl[1] & 0x10) { + strcpy (&str[stroff], "ACK_1 Reqd, "); + stroff += 12; + } + + if (fctl[1] & 0x2) { + strcpy (&str[stroff], "Rexmitted Seq, "); + stroff += 15; + } + + tmp = fctl[2] & 0xC0; + switch (tmp) { + case 0: + strcpy (&str[stroff], "Last Data Frame - No Info, "); + stroff += 27; + break; + case 1: + strcpy (&str[stroff], "Last Data Frame - Seq Imm, "); + stroff += 27; + break; + case 2: + strcpy (&str[stroff], "Last Data Frame - Seq Soon, "); + stroff += 28; + break; + case 3: + strcpy (&str[stroff], "Last Data Frame - Seq Delyd, "); + stroff += 29; + break; + } + + tmp = fctl[2] & 0x30; + switch (tmp) { + case 0: + if (is_ack) { + strcpy (&str[stroff], "ABTS - Cont, "); + stroff += 13; + } + else { + strcpy (&str[stroff], "ABTS - Abort/MS, "); + stroff += 17; + } + break; + case 0x10: + if (is_ack) { + strcpy (&str[stroff], "ABTS - Abort, "); + stroff += 14; + } + else { + strcpy (&str[stroff], "ABTS - Abort/SS, "); + stroff += 17; + } + break; + case 0x20: + if (is_ack) { + strcpy (&str[stroff], "ABTS - Stop, "); + stroff += 13; + } + else { + strcpy (&str[stroff], "ABTS - Process/IB, "); + stroff += 19; + } + break; + case 0x30: + if (is_ack) { + strcpy (&str[stroff], "ABTS - Imm Seq Retx, "); + stroff += 21; + } + else { + strcpy (&str[stroff], "ABTS - Discard/MS/Imm Retx, "); + stroff += 28; + } + break; + } + + if (fctl[2] & 0x8) { + strcpy (&str[stroff], "Rel Offset = 1"); + stroff += 14; + } + + return (str); +} + +/* BA_ACC & BA_RJT are decoded in this file itself instead of a traditional + * dedicated file and dissector format because the dissector would require some + * fields of the FC_HDR such as param in some cases, type in some others, the + * lower 4 bits of r_ctl in some other cases etc. So, we decode BLS & Link Ctl + * in this file itself. + */ +static void +dissect_fc_ba_acc (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + proto_item *ti; + proto_tree *acc_tree; + int offset = 0; + + /* Make entries in Protocol column and Info column on summary display */ + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "BLS"); + + if (check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "BA_ACC"); + + if (tree) { + ti = proto_tree_add_text (tree, tvb, 0, tvb_length (tvb), "Basic Link Svc"); + acc_tree = proto_item_add_subtree (ti, ett_fcbls); + + proto_tree_add_item (acc_tree, hf_fc_bls_seqid_vld, tvb, offset++, 1, 0); + proto_tree_add_item (acc_tree, hf_fc_bls_lastvld_seqid, tvb, offset++, 1, 0); + offset += 2; /* Skip reserved field */ + proto_tree_add_item (acc_tree, hf_fc_bls_oxid, tvb, offset, 2, 0); + offset += 2; + proto_tree_add_item (acc_tree, hf_fc_bls_rxid, tvb, offset, 2, 0); + offset += 2; + proto_tree_add_item (acc_tree, hf_fc_bls_lowseqcnt, tvb, offset, 2, 0); + offset += 2; + proto_tree_add_item (acc_tree, hf_fc_bls_hiseqcnt, tvb, offset, 2, 0); + } +} + +static void +dissect_fc_ba_rjt (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + proto_item *ti; + proto_tree *rjt_tree; + int offset = 0; + + /* Make entries in Protocol column and Info column on summary display */ + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "BLS"); + + if (check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "BA_RJT"); + + if (tree) { + ti = proto_tree_add_text (tree, tvb, 0, tvb_length (tvb), "Basic Link Svc"); + rjt_tree = proto_item_add_subtree (ti, ett_fcbls); + + proto_tree_add_item (rjt_tree, hf_fc_bls_rjtcode, tvb, offset+1, 1, 0); + proto_tree_add_item (rjt_tree, hf_fc_bls_rjtdetail, tvb, offset+2, 1, 0); + proto_tree_add_item (rjt_tree, hf_fc_bls_vendor, tvb, offset+3, 1, 0); + } +} + +static guint8 +fc_get_ftype (guint8 r_ctl, guint8 type) +{ + /* A simple attempt to determine the upper level protocol based on the + * r_ctl & type fields. + */ + switch (r_ctl & 0xF0) { + case FC_RCTL_DEV_DATA: + switch (type) { + case FC_TYPE_SWILS: + if ((r_ctl == 0x2) || (r_ctl == 0x3)) + return FC_FTYPE_SWILS; + else + return FC_FTYPE_UNDEF; + case FC_TYPE_IP: + return FC_FTYPE_IP; + case FC_TYPE_SCSI: + return FC_FTYPE_SCSI; + case FC_TYPE_FCCT: + return FC_FTYPE_FCCT; + default: + return FC_FTYPE_UNDEF; + } + break; + case FC_RCTL_ELS: + if (((r_ctl & 0x0F) == 0x2) || ((r_ctl & 0x0F) == 0x3)) + return FC_FTYPE_ELS; + else + return FC_FTYPE_UNDEF; + break; + case FC_RCTL_LINK_DATA: + return FC_FTYPE_LINKDATA; + break; + case FC_RCTL_VIDEO: + return FC_FTYPE_VDO; + break; + case FC_RCTL_BLS: + if (type == 0) + return FC_FTYPE_BLS; + else + return FC_FTYPE_UNDEF; + break; + case FC_RCTL_LINK_CTL: + return FC_FTYPE_LINKCTL; + break; + default: + return FC_FTYPE_UNDEF; + break; + } +} +/* Code to actually dissect the packets */ +static void +dissect_fc (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + proto_item *ti; + proto_tree *fc_tree = NULL; + tvbuff_t *next_tvb; + int offset = 0; + gboolean is_lastframe_inseq; + gboolean is_exchg_resp = 0; + fragment_data *fcfrag_head; + guint32 frag_id; + guint32 frag_size; + guint8 r_ctl, type; + + gchar str[256]; + guint32 param; + guint16 seqcnt; + guint8 ftype; + gboolean is_ack; + + /* Make entries in Protocol column and Info column on summary display */ + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "FC"); + + r_ctl = tvb_get_guint8 (tvb, offset); + + /* If the R_CTL is the EISL field, skip the first 8 bytes to retrieve the + * real FC header. EISL is Cisco-proprietary and is not decoded. + */ + if (r_ctl == FC_RCTL_EISL) { + offset += 8; + r_ctl = tvb_get_guint8 (tvb, offset); + } + + type = tvb_get_guint8 (tvb, offset+8); + seqcnt = tvb_get_ntohs (tvb, offset+14); + param = tvb_get_ntohl (tvb, offset+20); + + SET_ADDRESS (&pinfo->dst, AT_FC, 3, tvb_get_ptr (tvb, 1, 3)); + SET_ADDRESS (&pinfo->src, AT_FC, 3, tvb_get_ptr (tvb, 5, 3)); + pinfo->oxid = tvb_get_ntohs (tvb, offset+16); + pinfo->rxid = tvb_get_ntohs (tvb, offset+18); + pinfo->ptype = PT_EXCHG; + pinfo->r_ctl = r_ctl; + + is_ack = ((r_ctl == 0xC0) || (r_ctl == 0xC1)); + + ftype = fc_get_ftype (r_ctl, type); + + if (check_col (pinfo->cinfo, COL_INFO)) { + col_add_str (pinfo->cinfo, COL_INFO, match_strval (ftype, fc_ftype_vals)); + + if (ftype == FC_FTYPE_LINKCTL) + col_append_fstr (pinfo->cinfo, COL_INFO, ", %s", + match_strval ((r_ctl & 0x0F), + fc_lctl_proto_val)); + } + + /* In the interest of speed, if "tree" is NULL, don't do any work not + necessary to generate protocol tree items. */ + if (tree) { + ti = proto_tree_add_protocol_format (tree, proto_fc, tvb, offset, + FC_HEADER_SIZE, "Fibre Channel"); + fc_tree = proto_item_add_subtree (ti, ett_fc); + + if (ftype == FC_FTYPE_LINKCTL) { + /* the lower 4 bits of R_CTL indicate the type of link ctl frame */ + proto_tree_add_uint_format (fc_tree, hf_fc_rctl, tvb, offset, + FC_RCTL_SIZE, r_ctl, + "R_CTL: 0x%x(%s)", + r_ctl, + val_to_str ((r_ctl & 0x0F), + fc_lctl_proto_val, "0x%x")); + } + else if (ftype == FC_FTYPE_BLS) { + /* the lower 4 bits of R_CTL indicate the type of BLS frame */ + proto_tree_add_uint_format (fc_tree, hf_fc_rctl, tvb, offset, + FC_RCTL_SIZE, r_ctl, + "R_CTL: 0x%x(%s)", + r_ctl, + val_to_str ((r_ctl & 0x0F), + fc_bls_proto_val, "0x%x")); + } + else { + proto_tree_add_item (fc_tree, hf_fc_rctl, tvb, offset, 1, 0); + } + + proto_tree_add_uint_hidden (fc_tree, hf_fc_ftype, tvb, offset, 1, + ftype); + proto_tree_add_string (fc_tree, hf_fc_did, tvb, offset+1, 3, + fc_to_str ((guint8 *)tvb_get_ptr (tvb, + offset+1, 3))); + proto_tree_add_item (fc_tree, hf_fc_csctl, tvb, offset+4, 1, 0); + + proto_tree_add_string (fc_tree, hf_fc_sid, tvb, offset+5, 3, + fc_to_str ((guint8 *)tvb_get_ptr (tvb, + offset+5, 3))); + + if (ftype == FC_FTYPE_LINKCTL) { + if (((r_ctl & 0x0F) == FC_LCTL_FBSYB) || + ((r_ctl & 0x0F) == FC_LCTL_FBSYL)) { + /* for F_BSY frames, the upper 4 bits of the type field specify the + * reason for the BSY. + */ + proto_tree_add_uint_format (fc_tree, hf_fc_type, tvb, + offset+8, FC_TYPE_SIZE, + type, "Type: 0x%x(%s)", type, + fclctl_get_typestr (r_ctl & 0x0F, + type)); + } + else { + proto_tree_add_item (fc_tree, hf_fc_type, tvb, offset+8, 1, 0); + } + } + else { + proto_tree_add_item (fc_tree, hf_fc_type, tvb, offset+8, 1, 0); + } + + proto_tree_add_uint_format (fc_tree, hf_fc_fctl, tvb, offset+9, + 3, tvb_get_ntoh24 (tvb, offset+9), + "F_CTL: 0x%x (%s)", + tvb_get_ntoh24 (tvb, offset+9), + fctl_to_str (tvb_get_ptr (tvb, offset+9, 3), + str, is_ack)); + + /* Bit 23 if set => this frame is from the exchange originator */ + if (tvb_get_guint8 (tvb, offset+9) & 0x80) { + proto_tree_add_boolean_hidden (fc_tree, hf_fc_exchg_orig, tvb, + offset+9, 1, 0); + proto_tree_add_boolean_hidden (fc_tree, hf_fc_exchg_resp, tvb, + offset+9, 1, 1); + } + else { + proto_tree_add_boolean_hidden (fc_tree, hf_fc_exchg_orig, tvb, + offset+9, 1, 1); + proto_tree_add_boolean_hidden (fc_tree, hf_fc_exchg_resp, tvb, + offset+9, 1, 0); + } + + proto_tree_add_item (fc_tree, hf_fc_seqid, tvb, offset+12, 1, 0); + proto_tree_add_item (fc_tree, hf_fc_dfctl, tvb, offset+13, 1, 0); + proto_tree_add_item (fc_tree, hf_fc_seqcnt, tvb, offset+14, 2, 0); + proto_tree_add_item (fc_tree, hf_fc_oxid, tvb, offset+16, 2, 0); + proto_tree_add_uint (fc_tree, hf_fc_rxid, tvb, offset+18, 2, 0); + + if (ftype == FC_FTYPE_LINKCTL) { + if (((r_ctl & 0x0F) == FC_LCTL_FRJT) || + ((r_ctl & 0x0F) == FC_LCTL_PRJT) || + ((r_ctl & 0x0F) == FC_LCTL_PBSY)) { + /* In all these cases of Link Ctl frame, the parameter field + * encodes the detailed error message + */ + proto_tree_add_uint_format (fc_tree, hf_fc_param, tvb, + offset+20, 4, param, + "Parameter: 0x%x(%s)", param, + fclctl_get_paramstr ((r_ctl & 0x0F), + param)); + } + else { + proto_tree_add_item (fc_tree, hf_fc_param, tvb, offset+20, 4, 0); + } + } + else if (ftype == FC_FTYPE_BLS) { + if ((r_ctl & 0x0F) == FC_BLS_ABTS) { + proto_tree_add_uint_format (fc_tree, hf_fc_param, tvb, + offset+20, 4, param, + "Parameter: 0x%x(%s)", param, + ((param & 0x0F) == 1 ? "Abort Sequence" : + "Abort Exchange")); + } + else { + proto_tree_add_item (fc_tree, hf_fc_param, tvb, offset+20, + 4, 0); + } + } + else { + proto_tree_add_item (fc_tree, hf_fc_param, tvb, offset+20, 4, 0); + } + } + + if (ftype == FC_FTYPE_LINKCTL) { + /* ACK_1 frames and other LINK_CTL frames echo the last seq bit if the + * packet they're ack'ing did not have it set. So, we'll incorrectly + * flag them as being fragmented when they're not. This fixes the + * problem + */ + is_lastframe_inseq = TRUE; + } + else { + is_lastframe_inseq = tvb_get_guint8 (tvb, offset+9) & 0x08; + is_exchg_resp = ((tvb_get_guint8 (tvb, offset+20) & 0x80) == 0x80); + } + + frag_size = tvb_reported_length (tvb)-FC_HEADER_SIZE; + + if (!is_lastframe_inseq) { + /* Show this only as a fragmented FC frame */ + if (check_col (pinfo->cinfo, COL_INFO)) { + col_append_str (pinfo->cinfo, COL_INFO, " (Fragmented)"); + } + } + + /* If this is a fragment, attempt to check if fully reassembled frame is + * present, if we're configured to reassemble. + */ + if ((ftype != FC_FTYPE_LINKCTL) && (ftype != FC_FTYPE_BLS) && + seqcnt && fc_reassemble) { + /* Add this to the list of fragments */ + frag_id = (pinfo->oxid << 16) | is_exchg_resp; + + /* We assume that all frames are of the same max size */ + fcfrag_head = fragment_add (tvb, FC_HEADER_SIZE, pinfo, frag_id, + fc_fragment_table, + seqcnt * fc_max_frame_size, + frag_size, + TRUE); + + if (fcfrag_head) { + next_tvb = tvb_new_real_data (fcfrag_head->data, + fcfrag_head->datalen, + fcfrag_head->datalen); + tvb_set_child_real_data_tvbuff(tvb, next_tvb); + + /* Add the defragmented data to the data source list. */ + add_new_data_source(pinfo, next_tvb, "Reassembled FC"); + + if (tree) { + proto_tree_add_boolean_hidden (fc_tree, hf_fc_reassembled, + tvb, offset+9, 1, 1); + } + } + else { + if (tree) { + proto_tree_add_boolean_hidden (fc_tree, hf_fc_reassembled, + tvb, offset+9, 1, 0); + } + next_tvb = tvb_new_subset (tvb, offset+FC_HEADER_SIZE, -1, -1); + call_dissector (data_handle, next_tvb, pinfo, tree); + return; + } + } + else { + if (tree) { + proto_tree_add_boolean_hidden (fc_tree, hf_fc_reassembled, + tvb, offset+9, 1, 0); + } + next_tvb = tvb_new_subset (tvb, offset+FC_HEADER_SIZE, -1, -1); + } + + if ((ftype != FC_FTYPE_LINKCTL) && (ftype != FC_FTYPE_BLS)) { + if (!dissector_try_port (fcftype_dissector_table, ftype, next_tvb, + pinfo, tree)) { + call_dissector (data_handle, next_tvb, pinfo, tree); + } + } + else if (ftype == FC_FTYPE_BLS) { + if ((r_ctl & 0x0F) == FC_BLS_BAACC) { + dissect_fc_ba_acc (next_tvb, pinfo, tree); + } + else if ((r_ctl & 0x0F) == FC_BLS_BARJT) { + dissect_fc_ba_rjt (next_tvb, pinfo, tree); + } + } +} + + +/* Register the protocol with Ethereal */ + +/* this format is require because a script is used to build the C function + that calls all the protocol registration. +*/ + +void +proto_register_fc(void) +{ + +/* Setup list of header fields See Section 1.6.1 for details*/ + static hf_register_info hf[] = { + { &hf_fc_rctl, + { "R_CTL", "fc.r_ctl", FT_UINT8, BASE_HEX, NULL, 0x0, + "R_CTL", HFILL }}, + { &hf_fc_ftype, + {"Frame type", "fc.ftype", FT_UINT8, BASE_HEX, VALS(fc_ftype_vals), + 0x0, "Derived Type", HFILL}}, + { &hf_fc_did, + { "Dest Addr", "fc.d_id", FT_STRING, BASE_HEX, NULL, 0x0, + "Destination Address", HFILL}}, + { &hf_fc_csctl, + {"CS_CTL", "fc.cs_ctl", FT_UINT8, BASE_HEX, NULL, 0x0, + "CS_CTL", HFILL}}, + { &hf_fc_sid, + {"Src Addr", "fc.s_id", FT_STRING, BASE_HEX, NULL, 0x0, + "Source Address", HFILL}}, + { &hf_fc_type, + {"Type", "fc.type", FT_UINT8, BASE_HEX, VALS (fc_fc4_val), 0x0, + "", HFILL}}, + { &hf_fc_fctl, + {"F_CTL", "fc.f_ctl", FT_UINT24, BASE_HEX, NULL, 0x0, "", HFILL}}, + { &hf_fc_seqid, + {"SEQ_ID", "fc.seq_id", FT_UINT8, BASE_HEX, NULL, 0x0, + "Sequence ID", HFILL}}, + { &hf_fc_dfctl, + {"DF_CTL", "fc.df_ctl", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL}}, + { &hf_fc_seqcnt, + {"SEQ_CNT", "fc.seq_cnt", FT_UINT16, BASE_DEC, NULL, 0x0, + "Sequence Count", HFILL}}, + { &hf_fc_oxid, + {"OX_ID", "fc.ox_id", FT_UINT16, BASE_HEX, NULL, 0x0, "Originator ID", + HFILL}}, + { &hf_fc_rxid, + {"RX_ID", "fc.rx_id", FT_UINT16, BASE_HEX, NULL, 0x0, "Receiver ID", + HFILL}}, + { &hf_fc_param, + {"Parameter", "fc.parameter", FT_UINT32, BASE_HEX, NULL, 0x0, "Parameter", + HFILL}}, + + /* Basic Link Svc field definitions */ + { &hf_fc_bls_seqid_vld, + {"SEQID Valid", "fc.bls_seqidvld", FT_UINT8, BASE_HEX, + VALS (fc_bls_seqid_val), 0x0, "", HFILL}}, + { &hf_fc_bls_lastvld_seqid, + {"Last Valid SEQID", "fc.bls_lastseqid", FT_UINT8, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_fc_bls_oxid, + {"OXID", "fc.bls_oxid", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL}}, + { &hf_fc_bls_rxid, + {"RXID", "fc.bls_rxid", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL}}, + { &hf_fc_bls_lowseqcnt, + {"Low SEQCNT", "fc.bls_lseqcnt", FT_UINT16, BASE_HEX, NULL, 0x0, "", + HFILL}}, + { &hf_fc_bls_hiseqcnt, + {"High SEQCNT", "fc.bls_hseqcnt", FT_UINT16, BASE_HEX, NULL, 0x0, "", + HFILL}}, + { &hf_fc_bls_rjtcode, + {"Reason", "fc.bls_reason", FT_UINT8, BASE_HEX, VALS(fc_bls_barjt_val), + 0x0, "", HFILL}}, + { &hf_fc_bls_rjtdetail, + {"Reason Explanantion", "fc.bls_rjtdetail", FT_UINT8, BASE_HEX, + VALS (fc_bls_barjt_det_val), 0x0, "", HFILL}}, + { &hf_fc_bls_vendor, + {"Vendor Unique Reason", "fc.bls_vnduniq", FT_UINT8, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_fc_exchg_orig, + {"Exchange Originator", "fc.xchg_orig", FT_BOOLEAN, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_fc_exchg_resp, + {"Exchange Responder", "fc.xchg_resp", FT_BOOLEAN, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_fc_reassembled, + {"Reassembled Frame", "fc.reassembled", FT_BOOLEAN, BASE_HEX, NULL, + 0x0, "", HFILL}}, + }; + + /* Setup protocol subtree array */ + static gint *ett[] = { + &ett_fc, + &ett_fcbls, + }; + + module_t *fc_module; + + /* Register the protocol name and description */ + proto_fc = proto_register_protocol ("Fibre Channel", "FC", "fc"); + register_dissector ("fc", dissect_fc, proto_fc); + + /* Required function calls to register the header fields and subtrees used */ + proto_register_field_array(proto_fc, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + + fcftype_dissector_table = register_dissector_table ("fc.ftype", + "FC Frame Type", + FT_UINT8, BASE_HEX); + + /* Register preferences */ + fc_module = prefs_register_protocol (proto_fc, NULL); + prefs_register_bool_preference (fc_module, + "reassemble_fc", "Reassemble FC", + "If enabled, reassembly of multi-frame " + "sequences is done", + &fc_reassemble); + prefs_register_uint_preference (fc_module, + "fc_max_frame_size", "Max FC Frame Size", + "This is the size of non-last frames in a " + "multi-frame sequence", 10, + &fc_max_frame_size); + + register_init_routine(fc_defragment_init); +} + + +/* If this dissector uses sub-dissector registration add a registration routine. + This format is required because a script is used to find these routines and + create the code that calls these routines. +*/ +void +proto_reg_handoff_fc (void) +{ + dissector_handle_t fc_handle; + + fc_handle = create_dissector_handle (dissect_fc, proto_fc); + + data_handle = find_dissector("data"); +} diff --git a/packet-fc.h b/packet-fc.h new file mode 100644 index 0000000000..abfadbb813 --- /dev/null +++ b/packet-fc.h @@ -0,0 +1,153 @@ +/* packet-fc.h + * Basic Fibre Channel Header definitions + * Copyright 2002 Dinesh G Dutt (ddutt@cisco.com) + * + * $Id: packet-fc.h,v 1.1 2002/12/08 02:32:17 gerald Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * 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. + */ + +#ifndef __PACKET_FC_H_ +#define __PACKET_FC_H_ + +/* R_CTL upper bits creates a classification tree */ +#define FC_RCTL_DEV_DATA 0x00 +#define FC_RCTL_ELS 0x20 +#define FC_RCTL_LINK_DATA 0x30 +#define FC_RCTL_VIDEO 0x40 +#define FC_RCTL_BLS 0x80 +#define FC_RCTL_LINK_CTL 0xC0 + +/* TYPE (FC-4) Definitions */ + +#define FC_TYPE_SCSI 0x8 +#define FC_TYPE_IP 0x5 +#define FC_TYPE_LLCSNAP 0x4 +#define FC_TYPE_ELS 0x1 +#define FC_TYPE_FCCT 0x20 +#define FC_TYPE_SWILS 0x22 +#define FC_TYPE_AL 0x23 +#define FC_TYPE_SNMP 0x24 +#define FC_TYPE_CMNSVC 0x0 /* Used in PRLI Svc Param Page */ + +static const value_string fc_fc4_val[] = { + {FC_TYPE_SCSI , "FCP"}, + {FC_TYPE_IP , "IP/FC"}, + {FC_TYPE_LLCSNAP , "LLC_SNAP"}, + {FC_TYPE_ELS , "Ext Link Svc"}, + {FC_TYPE_FCCT , "FC_CT"}, + {FC_TYPE_SWILS , "SW_ILS"}, + {FC_TYPE_AL , "AL"}, + {FC_TYPE_SNMP , "SNMP"}, + {0, NULL}, +}; + +static const value_string fc_prli_fc4_val[] = { + {FC_TYPE_SCSI , "FCP"}, + {FC_TYPE_IP , "IP/FC"}, + {FC_TYPE_LLCSNAP , "LLC_SNAP"}, + {FC_TYPE_ELS , "Ext Link Svc"}, + {FC_TYPE_FCCT , "FC_CT"}, + {FC_TYPE_SWILS , "SW_ILS"}, + {FC_TYPE_AL , "AL"}, + {FC_TYPE_SNMP , "SNMP"}, + {FC_TYPE_CMNSVC , "Common to all FC-4 Types"}, + {0, NULL}, +}; + +/* Derived Frame types (used for ULP demux) */ +#define FC_FTYPE_UNDEF 0x0 +#define FC_FTYPE_SWILS 0x1 +#define FC_FTYPE_IP 0x2 +#define FC_FTYPE_SCSI 0x3 +#define FC_FTYPE_BLS 0x4 +#define FC_FTYPE_ELS 0x5 +#define FC_FTYPE_FCCT 0x7 +#define FC_FTYPE_LINKDATA 0x8 +#define FC_FTYPE_VDO 0x9 +#define FC_FTYPE_LINKCTL 0xA +#define FC_FTYPE_SWILS_RSP 0xB + +static const value_string fc_ftype_vals [] = { + {FC_FTYPE_UNDEF , "Unknown frame"}, + {FC_FTYPE_SWILS, "SW_ILS"}, + {FC_FTYPE_IP , "IP/FC"}, + {FC_FTYPE_SCSI , "FCP"}, + {FC_FTYPE_BLS , "Basic Link Svc"}, + {FC_FTYPE_ELS , "ELS"}, + {FC_FTYPE_FCCT , "FC_CT"}, + {FC_FTYPE_LINKDATA, "Link Data"}, + {FC_FTYPE_VDO, "Video Data"}, + {FC_FTYPE_LINKCTL, "Link Ctl"}, + {0, NULL}, +}; + +/* Well-known Address Definitions (in Network order) */ +#define FC_WKA_MULTICAST 0xFFFFF5 +#define FC_WKA_CLKSYNC 0xFFFFF6 +#define FC_WKA_KEYDIST 0xFFFFF7 +#define FC_WKA_ALIAS 0xFFFFF8 +#define FC_WKA_QOSF 0xFFFFF9 +#define FC_WKA_MGMT 0xFFFFFA +#define FC_WKA_TIME 0xFFFFFB +#define FC_WKA_DNS 0xFFFFFC +#define FC_WKA_FABRIC_CTRLR 0xFFFFFD +#define FC_WKA_FPORT 0xFFFFFE +#define FC_WKA_BCAST 0xFFFFFF + +/* Well-known Address Definitions (in little endian) */ + +static const value_string fc_wka_vals[] = { + {FC_WKA_MULTICAST, "Multicast Server"}, + {FC_WKA_CLKSYNC, "Clock Sync Server"}, + {FC_WKA_KEYDIST, "Key Distribution Server"}, + {FC_WKA_ALIAS, "Alias Server"}, + {FC_WKA_QOSF, "QoS Facilitator"}, + {FC_WKA_MGMT, "Management Server"}, + {FC_WKA_TIME, "Time Server"}, + {FC_WKA_DNS, "Directory Server"}, + {FC_WKA_FABRIC_CTRLR, "Fabric Ctlr"}, + {FC_WKA_FPORT, "F_Port Server"}, + {FC_WKA_BCAST, "Broadcast ID"}, + {0, NULL}, +}; + +/* Information Categories for Link Data & Link Control Frames */ +#define FC_IU_UNCATEGORIZED 0x0 +#define FC_IU_SOLICITED_DATA 0x1 +#define FC_IU_UNSOLICITED_CTL 0x2 +#define FC_IU_SOLICITED_CTL 0x3 +#define FC_IU_UNSOLICITED_DATA 0x4 +#define FC_IU_DATA_DESCRIPTOR 0x5 +#define FC_IU_UNSOLICITED_CMD 0x6 +#define FC_IU_CMD_STATUS 0x7 + +static const value_string fc_iu_val[] = { + {FC_IU_UNCATEGORIZED , "Uncategorized Data"}, + {FC_IU_SOLICITED_DATA , "Solicited Data"}, + {FC_IU_UNSOLICITED_CTL , "Unsolicited Control"}, + {FC_IU_SOLICITED_CTL , "Solicited Control"}, + {FC_IU_UNSOLICITED_DATA, "Solicited Data"}, + {FC_IU_DATA_DESCRIPTOR , "Data Descriptor"}, + {FC_IU_UNSOLICITED_CMD , "Unsolicited Command"}, + {FC_IU_CMD_STATUS , "Command Status"}, + {0, NULL}, +}; + +#endif diff --git a/packet-fcbls.h b/packet-fcbls.h new file mode 100644 index 0000000000..cda0ca02cb --- /dev/null +++ b/packet-fcbls.h @@ -0,0 +1,87 @@ +/* packet-fcbls.h + * Fibre Channel Basic Link Services header + * Copyright 2001, Dinesh G Dutt <ddutt@cisco.com> + * + * $Id: packet-fcbls.h,v 1.1 2002/12/08 02:32:17 gerald Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * 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. + */ + +#ifndef __PACKET_FCBLS_H_ +#define __PACKET_FCBLS_H_ + +#define FC_BLS_NOP 0x00 +#define FC_BLS_ABTS 0x01 +#define FC_BLS_RMC 0x02 +#define FC_BLS_BAACC 0x04 +#define FC_BLS_BARJT 0x05 +#define FC_BLS_PRMT 0x06 + +static const value_string fc_bls_proto_val[] = { + {FC_BLS_NOP , "NOP"}, + {FC_BLS_ABTS , "ABTS"}, + {FC_BLS_RMC , "RMC"}, + {FC_BLS_BAACC , "BA_ACC"}, + {FC_BLS_BARJT , "BA_RJT"}, + {FC_BLS_PRMT , "PRMT"}, + {0, NULL}, +}; + +#define FC_BLS_BARJT_INVCMDCODE 0x01 +#define FC_BLS_BARJT_LOGERR 0x03 +#define FC_BLS_BARJT_LOGBSY 0x05 +#define FC_BLS_BARJT_PROTERR 0x07 +#define FC_BLS_BARJT_GENFAIL 0x09 +#define FC_BLS_BARJT_VENDOR 0xFF + +static const value_string fc_bls_barjt_val[] = { + {FC_BLS_BARJT_INVCMDCODE, "Invalid Cmd Code"}, + {FC_BLS_BARJT_LOGERR , "Logical Error"}, + {FC_BLS_BARJT_LOGBSY , "Logical Busy"}, + {FC_BLS_BARJT_PROTERR , "Protocol Error"}, + {FC_BLS_BARJT_GENFAIL , "Unable to Perform Cmd"}, + {FC_BLS_BARJT_VENDOR , "Vendor Unique Error"}, + {0, NULL}, +}; + +#define FC_BLS_BARJT_DET_NODET 0x01 +#define FC_BLS_BARJT_DET_INVEXCHG 0x03 +#define FC_BLS_BARJT_DET_SEQABT 0x05 + +static const value_string fc_bls_barjt_det_val[] = { + {FC_BLS_BARJT_DET_NODET , "No Details"}, + {FC_BLS_BARJT_DET_INVEXCHG, "Invalid OXID-RXID Combo"}, + {FC_BLS_BARJT_DET_SEQABT , "Sequence Aborted"}, + {0, NULL}, +}; + +static const value_string fc_bls_seqid_val[] = { + {0x80, "Yes"}, + {0x0, "No"}, + {0, NULL}, +}; + +typedef struct _fc_bls_ba_rjt { + guint8 rsvd; + guint8 reason_code; + guint8 rjt_detail; + guint8 vendor_uniq; +} fc_bls_ba_rjt; + +#endif diff --git a/packet-fcels.c b/packet-fcels.c new file mode 100644 index 0000000000..0c57bb10d3 --- /dev/null +++ b/packet-fcels.c @@ -0,0 +1,1978 @@ +/* packet-fcels.c + * Routines for FC Extended Link Services + * Copyright 2001, Dinesh G Dutt <ddutt@cisco.com> + * + * $Id: packet-fcels.c,v 1.1 2002/12/08 02:32:17 gerald Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * Copyright 1998 Gerald Combs + * + * Copied from WHATEVER_FILE_YOU_USED (where "WHATEVER_FILE_YOU_USED" + * is a dissector file; if you just copied this from README.developer, + * don't bother with the "Copied from" - you don't even need to put + * in a "Copied from" if you copied an existing dissector, especially + * if the bulk of the code in the new dissector is your code) + * + * 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. + */ + +/* + * TODO Still (Complete compliance with FC-MI): + * - Decode RNID, RLIR + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif + +#include <glib.h> + +#ifdef NEED_SNPRINTF_H +# include "snprintf.h" +#endif + +#include <epan/packet.h> +#include <epan/conversation.h> +#include "etypes.h" +#include "packet-fc.h" +#include "packet-fcels.h" + +#define FC_ELS_RPLY 0 +#define FC_ELS_REQ 1 + +/* Initialize the protocol and registered fields */ +static int proto_fcels = -1; +static int hf_fcels_opcode = -1; +static int hf_fcels_rjtcode = -1; +static int hf_fcels_rjtdetcode = -1; +static int hf_fcels_vnduniq = -1; +static int hf_fcels_b2b = -1; +static int hf_fcels_cmnfeatures = -1; +static int hf_fcels_bbscnum = -1; +static int hf_fcels_rcvsize = -1; +static int hf_fcels_maxconseq = -1; +static int hf_fcels_reloffset = -1; +static int hf_fcels_edtov = -1; +static int hf_fcels_npname = -1; +static int hf_fcels_fnname = -1; +static int hf_fcels_cls1param = -1; +static int hf_fcels_cls2param = -1; +static int hf_fcels_cls3param = -1; +static int hf_fcels_cls4param = -1; +static int hf_fcels_vendorvers = -1; +static int hf_fcels_svcavail = -1; +static int hf_fcels_clsflags = -1; +static int hf_fcels_initctl = -1; +static int hf_fcels_rcptctl = -1; +static int hf_fcels_clsrcvsize = -1; +static int hf_fcels_conseq = -1; +static int hf_fcels_e2e = -1; +static int hf_fcels_openseq = -1; +static int hf_fcels_nportid = -1; +static int hf_fcels_oxid = -1; +static int hf_fcels_rxid = -1; +static int hf_fcels_recovqual = -1; +static int hf_fcels_fabricaddr = -1; +static int hf_fcels_fabricpname = -1; +static int hf_fcels_failedrcvr = -1; +static int hf_fcels_flacompliance = -1; +static int hf_fcels_loopstate = -1; +static int hf_fcels_publicloop_bmap = -1; +static int hf_fcels_pvtloop_bmap = -1; +static int hf_fcels_alpa_map = -1; +static int hf_fcels_scrregn = -1; +static int hf_fcels_farp_matchcodept = -1; +static int hf_fcels_farp_respaction = -1; +static int hf_fcels_resportid = -1; +static int hf_fcels_respname = -1; +static int hf_fcels_respnname = -1; +static int hf_fcels_reqipaddr = -1; +static int hf_fcels_respipaddr = -1; +static int hf_fcels_hardaddr = -1; +static int hf_fcels_rps_flag = -1; +static int hf_fcels_rps_portnum = -1; +static int hf_fcels_rps_portstatus = -1; +static int hf_fcels_rnft_fc4type = -1; +static int hf_fcels_rscn_evqual = -1; +static int hf_fcels_rscn_addrfmt = -1; +static int hf_fcels_rscn_domain = -1; +static int hf_fcels_rscn_area = -1; +static int hf_fcels_rscn_port = -1; +static int hf_fcels_nodeidfmt = -1; +static int hf_fcels_spidlen = -1; +static int hf_fcels_vendoruniq = -1; +static int hf_fcels_vendorsp = -1; +static int hf_fcels_asstype = -1; +static int hf_fcels_physport = -1; +static int hf_fcels_attnodes = -1; +static int hf_fcels_nodemgmt = -1; +static int hf_fcels_ipvers = -1; +static int hf_fcels_tcpport = -1; +static int hf_fcels_ip = -1; + +static gint ett_fcels; +static gint ett_fcels_lsrjt; +static gint ett_fcels_acc; +static gint ett_fcels_logi; +static gint ett_fcels_logi_cmnsvc; +static gint ett_fcels_logi_clssvc; +static gint ett_fcels_logo; +static gint ett_fcels_abtx; +static gint ett_fcels_rsi; +static gint ett_fcels_rrq; +static gint ett_fcels_prli; +static gint ett_fcels_prli_svcpg; +static gint ett_fcels_adisc; +static gint ett_fcels_farp; +static gint ett_fcels_rps; +static gint ett_fcels_rpl; +static gint ett_fcels_rplpb; +static gint ett_fcels_fan; +static gint ett_fcels_rscn; +static gint ett_fcels_rscn_rec; +static gint ett_fcels_scr; +static gint ett_fcels_rnft; +static gint ett_fcels_rnft_fc4; +static gint ett_fcels_lsts; +static gint ett_fcels_rnid; +static gint ett_fcels_rlir; +static gint ett_fcels_lirr; +static gint ett_fcels_srl; +static gint ett_fcels_rpsc; + +typedef struct _fcels_conv_key { + guint32 conv_idx; +} fcels_conv_key_t; + +typedef struct _fcels_conv_data { + guint32 opcode; +} fcels_conv_data_t; + +GHashTable *fcels_req_hash = NULL; +GMemChunk *fcels_req_keys = NULL; +GMemChunk *fcels_req_vals = NULL; +guint32 fcels_init_count = 25; + +static dissector_handle_t data_handle; + +/* + * Hash Functions + */ +static gint +fcels_equal(gconstpointer v, gconstpointer w) +{ + fcels_conv_key_t *v1 = (fcels_conv_key_t *)v; + fcels_conv_key_t *v2 = (fcels_conv_key_t *)w; + + return (v1->conv_idx == v2->conv_idx); +} + +static guint +fcels_hash (gconstpointer v) +{ + fcels_conv_key_t *key = (fcels_conv_key_t *)v; + guint val; + + val = key->conv_idx; + + return val; +} + +/* + * Protocol initialization + */ +static void +fcels_init_protocol(void) +{ + if (fcels_req_keys) + g_mem_chunk_destroy(fcels_req_keys); + if (fcels_req_vals) + g_mem_chunk_destroy(fcels_req_vals); + if (fcels_req_hash) + g_hash_table_destroy(fcels_req_hash); + + fcels_req_hash = g_hash_table_new(fcels_hash, fcels_equal); + fcels_req_keys = g_mem_chunk_new ("fcels_req_keys", + sizeof(fcels_conv_key_t), + fcels_init_count * + sizeof(fcels_conv_key_t), + G_ALLOC_AND_FREE); + fcels_req_vals = g_mem_chunk_new ("fcels_req_vals", + sizeof(fcels_conv_data_t), + fcels_init_count * + sizeof(fcels_conv_data_t), + G_ALLOC_AND_FREE); +} + +static void +construct_cmnsvc_string (guint16 flag, gchar *flagstr, guint8 opcode) +{ + int stroff = 0; + gchar punc[3]; + + punc[0] = '\0'; + + if ((opcode == FC_ELS_PLOGI) || (opcode == FC_ELS_PDISC)) { + if (flag & 0x8000) { + strcpy (flagstr, "Cont. Incr. Offset Supported"); + stroff += 28; + strcpy (punc, ", "); + } + if (flag & 0x4000) { + sprintf (&flagstr[stroff], "%sRRO Supported", punc); + stroff += 15; + strcpy (punc, ", "); + } + } + + if (flag & 0x2000) { + sprintf (flagstr, "%sValid Vendor Version Level", punc); + strcpy (punc, ", "); + stroff += 28; + } + + if (flag & 0x0800) { + sprintf (&flagstr[stroff], "%sNormal B2B Credit Mgmt", punc); + strcpy (punc, ", "); + stroff += 24; + } + else { + sprintf (&flagstr[stroff], "%sAlt B2B Credit Mgmt", punc); + strcpy (punc, ", "); + stroff += 21; + } + + if ((opcode == FC_ELS_PLOGI) || (opcode == FC_ELS_PDISC)) { + if (flag & 0x0400) { + strcpy (&flagstr[stroff], ", E_D_TOV Resolution in ns"); + } + else { + strcpy (&flagstr[stroff], ", E_D_TOV Resolution in ms"); + } + stroff += 26; + + if (flag & 0x0040) { + strcpy (&flagstr[stroff], ", Simplex Dedicated Conn Supported"); + stroff += 34; + } + } + + if (flag & 0x0010) { + strcpy (&flagstr[stroff], ", Clk Sync Prim Capable"); + stroff += 23; + } + if (flag & 0x0004) { + strcpy (&flagstr[stroff], ", DHD Capable"); + stroff += 13; + } + + if ((opcode == FC_ELS_PLOGI) || (opcode == FC_ELS_PDISC)) { + if (flag & 0x0002) { + strcpy (&flagstr[stroff], ", Cont. Incr SEQCNT rules"); + stroff += 25; + } + else { + strcpy (&flagstr[stroff], ", Normal SEQCNT rules"); + stroff += 21; + } + } + + if (flag & 0x0001) { + sprintf (&flagstr[stroff], ", Payload Len=256 bytes"); + } + else { + sprintf (&flagstr[stroff], ", Payload Len=116 bytes"); + } +} + +/* The next 3 routines decode only Class 2 & Class 3 relevant bits */ +static void +construct_clssvc_string (guint16 flag, gchar *flagstr, guint8 opcode) +{ + int stroff = 0; + + if (!(flag & 0x8000)) { + strcpy (flagstr, "Class Not Supported"); + return; + } + + if ((opcode == FC_ELS_FLOGI) || (opcode == FC_ELS_FDISC)) { + if (flag & 0x0800) { + strcpy (flagstr, "Seq Delivery Requested"); + stroff += 22; + } + else { + strcpy (flagstr, "Out of Order Delivery Requested"); + stroff += 31; + } + } + + if (flag & 0x0080) { + strcpy (&flagstr[stroff], ", Priority/preemption supported"); + stroff += 31; + } + + if ((opcode == FC_ELS_PLOGI) || (opcode == FC_ELS_PDISC)) { + if (flag & 0x0040) { + strcpy (&flagstr[stroff], "Non-zero CS_CTL Tolerated"); + } + else { + strcpy (&flagstr[stroff], "Non-zero CS_CTL Maybe Tolerated"); + } + } +} + +static void +construct_initctl_string (guint16 flag, gchar *flagstr, guint8 opcode) +{ + int stroff = 0; + + if ((opcode == FC_ELS_PLOGI) || (opcode == FC_ELS_PDISC)) { + switch ((flag & 0x3000)) { + case 0x0: + strcpy (flagstr, "Initial P_A Not Supported"); + stroff += 25; + break; + case 0x1000: + strcpy (flagstr, "Initial P_A Supported"); + stroff += 21; + break; + case 0x3000: + strcpy (flagstr, "Initial P_A Required & Supported"); + stroff += 32; + break; + } + + if (flag & 0x0800) { + strcpy (&flagstr[stroff], ", ACK0 Capable"); + stroff += 14; + } + + if (flag & 0x0200) { + strcpy (&flagstr[stroff], ", ACK Generation Assistance Avail"); + stroff += 33; + } + if (flag & 0x0010) { + strcpy (&flagstr[stroff], ", Clock Sync ELS Supported"); + } + } + else { + if (flag & 0x0010) { + strcpy (&flagstr[stroff], "Clock Sync ELS Supported"); + } + } +} + +static void +construct_rcptctl_string (guint16 flag, gchar *flagstr, guint8 opcode) +{ + int stroff = 0; + gchar punc[3]; + + punc[0] = '\0'; + + if ((opcode == FC_ELS_PLOGI) || (opcode == FC_ELS_PDISC)) { + if (flag & 0x8000) { + strcpy (flagstr, "ACK_0 Supported"); + stroff += 15; + } + else { + strcpy (flagstr, "ACK_0 Not Supported"); + stroff += 19; + } + + if (flag & 0x2000) { + strcpy (&flagstr[stroff], ", X_ID Interlock Reqd"); + stroff += 21; + } + + switch (flag & 0x1800) { + case 0x0: + strcpy (&flagstr[stroff], ", Error Policy: Discard Policy only"); + stroff += 43; + break; + case 0x1000: + strcpy (&flagstr[stroff], ", Error Policy: Both discard and process policies supported"); + stroff += 52; + break; + case 0x0800: + strcpy (&flagstr[stroff], ", Error Policy: Reserved"); + stroff += 41; + break; + case 0x1800: + strcpy (&flagstr[stroff], ", Error Policy: Reserved"); + stroff += 52; + break; + } + + switch (flag & 0x0030) { + case 0: + strcpy (&flagstr[stroff], ", 1 Category/Seq"); + stroff += 16; + break; + case 0x0010: + strcpy (&flagstr[stroff], ", 2 Categories/Seq"); + stroff += 18; + break; + case 0x0030: + strcpy (&flagstr[stroff], ", More than 2 Categories/Seq"); + stroff += 28; + break; + } + + if (flag & 0x0008) { + strcpy (&flagstr[stroff], ", Clk Sync ELS Supported"); + } + } + else { + if (flag & 0x0008) { + strcpy (&flagstr[stroff], "Clk Sync ELS Supported"); + } + } +} + + +static void +dissect_fcels_logi (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + proto_item *ti, guint8 opcode) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 0, + svcvld = 0, + class; + proto_tree *logi_tree, *cmnsvc_tree; + proto_item *subti; + gchar flagstr[256]; + guint16 flag; + + if (tree) { + logi_tree = proto_item_add_subtree (ti, ett_fcels_logi); + proto_tree_add_item (logi_tree, hf_fcels_opcode, tvb, offset, 1, 0); + + subti = proto_tree_add_text (logi_tree, tvb, offset+4, 16, + "Common Svc Parameters"); + cmnsvc_tree = proto_item_add_subtree (subti, ett_fcels_logi_cmnsvc); + proto_tree_add_item (cmnsvc_tree, hf_fcels_b2b, tvb, offset+6, 2, 0); + flag = tvb_get_ntohs (tvb, offset+8); + + if (flag & 0x0001) { + svcvld = 1; + } + + construct_cmnsvc_string (flag, flagstr, opcode); + proto_tree_add_uint_format (cmnsvc_tree, hf_fcels_cmnfeatures, tvb, + offset+8, 2, flag, + "Common Svc Parameters: 0x%x (%s)", + flag, flagstr); + + proto_tree_add_item (cmnsvc_tree, hf_fcels_bbscnum, tvb, offset+10, 1, 0); + proto_tree_add_item (cmnsvc_tree, hf_fcels_rcvsize, tvb, offset+10, 2, 0); + proto_tree_add_item (cmnsvc_tree, hf_fcels_maxconseq, tvb, offset+12, 2, 0); + proto_tree_add_item (cmnsvc_tree, hf_fcels_reloffset, tvb, offset+14, 2, 0); + proto_tree_add_item (cmnsvc_tree, hf_fcels_edtov, tvb, offset+16, 4, 0); + proto_tree_add_string (cmnsvc_tree, hf_fcels_npname, tvb, offset+20, 8, + fcwwn_to_str (tvb_get_ptr (tvb, offset+20, 8))); + proto_tree_add_string (cmnsvc_tree, hf_fcels_fnname, tvb, offset+28, 8, + fcwwn_to_str (tvb_get_ptr (tvb, offset+28, 8))); + + /* Add subtree for class paramters */ + offset = 36; + for (class = 1; class < 5; class++) { + subti = proto_tree_add_text (logi_tree, tvb, offset, 16, + "Class %d Svc Parameters", class); + cmnsvc_tree = proto_item_add_subtree (subti, ett_fcels_logi_cmnsvc); + + flag = tvb_get_ntohs (tvb, offset); + construct_clssvc_string (flag, flagstr, opcode); + proto_tree_add_uint_format (cmnsvc_tree, hf_fcels_clsflags, tvb, + offset, 2, flag, + "Service Options: 0x%x(%s)", flag, + flagstr); + if (flag & 0x8000) { + flag = tvb_get_ntohs (tvb, offset+2); + construct_initctl_string (flag, flagstr, opcode); + proto_tree_add_uint_format (cmnsvc_tree, hf_fcels_initctl, tvb, + offset+2, 2, flag, + "Initiator Control: 0x%x(%s)", flag, + flagstr); + + flag = tvb_get_ntohs (tvb, offset+4); + construct_rcptctl_string (flag, flagstr, opcode); + proto_tree_add_uint_format (cmnsvc_tree, hf_fcels_initctl, tvb, + offset+4, 2, flag, + "Recipient Control: 0x%x(%s)", flag, + flagstr); + + proto_tree_add_item (cmnsvc_tree, hf_fcels_clsrcvsize, tvb, + offset+6, 2, 0); + proto_tree_add_item (cmnsvc_tree, hf_fcels_conseq, tvb, + offset+8, 2, 0); + proto_tree_add_item (cmnsvc_tree, hf_fcels_e2e, tvb, + offset+10, 2, 0); + proto_tree_add_item (cmnsvc_tree, hf_fcels_openseq, tvb, + offset+12, 2, 0); + } + offset += 16; + } + proto_tree_add_item (logi_tree, hf_fcels_vendorvers, tvb, offset, 16, 0); + if (svcvld) { + proto_tree_add_item (logi_tree, hf_fcels_svcavail, tvb, offset+32, 8, 0); + } + } +} + +static void +dissect_fcels_plogi (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + guint8 isreq _U_, proto_item *ti) +{ + dissect_fcels_logi (tvb, pinfo, tree, ti, FC_ELS_PLOGI); +} + +static void +dissect_fcels_flogi (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + guint8 isreq _U_, proto_item *ti) +{ + dissect_fcels_logi (tvb, pinfo, tree, ti, FC_ELS_FLOGI); +} + +static void +dissect_fcels_logout (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + guint8 isreq, proto_item *ti) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 5; /* bypass opcode+rsvd field */ + proto_tree *logo_tree; + + if (tree) { + logo_tree = proto_item_add_subtree (ti, ett_fcels_logo); + + proto_tree_add_item (logo_tree, hf_fcels_opcode, tvb, offset-5, 1, 0); + + if (!isreq) { + /* Accept has no payload */ + return; + } + + proto_tree_add_string (logo_tree, hf_fcels_nportid, tvb, offset, 3, + fc_to_str (tvb_get_ptr (tvb, offset, 3))); + proto_tree_add_string (logo_tree, hf_fcels_npname, tvb, offset+3, 6, + fcwwn_to_str (tvb_get_ptr (tvb, offset+3, 6))); + } +} + +static void +dissect_fcels_abtx (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + guint8 isreq, proto_item *ti) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 0; + proto_tree *abtx_tree; + + if (tree) { + abtx_tree = proto_item_add_subtree (ti, ett_fcels_abtx); + + proto_tree_add_item (abtx_tree, hf_fcels_opcode, tvb, offset, 1, 0); + + if (!isreq) { + return; + } + + proto_tree_add_text (abtx_tree, tvb, offset+4, 1, + "Recovery Qualifier Status: 0x%x", + tvb_get_guint8 (tvb, offset+4)); + proto_tree_add_string (abtx_tree, hf_fcels_nportid, tvb, offset+5, 3, + fc_to_str (tvb_get_ptr (tvb, offset+5, 3))); + proto_tree_add_item (abtx_tree, hf_fcels_oxid, tvb, offset+8, 2, 0); + proto_tree_add_item (abtx_tree, hf_fcels_rxid, tvb, offset+10, 2, 0); + } +} + +static void +dissect_fcels_rsi (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + guint8 isreq, proto_item *ti) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 4; + proto_tree *rsi_tree; + + if (tree) { + rsi_tree = proto_item_add_subtree (ti, ett_fcels_rsi); + + proto_tree_add_item (rsi_tree, hf_fcels_opcode, tvb, offset-4, 1, 0); + if (!isreq) + return; + + proto_tree_add_item (rsi_tree, hf_fcels_recovqual, tvb, offset, 1, 0); + proto_tree_add_string (rsi_tree, hf_fcels_nportid, tvb, offset+1, 3, + fc_to_str (tvb_get_ptr (tvb, offset+1, 3))); + proto_tree_add_item (rsi_tree, hf_fcels_rxid, tvb, offset+4, 2, 0); + proto_tree_add_item (rsi_tree, hf_fcels_oxid, tvb, offset+6, 2, 0); + } +} + +static void +dissect_fcels_rrq (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + guint8 isreq, proto_item *ti) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 0; + proto_tree *rrq_tree; + + if (tree) { + rrq_tree = proto_item_add_subtree (ti, ett_fcels_rrq); + + proto_tree_add_item (rrq_tree, hf_fcels_opcode, tvb, offset, 1, 0); + if (!isreq) + return; + + proto_tree_add_string (rrq_tree, hf_fcels_nportid, tvb, offset+5, 3, + fc_to_str (tvb_get_ptr (tvb, offset+5, 3))); + proto_tree_add_item (rrq_tree, hf_fcels_oxid, tvb, offset+8, 2, 0); + proto_tree_add_item (rrq_tree, hf_fcels_rxid, tvb, offset+10, 2, 0); + } +} + +static void +dissect_fcels_pdisc (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + guint8 isreq _U_, proto_item *ti) +{ + dissect_fcels_logi (tvb, pinfo, tree, ti, FC_ELS_PDISC); +} + +static void +dissect_fcels_fdisc (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + guint8 isreq _U_, proto_item *ti) +{ + dissect_fcels_logi (tvb, pinfo, tree, ti, FC_ELS_FDISC); +} + +static void +dissect_fcels_adisc (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + guint8 isreq _U_, proto_item *ti) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 5; + proto_tree *adisc_tree; + + if (tree) { + adisc_tree = proto_item_add_subtree (ti, ett_fcels_adisc); + + proto_tree_add_item (adisc_tree, hf_fcels_opcode, tvb, offset-5, 1, 0); + + proto_tree_add_string (adisc_tree, hf_fcels_hardaddr, tvb, offset, 3, + fc_to_str (tvb_get_ptr (tvb, offset, 3))); + proto_tree_add_string (adisc_tree, hf_fcels_npname, tvb, offset+3, 8, + fcwwn_to_str (tvb_get_ptr (tvb, offset+3, 8))); + proto_tree_add_string (adisc_tree, hf_fcels_fnname, tvb, offset+11, 8, + fcwwn_to_str (tvb_get_ptr (tvb, offset+11, 8))); + proto_tree_add_string (adisc_tree, hf_fcels_nportid, tvb, offset+20, 3, + fc_to_str (tvb_get_ptr (tvb, offset+20, 3))); + } + +} + +static void +dissect_fcels_farp (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + proto_item *ti) +{ + int offset = 4; + proto_tree *farp_tree; + + if (tree) { + farp_tree = proto_item_add_subtree (ti, ett_fcels_farp); + + proto_tree_add_item (farp_tree, hf_fcels_opcode, tvb, offset-4, 1, 0); + + proto_tree_add_item (farp_tree, hf_fcels_farp_matchcodept, + tvb, offset, 1, 0); + proto_tree_add_string (farp_tree, hf_fcels_nportid, tvb, offset+1, + 3, fc_to_str (tvb_get_ptr (tvb, offset+1, 3))); + proto_tree_add_item (farp_tree, hf_fcels_farp_respaction, tvb, + offset+4, 1, 0); + proto_tree_add_string (farp_tree, hf_fcels_resportid, tvb, offset+5, + 3, fc_to_str (tvb_get_ptr (tvb, offset+5, 3))); + proto_tree_add_string (farp_tree, hf_fcels_npname, tvb, offset+8, + 8, fcwwn_to_str (tvb_get_ptr (tvb, offset+8, 8))); + proto_tree_add_string (farp_tree, hf_fcels_fnname, tvb, offset+16, + 8, fcwwn_to_str (tvb_get_ptr (tvb, offset+16, 8))); + proto_tree_add_string (farp_tree, hf_fcels_respname, tvb, offset+24, + 8, fcwwn_to_str (tvb_get_ptr (tvb, offset+24, 8))); + proto_tree_add_string (farp_tree, hf_fcels_respnname, tvb, offset+32, + 8, fcwwn_to_str (tvb_get_ptr (tvb, offset+32, 8))); + proto_tree_add_item (farp_tree, hf_fcels_reqipaddr, tvb, offset+40, + 16, 0); + proto_tree_add_item (farp_tree, hf_fcels_respipaddr, tvb, offset+56, + 16, 0); + } +} + +static void +dissect_fcels_farp_req (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + guint8 isreq _U_, proto_item *ti) +{ + dissect_fcels_farp (tvb, pinfo, tree, ti); +} + +static void +dissect_fcels_farp_rply (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + guint8 isreq _U_, proto_item *ti) +{ + dissect_fcels_farp (tvb, pinfo, tree, ti); +} + +static void +dissect_fcels_rps (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + guint8 isreq, proto_item *ti) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 3; + guint8 flag; + proto_tree *rps_tree; + + flag = tvb_get_guint8 (tvb, offset); + + if (tree) { + rps_tree = proto_item_add_subtree (ti, ett_fcels_rps); + + if (isreq) { + proto_tree_add_item (rps_tree, hf_fcels_rps_flag, tvb, offset, 1, 0); + + proto_tree_add_item (rps_tree, hf_fcels_opcode, tvb, offset-3, 1, 0); + + if (flag & 0x2) { + proto_tree_add_string (rps_tree, hf_fcels_npname, tvb, offset+1, + 8, fcwwn_to_str (tvb_get_ptr (tvb, + offset+1, + 8))); + } + else if (flag & 0x1) { + proto_tree_add_item (rps_tree, hf_fcels_rps_portnum, tvb, + offset+5, 3, 0); + } + } + else { + proto_tree_add_item (rps_tree, hf_fcels_rps_flag, tvb, offset, 1, 0); + proto_tree_add_item (rps_tree, hf_fcels_rps_portstatus, tvb, + offset+3, 2, 0); + /* Next 6 fields are from Link Error Status Block (LESB) */ + proto_tree_add_text (rps_tree, tvb, offset+5, 4, + "Link Failure Count: %d", + tvb_get_ntohl (tvb, offset+5)); + proto_tree_add_text (rps_tree, tvb, offset+9, 4, + "Loss of Sync Count: %d", + tvb_get_ntohl (tvb, offset+9)); + proto_tree_add_text (rps_tree, tvb, offset+13, 4, + "Loss of Signal Count: %d", + tvb_get_ntohl (tvb, offset+13)); + proto_tree_add_text (rps_tree, tvb, offset+17, 4, + "Primitive Seq Protocol Err: %d", + tvb_get_ntohl (tvb, offset+17)); + proto_tree_add_text (rps_tree, tvb, offset+21, 4, + "Invalid Xmission Word: %d", + tvb_get_ntohl (tvb, offset+21)); + proto_tree_add_text (rps_tree, tvb, offset+25, 4, + "Invalid CRC Count: %d", + tvb_get_ntohl (tvb, offset+25)); + if (flag & 0x01) { + /* Next 6 fields are from L_Port Extension field */ + proto_tree_add_text (rps_tree, tvb, offset+31, 2, + "L_Port Status: 0x%x", + tvb_get_ntohs (tvb, offset+31)); + proto_tree_add_text (rps_tree, tvb, offset+36, 1, + "LIP AL_PS: 0x%x", + tvb_get_guint8 (tvb, offset+36)); + proto_tree_add_text (rps_tree, tvb, offset+37, 4, + "LIP F7 Initiated Count: %d", + tvb_get_ntohl (tvb, offset+37)); + proto_tree_add_text (rps_tree, tvb, offset+41, 4, + "LIP F7 Received Count: %d", + tvb_get_ntohl (tvb, offset+41)); + proto_tree_add_text (rps_tree, tvb, offset+45, 4, + "LIP F8 Initiated Count: %d", + tvb_get_ntohl (tvb, offset+45)); + proto_tree_add_text (rps_tree, tvb, offset+49, 4, + "LIP F8 Received Count: %d", + tvb_get_ntohl (tvb, offset+49)); + proto_tree_add_text (rps_tree, tvb, offset+53, 4, + "LIP Reset Initiated Count: %d", + tvb_get_ntohl (tvb, offset+53)); + proto_tree_add_text (rps_tree, tvb, offset+57, 4, + "LIP Reset Received Count: %d", + tvb_get_ntohl (tvb, offset+57)); + } + } + } +} + +static void +dissect_fcels_rpl (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + guint8 isreq, proto_item *ti) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 0; + proto_tree *rpl_tree, *pb_tree; + proto_item *subti; + int loop; + + if (tree) { + rpl_tree = proto_item_add_subtree (ti, ett_fcels_rpl); + + proto_tree_add_item (rpl_tree, hf_fcels_opcode, tvb, offset, 1, 0); + + if (isreq) { + proto_tree_add_text (rpl_tree, tvb, offset+6, 2, + "Max Size: %d", + tvb_get_ntohs (tvb, offset+6)); + proto_tree_add_text (rpl_tree, tvb, offset+9, 3, + "Index: %d", + tvb_get_ntoh24 (tvb, offset+9)); + } + else { + /* Reply consists of a header and a number of port blocks */ + proto_tree_add_text (rpl_tree, tvb, offset+2, 2, + "Payload Length: %d", + tvb_get_ntohs (tvb, offset+2)); + proto_tree_add_text (rpl_tree, tvb, offset+5, 3, + "List Length: %d", + tvb_get_ntoh24 (tvb, offset+5)); + proto_tree_add_text (rpl_tree, tvb, offset+9, 3, + "Index of I Port Block: %d", + tvb_get_ntoh24 (tvb, offset+9)); + offset = 12; + /* The following loop is for dissecting the port blocks */ + for (loop = tvb_get_ntoh24 (tvb, 5); loop > 0; loop--) { + subti = proto_tree_add_text (rpl_tree, tvb, offset+12, 16, + "Port Block %d", loop); + pb_tree = proto_item_add_subtree (subti, ett_fcels_rplpb); + + proto_tree_add_text (pb_tree, tvb, offset, 4, + "Physical Port #: %d", + tvb_get_ntohl (tvb, offset)); + proto_tree_add_text (pb_tree, tvb, offset+5, 3, + "Port Identifier: %s", + fc_to_str (tvb_get_ptr (tvb, offset+5, 3))); + proto_tree_add_text (pb_tree, tvb, offset+8, 8, + "Port Name: %s", + fcwwn_to_str (tvb_get_ptr (tvb, offset+8, 8))); + offset += 16; + } + } + } +} + +static void +dissect_fcels_fan (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + guint8 isreq _U_, proto_item *ti) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 5; + proto_tree *fan_tree; + + if (tree) { + fan_tree = proto_item_add_subtree (ti, ett_fcels_fan); + + proto_tree_add_item (fan_tree, hf_fcels_opcode, tvb, offset-5, 1, 0); + + proto_tree_add_string (fan_tree, hf_fcels_fabricaddr, tvb, offset, 3, + fc_to_str (tvb_get_ptr (tvb, offset, 3))); + proto_tree_add_string (fan_tree, hf_fcels_fabricpname, tvb, offset+3, + 8, fcwwn_to_str (tvb_get_ptr (tvb, offset, 8))); + proto_tree_add_string (fan_tree, hf_fcels_fnname, tvb, offset+11, 8, + fcwwn_to_str (tvb_get_ptr (tvb, offset+11, 8))); + } +} + +static void +dissect_fcels_rscn (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + guint8 isreq, proto_item *ti) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 1; + proto_tree *rscn_tree, *rectree; + proto_item *subti; + int numrec, plen, i; + + if (tree) { + rscn_tree = proto_item_add_subtree (ti, ett_fcels_rscn); + + proto_tree_add_item (rscn_tree, hf_fcels_opcode, tvb, offset-1, 1, 0); + if (!isreq) + return; + + proto_tree_add_text (rscn_tree, tvb, offset, 1, + "Page Len: %d", tvb_get_guint8 (tvb, offset)); + plen = tvb_get_ntohs (tvb, offset+1); + proto_tree_add_text (rscn_tree, tvb, offset+1, 2, + "Payload Len: %d", plen); + numrec = (plen - 4)/4; + + offset = 4; + for (i = 0; i < numrec; i++) { + subti = proto_tree_add_text (rscn_tree, tvb, offset, 4, + "Affected N_Port Page %d", i); + rectree = proto_item_add_subtree (subti, ett_fcels_rscn_rec); + + proto_tree_add_item (rectree, hf_fcels_rscn_evqual, tvb, offset, + 1, 0); + proto_tree_add_item (rectree, hf_fcels_rscn_addrfmt, tvb, offset, + 1, 0); + proto_tree_add_item (rectree, hf_fcels_rscn_domain, tvb, offset+1, + 1, 0); + proto_tree_add_item (rectree, hf_fcels_rscn_area, tvb, offset+2, + 1, 0); + proto_tree_add_item (rectree, hf_fcels_rscn_port, tvb, offset+3, + 1, 0); + offset += 4; + } + } +} + +static void +dissect_fcels_scr (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + guint8 isreq, proto_item *ti) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 7; + proto_tree *scr_tree; + + if (tree) { + scr_tree = proto_item_add_subtree (ti, ett_fcels_scr); + proto_tree_add_item (scr_tree, hf_fcels_opcode, tvb, offset-7, 1, 0); + if (isreq) + proto_tree_add_item (scr_tree, hf_fcels_scrregn, tvb, offset, 1, 0); + } +} + +static void +dissect_fcels_rnft (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + guint8 isreq, proto_item *ti) +{ + int offset = 0; + guint16 numrec, i; + proto_tree *rnft_tree, *fc4_tree; + proto_item *subti; + + if (tree) { + rnft_tree = proto_item_add_subtree (ti, ett_fcels_rnft); + + proto_tree_add_item (rnft_tree, hf_fcels_opcode, tvb, offset, 1, 0); + + if (isreq) { + proto_tree_add_text (rnft_tree, tvb, offset+2, 2, + "Max Size: %d", tvb_get_ntohs (tvb, offset+2)); + proto_tree_add_text (rnft_tree, tvb, offset+7, 1, + "Index: %d", tvb_get_guint8 (tvb, offset+7)); + } + else { + proto_tree_add_text (rnft_tree, tvb, offset+2, 2, + "Payload Len: %d", + tvb_get_ntohs (tvb, offset+2)); + numrec = tvb_get_guint8 (tvb, offset+5); + proto_tree_add_text (rnft_tree, tvb, offset+5, 1, + "List Length: %d", numrec); + proto_tree_add_text (rnft_tree, tvb, offset+7, 1, + "Index of First Rec in List: %d", + tvb_get_guint8 (tvb, offset+7)); + offset = 8; + for (i = 0; i < numrec; i++) { + subti = proto_tree_add_text (rnft_tree, tvb, offset, 4, + "FC-4 Entry #%d", i); + fc4_tree = proto_item_add_subtree (subti, ett_fcels_rnft_fc4); + + proto_tree_add_item (fc4_tree, hf_fcels_rnft_fc4type, tvb, + offset, 1, 0); + proto_tree_add_text (fc4_tree, tvb, offset+1, 3, + "FC-4 Qualifier 0x%x", + tvb_get_ntoh24 (tvb, offset+1)); + offset += 4; + } + } + } +} + +static void +dissect_fcels_lsts (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + guint8 isreq, proto_item *ti) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 5; + proto_tree *lsts_tree; + + if (tree) { + lsts_tree = proto_item_add_subtree (ti, ett_fcels_lsts); + + proto_tree_add_item (lsts_tree, hf_fcels_opcode, tvb, offset-5, 1, 0); + if (isreq) { + /* In case of LSTS, the reply has the meat */ + return; + } + proto_tree_add_item (lsts_tree, hf_fcels_failedrcvr, tvb, offset, 1, 0); + proto_tree_add_item (lsts_tree, hf_fcels_flacompliance, tvb, offset+1, + 1, 0); + proto_tree_add_item (lsts_tree, hf_fcels_loopstate, tvb, offset+2, 1, 0); + proto_tree_add_item (lsts_tree, hf_fcels_publicloop_bmap, tvb, offset+3, + 16, 0); + proto_tree_add_item (lsts_tree, hf_fcels_pvtloop_bmap, tvb, offset+19, + 16, 0); + proto_tree_add_item (lsts_tree, hf_fcels_alpa_map, tvb, offset+35, + 128, 0); + } +} + +static void +dissect_fcels_prlilo_payload (tvbuff_t *tvb, packet_info *pinfo _U_, + guint8 isreq, proto_item *ti, guint8 opcode) +{ + int offset = 0, + stroff = 0; + guint8 type; + proto_tree *prli_tree, *svcpg_tree; + int num_svcpg, payload_len, i, flag; + proto_item *subti; + gchar flagstr[100]; + + /* We're assuming that we're invoked only if tree is not NULL i.e. + * we don't do the usual "if (tree)" check here, the caller must. + */ + prli_tree = proto_item_add_subtree (ti, ett_fcels_prli); + + proto_tree_add_item (prli_tree, hf_fcels_opcode, tvb, offset, 1, 0); + + proto_tree_add_text (prli_tree, tvb, offset+1, 1, + "Page Length: %d", + tvb_get_guint8 (tvb, offset+1)); + payload_len = tvb_get_ntohs (tvb, offset+2); + proto_tree_add_text (prli_tree, tvb, offset+2, 2, + "Payload Length: %d", payload_len); + num_svcpg = payload_len/16; + + offset = 4; + for (i = 0; i < num_svcpg; i++) { + subti = proto_tree_add_text (prli_tree, tvb, offset, 16, + "Service Parameter Page %d", i); + svcpg_tree = proto_item_add_subtree (subti, ett_fcels_prli_svcpg); + + type = tvb_get_guint8 (tvb, offset); + proto_tree_add_text (svcpg_tree, tvb, offset, 1, + "TYPE: %s", + val_to_str (type, + fc_prli_fc4_val, "0x%x")); + proto_tree_add_text (svcpg_tree, tvb, offset+1, 1, + "TYPE Code Extension: %d", + tvb_get_guint8 (tvb, offset+1)); + + flag = tvb_get_guint8 (tvb, offset+2); + flagstr[0] = '\0'; + stroff = 0; + if (opcode != FC_ELS_TPRLO) { + if (flag & 0x80) { + strcpy (flagstr, "Orig PA Valid, "); + stroff += 15; + } + if (flag & 0x40) { + strcpy (&flagstr[stroff], "Resp PA Valid, "); + stroff += 15; + } + + if (opcode == FC_ELS_PRLI) { + if (!isreq) { + if (flag & 0x20) { + strcpy (&flagstr[stroff], "Image Pair Estd., "); + stroff += 18; + } + else { + strcpy (&flagstr[stroff], "Image Pair Not Estd., "); + stroff += 22; + } + } + else { + if (flag & 0x20) { + strcpy (&flagstr[stroff], "Est Image Pair & Exchg Svc Param, "); + stroff += 34; + } + else { + strcpy (&flagstr[stroff], "Exchange Svc Param Only, "); + stroff += 25; + } + } + } + } + else { /* Assuming opcode is TPRLO */ + if (flag & 0x80) { + strcpy (flagstr, "3rd Party Orig PA Valid, "); + stroff += 25; + } + if (flag & 0x40) { + strcpy (&flagstr[stroff], "Resp PA Valid, "); + stroff += 15; + } + if (flag & 0x20) { + strcpy (&flagstr[stroff], "3rd Party N_Port Valid, "); + stroff += 24; + } + if (flag & 0x10) { + strcpy (&flagstr[stroff], "Global PRLO, "); + stroff += 13; + } + } + + proto_tree_add_text (svcpg_tree, tvb, offset+2, 1, + "Flags: %s", flagstr); + if (!isreq && (opcode != FC_ELS_TPRLO)) { + /* This is valid only for ACC */ + proto_tree_add_text (svcpg_tree, tvb, offset+2, 1, + "Response Code: 0x%x", + (tvb_get_guint8 (tvb, offset+2) & 0x0F)); + } + if (opcode != FC_ELS_TPRLO) { + proto_tree_add_text (svcpg_tree, tvb, offset+4, 4, + "Originator PA: 0x%x", + tvb_get_ntohl (tvb, offset+4)); + } + else { + proto_tree_add_text (svcpg_tree, tvb, offset+4, 4, + "3rd Party Originator PA: 0x%x", + tvb_get_ntohl (tvb, offset+4)); + } + proto_tree_add_text (svcpg_tree, tvb, offset+8, 4, + "Responder PA: 0x%x", + tvb_get_ntohl (tvb, offset+8)); + + if (type == FC_TYPE_SCSI) { + flag = tvb_get_ntohs (tvb, offset+14); + flagstr[0] = '\0'; + stroff = 0; + + if (flag & 0x2000) { + if (isreq) { + strcpy (flagstr, "Task Retry Ident Req, "); + stroff += 22; + } + else { + strcpy (flagstr, "Task Retry Ident Acc, "); + stroff += 22; + } + } + if (flag & 0x1000) { + strcpy (&flagstr[stroff], "Retry Possible, "); + stroff += 16; + } + if (flag & 0x0080) { + strcpy (&flagstr[stroff], "Confirmed Comp, "); + stroff += 16; + } + if (flag & 0x0040) { + strcpy (&flagstr[stroff], "Data Overlay, "); + stroff += 14; + } + if (flag & 0x0020) { + strcpy (&flagstr[stroff], "Initiator, "); + stroff += 11; + } + if (flag & 0x0010) { + strcpy (&flagstr[stroff], "Target, "); + stroff += 8; + } + if (flag & 0x0002) { + strcpy (&flagstr[stroff], "Rd Xfer_Rdy Dis, "); + stroff += 17; + } + if (flag & 0x0001) { + strcpy (&flagstr[stroff], "Wr Xfer_Rdy Dis"); + stroff += 15; + } + proto_tree_add_text (svcpg_tree, tvb, offset+12, 4, + "FCP Flags: 0x%x (%s)", flag, + flagstr); + } + else if ((opcode == FC_ELS_PRLI) && !isreq) { + proto_tree_add_text (svcpg_tree, tvb, offset+12, 4, + "Service Parameter Response: 0x%x", + tvb_get_ntohl (tvb, offset+12)); + } + else if (opcode == FC_ELS_TPRLO) { + proto_tree_add_text (svcpg_tree, tvb, offset+13, 3, + "3rd Party N_Port Id: %s", + fc_to_str (tvb_get_ptr (tvb, offset+13, 3))); + } + } +} + +static void +dissect_fcels_prli (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + guint8 isreq, proto_item *ti) +{ + if (tree) { + dissect_fcels_prlilo_payload (tvb, pinfo, isreq, ti, FC_ELS_PRLI); + } +} + +static void +dissect_fcels_prlo (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + guint8 isreq, proto_item *ti) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + if (tree) { + dissect_fcels_prlilo_payload (tvb, pinfo, isreq, ti, FC_ELS_PRLO); + } +} + +static void +dissect_fcels_tprlo (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + guint8 isreq, proto_item *ti) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + + if (tree) { + dissect_fcels_prlilo_payload (tvb, pinfo, isreq, ti, FC_ELS_TPRLO); + } +} + +static void +dissect_fcels_lirr (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + guint8 isreq _U_, proto_item *ti) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 4; + proto_tree *lirr_tree; + guint8 lirr_fmt; + + if (tree) { + lirr_tree = proto_item_add_subtree (ti, ett_fcels_lirr); + + proto_tree_add_item (lirr_tree, hf_fcels_opcode, tvb, offset-4, 1, 0); + + proto_tree_add_text (lirr_tree, tvb, offset, 1, + "Regn. Function: %s", + val_to_str (tvb_get_guint8 (tvb, offset), + fc_els_lirr_regfunc_val, + "Reserved (0x%x)")); + lirr_fmt = tvb_get_guint8 (tvb, offset+1); + if (!lirr_fmt) { + /* This scheme is resorted to because the value 0 has a string in + * the value_string that is not what we want displayed here. + */ + proto_tree_add_text (lirr_tree, tvb, offset, 1, + "Regn. Format: Common Format"); + } + else { + proto_tree_add_text (lirr_tree, tvb, offset, 1, + "Regn. Format: %s", + val_to_str (lirr_fmt, fc_fc4_val, "0x%x")); + } + } +} + +static void +dissect_fcels_srl (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + guint8 isreq, proto_item *ti) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 4, + flag = 0; + proto_tree *srl_tree; + + if (tree) { + srl_tree = proto_item_add_subtree (ti, ett_fcels_srl); + + proto_tree_add_item (srl_tree, hf_fcels_opcode, tvb, offset-4, 1, 0); + if (!isreq) + return; + + flag = tvb_get_guint8 (tvb, offset); + if (flag & 0x1) { + proto_tree_add_text (srl_tree, tvb, offset, 1, + "Flag: Scan only specified FL Port"); + } + else { + proto_tree_add_text (srl_tree, tvb, offset, 1, + "Flag: Scan all loops in domain"); + } + proto_tree_add_text (srl_tree, tvb, offset+1, 3, + "FL_Port Addr: %s", + fc_to_str (tvb_get_ptr (tvb, offset+1, 3))); + } +} + +static void +dissect_fcels_rpsc (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + guint8 isreq, proto_item *ti) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 2; + int num_entries, i, cap; + gchar speed_str[40]; + int stroff = 0; + proto_tree *rpsc_tree; + + if (tree) { + rpsc_tree = proto_item_add_subtree (ti, ett_fcels_rpsc); + + proto_tree_add_item (rpsc_tree, hf_fcels_opcode, tvb, offset-2, 1, 0); + if (isreq) + return; + + num_entries = tvb_get_ntohs (tvb, offset); + proto_tree_add_text (rpsc_tree, tvb, offset, 2, + "Number of Entries: %d", num_entries); + offset = 4; + for (i = 0; i < num_entries; i++) { + cap = tvb_get_ntohs (tvb, offset); + speed_str[0] = '\0'; + stroff = 0; + if (cap & 0x8000) { + strcpy (speed_str, "1,"); + stroff += 2; + } + if (cap & 0x4000) { + strcpy (speed_str, "2,"); + stroff += 2; + } + if (cap & 0x2000) { + strcpy (speed_str, "4,"); + stroff += 2; + } + if (cap & 0x1000) { + strcpy (speed_str, "10"); + stroff += 2; + } + strcpy (&speed_str[stroff], "Gb"); + proto_tree_add_text (rpsc_tree, tvb, offset, 2, + "Port Speed Capabilities (Port %d): %s", i, + speed_str); + cap = tvb_get_ntohs (tvb, offset+2); + proto_tree_add_text (rpsc_tree, tvb, offset+2, 2, + "Port Oper Speed: %s", + val_to_str (cap, fc_els_portspeed_val, + "0x%x")); + } + } +} + +static void +dissect_fcels_rnid (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + guint8 isreq, proto_item *ti) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 0; + int clen, slen; + proto_tree *rnid_tree; + + if (tree) { + rnid_tree = proto_item_add_subtree (ti, ett_fcels_rnid); + + proto_tree_add_item (rnid_tree, hf_fcels_opcode, tvb, offset, 1, 0); + if (isreq) { + proto_tree_add_item (rnid_tree, hf_fcels_nodeidfmt, tvb, offset+4, + 1, 0); + } + else { + /* We only decode responses to nodeid fmt DF */ + proto_tree_add_item (rnid_tree, hf_fcels_nodeidfmt, tvb, offset+4, + 1, 0); + clen = tvb_get_guint8 (tvb, offset+5); + proto_tree_add_text (rnid_tree, tvb, offset+5, 1, + "Common Identification Data Length: %d", clen); + slen = tvb_get_guint8 (tvb, offset+7); + proto_tree_add_item (rnid_tree, hf_fcels_spidlen, tvb, offset+7, + 1, 0); + if (clen) { + proto_tree_add_string (rnid_tree, hf_fcels_npname, tvb, + offset+8, 8, + fcwwn_to_str (tvb_get_ptr (tvb, offset+8, + 8))); + proto_tree_add_string (rnid_tree, hf_fcels_fnname, tvb, + offset+16, 8, + fcwwn_to_str (tvb_get_ptr (tvb, + offset+16, + 8))); + } + if (tvb_get_guint8 (tvb, offset+4) == 0xDF) { + /* Decode the Specific Node ID Format as this is known */ + proto_tree_add_item (rnid_tree, hf_fcels_vendoruniq, tvb, + offset+24, 16, 0); + proto_tree_add_item (rnid_tree, hf_fcels_asstype, tvb, + offset+40, 4, 0); + proto_tree_add_item (rnid_tree, hf_fcels_physport, tvb, + offset+44, 4, 0); + proto_tree_add_item (rnid_tree, hf_fcels_attnodes, tvb, + offset+48, 4, 0); + proto_tree_add_item (rnid_tree, hf_fcels_nodemgmt, tvb, + offset+52, 1, 0); + proto_tree_add_item (rnid_tree, hf_fcels_ipvers, tvb, + offset+53, 1, 0); + proto_tree_add_item (rnid_tree, hf_fcels_tcpport, tvb, + offset+54, 2, 0); + proto_tree_add_item (rnid_tree, hf_fcels_ip, tvb, offset+56, + 16, 0); + proto_tree_add_item (rnid_tree, hf_fcels_vendorsp, tvb, + offset+74, 2, 0); + } + } + } +} + +static void +dissect_fcels_rlir (tvbuff_t *tvb _U_, packet_info *pinfo _U_, + proto_tree *tree, guint8 isreq _U_, + proto_item *ti _U_) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + + if (tree) { + } +} + +static void +dissect_fcels_lsrjt (tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, + guint8 isreq _U_, proto_item *ti) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 5; + proto_tree *lsrjt_tree; + + if (tree) { + lsrjt_tree = proto_item_add_subtree (ti, ett_fcels_lsrjt); + + proto_tree_add_item (lsrjt_tree, hf_fcels_opcode, tvb, offset-5, 1, 0); + + proto_tree_add_item (lsrjt_tree, hf_fcels_rjtcode, tvb, offset++, 1, 0); + proto_tree_add_item (lsrjt_tree, hf_fcels_rjtdetcode, tvb, offset++, 1, 0); + proto_tree_add_item (lsrjt_tree, hf_fcels_vnduniq, tvb, offset, 1, 0); + } +} + +static void +dissect_fcels (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + +/* Set up structures needed to add the protocol subtree and manage it */ + proto_item *ti = NULL; + proto_tree *acc_tree; + guint8 isreq = FC_ELS_REQ; + int offset = 0; + guint8 opcode, + failed_opcode = 0; + conversation_t *conversation; + fcels_conv_data_t *cdata; + fcels_conv_key_t ckey, *req_key; + guint options; + address dstaddr; + guint8 addrdata[3]; + + /* Make entries in Protocol column and Info column on summary display */ + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "FC ELS"); + + /* decoding of this is done by each individual opcode handler */ + opcode = tvb_get_guint8 (tvb, 0); + + if (tree) { + ti = proto_tree_add_protocol_format (tree, proto_fcels, tvb, 0, + tvb_length (tvb), "FC ELS"); + } + + /* Register conversation in case this is not a response */ + if ((opcode != FC_ELS_LSRJT) && (opcode != FC_ELS_ACC)) { + if (opcode == FC_ELS_FLOGI) { + if (pinfo->src.data[2]) { + /* If it is a loop port, we'll need to remember the ALPA */ + options = NO_PORT2; + } + else { + options = NO_PORT2 | NO_ADDR2; + } + } + else { + options = NO_PORT2; + } + conversation = find_conversation (&pinfo->dst, &pinfo->src, + pinfo->ptype, pinfo->oxid, + pinfo->rxid, options); + + if (!conversation) { + conversation = conversation_new (&pinfo->dst, &pinfo->src, + pinfo->ptype, pinfo->oxid, + pinfo->rxid, options); + } + + ckey.conv_idx = conversation->index; + + cdata = (fcels_conv_data_t *)g_hash_table_lookup (fcels_req_hash, + &ckey); + if (cdata) { + /* Since we never free the memory used by an exchange, this maybe a + * case of another request using the same exchange as a previous + * req. + */ + cdata->opcode = opcode; + } + else { + req_key = g_mem_chunk_alloc (fcels_req_keys); + req_key->conv_idx = conversation->index; + + cdata = g_mem_chunk_alloc (fcels_req_vals); + cdata->opcode = opcode; + + g_hash_table_insert (fcels_req_hash, req_key, cdata); + } + } + else { + isreq = FC_ELS_RPLY; + + options = NO_PORT2; + conversation = find_conversation (&pinfo->dst, &pinfo->src, + pinfo->ptype, pinfo->oxid, + pinfo->rxid, options); + if (!conversation) { + /* FLOGI has two ways to save state: without the src and using just + * the port (ALPA) part of the address. Try both. + */ + addrdata[0] = addrdata[1] = 0; + addrdata[2] = pinfo->dst.data[2]; + SET_ADDRESS (&dstaddr, AT_FC, 3, addrdata); + conversation = find_conversation (&dstaddr, &pinfo->src, + pinfo->ptype, pinfo->oxid, + pinfo->rxid, options); + } + + if (!conversation) { + /* Finally check for FLOGI with both NO_PORT2 and NO_ADDR2 set */ + options = NO_ADDR2 | NO_PORT2; + conversation = find_conversation (&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->oxid, + pinfo->rxid, options); + if (!conversation) { + if (tree && (opcode == FC_ELS_ACC)) { + /* No record of what this accept is for. Can't decode */ + acc_tree = proto_item_add_subtree (ti, ett_fcels_acc); + proto_tree_add_text (acc_tree, tvb, offset, tvb_length (tvb), + "No record of Exchange. Unable to decode ACC"); + return; + } + failed_opcode = 0; + } + } + + if (conversation) { + ckey.conv_idx = conversation->index; + + cdata = (fcels_conv_data_t *)g_hash_table_lookup (fcels_req_hash, &ckey); + + if (cdata != NULL) { + if ((options & NO_ADDR2) && (cdata->opcode != FC_ELS_FLOGI)) { + /* only FLOGI can have this special check */ + if (tree && (opcode == FC_ELS_ACC)) { + /* No record of what this accept is for. Can't decode */ + acc_tree = proto_item_add_subtree (ti, + ett_fcels_acc); + proto_tree_add_text (acc_tree, tvb, offset, + tvb_length (tvb), + "No record of Exchg. Unable to decode ACC"); + return; + } + } + if (opcode == FC_ELS_ACC) + opcode = cdata->opcode; + else + failed_opcode = cdata->opcode; + } + + if (tree) { + if ((cdata == NULL) && (opcode != FC_ELS_LSRJT)) { + /* No record of what this accept is for. Can't decode */ + acc_tree = proto_item_add_subtree (ti, ett_fcels_acc); + proto_tree_add_text (acc_tree, tvb, offset, tvb_length (tvb), + "No record of ELS Req. Unable to decode ACC"); + return; + } + } + } + } + + if (check_col (pinfo->cinfo, COL_INFO)) { + if (isreq == FC_ELS_REQ) { + col_add_str (pinfo->cinfo, COL_INFO, + val_to_str (opcode, fc_els_proto_val, "0x%x")); + } + else if (opcode == FC_ELS_LSRJT) { + col_add_fstr (pinfo->cinfo, COL_INFO, "LS_RJT (%s)", + val_to_str (failed_opcode, fc_els_proto_val, "0x%x")); + } + else { + col_add_fstr (pinfo->cinfo, COL_INFO, "ACC (%s)", + val_to_str (opcode, fc_els_proto_val, "0x%x")); + } + } + + switch (opcode) { + case FC_ELS_LSRJT: + dissect_fcels_lsrjt (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_PLOGI: + dissect_fcels_plogi (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_FLOGI: + dissect_fcels_flogi (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_LOGOUT: + dissect_fcels_logout (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_ABTX: + dissect_fcels_abtx (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_RSI: + dissect_fcels_rsi (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_RRQ: + dissect_fcels_rrq (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_PRLI: + dissect_fcels_prli (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_PRLO: + dissect_fcels_prlo (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_TPRLO: + dissect_fcels_tprlo (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_PDISC: + dissect_fcels_pdisc (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_FDISC: + dissect_fcels_fdisc (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_ADISC: + dissect_fcels_adisc (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_FARP_REQ: + dissect_fcels_farp_req (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_FARP_RPLY: + dissect_fcels_farp_rply (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_RPS: + dissect_fcels_rps (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_RPL: + dissect_fcels_rpl (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_FAN: + dissect_fcels_fan (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_RSCN: + dissect_fcels_rscn (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_SCR: + dissect_fcels_scr (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_RNFT: + dissect_fcels_rnft (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_LSTS: + dissect_fcels_lsts (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_RNID: + dissect_fcels_rnid (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_RLIR: + dissect_fcels_rlir (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_LIRR: + dissect_fcels_lirr (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_SRL: + dissect_fcels_srl (tvb, pinfo, tree, isreq, ti); + break; + case FC_ELS_RPSC: + dissect_fcels_rpsc (tvb, pinfo, tree, isreq, ti); + break; + default: + /* proto_tree_add_text ( */ + call_dissector (data_handle, tvb, pinfo, tree); + break; + } +} + +void +proto_register_fcels (void) +{ + +/* Setup list of header fields See Section 1.6.1 for details*/ + static hf_register_info hf[] = { + { &hf_fcels_opcode, + {"Cmd Code", "fcels.opcode", FT_UINT8, BASE_HEX, + VALS (fc_els_proto_val), 0x0, "", HFILL}}, + { &hf_fcels_rjtcode, + {"Reason Code", "fcels.rjt.reason", FT_UINT8, BASE_HEX, + VALS (fc_els_rjt_val), 0x0, "", HFILL}}, + { &hf_fcels_rjtdetcode, + {"Reason Explanation", "fcels.rjt.detail", FT_UINT8, BASE_HEX, + VALS (fc_els_rjt_det_val), 0x0, "", HFILL}}, + { &hf_fcels_vnduniq, + {"Vendor Unique", "fcels.rjt.vnduniq", FT_UINT8, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_fcels_b2b, + {"B2B Credit", "fcels.logi.b2b", FT_UINT8, BASE_DEC, NULL, 0x0, "", + HFILL}}, + { &hf_fcels_cmnfeatures, + {"Common Features", "fcels.logi.cmnfeatures", FT_UINT16, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_fcels_bbscnum, + {"BB_SC Number", "fcels.logi.bbscnum", FT_UINT8, BASE_DEC, NULL, 0xF0, "", + HFILL}}, + { &hf_fcels_rcvsize, + {"Receive Size", "fcels.logi.rcvsize", FT_UINT16, BASE_DEC, NULL, 0x0FFF, "", + HFILL}}, + { &hf_fcels_maxconseq, + {"Max Concurrent Seq", "fcels.logi.maxconseq", FT_UINT16, BASE_DEC, NULL, + 0x0, "", HFILL}}, + { &hf_fcels_reloffset, + {"Relative Offset By Info Cat", "fcels.logi.reloff", FT_UINT16, BASE_DEC, + NULL, 0x0, "", HFILL}}, + { &hf_fcels_edtov, + {"E_D_TOV", "fcels.edtov", FT_UINT16, BASE_DEC, NULL, 0x0, "", HFILL}}, + { &hf_fcels_npname, + {"N_Port Port_Name", "fcels.npname", FT_STRING, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_fcels_fnname, + {"Fabric/Node Name", "fcels.fnname", FT_STRING, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_fcels_cls1param, + {"Class 1 Svc Param", "fcels.logi.cls1param", FT_BYTES, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_fcels_cls2param, + {"Class 2 Svc Param", "fcels.logi.cls2param", FT_BYTES, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_fcels_cls3param, + {"Class 3 Svc Param", "fcels.logi.cls3param", FT_BYTES, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_fcels_cls4param, + {"Class 4 Svc Param", "fcels.logi.cls4param", FT_BYTES, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_fcels_vendorvers, + {"Vendor Version", "fcels.logi.vendvers", FT_BYTES, BASE_HEX, NULL, 0x0, "", + HFILL}}, + { &hf_fcels_svcavail, + {"Services Availability", "fcels.logi.svcavail", FT_BYTES, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_fcels_clsflags, + {"Class Flags", "fcels.logi.clsflags", FT_UINT16, BASE_HEX, NULL, 0x0, "", + HFILL}}, + { &hf_fcels_initctl, + {"Initiator Ctl", "fcels.logi.initctl", FT_UINT16, BASE_HEX, NULL, 0x0, "", + HFILL}}, + { &hf_fcels_rcptctl, + {"Recipient Ctl", "fcels.logi.rcptctl", FT_UINT16, BASE_HEX, NULL, 0x0, "", + HFILL}}, + { &hf_fcels_clsrcvsize, + {"Class Recv Size", "fcels.logi.clsrcvsize", FT_UINT16, BASE_DEC, NULL, + 0x0, "", HFILL}}, + { &hf_fcels_conseq, + {"Total Concurrent Seq", "fcels.logi.totconseq", FT_UINT8, BASE_DEC, NULL, + 0x0, "", HFILL}}, + { &hf_fcels_e2e, + {"End2End Credit", "fcels.logi.e2e", FT_UINT16, BASE_DEC, NULL, 0x0, "", + HFILL}}, + { &hf_fcels_openseq, + {"Open Seq Per Exchg", "fcels.logi.openseq", FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL}}, + { &hf_fcels_nportid, + {"Originator S_ID", "fcels.portid", FT_STRING, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_fcels_oxid, + {"OXID", "fcels.oxid", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL}}, + { &hf_fcels_rxid, + {"RXID", "fcels.rxid", FT_UINT16, BASE_HEX, NULL, 0x0, "", HFILL}}, + { &hf_fcels_recovqual, + {"Recovery Qualifier", "fcels.rcovqual", FT_UINT8, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_fcels_fabricaddr, + {"Fabric Address", "fcels.faddr", FT_STRING, BASE_HEX, NULL, 0x0, "", + HFILL}}, + { &hf_fcels_fabricpname, + {"Fabric Port Name", "fcels.fpname", FT_STRING, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_fcels_failedrcvr, + {"Failed Receiver AL_PA", "fcels.faildrcvr", FT_UINT8, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_fcels_flacompliance, + {"FC-FLA Compliance", "fcels.flacompliance", FT_UINT8, BASE_HEX, + VALS (fc_els_flacompliance_val), 0x0, "", HFILL}}, + { &hf_fcels_loopstate, + {"Loop State", "fcels.loopstate", FT_UINT8, BASE_HEX, + VALS (fc_els_loopstate_val), 0x0, "", HFILL}}, + { &hf_fcels_publicloop_bmap, + {"Public Loop Device Bitmap", "fcels.pubdev_bmap", FT_BYTES, BASE_HEX, + NULL, 0x0, "", HFILL}}, + { &hf_fcels_pvtloop_bmap, + {"Private Loop Device Bitmap", "fcels.pvtdev_bmap", FT_BYTES, + BASE_HEX, NULL, 0x0, "", HFILL}}, + { &hf_fcels_alpa_map, + {"AL_PA Map", "fcels.alpa", FT_BYTES, BASE_HEX, NULL, 0x0, "", + HFILL}}, + { &hf_fcels_scrregn, + {"Registration Function", "fcels.scr.regn", FT_UINT8, BASE_HEX, + VALS (fc_els_scr_reg_val), 0x0, "", HFILL}}, + { &hf_fcels_farp_matchcodept, + {"Match Address Code Points", "fcels.matchcp", FT_UINT8, BASE_BIN, + NULL, 0x0, "", HFILL}}, + { &hf_fcels_farp_respaction, + {"Responder Action", "fcels.respaction", FT_UINT8, BASE_HEX, + VALS (fc_els_farp_respaction_val), 0x0, "", HFILL}}, + { &hf_fcels_resportid, + {"Responding Port ID", "fcels.resportid", FT_STRING, BASE_HEX, + NULL, 0x0, "", HFILL}}, + { &hf_fcels_respname, + {"Responding Port Name", "fcels.respname", FT_STRING, BASE_HEX, + NULL, 0x0, "", HFILL}}, + { &hf_fcels_respnname, + {"Responding Node Name", "fcels.respnname", FT_STRING, BASE_HEX, + NULL, 0x0, "", HFILL}}, + { &hf_fcels_reqipaddr, + {"Requesting IP Address", "fcels.reqipaddr", FT_IPv6, BASE_DEC, + NULL, 0x0, "", HFILL}}, + { &hf_fcels_respipaddr, + {"Responding IP Address", "fcels.respipaddr", FT_IPv6, BASE_DEC, + NULL, 0x0, "", HFILL}}, + { &hf_fcels_hardaddr, + {"Hard Address of Originator", "fcels.hrdaddr", FT_STRING, BASE_HEX, + NULL, 0x0, "", HFILL}}, + { &hf_fcels_rps_flag, + {"Flag", "fcels.flag", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL}}, + { &hf_fcels_rps_portnum, + {"Physical Port Number", "fcels.portnum", FT_UINT32, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_fcels_rps_portstatus, + {"Port Status", "fcels.portstatus", FT_UINT16, BASE_HEX, + VALS(fc_els_portstatus_val), 0x0, "", HFILL}}, + { &hf_fcels_rnft_fc4type, + {"FC-4 Type", "fcels.rnft.fc4type", FT_UINT8, BASE_HEX, + VALS (fc_fc4_val), 0x0, "", HFILL}}, + { &hf_fcels_rscn_evqual, + {"Event Qualifier", "fcels.rscn.evqual", FT_UINT8, BASE_HEX, + VALS (fc_els_rscn_evqual_val), 0x3C, "", HFILL}}, + { &hf_fcels_rscn_addrfmt, + {"Address Format", "fcels.rscn.addrfmt", FT_UINT8, BASE_HEX, + VALS (fc_els_rscn_addrfmt_val), 0x03, "", HFILL}}, + { &hf_fcels_rscn_domain, + {"Affected Domain", "fcels.rscn.domain", FT_UINT8, BASE_HEX, + NULL, 0x0, "", HFILL}}, + { &hf_fcels_rscn_area, + {"Affected Area", "fcels.rscn.area", FT_UINT8, BASE_HEX, + NULL, 0x0, "", HFILL}}, + { &hf_fcels_rscn_port, + {"Affected Port", "fcels.rscn.port", FT_UINT8, BASE_HEX, + NULL, 0x0, "", HFILL}}, + { &hf_fcels_nodeidfmt, + {"Node Identification Format", "fcels.rnid.nodeidfmt", FT_UINT8, + BASE_HEX, VALS (fc_els_nodeid_val), 0x0, "", HFILL}}, + { &hf_fcels_spidlen, + {"Specific Id Length", "fcels.rnid.spidlen", FT_UINT8, BASE_DEC, NULL, + 0x0, "", HFILL}}, + { &hf_fcels_vendoruniq, + {"Vendor Unique", "fcels.rnid.vendoruniq", FT_BYTES, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_fcels_vendorsp, + {"Vendor Specific", "fcels.rnid.vendorsp", FT_UINT16, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_fcels_asstype, + {"Associated Type", "fcels.rnid.asstype", FT_UINT32, BASE_HEX, + VALS (fc_els_rnid_asstype_val), 0x0, "", HFILL}}, + { &hf_fcels_physport, + {"Physical Port Number", "fcels.rnid.physport", FT_UINT32, BASE_HEX, + NULL, 0x0, "", HFILL}}, + { &hf_fcels_attnodes, + {"Number of Attached Nodes", "fcels.rnid.attnodes", FT_UINT32, + BASE_HEX, NULL, 0x0, "", HFILL}}, + { &hf_fcels_nodemgmt, + {"Node Management", "fcels.rnid.nodemgmt", FT_UINT8, BASE_HEX, + VALS (fc_els_rnid_mgmt_val), 0x0, "", HFILL}}, + { &hf_fcels_ipvers, + {"IP Version", "fcels.rnid.ipvers", FT_UINT8, BASE_HEX, + VALS (fc_els_rnid_ipvers_val), 0x0, "", HFILL}}, + { &hf_fcels_tcpport, + {"TCP/UDP Port Number", "fcels.rnid.tcpport", FT_UINT16, BASE_DEC, + NULL, 0x0, "", HFILL}}, + { &hf_fcels_ip, + {"IP Address", "fcels.rnid.ip", FT_IPv6, BASE_HEX, NULL, 0x0, "", + HFILL}}, + }; + + static gint *ett[] = { + &ett_fcels, + &ett_fcels, + &ett_fcels_lsrjt, + &ett_fcels_acc, + &ett_fcels_logi, + &ett_fcels_logi_cmnsvc, + &ett_fcels_logi_clssvc, + &ett_fcels_logo, + &ett_fcels_abtx, + &ett_fcels_rsi, + &ett_fcels_rrq, + &ett_fcels_prli, + &ett_fcels_prli_svcpg, + &ett_fcels_adisc, + &ett_fcels_farp, + &ett_fcels_rps, + &ett_fcels_rpl, + &ett_fcels_rplpb, + &ett_fcels_fan, + &ett_fcels_rscn, + &ett_fcels_rscn_rec, + &ett_fcels_scr, + &ett_fcels_rnft, + &ett_fcels_rnft_fc4, + &ett_fcels_lsts, + &ett_fcels_rnid, + &ett_fcels_rlir, + &ett_fcels_lirr, + &ett_fcels_srl, + &ett_fcels_rpsc, + }; + + /* Register the protocol name and description */ + proto_fcels = proto_register_protocol("FC Extended Link Svc", "FC ELS", "els"); + + /* Required function calls to register the header fields and subtrees used */ + proto_register_field_array(proto_fcels, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + register_init_routine (&fcels_init_protocol); +} + +void +proto_reg_handoff_fcels (void) +{ + dissector_handle_t els_handle; + + els_handle = create_dissector_handle (dissect_fcels, proto_fcels); + dissector_add("fc.ftype", FC_FTYPE_ELS, els_handle); + + data_handle = find_dissector ("data"); +} diff --git a/packet-fcels.h b/packet-fcels.h new file mode 100644 index 0000000000..cb6a39b2da --- /dev/null +++ b/packet-fcels.h @@ -0,0 +1,321 @@ +/* packet-fcels.h + * Fibre Channel Extended Link Services Definitions (ddutt@cisco.com) + * Copyright 2001, Dinesh G Dutt <ddutt@cisco.com> + * + * $Id: packet-fcels.h,v 1.1 2002/12/08 02:32:17 gerald Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * 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. + */ + +#ifndef __PACKET_FCELS_H_ +#define __PACKET_FCELS_H_ + +#define FC_ELS_LSRJT 0x01 +#define FC_ELS_ACC 0x02 +#define FC_ELS_PLOGI 0x03 +#define FC_ELS_FLOGI 0x04 +#define FC_ELS_LOGOUT 0x05 +#define FC_ELS_ABTX 0x06 +#define FC_ELS_RSI 0x0A +#define FC_ELS_TEST 0x11 +#define FC_ELS_RRQ 0x12 +#define FC_ELS_PRLI 0x20 +#define FC_ELS_PRLO 0x21 +#define FC_ELS_TPRLO 0x24 +#define FC_ELS_PDISC 0x50 +#define FC_ELS_FDISC 0x51 +#define FC_ELS_ADISC 0x52 +#define FC_ELS_FARP_REQ 0x54 +#define FC_ELS_FARP_RPLY 0x55 +#define FC_ELS_RPS 0x56 +#define FC_ELS_RPL 0x57 +#define FC_ELS_FAN 0x60 +#define FC_ELS_RSCN 0x61 +#define FC_ELS_SCR 0x62 +#define FC_ELS_RNFT 0x63 +#define FC_ELS_LINIT 0x70 +#define FC_ELS_LSTS 0x72 +#define FC_ELS_RNID 0x78 +#define FC_ELS_RLIR 0x79 +#define FC_ELS_LIRR 0x7A +#define FC_ELS_SRL 0x7B +#define FC_ELS_RPSC 0x7D + +static const value_string fc_els_proto_val[] = { + {FC_ELS_LSRJT , "LS_RJT"}, + {FC_ELS_ACC , "ACC"}, + {FC_ELS_PLOGI , "PLOGI"}, + {FC_ELS_FLOGI , "FLOGI"}, + {FC_ELS_LOGOUT , "LOGO"}, + {FC_ELS_ABTX , "ABTX"}, + {FC_ELS_RSI , "RSI"}, + {FC_ELS_TEST , "TEST"}, + {FC_ELS_RRQ , "RRQ"}, + {FC_ELS_PRLI , "PRLI"}, + {FC_ELS_PRLO , "PRLO"}, + {FC_ELS_TPRLO , "TPRLO"}, + {FC_ELS_PDISC , "PDISC"}, + {FC_ELS_FDISC , "FDISC"}, + {FC_ELS_ADISC , "ADISC"}, + {FC_ELS_FARP_REQ , "FARP-REQ"}, + {FC_ELS_FARP_RPLY , "FARP-REPLY"}, + {FC_ELS_RPS , "RPS"}, + {FC_ELS_RPL , "RPL"}, + {FC_ELS_FAN , "FAN"}, + {FC_ELS_RSCN , "RSCN"}, + {FC_ELS_SCR , "SCR"}, + {FC_ELS_RNFT , "RNFT"}, + {FC_ELS_LINIT , "LINIT"}, + {FC_ELS_LSTS , "LSTS"}, + {FC_ELS_RNID , "RNID"}, + {FC_ELS_RLIR , "RLIR"}, + {FC_ELS_LIRR , "LIRR"}, + {FC_ELS_SRL , "SRL"}, + {FC_ELS_RPSC , "RPSC"}, + {0, NULL}, +}; + +/* Reject Reason Codes */ +#define FC_ELS_RJT_INVCMDCODE 0x01 +#define FC_ELS_RJT_LOGERR 0x03 +#define FC_ELS_RJT_LOGBSY 0x05 +#define FC_ELS_RJT_PROTERR 0x07 +#define FC_ELS_RJT_GENFAIL 0x09 +#define FC_ELS_RJT_CMDNOTSUPP 0x0B +#define FC_ELS_RJT_GENFAIL2 0x0D +#define FC_ELS_RJT_CMDINPROG 0x0E +#define FC_ELS_RJT_VENDOR 0xFF + +static const value_string fc_els_rjt_val[] = { + {FC_ELS_RJT_INVCMDCODE, "Invalid Cmd Code"}, + {FC_ELS_RJT_LOGERR , "Logical Error"}, + {FC_ELS_RJT_LOGBSY , "Logical Busy"}, + {FC_ELS_RJT_PROTERR , "Protocol Error"}, + {FC_ELS_RJT_GENFAIL , "Unable to Perform Cmd"}, + {FC_ELS_RJT_CMDNOTSUPP, "Command Not Supported"}, + {FC_ELS_RJT_GENFAIL2 , "Unable to Perform Cmd"}, + {FC_ELS_RJT_CMDINPROG , "Command in Progress Already"}, + {FC_ELS_RJT_VENDOR , "Vendor Unique Error"}, + {0, NULL}, +}; + +#define FC_ELS_RJT_DET_NODET 0x00 +#define FC_ELS_RJT_DET_SVCPARM_OPT 0x01 +#define FC_ELS_RJT_DET_SVCPARM_INITCTL 0x03 +#define FC_ELS_RJT_DET_SVCPARM_RCPTCTL 0x05 +#define FC_ELS_RJT_DET_SVCPARM_RCVSZE 0x07 +#define FC_ELS_RJT_DET_SVCPARM_CSEQ 0x09 +#define FC_ELS_RJT_DET_SVCPARM_CREDIT 0x0B +#define FC_ELS_RJT_DET_INV_PFNAME 0x0D +#define FC_ELS_RJT_DET_INV_NFNAME 0x0E +#define FC_ELS_RJT_DET_INV_CMNSVCPARM 0x0F +#define FC_ELS_RJT_DET_INV_ASSOCHDR 0x11 +#define FC_ELS_RJT_DET_ASSOCHDR_REQD 0x13 +#define FC_ELS_RJT_DET_INV_OSID 0x15 +#define FC_ELS_RJT_DET_EXCHG_COMBO 0x17 +#define FC_ELS_RJT_DET_CMDINPROG 0x19 +#define FC_ELS_RJT_DET_PLOGI_REQ 0x1E +#define FC_ELS_RJT_DET_INV_NPID 0x1F +#define FC_ELS_RJT_DET_INV_SEQID 0x21 +#define FC_ELS_RJT_DET_INV_EXCHG 0x23 +#define FC_ELS_RJT_DET_INACTIVE_EXCHG 0x25 +#define FC_ELS_RJT_DET_RQUAL_REQD 0x27 +#define FC_ELS_RJT_DET_OORSRC 0x29 +#define FC_ELS_RJT_DET_SUPPLYFAIL 0x2A +#define FC_ELS_RJT_DET_REQNOTSUPP 0x2C +#define FC_ELS_RJT_DET_INV_PLEN 0x2D +#define FC_ELS_RJT_DET_INV_ALIASID 0x30 +#define FC_ELS_RJT_DET_OORSRC_ALIASID 0x31 +#define FC_ELS_RJT_DET_INACTIVE_ALIASID 0x32 +#define FC_ELS_RJT_DET_DEACT_ALIAS_FAIL1 0x33 +#define FC_ELS_RJT_DET_DEACT_ALIAS_FAIL2 0x34 +#define FC_ELS_RJT_DET_SVCPARM_CONFLICT 0x35 +#define FC_ELS_RJT_DET_INV_ALIASTOK 0x36 +#define FC_ELS_RJT_DET_UNSUPP_ALIASTOK 0x37 +#define FC_ELS_RJT_DET_GRPFORM_FAIL 0x38 +#define FC_ELS_RJT_DET_QOSPARM_ERR 0x40 +#define FC_ELS_RJT_DET_INV_VCID 0x41 +#define FC_ELS_RJT_DET_OORSRC_C4 0x42 +#define FC_ELS_RJT_DET_INV_PNNAME 0x44 + +static const value_string fc_els_rjt_det_val[] = { + {FC_ELS_RJT_DET_NODET , "No further details"}, + {FC_ELS_RJT_DET_SVCPARM_OPT , "Svc Param - Options Error"}, + {FC_ELS_RJT_DET_SVCPARM_INITCTL , "Svc Param - Initiator Ctl Error"}, + {FC_ELS_RJT_DET_SVCPARM_RCPTCTL , "Svc Param - Recipient Ctl Error"}, + {FC_ELS_RJT_DET_SVCPARM_RCVSZE , "Svc Param - Recv Size Error"}, + {FC_ELS_RJT_DET_SVCPARM_CSEQ , "Svc Param - Concurrent Seq Error"}, + {FC_ELS_RJT_DET_SVCPARM_CREDIT , "Svc Param - Credit Error"}, + {FC_ELS_RJT_DET_INV_PFNAME , "Invalid N_/F_Port Name"}, + {FC_ELS_RJT_DET_INV_NFNAME , "Invalid Node/Fabric Name"}, + {FC_ELS_RJT_DET_INV_CMNSVCPARM , "Invalid Common Svc Param"}, + {FC_ELS_RJT_DET_INV_ASSOCHDR , "Invalid Association Header"}, + {FC_ELS_RJT_DET_ASSOCHDR_REQD , "Association Header Reqd"}, + {FC_ELS_RJT_DET_INV_OSID , "Invalid Orig S_ID"}, + {FC_ELS_RJT_DET_EXCHG_COMBO , "Invalid OXID-RXID Combo"}, + {FC_ELS_RJT_DET_CMDINPROG , "Cmd Already in Progress"}, + {FC_ELS_RJT_DET_PLOGI_REQ , "N_Port Login Required"}, + {FC_ELS_RJT_DET_INV_NPID , "Invalid N_Port Id"}, + {FC_ELS_RJT_DET_INV_SEQID , "Invalid SeqID"}, + {FC_ELS_RJT_DET_INV_EXCHG , "Attempt to Abort Invalid Exchg"}, + {FC_ELS_RJT_DET_INACTIVE_EXCHG , "Attempt to Abort Inactive Exchg"}, + {FC_ELS_RJT_DET_RQUAL_REQD , "Resource Qualifier Required"}, + {FC_ELS_RJT_DET_OORSRC , "Insufficient Resources for Login"}, + {FC_ELS_RJT_DET_SUPPLYFAIL , "Unable to Supply Req Data"}, + {FC_ELS_RJT_DET_REQNOTSUPP , "Command Not Supported"}, + {FC_ELS_RJT_DET_INV_PLEN , "Invalid Payload Length"}, + {FC_ELS_RJT_DET_INV_ALIASID , "No Alias IDs available"}, + {FC_ELS_RJT_DET_OORSRC_ALIASID , "Alias_ID Cannot be Activated (Out of Rsrc)"}, + {FC_ELS_RJT_DET_INACTIVE_ALIASID , "Alias_ID Cannot be Activated (Inv AID)"}, + {FC_ELS_RJT_DET_DEACT_ALIAS_FAIL1, "Alias_ID Cannot be Deactivated"}, + {FC_ELS_RJT_DET_DEACT_ALIAS_FAIL2, "Alias_ID Cannot be Deactivated"}, + {FC_ELS_RJT_DET_SVCPARM_CONFLICT , "Svc Parameter Conflict"}, + {FC_ELS_RJT_DET_INV_ALIASTOK , "Invalid Alias Token"}, + {FC_ELS_RJT_DET_UNSUPP_ALIASTOK , "Unsupported Alias Token"}, + {FC_ELS_RJT_DET_GRPFORM_FAIL , "Alias Grp Cannot be Formed"}, + {FC_ELS_RJT_DET_QOSPARM_ERR , "QoS Param Error"}, + {FC_ELS_RJT_DET_INV_VCID , "VC_ID Not Found"}, + {FC_ELS_RJT_DET_OORSRC_C4 , "No Resources to Support Class 4 Conn"}, + {FC_ELS_RJT_DET_INV_PNNAME , "Invalid Port/Node Name"}, +}; + +static const value_string fc_els_flacompliance_val[] = { + {1, "FC-FLA Level 1"}, + {2, "FC-FLA Level 2"}, + {0, NULL}, +}; + +static const value_string fc_els_loopstate_val[] = { + {1, "Online"}, + {2, "Loop Failure"}, + {3, "Initialization Failure"}, + {4, "Initializing"}, + {0, NULL}, +}; + +static const value_string fc_els_scr_reg_val[] = { + {1, "Fabric Detected Regn"}, + {2, "N_Port Detected Regn"}, + {3, "Full Regn"}, + {255, "Clear All Regn"}, + {0, NULL}, +}; + +static const value_string fc_els_farp_respaction_val[] = { + {0, "No Action"}, + {1, "Login Using Requesting Port ID"}, + {2, "Respond with FARP-REPLY"}, + {3, "Login & send FARP-REPLY"}, + {0, NULL}, +}; + +static const value_string fc_els_portstatus_val[] = { + {0x20, "Point-to-Point Connection | No Fabric"}, + {0x10, "AL Connection | No Fabric"}, + {0x28, "Point-to-Point Connection | Fabric Detected"}, + {0x2C, "Point-to-Point Connection | Fabric Detected | Loss of Signal"}, + {0x24, "Point-to-Point Connection | Loss of Signal"}, + {0x18, "AL Connection | Fabric Detected"}, + {0x14, "AL Connection | Loss of Signal"}, + {0x1C, "AL Connection | Fabric Detected | Loss of Signal"}, + {0x04, "Loss of Signal"}, + {0x02, "Loss of Synchronization"}, + {0x01, "Link Reset Protocol in Progress"}, + {0, NULL}, +}; + +static const value_string fc_els_portspeed_val[] = { + {0x8000, "1 Gb"}, + {0x4000, "2 Gb"}, + {0x2000, "4 Gb"}, + {0x1000, "10 Gb"}, + {0x0002, "Unknown"}, + {0x0001, "Speed Not Estd."}, + {0, NULL} +}; + +static const value_string fc_els_lirr_regfunc_val[] = { + {0x1, "Set Reg: Conditionally Receive"}, + {0x2, "Set Reg: Always Receive"}, + {0xFF, "Clear Reg"}, + {0, NULL}, +}; + +static const value_string fc_els_rscn_evqual_val[] = { + {0x00, "Event is not specified"}, + {0x01, "Changed Name Server Object"}, + {0x02, "Changed Port Attribute"}, + {0x03, "Changed Service Object"}, + {0x04, "Changed Switch Config"}, + {0, NULL}, +}; + +static const value_string fc_els_rscn_addrfmt_val[] = { + {0, "Port Addr (single N/L Port or service)"}, + {1, "Area Addr Group (area of E/L/N Port addresses)"}, + {2, "Domain Addr Group"}, + {3, "Fabric Addr Group"}, + {0, NULL}, +}; + +static const value_string fc_els_nodeid_val[] = { + {0x00, "Common Identification Data Only"}, + {0x05, "IP Specific Data"}, + {0x08, "FCP-Specific Data"}, + {0x20, "FC_CT Specific Data"}, + {0x22, "SW_ILS Specific Data"}, + {0x23, "AL Specific Data"}, + {0x24, "SNMP Specific Data"}, + {0xDF, "Common ID Data + General Topology Discovery Format"}, + {0, NULL}, +}; + +static const value_string fc_els_rnid_asstype_val[] = { + {0x0, "Reserved"}, + {0x1, "Unknown"}, + {0x2, "Other"}, + {0x3, "Hub"}, + {0x4, "Switch"}, + {0x5, "Gateway"}, + {0x6, "Converter"}, + {0x7, "HBA"}, + {0x9, "Storage Device"}, + {0xA, "Host"}, + {0xB, "Storage Subsystem"}, + {0xE, "Storage Access Device"}, + {0x11, "NAS Device"}, + {0, NULL}, +}; + +static const value_string fc_els_rnid_mgmt_val[] = { + {0, "IP/UDP/SNMP"}, + {1, "IP/TCP/Telnet"}, + {2, "IP/TCP/HTTP"}, + {3, "IP/TCP/HTTPS"}, + {0, NULL}, +}; + +static const value_string fc_els_rnid_ipvers_val[] = { + {0, "None"}, + {1, "IPv4"}, + {2, "IPv6"}, + {0, NULL}, +}; + +#endif diff --git a/packet-fcip.c b/packet-fcip.c new file mode 100644 index 0000000000..fdcc4df335 --- /dev/null +++ b/packet-fcip.c @@ -0,0 +1,619 @@ +/* packet-fcip.c + * Routines for FCIP dissection + * Copyright 2001, Dinesh G Dutt (ddutt@cisco.com) + * + * $Id: packet-fcip.c,v 1.1 2002/12/08 02:32:17 gerald Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * 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 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <glib.h> + +#include <epan/packet.h> +#include "prefs.h" +#include <epan/conversation.h> + +#define FCIP_ENCAP_HEADER_LEN 28 +#define FCIP_MIN_HEADER_LEN 16 /* upto frame len field */ +#define FCIP_ENCAP_PROTO_VER 0xFEFE0101 +#define FCIP_IS_SF(pflags) ((pflags & 0x1) == 0x1) +#define FCIP_IS_CH(pflags) ((pflags & 0x80) == 0x80) + +typedef enum { + FCIP_EOFn = 0x41, + FCIP_EOFt = 0x42, + FCIP_EOFrt = 0x44, + FCIP_EOFdt = 0x46, + FCIP_EOFni = 0x49, + FCIP_EOFdti = 0x4E, + FCIP_EOFrti = 0x4F, + FCIP_EOFa = 0x50, +} fcip_eof_t; + +typedef enum { + FCIP_SOFf = 0x28, + FCIP_SOFi4 = 0x29, + FCIP_SOFi2 = 0x2D, + FCIP_SOFi3 = 0x2E, + FCIP_SOFn4 = 0x31, + FCIP_SOFn2 = 0x35, + FCIP_SOFn3 = 0x36, + FCIP_SOFc4 = 0x39, +} fcip_sof_t; + +typedef enum { + FCENCAP_PROTO_FCIP = 1, + FCENCAP_PROTO_iFCP = 2, +} fcencap_proto_t; + +static const value_string fcip_eof_vals[] = { + {FCIP_EOFn, "EOFn" }, + {FCIP_EOFt, "EOFt" }, + {FCIP_EOFrt, "EOFrt" }, + {FCIP_EOFdt, "EOFdt" }, + {FCIP_EOFni, "EOFni" }, + {FCIP_EOFdti, "EOFdti" }, + {FCIP_EOFrti, "EOFrti" }, + {FCIP_EOFa, "EOFa" }, + {0, NULL}, +}; + +static const value_string fcip_sof_vals[] = { + {FCIP_SOFf, "SOFf" }, + {FCIP_SOFi4, "SOFi4" }, + {FCIP_SOFi2, "SOFi2" }, + {FCIP_SOFi3, "SOFi3" }, + {FCIP_SOFn4, "SOFn4" }, + {FCIP_SOFn2, "SOFn2" }, + {FCIP_SOFn3, "SOFn3" }, + {FCIP_SOFc4, "SOFc4" }, + {0, NULL}, +}; + +static const value_string fcencap_proto_vals[] = { + {FCENCAP_PROTO_FCIP, "FCIP"}, + {FCENCAP_PROTO_iFCP, "iFCP"}, +}; + +static const value_string fsf_conn_flag_vals[] = { + {0, NULL}, +}; + +static const gchar * sof_strings[] = { + "SOFf", "SOFi4", "", "", "", "SOFi2", "SOFi3", "", "", "SOFn4", "", "", "", + "SOFn2", "SOFn3", "", "", "SOFc4", "", +}; + +static const gchar *eof_strings[] = { + "EOFn", "EOFt", "", "EOFrt", "", "EOFdt", "", "", "EOFni", "", "", "", "", + "EOFdti", "EOFrti", "EOFa", "", +}; + +static guint fcip_header_2_bytes[2] = {FCIP_ENCAP_PROTO_VER, + FCIP_ENCAP_PROTO_VER}; + +static int proto_fcip = -1; + +static int hf_fcip_protocol = -1; +static int hf_fcip_protocol_c = -1; +static int hf_fcip_version = -1; +static int hf_fcip_version_c = -1; +static int hf_fcip_encap_word1 = -1; +static int hf_fcip_flags = -1; +static int hf_fcip_flags_c = -1; +static int hf_fcip_framelen = -1; +static int hf_fcip_framelen_c = -1; +static int hf_fcip_tsec = -1; +static int hf_fcip_tusec = -1; +static int hf_fcip_encap_crc = -1; +static int hf_fcip_sof = -1; +static int hf_fcip_sof_c = -1; +static int hf_fcip_eof = -1; +static int hf_fcip_eof_c = -1; +static int hf_fcip_pflags_changed = -1; +static int hf_fcip_pflags_special = -1; +static int hf_fcip_pflags_c = -1; +static int hf_fcip_src_wwn = -1; +static int hf_fcip_dst_wwn = -1; +static int hf_fcip_conn_code = -1; +static int hf_fcip_katov = -1; +static int hf_fcip_src_entity_id = -1; +static int hf_fcip_conn_nonce = -1; +static int hf_fcip_conn_flags = -1; + +static int ett_fcip = -1; + +static guint fcip_port = 3225; +static gboolean fcip_desegment = TRUE; + +static dissector_handle_t data_handle; +static dissector_handle_t fc_handle; + +/* This routine attempts to locate the position of the next header in the + * provided segment + */ +static guint +get_next_fcip_header_offset (tvbuff_t *tvb, packet_info *pinfo, gint offset) +{ + gint bytes_remaining = tvb_length_remaining (tvb, offset); + gint frame_len; + guint16 flen, flen1; + fcip_eof_t eof, eofc; + + /* + * As per the FCIP standard, the following tests must PASS: + * 1) Frame Length field validation -- 15 < Frame Length < 545; + * 2) Comparison of Frame Length field to its ones complement; and + * 3) A valid EOF is found in the word preceding the start of the next + * FCIP header as indicated by the Frame Length field, to be tested + * as follows: + * 1) Bits 24-31 and 16-23 contain identical legal EOF values (the + * list of legal EOF values is in the FC Frame Encapsulation + * [21]); and + * 2) Bits 8-15 and 0-7 contain the ones complement of the EOF + * value found in bits 24-31. + * + * As per the FCIP standard, in addition, at least 3 of the following set + * of tests must be performed to identify that we've located the start of + * an FCIP frame. + * a) Protocol# ones complement field (1 test); + * b) Version ones complement field (1 test); + * c) Replication of encapsulation word 0 in word 1 (1 test); + * d) Reserved field and its ones complement (2 tests); + * e) Flags field and its ones complement (2 tests); + * f) CRC field is equal to zero (1 test); + * g) SOF fields and ones complement fields (4 tests); + * h) Format and values of FC header (1 test); + * i) CRC of FC Frame (2 tests); + * j) FC Frame Encapsulation header information in the next FCIP Frame + * (1 test). + * + * At least 3 of the 16 tests listed above SHALL be performed. Failure + * of any of the above tests actually performed SHALL indicate an + * encapsulation error and the FC Frame SHALL NOT be forwarded on to + * the FC Entity. + */ + +NXT_BYTE: while (bytes_remaining) { + if (bytes_remaining < FCIP_ENCAP_HEADER_LEN) { + if(fcip_desegment && pinfo->can_desegment) { + /* + * This frame doesn't have all of the data for + * this message, but we can do reassembly on it. + * + * Tell the TCP dissector where the data for this + * message starts in the data it handed us, and + * how many more bytes we need, and return. + */ + pinfo->desegment_offset = offset; + pinfo->desegment_len = FCIP_ENCAP_HEADER_LEN; + return -2; + } + } + + /* I check that we have a valid header before checking for the frame + * length and the other initial tests. + */ + + /* + * Tests a, b and c + */ + if (memcmp ((void *)tvb_get_ptr (tvb, offset, 8), + (void *)fcip_header_2_bytes, 8) != 0) { + offset++; + bytes_remaining--; + goto NXT_BYTE; + } + + flen = (tvb_get_ntohs (tvb, offset+12)) & 0x03FF; + frame_len = (tvb_get_ntohs (tvb, offset+12) & 0x03FF)*4; + + if ((flen < 15) || (flen > 545)) { + /* Frame length check failed. Skip byte and try again */ + offset++; + bytes_remaining--; + goto NXT_BYTE; + } + + flen1 = (tvb_get_ntohs (tvb, offset+14)) & 0x03FF; + + if ((flen & 0x03FF) != ((~flen1)&0x03FF)) { + /* frame_len and its one's complement are not the same */ + offset++; + bytes_remaining--; + goto NXT_BYTE; + } + + /* Valid EOF check */ + if (tvb_bytes_exist (tvb, offset+(frame_len-1)*4, 4)) { + eof = (fcip_eof_t)tvb_get_guint8 (tvb, offset+(frame_len-1)*4); + eofc = (fcip_eof_t)tvb_get_guint8 (tvb, offset+(frame_len-1)*4+2); + + if ((eof != FCIP_EOFn) && (eof != FCIP_EOFt) && (eof != FCIP_EOFrt) + && (eof != FCIP_EOFdt) && (eof != FCIP_EOFni) && + (eof != FCIP_EOFdti) && (eof != FCIP_EOFrti) && + (eof != FCIP_EOFa)) { + offset++; + bytes_remaining--; + goto NXT_BYTE; + } + + if ((eof != ~eofc) || + (eof != tvb_get_guint8 (tvb, offset+(frame_len-1)*4+1)) || + (eofc != tvb_get_guint8 (tvb, offset+(frame_len-1)*4+3))) { + offset++; + bytes_remaining--; + goto NXT_BYTE; + } + } + + /* Test d */ + if ((tvb_get_guint8 (tvb, offset+9) != 0) || + (tvb_get_guint8 (tvb, offset+11) != 0xFF)) { + /* Failed */ + offset++; + bytes_remaining--; + goto NXT_BYTE; + } + + /* Test e */ + + /* Test f */ + if (tvb_get_ntohl (tvb, offset+24)) { + /* Failed */ + offset++; + bytes_remaining--; + goto NXT_BYTE; + } + + if (bytes_remaining >= (frame_len)) { + if (tvb_bytes_exist (tvb, offset+frame_len, 8)) { + /* The start of the next header matches what we wish to see */ + if (memcmp ((void *)tvb_get_ptr (tvb, offset+frame_len, 8), + (void *)fcip_header_2_bytes, 8) == 0) { + return (offset); + } + else { + offset++; + bytes_remaining--; + goto NXT_BYTE; + } + } + else { + return (offset); + } + } + else { + if(fcip_desegment && pinfo->can_desegment) { + /* + * This frame doesn't have all of the data for + * this message, but we can do reassembly on it. + * + * Tell the TCP dissector where the data for this + * message starts in the data it handed us, and + * how many more bytes we need, and return. + */ + pinfo->desegment_offset = offset; + pinfo->desegment_len = frame_len - bytes_remaining; + return -2; + } + else { + return (offset); + } + } + } + + return (-1); /* Unable to find FCIP header */ +} + +static void +dissect_fcencap_header (tvbuff_t *tvb, proto_tree *tree, gint offset) +{ + guint8 protocol = tvb_get_guint8 (tvb, offset); + + if (tree) { + proto_tree_add_item (tree, hf_fcip_protocol, tvb, offset, 1, 0); + proto_tree_add_item (tree, hf_fcip_version, tvb, offset+1, 1, 0); + proto_tree_add_item (tree, hf_fcip_protocol_c, tvb, offset+2, 1, 0); + proto_tree_add_item (tree, hf_fcip_version_c, tvb, offset+3, 1, 0); + + if (protocol == FCENCAP_PROTO_FCIP) { + proto_tree_add_item (tree, hf_fcip_encap_word1, tvb, offset+4, + 4, 0); + proto_tree_add_item (tree, hf_fcip_pflags_changed, tvb, offset+8, + 1, 0); + proto_tree_add_item (tree, hf_fcip_pflags_special, tvb, offset+8, + 1, 0); + proto_tree_add_item (tree, hf_fcip_pflags_c, tvb, offset+10, 1, 0); + } + + proto_tree_add_item (tree, hf_fcip_flags, tvb, offset+12, 1, 0); + proto_tree_add_item (tree, hf_fcip_framelen, tvb, offset+12, 2, 0); + proto_tree_add_item (tree, hf_fcip_flags_c, tvb, offset+14, 1, 0); + proto_tree_add_item (tree, hf_fcip_framelen_c, tvb, offset+14, 2, 0); + proto_tree_add_item (tree, hf_fcip_tsec, tvb, offset+16, 4, 0); + proto_tree_add_item (tree, hf_fcip_tusec, tvb, offset+20, 4, 0); + proto_tree_add_item (tree, hf_fcip_encap_crc, tvb, offset+24, 4, 0); + } +} + +static void +dissect_fcip_sf (tvbuff_t *tvb, proto_tree *tree, gint offset) +{ + if (tree) { + proto_tree_add_string (tree, hf_fcip_src_wwn, tvb, offset, 8, + fcwwn_to_str (tvb_get_ptr (tvb, offset, 8))); + proto_tree_add_bytes (tree, hf_fcip_src_entity_id, tvb, offset+8, 8, + tvb_get_ptr (tvb, offset+8, 8)); + proto_tree_add_bytes (tree, hf_fcip_conn_nonce, tvb, offset+16, 8, + tvb_get_ptr (tvb, offset+16, 8)); + proto_tree_add_item (tree, hf_fcip_conn_flags, tvb, offset+24, 1, 0); + proto_tree_add_item (tree, hf_fcip_conn_code, tvb, offset+26, 2, 0); + proto_tree_add_string (tree, hf_fcip_dst_wwn, tvb, offset+30, 8, + fcwwn_to_str (tvb_get_ptr (tvb, offset+30, 8))); + proto_tree_add_item (tree, hf_fcip_katov, tvb, offset+38, 4, 0); + } +} + +static gboolean +dissect_fcip (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + gint offset = 0, + start = 0, + frame_len = 0; + guint bytes_remaining = tvb_length_remaining (tvb, offset); + guint8 pflags, sof, eof; + /* Set up structures needed to add the protocol subtree and manage it */ + proto_item *ti; + proto_tree *fcip_tree = NULL; + tvbuff_t *next_tvb; + + if (!proto_is_protocol_enabled(proto_fcip)) + return FALSE; /* iSCSI has been disabled */ + + if (bytes_remaining < FCIP_ENCAP_HEADER_LEN) { + return FALSE; + } + + if ((pinfo->srcport != fcip_port) && (pinfo->destport != fcip_port)) { + return FALSE; + } + + while (bytes_remaining > FCIP_ENCAP_HEADER_LEN) { + if ((offset = get_next_fcip_header_offset (tvb, pinfo, offset)) == -1) { + return FALSE; + } + else if (offset == -2) { + /* We need more data to desegment */ + return (TRUE); + } + + start = offset; + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "FCIP"); + + frame_len = (tvb_get_ntohs (tvb, offset+12) & 0x03FF)*4; + + pflags = tvb_get_guint8 (tvb, start+8); + + if (tree) { + if (FCIP_IS_SF (pflags)) { + ti = proto_tree_add_protocol_format (tree, proto_fcip, tvb, 0, + FCIP_ENCAP_HEADER_LEN, + "FCIP"); + } + else { + sof = tvb_get_guint8 (tvb, offset+FCIP_ENCAP_HEADER_LEN) - 0x28; + eof = tvb_get_guint8 (tvb, offset+frame_len - 4) - 0x41; + + if (sof > 18) { + sof = 18; /* The last SOF */ + } + if (eof > 15) { + eof = 16; + } + + ti = proto_tree_add_protocol_format (tree, proto_fcip, tvb, 0, + FCIP_ENCAP_HEADER_LEN, + "FCIP (%s/%s)", + sof_strings[sof], + eof_strings[eof]); + } + fcip_tree = proto_item_add_subtree (ti, ett_fcip); + /* Dissect the Common FC Encap header */ + dissect_fcencap_header (tvb, fcip_tree, offset); + + offset += FCIP_ENCAP_HEADER_LEN; + + if (!FCIP_IS_SF (pflags)) { + /* print SOF */ + proto_tree_add_item (fcip_tree, hf_fcip_sof, tvb, offset, 1, 0); + proto_tree_add_item (fcip_tree, hf_fcip_sof_c, tvb, offset+2, 1, 0); + /* print EOF */ + + offset += (frame_len-FCIP_ENCAP_HEADER_LEN-4); + proto_tree_add_item (fcip_tree, hf_fcip_eof, tvb, offset, 1, 0); + proto_tree_add_item (fcip_tree, hf_fcip_eof_c, tvb, offset+2, 1, 0); + } + } + + /* Call the FC Dissector if this is carrying an FC frame */ + if (!FCIP_IS_SF(pflags)) { + /* Special frame bit is not set */ + next_tvb = tvb_new_subset (tvb, FCIP_ENCAP_HEADER_LEN+4, -1, -1); + if (fc_handle) { + call_dissector (fc_handle, next_tvb, pinfo, tree); + } + else if (data_handle) { + call_dissector (data_handle, next_tvb, pinfo, tree); + } + } + else { + if (check_col(pinfo->cinfo, COL_INFO)) + col_set_str(pinfo->cinfo, COL_INFO, "Special Frame"); + if (FCIP_IS_CH (pflags)) { + if (check_col(pinfo->cinfo, COL_INFO)) + col_append_str(pinfo->cinfo, COL_INFO, "(Changed)"); + } + + dissect_fcip_sf (tvb, fcip_tree, start); + } + + bytes_remaining -= frame_len; + } + + return (TRUE); +} + +void +proto_register_fcip (void) +{ + + /* Setup list of header fields See Section 1.6.1 for details*/ + static hf_register_info hf[] = { + { &hf_fcip_protocol, + { "Protocol", "fcencap.proto", FT_UINT8, BASE_DEC, NULL, 0, + "Protocol", HFILL }}, + { &hf_fcip_protocol_c, + {"Protocol (1's Complement)", "fcencap.protoc", FT_UINT8, BASE_DEC, NULL, + 0, "Protocol (1's Complement)", HFILL}}, + { &hf_fcip_version, + {"Version", "fcencap.version", FT_UINT8, BASE_DEC, NULL, 0, "", + HFILL}}, + { &hf_fcip_version_c, + {"Version (1's Complement)", "fcencap.versionc", FT_UINT8, BASE_DEC, + NULL, 0, "", HFILL}}, + { &hf_fcip_encap_word1, + {"FCIP Encapsulation Word1", "fcip.word1", FT_UINT32, BASE_HEX, NULL, + 0, "", HFILL}}, + { &hf_fcip_flags, + {"Flags", "fcencap.flags", FT_UINT8, BASE_HEX, NULL, 0xFC, "", HFILL}}, + { &hf_fcip_flags_c, + {"Flags (1's Complement)", "fcencap.flagsc", FT_UINT8, BASE_HEX, + NULL, 0xFC, "", HFILL}}, + { &hf_fcip_framelen, + {"Frame Length (in Words)", "fcencap.framelen", FT_UINT16, BASE_DEC, + NULL, 0x03FF, "", HFILL}}, + { &hf_fcip_framelen_c, + {"Frame Length (1's Complement)", "fcencap.framelenc", FT_UINT16, + BASE_DEC, NULL, 0x03FF, "", HFILL}}, + { &hf_fcip_tsec, + {"Time (secs)", "fcencap.tsec", FT_UINT32, BASE_DEC, NULL, 0, "", + HFILL}}, + { &hf_fcip_tusec, + {"Time (fraction)", "fcencap.tusec", FT_UINT32, BASE_DEC, NULL, 0, + "", HFILL}}, + { &hf_fcip_encap_crc, + {"CRC", "fcencap.crc", FT_UINT32, BASE_HEX, NULL, 0, "", HFILL}}, + { &hf_fcip_sof, + {"SOF", "fcip.sof", FT_UINT8, BASE_HEX, VALS (&fcip_sof_vals), 0, + "", HFILL}}, + { &hf_fcip_sof_c, + {"SOF (1's Complement)", "fcip.sofc", FT_UINT8, BASE_HEX, NULL, + 0, "", HFILL}}, + { &hf_fcip_eof, + {"EOF", "fcip.eof", FT_UINT8, BASE_HEX, VALS (&fcip_eof_vals), 0, + "", HFILL}}, + { &hf_fcip_eof_c, + {"EOF (1's Complement)", "fcip.eofc", FT_UINT8, BASE_HEX, NULL, + 0, "", HFILL}}, + { &hf_fcip_pflags_changed, + {"Changed Flag", "fcip.pflags.ch", FT_BOOLEAN, BASE_DEC, NULL, 0x80, + "", HFILL}}, + { &hf_fcip_pflags_special, + {"Special Frame Flag", "fcip.pflags.sf", FT_BOOLEAN, BASE_DEC, NULL, + 0x1, "", HFILL}}, + { &hf_fcip_pflags_c, + {"Pflags (1's Complement)", "fcip.pflagsc", FT_UINT8, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_fcip_src_wwn, + {"Source Fabric WWN", "fcip.srcwwn", FT_STRING, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_fcip_dst_wwn, + {"Destination Fabric WWN", "fcip.dstwwn", FT_STRING, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_fcip_src_entity_id, + {"FC/FCIP Entity Id", "fcip.srcid", FT_BYTES, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_fcip_conn_flags, + {"Connection Usage Flags", "fcip.connflags", FT_UINT8, BASE_HEX, + NULL, 0x0, "", HFILL}}, + { &hf_fcip_conn_code, + {"Connection Usage Code", "fcip.conncode", FT_UINT16, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_fcip_katov, + {"K_A_TOV", "fcip.katov", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL}}, + { &hf_fcip_conn_nonce, + {"Connection Nonce", "fcip.nonce", FT_BYTES, BASE_HEX, NULL, 0x0, "", + HFILL}}, + }; + + static gint *ett[] = { + &ett_fcip, + }; + + module_t *fcip_module; + + /* Register the protocol name and description */ + proto_fcip = proto_register_protocol("FCIP", "FCIP", "fcip"); + + /* Required function calls to register the header fields and + * subtrees used */ + proto_register_field_array(proto_fcip, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + + fcip_module = prefs_register_protocol(proto_fcip, NULL); + prefs_register_bool_preference(fcip_module, + "desegment_fcip_messages", + "Desegment FCIP messages", + "When enabled, FCIP messages that span multiple TCP segments are desegmented", + &fcip_desegment); + prefs_register_uint_preference(fcip_module, + "fcip_port", + "Target port", + "Port number used for FCIP", + 10, + &fcip_port); +} + + +/* + * If this dissector uses sub-dissector registration add a + * registration routine. + */ + +/* + * This format is required because a script is used to find these + * routines and create the code that calls these routines. + */ +void +proto_reg_handoff_fcip (void) +{ + heur_dissector_add("tcp", dissect_fcip, proto_fcip); + data_handle = find_dissector("data"); + fc_handle = find_dissector("fc"); +} diff --git a/packet-fclctl.c b/packet-fclctl.c new file mode 100644 index 0000000000..ac67f2850d --- /dev/null +++ b/packet-fclctl.c @@ -0,0 +1,102 @@ +/* packet-fclctl.c + * Routines for FC Link Control Frames + * Copyright 2001, Dinesh G Dutt <ddutt@cisco.com> + * + * $Id: packet-fclctl.c,v 1.1 2002/12/08 02:32:17 gerald Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * Copyright 1998 Gerald Combs + * + * Copied from WHATEVER_FILE_YOU_USED (where "WHATEVER_FILE_YOU_USED" + * is a dissector file; if you just copied this from README.developer, + * don't bother with the "Copied from" - you don't even need to put + * in a "Copied from" if you copied an existing dissector, especially + * if the bulk of the code in the new dissector is your code) + * + * 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 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif + +#include <glib.h> + +#ifdef NEED_SNPRINTF_H +# include "snprintf.h" +#endif + +#include <epan/packet.h> +#include "etypes.h" +#include "packet-fc.h" +#include "packet-fclctl.h" + +static gchar errstr[64]; + +gchar * +fclctl_get_typestr (guint8 linkctl_type, guint8 type) +{ + if ((linkctl_type == FC_LCTL_FBSYB) || + (linkctl_type == FC_LCTL_FBSYL)) { + return (val_to_str ((type & 0xF0), fc_lctl_fbsy_val, "0x%x")); + } + else return ("\0"); +} + +gchar * +fclctl_get_paramstr (guint32 linkctl_type, guint32 param) +{ + int len; + + errstr[0] = '\0'; + + if (linkctl_type == FC_LCTL_PBSY) { + strcpy (errstr, val_to_str (((param & 0xFF000000) >> 24), + fc_lctl_pbsy_acode_val, "0x%x")); + len = strlen (errstr); + strcpy (&errstr[len], ", "); + len = strlen (errstr); + strcpy (&errstr[len], + val_to_str (((param & 0x00FF0000) >> 16), + fc_lctl_pbsy_rjt_val, "0x%x")); + } + else if ((linkctl_type == FC_LCTL_FRJT) || + (linkctl_type == FC_LCTL_PRJT)) { + strcpy (errstr, + val_to_str (((param & 0xFF000000) >> 24), + fc_lctl_rjt_acode_val, "0x%x")); + len = strlen (errstr); + strcpy (&errstr[len], ", "); + len = strlen (errstr); + strcpy (&errstr[len], + val_to_str (((param & 0x00FF0000) >> 16), fc_lctl_rjt_val, + "%x")); + } + + return (errstr); +} diff --git a/packet-fclctl.h b/packet-fclctl.h new file mode 100644 index 0000000000..7cd60991ba --- /dev/null +++ b/packet-fclctl.h @@ -0,0 +1,174 @@ +/* packet-fclctl.h + * Fibre Channel Link Control definitions + * Copyright 2001 Dinesh G Dutt (ddutt@cisco.com) + * + * $Id: packet-fclctl.h,v 1.1 2002/12/08 02:32:17 gerald Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * 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. + */ + +#ifndef __PACKET_FCLCTL_H_ +#define __PACKET_FCLCTL_H_ + +#define FC_LCTL_ACK1 0x00 +#define FC_LCTL_ACK0 0x01 +#define FC_LCTL_PRJT 0x02 +#define FC_LCTL_FRJT 0x03 +#define FC_LCTL_PBSY 0x04 +#define FC_LCTL_FBSYL 0x05 +#define FC_LCTL_FBSYB 0x06 +#define FC_LCTL_LCR 0x07 +#define FC_LCTL_NTY 0x08 +#define FC_LCTL_END 0x09 + +static const value_string fc_lctl_proto_val[] = { + {FC_LCTL_ACK1 , "ACK1"}, + {FC_LCTL_ACK0 , "ACK0"}, + {FC_LCTL_PRJT , "P_RJT"}, + {FC_LCTL_FRJT , "F_RJT"}, + {FC_LCTL_PBSY , "P_BSY"}, + {FC_LCTL_FBSYL , "F_BSY (Data frame)"}, + {FC_LCTL_FBSYB , "F_BSY (Link Ctl)"}, + {FC_LCTL_LCR , "LCR"}, + {FC_LCTL_NTY , "NTY"}, + {FC_LCTL_END , "END"}, + {0, NULL}, +}; + +#define FC_LCTL_FBSY_FBSY 0x01 +#define FC_LCTL_FBSY_NBSY 0x03 + +static const value_string fc_lctl_fbsy_val[] = { + {FC_LCTL_FBSY_FBSY, "Fabric Busy"}, + {FC_LCTL_FBSY_NBSY, "N_Port Busy"}, + {0, NULL} +}; + +#define FC_LCTL_PBSY_ACODE_SEQBSY 0x01 +#define FC_LCTL_PBSY_ACODE_C2BSY 0x02 + +static const value_string fc_lctl_pbsy_acode_val[] = { + {FC_LCTL_PBSY_ACODE_SEQBSY, "Sequence Marked Busy"}, + {FC_LCTL_PBSY_ACODE_C2BSY, "Class 2 Frame Busy"}, + {0, NULL}, +}; + +#define FC_LCTL_PBSY_PORTBSY 0x01 +#define FC_LCTL_PBSY_RSRCBSY 0x03 +#define FC_LCTL_PBSY_MCASTBSY 0x07 +#define FC_LCTL_PBSY_VENDBSY 0xFF + +static const value_string fc_lctl_pbsy_rjt_val[] = { + {FC_LCTL_PBSY_PORTBSY , "Physical N_Port Busy"}, + {FC_LCTL_PBSY_RSRCBSY , "N_Port Resource Busy"}, + {FC_LCTL_PBSY_MCASTBSY, "Partial Multicast Busy"}, + {FC_LCTL_PBSY_VENDBSY , "Vendor unique Busy"}, + {0, NULL}, +}; + +#define FC_LCTL_RJT_ACODE_RETRY 0x01 +#define FC_LCTL_RJT_ACODE_NORETRY 0x02 + +static const value_string fc_lctl_rjt_acode_val[] = { + {FC_LCTL_RJT_ACODE_RETRY, "Retryable Error"}, + {FC_LCTL_RJT_ACODE_NORETRY, "Non-retryable Error"}, + {0, NULL}, +}; + +#define FC_LCTL_RJT_INVDID 0x01 +#define FC_LCTL_RJT_INVSID 0x02 +#define FC_LCTL_RJT_NPORT_NOTAVAIL_T 0x03 +#define FC_LCTL_RJT_NPORT_NOTAVAIL_P 0x04 +#define FC_LCTL_RJT_CLASS_NOTSUPP 0x05 +#define FC_LCTL_RJT_DELIM_USERR 0x06 +#define FC_LCTL_RJT_TYPE_NOTSUPP 0x07 +#define FC_LCTL_RJT_INV_LCTL 0x08 +#define FC_LCTL_RJT_INV_RCTL 0x09 +#define FC_LCTL_RJT_INV_FCTL 0x0A +#define FC_LCTL_RJT_INV_OXID 0x0B +#define FC_LCTL_RJT_INV_RXID 0x0C +#define FC_LCTL_RJT_INV_SEQID 0x0D +#define FC_LCTL_RJT_INV_DFCTL 0x0E +#define FC_LCTL_RJT_INV_SEQCNT 0x0F +#define FC_LCTL_RJT_INV_PARAM 0x10 +#define FC_LCTL_RJT_EXCHG_ERR 0x11 +#define FC_LCTL_RJT_PROTO_ERR 0x12 +#define FC_LCTL_RJT_INV_LEN 0x13 +#define FC_LCTL_RJT_UNEXP_ACK 0x14 +#define FC_LCTL_RJT_CLS_NOTSUPP 0x15 +#define FC_LCTL_RJT_LOGI_REQD 0x16 +#define FC_LCTL_RJT_TOOMANY_SEQ 0x17 +#define FC_LCTL_RJT_EXCHG_NOTESTD 0x18 +#define FC_LCTL_RJT_RSVD 0x19 +#define FC_LCTL_RJT_FPATH_NOTAVAIL 0x1A +#define FC_LCTL_RJT_INV_VCID 0x1B +#define FC_LCTL_RJT_INV_CSCTL 0x1C +#define FC_LCTL_RJT_OORSRC 0x1D +#define FC_LCTL_RJT_INV_CLASS 0x1F +#define FC_LCTL_RJT_PRMPT_RJT 0x20 +#define FC_LCTL_RJT_PRMPT_DIS 0x21 +#define FC_LCTL_RJT_MCAST_ERR 0x22 +#define FC_LCTL_RJT_MCAST_TERM 0x23 +#define FC_LCTL_RJT_PRLI_REQD 0x24 +#define FC_LCTL_RJT_VEND_ERR 0xFF + +static const value_string fc_lctl_rjt_val[] = { + {FC_LCTL_RJT_INVSID , "Invalid S_ID"}, + {FC_LCTL_RJT_INVDID , "Invalid D_ID"}, + {FC_LCTL_RJT_NPORT_NOTAVAIL_T , "N_Port Not Avail (Temporary)"}, + {FC_LCTL_RJT_NPORT_NOTAVAIL_P , "N_Port Not Avail (Permanent)"}, + {FC_LCTL_RJT_CLASS_NOTSUPP , "Class Not Supported"}, + {FC_LCTL_RJT_DELIM_USERR , "Delimiter Usage Error"}, + {FC_LCTL_RJT_TYPE_NOTSUPP , "Type Not Supported"}, + {FC_LCTL_RJT_INV_LCTL , "Invalid Link Ctl Frame"}, + {FC_LCTL_RJT_INV_RCTL , "Invalid R_CTL"}, + {FC_LCTL_RJT_INV_FCTL , "Invalid F_CTL"}, + {FC_LCTL_RJT_INV_OXID , "Invalid OX_ID"}, + {FC_LCTL_RJT_INV_RXID , "Invalid RX_ID"}, + {FC_LCTL_RJT_INV_SEQID , "Invalid SEQID"}, + {FC_LCTL_RJT_INV_DFCTL , "Invalid DF_CTL"}, + {FC_LCTL_RJT_INV_SEQCNT , "Invalid SEQCNT"}, + {FC_LCTL_RJT_INV_PARAM , "Invalid Parameter"}, + {FC_LCTL_RJT_EXCHG_ERR , "Exchange Error"}, + {FC_LCTL_RJT_PROTO_ERR , "Protocol Error"}, + {FC_LCTL_RJT_INV_LEN , "Incorrect Length"}, + {FC_LCTL_RJT_UNEXP_ACK , "Unexpected ACK"}, + {FC_LCTL_RJT_CLS_NOTSUPP , "Class Not Supported by Entity at 0xFFFFFE"}, + {FC_LCTL_RJT_LOGI_REQD , "Login Required"}, + {FC_LCTL_RJT_TOOMANY_SEQ , "Excessive Sequences Attempted"}, + {FC_LCTL_RJT_EXCHG_NOTESTD , "Exchange Not Established"}, + {FC_LCTL_RJT_RSVD , "Reserved"}, + {FC_LCTL_RJT_FPATH_NOTAVAIL , "Fabric Path Not Available"}, + {FC_LCTL_RJT_INV_VCID , "Invalid VC_ID"}, + {FC_LCTL_RJT_INV_CSCTL , "Invalid CS_CTL"}, + {FC_LCTL_RJT_OORSRC , "Insufficient Resources of VC (Class 4)"}, + {FC_LCTL_RJT_INV_CLASS , "Invalid Class of Service"}, + {FC_LCTL_RJT_PRMPT_RJT , "Preemption Request Rejected"}, + {FC_LCTL_RJT_PRMPT_DIS , "Preemption Not Enabled"}, + {FC_LCTL_RJT_MCAST_ERR , "Multicast Error"}, + {FC_LCTL_RJT_MCAST_TERM , "Multicast Error Terminate"}, + {FC_LCTL_RJT_PRLI_REQD , "PRLI Required"}, + {FC_LCTL_RJT_VEND_ERR , "Vendor Unique Error"}, + {0, NULL}, +}; + +/* Function definitions */ +gchar *fclctl_get_typestr (guint8 linkctl_type, guint8 type); +gchar *fclctl_get_paramstr (guint32 linkctl_type, guint32 param); +#endif diff --git a/packet-fcp.c b/packet-fcp.c new file mode 100644 index 0000000000..7dac211fb9 --- /dev/null +++ b/packet-fcp.c @@ -0,0 +1,690 @@ +/* packet-fcp.c + * Routines for Fibre Channel Protocol for SCSI (FCP) + * Copyright 2001, Dinesh G Dutt <ddutt@cisco.com> + * + * $Id: packet-fcp.c,v 1.1 2002/12/08 02:32:17 gerald Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * Copyright 1998 Gerald Combs + * + * Copied from WHATEVER_FILE_YOU_USED (where "WHATEVER_FILE_YOU_USED" + * is a dissector file; if you just copied this from README.developer, + * don't bother with the "Copied from" - you don't even need to put + * in a "Copied from" if you copied an existing dissector, especially + * if the bulk of the code in the new dissector is your code) + * + * 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 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif + +#include <glib.h> + +#ifdef NEED_SNPRINTF_H +# include "snprintf.h" +#endif + +#include "prefs.h" +#include <epan/packet.h> +#include <epan/conversation.h> +#include "etypes.h" +#include "packet-fc.h" +#include "packet-fcp.h" +#include "packet-scsi.h" + +/* Initialize the protocol and registered fields */ +static int proto_fcp = -1; +static int hf_fcp_multilun = -1; +static int hf_fcp_singlelun = -1; +static int hf_fcp_crn = -1; +static int hf_fcp_taskattr = -1; +static int hf_fcp_taskmgmt = -1; +static int hf_fcp_addlcdblen = -1; +static int hf_fcp_rddata = -1; +static int hf_fcp_wrdata = -1; +static int hf_fcp_dl = -1; +static int hf_fcp_data_ro = -1; +static int hf_fcp_burstlen = -1; +static int hf_fcp_rspflags = -1; +static int hf_fcp_resid = -1; +static int hf_fcp_snslen = -1; +static int hf_fcp_rsplen = -1; +static int hf_fcp_rspcode = -1; +static int hf_fcp_scsistatus = -1; +static int hf_fcp_type = -1; + + +/* Initialize the subtree pointers */ +static gint ett_fcp = -1; +static dissector_table_t fcp_dissector; +static dissector_handle_t data_handle; + +typedef struct _fcp_conv_key { + guint32 conv_idx; +} fcp_conv_key_t; + +typedef struct _fcp_conv_data { + guint32 fcp_dl; + gint32 fcp_lun; + guint32 abs_secs; + guint32 abs_usecs; +} fcp_conv_data_t; + +GHashTable *fcp_req_hash = NULL; +GMemChunk *fcp_req_keys = NULL; +GMemChunk *fcp_req_vals = NULL; +guint32 fcp_init_count = 25; + +/* + * Hash Functions + */ +static gint +fcp_equal(gconstpointer v, gconstpointer w) +{ + fcp_conv_key_t *v1 = (fcp_conv_key_t *)v; + fcp_conv_key_t *v2 = (fcp_conv_key_t *)w; + + return (v1->conv_idx == v2->conv_idx); +} + +static guint +fcp_hash (gconstpointer v) +{ + fcp_conv_key_t *key = (fcp_conv_key_t *)v; + guint val; + + val = key->conv_idx; + + return val; +} + +/* + * Protocol initialization + */ +static void +fcp_init_protocol(void) +{ + if (fcp_req_keys) + g_mem_chunk_destroy(fcp_req_keys); + if (fcp_req_vals) + g_mem_chunk_destroy(fcp_req_vals); + if (fcp_req_hash) + g_hash_table_destroy(fcp_req_hash); + + fcp_req_hash = g_hash_table_new(fcp_hash, fcp_equal); + fcp_req_keys = g_mem_chunk_new("fcp_req_keys", + sizeof(fcp_conv_key_t), + fcp_init_count * sizeof(fcp_conv_key_t), + G_ALLOC_AND_FREE); + fcp_req_vals = g_mem_chunk_new("fcp_req_vals", + sizeof(fcp_conv_data_t), + fcp_init_count * sizeof(fcp_conv_data_t), + G_ALLOC_AND_FREE); +} + +static gchar * +task_mgmt_flags_to_str (guint8 flags, gchar *str) +{ + int stroff = 0; + + if (str == NULL) + return str; + + *str = '\0'; + + if (flags & 0x80) { + strcpy (str, "Obsolete, "); + stroff += 10; + } + + if (flags & 0x40) { + strcpy (&str[stroff], "Clear ACA, "); + stroff += 11; + } + + if (flags & 0x20) { + strcpy (&str[stroff], "Target Reset, "); + stroff += 14; + } + + if (flags & 0x10) { + strcpy (&str[stroff], "LU Reset, "); + stroff += 10; + } + + if (flags & 0x08) { + strcpy (&str[stroff], "Rsvd, "); + stroff += 6; + } + + if (flags & 0x04) { + strcpy (&str[stroff], "Clear Task Set, "); + stroff += 16; + } + + if (flags & 0x02) { + strcpy (&str[stroff], "Abort Task Set"); + stroff += 14; + } + + return (str); +} + +static gchar * +rspflags_to_str (guint8 flags, gchar *str) +{ + int stroff = 0; + + if (str == NULL) + return (str); + + *str = '\0'; + + if (flags & 0x10) { + strcpy (str, "FCP_CONF_REQ | "); + stroff += 15; + } + if (flags & 0x08) { + strcpy (&str[stroff], "FCP_RESID_UNDER | "); + stroff += 18; + } + if (flags & 0x04) { + strcpy (&str[stroff], "FCP_RESID_OVER | "); + stroff += 17; + } + if (flags & 0x02) { + strcpy (&str[stroff], "FCP_SNS_LEN_VLD | "); + stroff += 18; + } + if (flags & 0x01) { + strcpy (&str[stroff], "FCP_RSP_LEN_VLD | "); + } + + return (str); +} + +/* Code to actually dissect the packets */ +static void +dissect_fcp_cmnd (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + int offset = 0; + int len, + add_len = 0; + gchar str[128]; + guint8 flags, lun0; + proto_item *ti; + proto_tree *fcp_tree = NULL; + conversation_t *conversation; + fcp_conv_data_t *cdata; + fcp_conv_key_t ckey, *req_key; + scsi_task_id_t task_key; + + /* Determine the length of the FCP part of the packet */ + flags = tvb_get_guint8 (tvb, offset+10); + if (flags) { + add_len = tvb_get_guint8 (tvb, offset+11) & 0x7C; + add_len = add_len >> 2; + + len = FCP_DEF_CMND_LEN + add_len; + } + else { + len = FCP_DEF_CMND_LEN; + } + + /* We track the conversation to determine how many bytes is required */ + /* by the data that is sent back or sent next by the initiator as part */ + /* of this command. The state is destroyed in the response dissector */ + + conversation = find_conversation (&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->oxid, + pinfo->rxid, NO_PORT2); + if (!conversation) { + conversation = conversation_new (&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->oxid, + pinfo->rxid, NO_PORT2); + } + + ckey.conv_idx = conversation->index; + task_key.conv_id = conversation->index; + task_key.task_id = conversation->index; + pinfo->private_data = (void *)&task_key; + + cdata = (fcp_conv_data_t *)g_hash_table_lookup (fcp_req_hash, + &ckey); + if (cdata) { + /* Since we never free the memory used by an exchange, this maybe a + * case of another request using the same exchange as a previous + * req. + */ + cdata->fcp_dl = tvb_get_ntohl (tvb, offset+12+16+add_len); + cdata->abs_usecs = pinfo->fd->abs_usecs; + cdata->abs_secs = pinfo->fd->abs_secs; + } + else { + req_key = g_mem_chunk_alloc (fcp_req_keys); + req_key->conv_idx = conversation->index; + + cdata = g_mem_chunk_alloc (fcp_req_vals); + cdata->fcp_dl = tvb_get_ntohl (tvb, offset+12+16+add_len); + cdata->abs_usecs = pinfo->fd->abs_usecs; + cdata->abs_secs = pinfo->fd->abs_secs; + + g_hash_table_insert (fcp_req_hash, req_key, cdata); + } + + dissect_scsi_cdb (tvb, pinfo, fcp_tree, offset+12, 16+add_len, + SCSI_DEV_UNKNOWN); + + if (tree) { + ti = proto_tree_add_protocol_format (tree, proto_fcp, tvb, 0, len, + "FCP_CMND"); + fcp_tree = proto_item_add_subtree (ti, ett_fcp); + proto_tree_add_uint_hidden (fcp_tree, hf_fcp_type, tvb, offset, 0, 0); + + lun0 = tvb_get_guint8 (tvb, offset); + + /* Display single-level LUNs in decimal for clarity */ + /* I'm taking a shortcut here by assuming that if the first byte of the + * LUN field is 0, it is a single-level LUN. This is not true. For a + * real single-level LUN, all 8 bytes except byte 1 must be 0. + */ + if (lun0) { + cdata->fcp_lun = -1; + proto_tree_add_item (fcp_tree, hf_fcp_multilun, tvb, offset, 8, 0); + } + else { + cdata->fcp_lun = tvb_get_guint8 (tvb, offset+1); + proto_tree_add_item (fcp_tree, hf_fcp_singlelun, tvb, offset+1, + 1, 0); + } + + proto_tree_add_item (fcp_tree, hf_fcp_crn, tvb, offset+8, 1, 0); + proto_tree_add_item (fcp_tree, hf_fcp_taskattr, tvb, offset+9, 1, 0); + proto_tree_add_uint_format (fcp_tree, hf_fcp_taskmgmt, tvb, offset+10, + 1, flags, + "Task Management Flags: 0x%x (%s)", + flags, + task_mgmt_flags_to_str (flags, str)); + proto_tree_add_item (fcp_tree, hf_fcp_addlcdblen, tvb, offset+11, 1, 0); + proto_tree_add_item (fcp_tree, hf_fcp_rddata, tvb, offset+11, 1, 0); + proto_tree_add_item (fcp_tree, hf_fcp_wrdata, tvb, offset+11, 1, 0); + dissect_scsi_cdb (tvb, pinfo, tree, offset+12, 16+add_len, + SCSI_DEV_UNKNOWN); + proto_tree_add_item (fcp_tree, hf_fcp_dl, tvb, offset+12+16+add_len, + 4, 0); + } +} + +static void +dissect_fcp_data (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + conversation_t *conversation; + fcp_conv_data_t *cdata = NULL; + fcp_conv_key_t ckey; + proto_item *ti; + proto_tree *fcp_tree; + scsi_task_id_t task_key; + + /* Retrieve conversation state to determine expected payload */ + conversation = find_conversation (&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->oxid, + pinfo->rxid, NO_PORT2); + if (conversation) { + ckey.conv_idx = conversation->index; + + cdata = (fcp_conv_data_t *)g_hash_table_lookup (fcp_req_hash, + &ckey); + task_key.conv_id = conversation->index; + task_key.task_id = conversation->index; + pinfo->private_data = (void *)&task_key; + } + else { + pinfo->private_data = NULL; + } + if (cdata) { + ti = proto_tree_add_protocol_format (tree, proto_fcp, tvb, 0, 0, + "FCP_DATA"); + fcp_tree = proto_item_add_subtree (ti, ett_fcp); + + if (cdata->fcp_lun >= 0) + proto_tree_add_uint_hidden (fcp_tree, hf_fcp_singlelun, tvb, + 0, 0, cdata->fcp_lun); + + dissect_scsi_payload (tvb, pinfo, tree, 0, FALSE, cdata->fcp_dl); + } + else { + dissect_scsi_payload (tvb, pinfo, tree, 0, FALSE, 0); + } +} + +static void +dissect_fcp_rsp (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + guint32 offset = 0, + del_usecs = 0; + guint len = 0, + add_len = 0, + rsplen = 0; + gchar str[128]; + guint8 flags; + proto_item *ti; + proto_tree *fcp_tree; + guint8 status; + conversation_t *conversation; + fcp_conv_data_t *cdata = NULL; + fcp_conv_key_t ckey; + scsi_task_id_t task_key; + + status = tvb_get_guint8 (tvb, offset+11); + + if (check_col (pinfo->cinfo, COL_INFO)) { + col_append_fstr (pinfo->cinfo, COL_INFO, " , %s", + val_to_str (status, scsi_status_val, "0x%x")); + } + + /* Response marks the end of the conversation. So destroy state */ + conversation = find_conversation (&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->oxid, + pinfo->rxid, NO_PORT2); + if (conversation) { + ckey.conv_idx = conversation->index; + + cdata = (fcp_conv_data_t *)g_hash_table_lookup (fcp_req_hash, + &ckey); + task_key.conv_id = task_key.task_id = conversation->index; + pinfo->private_data = (void *)&task_key; + } + + if (tree) { + /* Determine the length of the FCP part of the packet */ + len = FCP_DEF_RSP_LEN; + + flags = tvb_get_guint8 (tvb, offset+10); + if (flags & 0x2) { + add_len = tvb_get_ntohl (tvb, offset+20); + len += add_len; + } + if (flags & 0x1) { + add_len = tvb_get_ntohl (tvb, offset+16); + len += add_len; + } + + ti = proto_tree_add_protocol_format (tree, proto_fcp, tvb, 0, len, + "FCP_RSP"); + fcp_tree = proto_item_add_subtree (ti, ett_fcp); + proto_tree_add_uint_hidden (fcp_tree, hf_fcp_type, tvb, offset, 0, 0); + + if (cdata) { + del_usecs = (pinfo->fd->abs_secs - cdata->abs_secs)* 1000000 + + (pinfo->fd->abs_usecs - cdata->abs_usecs); + if (del_usecs > 1000) + proto_tree_add_text (fcp_tree, tvb, offset, 0, + "Cmd Response Time: %d msecs", + del_usecs/1000); + else + proto_tree_add_text (fcp_tree, tvb, offset, 0, + "Cmd Response Time: %d usecs", + del_usecs); + if (cdata->fcp_lun >= 0) + proto_tree_add_uint_hidden (fcp_tree, hf_fcp_singlelun, tvb, + offset, 0, cdata->fcp_lun); + } + proto_tree_add_uint_format (fcp_tree, hf_fcp_rspflags, tvb, offset+10, + 1, flags, "Flags: 0x%x (%s)", flags, + rspflags_to_str (flags, str)); + proto_tree_add_item (fcp_tree, hf_fcp_scsistatus, tvb, offset+11, 1, 0); + if (flags & 0xC) + proto_tree_add_item (fcp_tree, hf_fcp_resid, tvb, offset+12, 4, 0); + if (flags & 0x2) + proto_tree_add_item (fcp_tree, hf_fcp_snslen, tvb, offset+16, 4, 0); + if (flags & 0x1) { + rsplen = tvb_get_ntohl (tvb, offset+20); + proto_tree_add_item (fcp_tree, hf_fcp_rsplen, tvb, offset+20, 4, 0); + proto_tree_add_item (fcp_tree, hf_fcp_rspcode, tvb, offset+27, 1, + 0); + } + if (flags & 0x2) { + dissect_scsi_snsinfo (tvb, pinfo, tree, offset+24+rsplen, + tvb_get_ntohl (tvb, offset+16)); + } + if (cdata) { + g_mem_chunk_free (fcp_req_vals, cdata); + g_hash_table_remove (fcp_req_hash, &ckey); + } + } +} + +static void +dissect_fcp_xfer_rdy (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + int offset = 0; + proto_item *ti; + proto_tree *fcp_tree; + guint del_usecs; + + conversation_t *conversation; + fcp_conv_data_t *cdata = NULL; + fcp_conv_key_t ckey, *req_key; + + /* Retrieve conversation state to determine expected payload */ + conversation = find_conversation (&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->oxid, + pinfo->rxid, NO_PORT2); + if (!conversation) { + conversation = conversation_new (&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->oxid, + pinfo->rxid, NO_PORT2); + } + + if (conversation) { + ckey.conv_idx = conversation->index; + + cdata = (fcp_conv_data_t *)g_hash_table_lookup (fcp_req_hash, + &ckey); + if (cdata != NULL) { + cdata->fcp_dl = tvb_get_ntohl (tvb, offset+4); + } + else { + req_key = g_mem_chunk_alloc (fcp_req_keys); + req_key->conv_idx = conversation->index; + + cdata = g_mem_chunk_alloc (fcp_req_vals); + cdata->fcp_dl = tvb_get_ntohl (tvb, offset+4); + cdata->fcp_lun = -1; + + g_hash_table_insert (fcp_req_hash, req_key, cdata); + } + } + + if (tree) { + ti = proto_tree_add_protocol_format (tree, proto_fcp, tvb, 0, 12, + "FCP_XFER_RDY"); + fcp_tree = proto_item_add_subtree (ti, ett_fcp); + proto_tree_add_uint_hidden (fcp_tree, hf_fcp_type, tvb, offset, 0, 0); + + if (cdata) { + del_usecs = (pinfo->fd->abs_secs - cdata->abs_secs)* 1000000 + + (pinfo->fd->abs_usecs - cdata->abs_usecs); + if (del_usecs > 1000) + proto_tree_add_text (fcp_tree, tvb, offset, 0, + "Cmd Response Time: %d msecs", + del_usecs/1000); + else + proto_tree_add_text (fcp_tree, tvb, offset, 0, + "Cmd Response Time: %d usecs", + del_usecs); + if (cdata->fcp_lun >= 0) + proto_tree_add_uint_hidden (fcp_tree, hf_fcp_singlelun, tvb, + offset, 0, cdata->fcp_lun); + } + proto_tree_add_item (fcp_tree, hf_fcp_data_ro, tvb, offset, 4, 0); + proto_tree_add_item (fcp_tree, hf_fcp_burstlen, tvb, offset+4, 4, 0); + } +} + +static void +dissect_fcp (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + +/* Set up structures needed to add the protocol subtree and manage it */ + guint8 r_ctl; + + /* Make entries in Protocol column and Info column on summary display */ + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "FCP"); + + r_ctl = pinfo->r_ctl; + + r_ctl &= 0xF; + + if (check_col (pinfo->cinfo, COL_INFO)) { + col_set_str (pinfo->cinfo, COL_INFO, val_to_str (r_ctl, fcp_iu_val, + "0x%x")); + } + + switch (r_ctl) { + case FCP_IU_DATA: + dissect_fcp_data (tvb, pinfo, tree); + break; + case FCP_IU_CONFIRM: + /* Nothing to be done here */ + break; + case FCP_IU_XFER_RDY: + dissect_fcp_xfer_rdy (tvb, pinfo, tree); + break; + case FCP_IU_CMD: + dissect_fcp_cmnd (tvb, pinfo, tree); + break; + case FCP_IU_RSP: + dissect_fcp_rsp (tvb, pinfo, tree); + break; + default: + call_dissector (data_handle, tvb, pinfo, tree); + break; + } +} + +/* Register the protocol with Ethereal */ + +/* this format is require because a script is used to build the C function + that calls all the protocol registration. +*/ + +void +proto_register_fcp (void) +{ + + /* Setup list of header fields See Section 1.6.1 for details*/ + static hf_register_info hf[] = { + { &hf_fcp_type, + {"Field to branch off to SCSI", "fcp.type", FT_UINT8, BASE_HEX, NULL, + 0x0, "", HFILL}}, + {&hf_fcp_multilun, + {"Multi-Level LUN", "fcp.multilun", FT_BYTES, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_fcp_singlelun, + {"LUN", "fcp.lun", FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL}}, + { &hf_fcp_crn, + {"Command Ref Num", "fcp.crn", FT_UINT8, BASE_DEC, NULL, 0x0, "", + HFILL}}, + { &hf_fcp_taskattr, + {"Task Attribute", "fcp.taskattr", FT_UINT8, BASE_HEX, + VALS (fcp_task_attr_val), 0x7, "", HFILL}}, + { &hf_fcp_taskmgmt, + {"Task Management Flags", "fcp.taskmgmt", FT_UINT8, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_fcp_addlcdblen, + {"Additional CDB Length", "fcp.addlcdblen", FT_UINT8, BASE_DEC, NULL, + 0xFC, "", HFILL}}, + { &hf_fcp_rddata, + {"RDDATA", "fcp.rddata", FT_UINT8, BASE_BIN, NULL, 0x2, "", HFILL}}, + { &hf_fcp_wrdata, + {"WRDATA", "fcp.wrdata", FT_UINT8, BASE_BIN, NULL, 0x1, "", HFILL}}, + { &hf_fcp_dl, + {"FCP_DL", "fcp.dl", FT_UINT32, BASE_DEC, NULL, 0x0, "", HFILL}}, + { &hf_fcp_data_ro, + {"FCP_DATA_RO", "fcp.data_ro", FT_UINT32, BASE_DEC, NULL, 0x0, "", + HFILL}}, + { &hf_fcp_burstlen, + {"Burst Length", "fcp.burstlen", FT_UINT32, BASE_DEC, NULL, 0x0, "", + HFILL}}, + { &hf_fcp_rspflags, + {"FCP_RSP Flags", "fcp.rspflags", FT_UINT8, BASE_BIN, NULL, 0x0, "", + HFILL}}, + { &hf_fcp_resid, + {"FCP_RESID", "fcp.resid", FT_UINT32, BASE_DEC, NULL, 0x0, "", + HFILL}}, + { &hf_fcp_snslen, + {"FCP_SNS_LEN", "fcp.snslen", FT_UINT32, BASE_DEC, NULL, 0x0, "", + HFILL}}, + { &hf_fcp_rsplen, + {"FCP_RSP_LEN", "fcp.rsplen", FT_UINT32, BASE_DEC, NULL, 0x0, "", + HFILL}}, + { &hf_fcp_rspcode, + {"RSP_CODE", "fcp.rspcode", FT_UINT8, BASE_HEX, + VALS (fcp_rsp_code_val), 0x0, "", HFILL}}, + { &hf_fcp_scsistatus, + {"SCSI Status", "fcp.status", FT_UINT8, BASE_HEX, + VALS (scsi_status_val), 0x0, "", HFILL}}, + }; + + /* Setup protocol subtree array */ + static gint *ett[] = { + &ett_fcp, + }; + + /* Register the protocol name and description */ + proto_fcp = proto_register_protocol("Fibre Channel Protocol for SCSI", + "FCP", "fcp"); + + /* Required function calls to register the header fields and subtrees used */ + proto_register_field_array(proto_fcp, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + fcp_dissector = register_dissector_table ("fcp.type", "FCP Type", FT_UINT8, + BASE_HEX); + register_init_routine (&fcp_init_protocol); +} + +/* If this dissector uses sub-dissector registration add a registration routine. + This format is required because a script is used to find these routines and + create the code that calls these routines. +*/ +void +proto_reg_handoff_fcp (void) +{ + dissector_handle_t fcp_handle; + + fcp_handle = create_dissector_handle (dissect_fcp, proto_fcp); + dissector_add("fc.ftype", FC_FTYPE_SCSI, fcp_handle); + + data_handle = find_dissector ("data"); +} + + diff --git a/packet-fcp.h b/packet-fcp.h new file mode 100644 index 0000000000..3960654288 --- /dev/null +++ b/packet-fcp.h @@ -0,0 +1,69 @@ +/* packet-fcp.h + * Fibre Channel SCSI (FCP) Protocol definitions + * Copyright 2001 Dinesh G Dutt (ddutt@cisco.com) + * + * $Id: packet-fcp.h,v 1.1 2002/12/08 02:32:17 gerald Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * 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. + */ + +#ifndef __PACKET_FCP_H_ +#define __PACKET_FCP_H_ + +/* Information Categories based on lower 4 bits of R_CTL */ +#define FCP_IU_DATA 0x1 +#define FCP_IU_CONFIRM 0x3 +#define FCP_IU_XFER_RDY 0x5 +#define FCP_IU_CMD 0x6 +#define FCP_IU_RSP 0x7 + +static const value_string fcp_iu_val[] = { + {FCP_IU_DATA , "FCP_DATA"}, + {FCP_IU_CONFIRM , "Confirm"}, + {FCP_IU_XFER_RDY , "XFER_RDY"}, + {FCP_IU_CMD , "FCP_CMND"}, + {FCP_IU_RSP , "FCP_RSP"}, + {0, NULL}, +}; + +/* Task Attribute Values */ +static const value_string fcp_task_attr_val[] = { + {0, "Simple"}, + {1, "Head of Queue"}, + {2, "Ordered"}, + {4, "ACA"}, + {5, "Untagged"}, + {0, NULL}, +}; + +/* RSP Code Definitions (from FCP_RSP_INFO) */ +static const value_string fcp_rsp_code_val[] = { + {0, "Task Management Function Complete"}, + {1, "FCP_DATA length Different from FCP_BURST_LEN"}, + {2, "FCP_CMND Fields Invalid"}, + {3, "FCP_DATA Parameter Mismatch With FCP_DATA_RO"}, + {4, "Task Management Function Rejected"}, + {5, "Task Management Function Failed"}, + {0, NULL}, +}; + +#define FCP_DEF_CMND_LEN 32 /* by default cmnd is 32 bytes */ +#define FCP_DEF_RSP_LEN 24 /* default FCP_RSP len */ + +#endif diff --git a/packet-fcswils.c b/packet-fcswils.c new file mode 100644 index 0000000000..cbf45aafbf --- /dev/null +++ b/packet-fcswils.c @@ -0,0 +1,1727 @@ +/* packet-fcswils + * Routines for FC Inter-switch link services + * Copyright 2001, Dinesh G Dutt <ddutt@cisco.com> + * + * $Id: packet-fcswils.c,v 1.1 2002/12/08 02:32:17 gerald Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * Copyright 1998 Gerald Combs + * + * Copied from WHATEVER_FILE_YOU_USED (where "WHATEVER_FILE_YOU_USED" + * is a dissector file; if you just copied this from README.developer, + * don't bother with the "Copied from" - you don't even need to put + * in a "Copied from" if you copied an existing dissector, especially + * if the bulk of the code in the new dissector is your code) + * + * 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 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif + +#include <glib.h> + +#ifdef NEED_SNPRINTF_H +# include "snprintf.h" +#endif + +#include <epan/packet.h> +#include <epan/conversation.h> +#include "etypes.h" +#include "packet-fc.h" +#include "packet-fcswils.h" + +#define FC_SWILS_RPLY 0x0 +#define FC_SWILS_REQ 0x1 + +/* Zone name has the structure: + * name_len (1 byte), rsvd (3 bytes), name (m bytes), fill (n bytes) + * name_len excludes the 4 initial bytes before the name + */ +#define ZONENAME_LEN(x, y) (tvb_get_guint8(x, y)+4) + +/* Initialize the protocol and registered fields */ +static int proto_fcswils = -1; +static int hf_swils_opcode = -1; +static int hf_swils_elp_rev = -1; +static int hf_swils_elp_flags = -1; +static int hf_swils_elp_r_a_tov = -1; +static int hf_swils_elp_e_d_tov = -1; +static int hf_swils_elp_req_epn = -1; +static int hf_swils_elp_req_esn = -1; +static int hf_swils_elp_clsf_svcp = -1; +static int hf_swils_elp_clsf_rcvsz = -1; +static int hf_swils_elp_clsf_conseq = -1; +static int hf_swils_elp_clsf_e2e = -1; +static int hf_swils_elp_clsf_openseq = -1; +static int hf_swils_elp_cls1_svcp = -1; +static int hf_swils_elp_cls1_rcvsz = -1; +static int hf_swils_elp_cls2_svcp = -1; +static int hf_swils_elp_cls2_rcvsz = -1; +static int hf_swils_elp_cls3_svcp = -1; +static int hf_swils_elp_cls3_rcvsz = -1; +static int hf_swils_elp_isl_fc_mode = -1; +static int hf_swils_elp_fcplen = -1; +static int hf_swils_elp_b2bcredit = -1; +static int hf_swils_elp_compat1 = -1; +static int hf_swils_elp_compat2 = -1; +static int hf_swils_elp_compat3 = -1; +static int hf_swils_elp_compat4 = -1; +static int hf_swils_efp_rec_type = -1; +static int hf_swils_efp_dom_id = -1; +static int hf_swils_efp_switch_name = -1; +static int hf_swils_efp_mcast_grpno = -1; +static int hf_swils_efp_alias_token = -1; +static int hf_swils_efp_payload_len = -1; +static int hf_swils_efp_pswitch_pri = -1; +static int hf_swils_efp_pswitch_name = -1; +static int hf_swils_dia_switch_name = -1; +static int hf_swils_rdi_payload_len = -1; +static int hf_swils_rdi_req_sname = -1; +static int hf_swils_fspfh_cmd = -1; +static int hf_swils_fspfh_rev = -1; +static int hf_swils_fspfh_ar_num = -1; +static int hf_swils_fspfh_auth_type = -1; +static int hf_swils_fspfh_dom_id = -1; +static int hf_swils_fspfh_auth = -1; +static int hf_swils_hlo_options = -1; +static int hf_swils_hlo_hloint = -1; +static int hf_swils_hlo_deadint = -1; +static int hf_swils_hlo_rcv_domid = -1; +static int hf_swils_hlo_orig_pidx = -1; +static int hf_swils_ldrec_linkid = -1; +static int hf_swils_ldrec_out_pidx = -1; +static int hf_swils_ldrec_nbr_pidx = -1; +static int hf_swils_ldrec_link_type = -1; +static int hf_swils_ldrec_link_cost = -1; +static int hf_swils_lsrh_lsr_type = -1; +static int hf_swils_lsrh_lsid = -1; +static int hf_swils_lsrh_adv_domid = -1; +static int hf_swils_lsrh_ls_incid = -1; +static int hf_swils_esc_pdesc_vendorid = -1; +static int hf_swils_esc_swvendorid = -1; +static int hf_swils_esc_protocolid = -1; +static int hf_swils_rscn_evtype = -1; +static int hf_swils_rscn_addrfmt = -1; +static int hf_swils_rscn_detectfn = -1; +static int hf_swils_rscn_affectedport = -1; +static int hf_swils_rscn_portstate = -1; +static int hf_swils_rscn_portid = -1; +static int hf_swils_rscn_pwwn = -1; +static int hf_swils_rscn_nwwn = -1; +static int hf_swils_zone_activezonenm = -1; +static int hf_swils_zone_objname = -1; +static int hf_swils_zone_objtype = -1; +static int hf_swils_zone_mbrtype = -1; +static int hf_swils_zone_protocol = -1; +static int hf_swils_zone_mbrid = -1; +static int hf_swils_zone_status = -1; +static int hf_swils_zone_reason = -1; +static int hf_swils_aca_domainid = -1; +static int hf_swils_sfc_opcode = -1; +static int hf_swils_sfc_zonenm = -1; +static int hf_swils_rjt = -1; +static int hf_swils_rjtdet = -1; +static int hf_swils_rjtvendor = -1; +static int hf_swils_zone_mbrid_lun = -1; + +/* Initialize the subtree pointers */ +static gint ett_fcswils = -1; +static gint ett_fcswils_swacc = -1; +static gint ett_fcswils_swrjt = -1; +static gint ett_fcswils_elp = -1; +static gint ett_fcswils_efp = -1; +static gint ett_fcswils_efplist = -1; +static gint ett_fcswils_dia = -1; +static gint ett_fcswils_rdi = -1; +static gint ett_fcswils_fspfhdr = -1; +static gint ett_fcswils_hlo = -1; +static gint ett_fcswils_lsrec = -1; +static gint ett_fcswils_lsrechdr = -1; +static gint ett_fcswils_ldrec = -1; +static gint ett_fcswils_lsu = -1; +static gint ett_fcswils_lsa = -1; +static gint ett_fcswils_bf = -1; +static gint ett_fcswils_rcf = -1; +static gint ett_fcswils_rscn = -1; +static gint ett_fcswils_rscn_dev = -1; +static gint ett_fcswils_drlir = -1; +static gint ett_fcswils_mr = -1; +static gint ett_fcswils_zoneobjlist = -1; +static gint ett_fcswils_zoneobj = -1; +static gint ett_fcswils_zonembr = -1; +static gint ett_fcswils_aca = -1; +static gint ett_fcswils_rca = -1; +static gint ett_fcswils_sfc = -1; +static gint ett_fcswils_ufc = -1; +static gint ett_fcswils_esc = -1; +static gint ett_fcswils_esc_pdesc = -1; + +typedef struct _zonename { + guint32 namelen:8, + rsvd:24; + gchar *name; + gchar *pad; +} zonename_t; + +typedef struct _fcswils_conv_key { + guint32 conv_idx; +} fcswils_conv_key_t; + +typedef struct _fcswils_conv_data { + guint32 opcode; +} fcswils_conv_data_t; + +#ifndef WIN32 +#define PACKED __attribute__((__packed__)) +#else +#define PACKED +#endif + +GHashTable *fcswils_req_hash = NULL; +GMemChunk *fcswils_req_keys = NULL; +GMemChunk *fcswils_req_vals = NULL; +guint32 fcswils_init_count = 25; + +static dissector_handle_t data_handle; + +static gint get_zoneobj_len (tvbuff_t *tvb, gint offset); + +/* + * Hash Functions + */ +static gint +fcswils_equal(gconstpointer v, gconstpointer w) +{ + fcswils_conv_key_t *v1 = (fcswils_conv_key_t *)v; + fcswils_conv_key_t *v2 = (fcswils_conv_key_t *)w; + + return (v1->conv_idx == v2->conv_idx); +} + +static guint +fcswils_hash (gconstpointer v) +{ + fcswils_conv_key_t *key = (fcswils_conv_key_t *)v; + guint val; + + val = key->conv_idx; + + return val; +} + +/* + * Protocol initialization + */ +static void +fcswils_init_protocol(void) +{ + if (fcswils_req_keys) + g_mem_chunk_destroy (fcswils_req_keys); + if (fcswils_req_vals) + g_mem_chunk_destroy (fcswils_req_vals); + if (fcswils_req_hash) + g_hash_table_destroy (fcswils_req_hash); + + fcswils_req_hash = g_hash_table_new(fcswils_hash, fcswils_equal); + fcswils_req_keys = g_mem_chunk_new("fcswils_req_keys", + sizeof(fcswils_conv_key_t), + fcswils_init_count * sizeof(fcswils_conv_key_t), + G_ALLOC_AND_FREE); + fcswils_req_vals = g_mem_chunk_new("fcswils_req_vals", + sizeof(fcswils_conv_data_t), + fcswils_init_count * sizeof(fcswils_conv_data_t), + G_ALLOC_AND_FREE); +} + +static gchar * +zonenm_to_str (tvbuff_t *tvb, gint offset) +{ + int len = tvb_get_guint8 (tvb, offset); + return ((gchar *)tvb_get_ptr (tvb, offset+4, len)); +} + +/* Offset points to the start of the zone object */ +static gint +get_zoneobj_len (tvbuff_t *tvb, gint offset) +{ + gint numrec, numrec1; + guint8 objtype; + gint i, j, len; + + /* zone object structure is: + * type (1 byte), protocol (1 byte), rsvd (2 bytes), obj name (x bytes), + * num of zone mbrs (4 bytes ), list of zone members (each member if of + * variable length). + * + * zone member structure is: + * type (1 byte), rsvd (1 byte), flags (1 byte), id_len (1 byte), + * id (id_len bytes) + */ + objtype = tvb_get_guint8 (tvb, offset); + len = 4 + ZONENAME_LEN (tvb, offset+4); /* length upto num_of_mbrs field */ + numrec = tvb_get_ntohl (tvb, offset+len); /* gets us num of zone mbrs */ + + len += 4; /* + num_mbrs */ + for (i = 0; i < numrec; i++) { + if (objtype == FC_SWILS_ZONEOBJ_ZONESET) { + len += 4 + ZONENAME_LEN (tvb, offset+4+len); /* length upto num_of_mbrs field */ + numrec1 = tvb_get_ntohl (tvb, offset+len); + + len += 4; + for (j = 0; j < numrec1; j++) { + len += 4 + tvb_get_guint8 (tvb, offset+3+len); + } + } + else if (objtype == FC_SWILS_ZONEOBJ_ZONE) { + len += 4 + tvb_get_guint8 (tvb, offset+3+len); + } + } + + return len; +} + +static void +dissect_swils_elp (tvbuff_t *tvb, proto_tree *elp_tree, guint8 isreq _U_) +{ + + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 0, + stroff = 0; + gchar flags[40]; + fcswils_elp elp; + + /* Response i.e. SW_ACC for an ELP has the same format as the request */ + /* We skip the initial 4 bytes as we don't care about the opcode */ + tvb_memcpy (tvb, (guint8 *)&elp, 4, FC_SWILS_ELP_SIZE); + + elp.r_a_tov = ntohl (elp.r_a_tov); + elp.e_d_tov = ntohl (elp.e_d_tov); + elp.isl_flwctrl_mode = ntohs (elp.isl_flwctrl_mode); + elp.flw_ctrl_parmlen = ntohs (elp.flw_ctrl_parmlen); + + if (elp_tree) { + offset += 4; + proto_tree_add_item (elp_tree, hf_swils_elp_rev, tvb, offset++, 1, 0); + proto_tree_add_item (elp_tree, hf_swils_elp_flags, tvb, offset, 2, 0); + offset += 3; + proto_tree_add_uint_format (elp_tree, hf_swils_elp_r_a_tov, tvb, offset, 4, + elp.r_a_tov, "R_A_TOV: %d msecs", elp.r_a_tov); + offset += 4; + proto_tree_add_uint_format (elp_tree, hf_swils_elp_e_d_tov, tvb, offset, 4, + elp.e_d_tov, "E_D_TOV: %d msecs", elp.e_d_tov); + offset += 4; + proto_tree_add_string (elp_tree, hf_swils_elp_req_epn, tvb, offset, 8, + fcwwn_to_str (elp.req_epname)); + offset += 8; + proto_tree_add_string (elp_tree, hf_swils_elp_req_esn, tvb, offset, 8, + fcwwn_to_str (elp.req_sname)); + offset += 8; + + flags[0] = '\0'; + if (elp.clsf_svcparm[0] & 0x80) { + strcpy (flags, "Class F Valid"); + + if (elp.clsf_svcparm[4] & 0x20) { + strcpy (&flags[13], " | X_ID Interlock"); + } + else { + strcpy (&flags[13], " | No X_ID Interlk"); + } + } + else { + strcpy (flags, "Class F Invld"); + } + proto_tree_add_bytes_format (elp_tree, hf_swils_elp_clsf_svcp, tvb, offset, 6, + &elp.clsf_svcparm[0], "Class F Svc Parameters: (%s)", flags); + offset += 6; + + proto_tree_add_item (elp_tree, hf_swils_elp_clsf_rcvsz, tvb, offset, 2, 0); + offset += 2; + proto_tree_add_item (elp_tree, hf_swils_elp_clsf_conseq, tvb, offset, 2, 0); + offset += 2; + proto_tree_add_item (elp_tree, hf_swils_elp_clsf_e2e, tvb, offset, 2, 0); + offset += 2; + proto_tree_add_item (elp_tree, hf_swils_elp_clsf_openseq, tvb, offset, 2, 0); + offset += 4; + + flags[0] = '\0'; + stroff = 0; + if (elp.cls1_svcparm[0] & 0x80) { + strcpy (&flags[stroff], "Class 1 Valid"); + stroff += 13; + if (elp.cls1_svcparm[0] & 0x40) { + strcpy (&flags[stroff], " | IMX"); + stroff += 6; + } + if (elp.cls1_svcparm[0] & 0x20) { + strcpy (&flags[stroff], " | XPS"); + stroff += 6; + } + if (elp.cls1_svcparm[0] & 0x10) { + strcpy (&flags[stroff], " | LKS"); + } + } + else { + strcpy (&flags[0], "Class 1 Invalid"); + } + + proto_tree_add_bytes_format (elp_tree, hf_swils_elp_cls1_svcp, tvb, offset, 2, + tvb_get_ptr (tvb, offset, 2), + "Class 1 Svc Parameters: (%s)", flags); + offset += 2; + if (elp.cls1_svcparm[0] & 0x80) { + proto_tree_add_item (elp_tree, hf_swils_elp_cls1_rcvsz, tvb, offset, 2, 0); + } + offset += 2; + + flags[0] = '\0'; + if (elp.cls2_svcparm[0] & 0x80) { + strcpy (flags, "Class 2 Valid"); + + if (elp.cls2_svcparm[0] & 0x08) { + strcpy (&flags[13], " | Seq Delivery"); + } + else { + strcpy (&flags[13], " | No Seq Delivery"); + } + } + else { + strcpy (flags, "Class 2 Invld"); + } + + proto_tree_add_bytes_format (elp_tree, hf_swils_elp_cls2_svcp, tvb, offset, 2, + &elp.cls2_svcparm[0], + "Class 2 Svc Parameters: (%s)", flags); + offset += 2; + + if (elp.cls2_svcparm[0] & 0x80) { + proto_tree_add_item (elp_tree, hf_swils_elp_cls2_rcvsz, tvb, offset, 2, 0); + } + offset += 2; + + flags[0] = '\0'; + if (elp.cls3_svcparm[0] & 0x80) { + strcpy (flags, "Class 3 Valid"); + + if (elp.cls3_svcparm[0] & 0x08) { + strcpy (&flags[13], " | Seq Delivery"); + } + else { + strcpy (&flags[13], " | No Seq Delivery"); + } + } + else { + strcpy (flags, "Class 3 Invld"); + } + proto_tree_add_bytes_format (elp_tree, hf_swils_elp_cls3_svcp, tvb, offset, 2, + &elp.cls3_svcparm[0], + "Class 3 Svc Parameters: (%s)", flags); + offset += 2; + + if (elp.cls3_svcparm[0] & 0x80) { + proto_tree_add_item (elp_tree, hf_swils_elp_cls3_rcvsz, tvb, offset, 2, 0); + } + offset += 22; + + proto_tree_add_string (elp_tree, hf_swils_elp_isl_fc_mode, tvb, offset, 2, + val_to_str (elp.isl_flwctrl_mode, fcswils_elp_fc_val, "Vendor Unique")); + offset += 2; + proto_tree_add_item (elp_tree, hf_swils_elp_fcplen, tvb, offset, 2, 0); + offset += 2; + proto_tree_add_item (elp_tree, hf_swils_elp_b2bcredit, tvb, offset, 4, 0); + offset += 4; + proto_tree_add_item (elp_tree, hf_swils_elp_compat1, tvb, offset, 4, 0); + offset += 4; + proto_tree_add_item (elp_tree, hf_swils_elp_compat2, tvb, offset, 4, 0); + offset += 4; + proto_tree_add_item (elp_tree, hf_swils_elp_compat3, tvb, offset, 4, 0); + offset += 4; + proto_tree_add_item (elp_tree, hf_swils_elp_compat4, tvb, offset, 4, 0); + } + +} + +static void +dissect_swils_efp (tvbuff_t *tvb, proto_tree *efp_tree, guint8 isreq _U_) +{ + +/* Set up structures needed to add the protocol subtree and manage it */ + proto_item *subti; + proto_tree *lrec_tree; + int num_listrec = 0, + offset = 0; + fcswils_efp efp; + fcswils_efp_listrec *lrec; + + tvb_memcpy (tvb, (guint8 *)&efp, offset, FC_SWILS_EFP_SIZE); + efp.payload_len = ntohs (efp.payload_len); + efp.listrec = (fcswils_efp_listrec *)tvb_get_ptr (tvb, FC_SWILS_EFP_SIZE, + efp.payload_len - FC_SWILS_EFP_SIZE); + + if (efp_tree) { + offset += 2; + proto_tree_add_item (efp_tree, hf_swils_efp_payload_len, tvb, offset, 2, 0); + offset += 5; + proto_tree_add_item (efp_tree, hf_swils_efp_pswitch_pri, tvb, + offset++, 1, 0); + proto_tree_add_string (efp_tree, hf_swils_efp_pswitch_name, tvb, offset, + 8, fcwwn_to_str (efp.pswitch_name)); + offset += 8; + + /* Add List Records now */ + if (efp.reclen) { + num_listrec = (efp.payload_len - FC_SWILS_EFP_SIZE)/efp.reclen; + } + else { + num_listrec = 0; + } + + while (num_listrec--) { + lrec = (fcswils_efp_listrec *)tvb_get_ptr (tvb, offset, efp.reclen); + if (lrec != NULL) { + if (lrec->didrec.rec_type == FC_SWILS_LRECTYPE_DOMAIN) { + subti = proto_tree_add_text (efp_tree, tvb, offset, + (efp.payload_len - FC_SWILS_EFP_SIZE), + "Domain ID Record"); + lrec_tree = proto_item_add_subtree (subti, ett_fcswils_efplist); + proto_tree_add_item (lrec_tree, hf_swils_efp_dom_id, tvb, offset+1, 1, 0); + proto_tree_add_string (lrec_tree, hf_swils_efp_switch_name, tvb, offset+8, 8, + fcwwn_to_str (lrec->didrec.sname)); + } + else if (lrec->didrec.rec_type == FC_SWILS_LRECTYPE_MCAST) { + subti = proto_tree_add_text (efp_tree, tvb, offset, + (efp.payload_len - FC_SWILS_EFP_SIZE), + "Multicast ID Record"); + lrec_tree = proto_item_add_subtree (subti, ett_fcswils_efplist); + proto_tree_add_item (lrec_tree, hf_swils_efp_mcast_grpno, tvb, offset+1, 1, 0); + } + offset += efp.reclen; + } + } + } +} + +static void +dissect_swils_dia (tvbuff_t *tvb, proto_tree *dia_tree, guint8 isreq _U_) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 0; + + if (dia_tree) { + proto_tree_add_string (dia_tree, hf_swils_dia_switch_name, tvb, offset+4, + 8, fcwwn_to_str (tvb_get_ptr (tvb, offset+4, 8))); + } +} + +static void +dissect_swils_rdi (tvbuff_t *tvb, proto_tree *rdi_tree, guint8 isreq) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 0; + int i, plen, numrec; + + if (rdi_tree) { + plen = tvb_get_ntohs (tvb, offset+2); + + proto_tree_add_item (rdi_tree, hf_swils_rdi_payload_len, tvb, offset+2, 2, 0); + proto_tree_add_string (rdi_tree, hf_swils_rdi_req_sname, tvb, offset+4, + 8, fcwwn_to_str (tvb_get_ptr (tvb, offset+4, 8))); + + /* 12 is the length of the initial header and 4 is the size of each + * domain request record. + */ + numrec = (plen - 12)/4; + offset = 12; + for (i = 0; i < numrec; i++) { + if (isreq) { + proto_tree_add_text (rdi_tree, tvb, offset+3, 1, + "Requested Domain ID: %d", + tvb_get_guint8 (tvb, offset+3)); + } + else { + proto_tree_add_text (rdi_tree, tvb, offset+3, 1, + "Granted Domain ID: %d", + tvb_get_guint8 (tvb, offset+3)); + } + offset += 4; + } + } +} + +static void +dissect_swils_fspf_hdr (tvbuff_t *tvb, proto_tree *tree, int offset) +{ + proto_item *subti; + proto_tree *fspfh_tree; + + if (tree) { + /* 20 is the size of FSPF header */ + subti = proto_tree_add_text (tree, tvb, offset, 20, "FSPF Header"); + fspfh_tree = proto_item_add_subtree (subti, ett_fcswils_fspfhdr); + + proto_tree_add_item (fspfh_tree, hf_swils_fspfh_rev, tvb, offset+4, + 1, 0); + proto_tree_add_item (fspfh_tree, hf_swils_fspfh_ar_num, tvb, + offset+5, 1, 0); + proto_tree_add_item (fspfh_tree, hf_swils_fspfh_auth_type, tvb, + offset+6, 1, 0); + proto_tree_add_item (fspfh_tree, hf_swils_fspfh_dom_id, tvb, offset+11, + 1, 0); + proto_tree_add_item (fspfh_tree, hf_swils_fspfh_auth, tvb, offset+12, + 8, 0); + } +} + +static void +dissect_swils_fspf_lsrechdr (tvbuff_t *tvb, proto_tree *lsrec_tree, int offset) +{ + proto_tree *tree; + proto_item *subti; + + subti = proto_tree_add_text (lsrec_tree, tvb, offset, 24, + "Link State Record Header"); + tree = proto_item_add_subtree (subti, ett_fcswils_lsrechdr); + + proto_tree_add_item (tree, hf_swils_lsrh_lsr_type, tvb, offset, 1, 0); + proto_tree_add_text (tree, tvb, offset+2, 2, "LSR Age: %d secs", + tvb_get_ntohs (tvb, offset+2)); + proto_tree_add_text (tree, tvb, offset+4, 4, "Options : 0x%x", + tvb_get_ntohl (tvb, offset+4)); + proto_tree_add_item (tree, hf_swils_lsrh_lsid, tvb, offset+11, 1, 0); + proto_tree_add_item (tree, hf_swils_lsrh_adv_domid, tvb, offset+15, 1, 0); + proto_tree_add_item (tree, hf_swils_lsrh_ls_incid, tvb, offset+16, 4, 0); + proto_tree_add_text (tree, tvb, offset+20, 2, "Checksum: 0x%x", + tvb_get_ntohs (tvb, offset+20)); + proto_tree_add_text (tree, tvb, offset+22, 2, "LSR Length: %d", + tvb_get_ntohs (tvb, offset+22)); +} + +static void +dissect_swils_fspf_ldrec (tvbuff_t *tvb, proto_tree *tree, int offset) +{ + proto_tree_add_string (tree, hf_swils_ldrec_linkid, tvb, offset, 4, + fc_to_str (tvb_get_ptr (tvb, offset+1, 3))); + proto_tree_add_item (tree, hf_swils_ldrec_out_pidx, tvb, offset+5, 3, 0); + proto_tree_add_item (tree, hf_swils_ldrec_nbr_pidx, tvb, offset+9, 3, 0); + proto_tree_add_item (tree, hf_swils_ldrec_link_type, tvb, offset+12, 1, 0); + proto_tree_add_item (tree, hf_swils_ldrec_link_cost, tvb, offset+14, 2, 0); +} + +static void +dissect_swils_fspf_lsrec (tvbuff_t *tvb, proto_tree *tree, int offset, + int num_lsrec) +{ + int i, j, num_ldrec; + proto_item *subti1, *subti; + proto_tree *lsrec_tree, *ldrec_tree; + + if (tree) { + for (j = 0; j < num_lsrec; j++) { + num_ldrec = tvb_get_ntohs (tvb, offset+26); + subti = proto_tree_add_text (tree, tvb, offset, (28+num_ldrec*16), + "Link State Record %d", j); + lsrec_tree = proto_item_add_subtree (subti, ett_fcswils_lsrec); + + dissect_swils_fspf_lsrechdr (tvb, lsrec_tree, offset); + proto_tree_add_text (tree, tvb, offset+26, 2, "Number of Links: %d", + num_ldrec); + offset += 28; + + for (i = 0; i < num_ldrec; i++) { + subti1 = proto_tree_add_text (tree, tvb, offset, 16, + "Link Descriptor %d", i); + ldrec_tree = proto_item_add_subtree (subti1, ett_fcswils_ldrec); + dissect_swils_fspf_ldrec (tvb, ldrec_tree, offset); + offset += 16; + } + } + } +} + +static void +dissect_swils_hello (tvbuff_t *tvb, proto_tree *hlo_tree, guint8 isreq _U_) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 0; + + if (hlo_tree) { + dissect_swils_fspf_hdr (tvb, hlo_tree, offset); + + proto_tree_add_item (hlo_tree, hf_swils_hlo_options, tvb, offset+20, 4, 0); + proto_tree_add_item (hlo_tree, hf_swils_hlo_hloint, tvb, offset+24, 4, 0); + proto_tree_add_item (hlo_tree, hf_swils_hlo_deadint, tvb, offset+28, 4, 0); + proto_tree_add_item (hlo_tree, hf_swils_hlo_rcv_domid, tvb, offset+35, 1, 0); + proto_tree_add_item (hlo_tree, hf_swils_hlo_orig_pidx, tvb, offset+37, 3, 0); + } +} + +static void +dissect_swils_lsupdate (tvbuff_t *tvb, proto_tree *lsu_tree, guint8 isreq _U_) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 0; + int num_lsrec; + + if (lsu_tree) { + dissect_swils_fspf_hdr (tvb, lsu_tree, offset); + + proto_tree_add_text (lsu_tree, tvb, offset+23, 1, "Flags : %s", + val_to_str (tvb_get_guint8 (tvb, offset+23), + fc_swils_fspf_lsrflags_val, "0x%x")); + num_lsrec = tvb_get_ntohl (tvb, offset+24); + + proto_tree_add_text (lsu_tree, tvb, offset+24, 4, "Num of LSRs: %d", + num_lsrec); + + offset = 28; + dissect_swils_fspf_lsrec (tvb, lsu_tree, offset, num_lsrec); + } +} + +static void +dissect_swils_lsack (tvbuff_t *tvb, proto_tree *lsa_tree, guint8 isreq _U_) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 0; + int num_lsrechdr, i; + + if (lsa_tree) { + dissect_swils_fspf_hdr (tvb, lsa_tree, offset); + + proto_tree_add_text (lsa_tree, tvb, offset+23, 1, "Flags : %s", + val_to_str (tvb_get_guint8 (tvb, offset+23), + fc_swils_fspf_lsrflags_val, "0x%x")); + num_lsrechdr = tvb_get_ntohl (tvb, offset+24); + + proto_tree_add_text (lsa_tree, tvb, offset+24, 4, "Num of LSR Headers: %d", + num_lsrechdr); + + offset = 28; + + for (i = 0; i < num_lsrechdr; i++) { + dissect_swils_fspf_lsrechdr (tvb, lsa_tree, offset); + offset += 24; + } + } +} + +static void +dissect_swils_rscn (tvbuff_t *tvb, proto_tree *rscn_tree, guint8 isreq) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 0; + proto_tree *dev_tree; + int addrfmt, evtype; + int numrec, i; + proto_item *subti; + + if (rscn_tree) { + if (!isreq) + return; + + evtype = tvb_get_guint8 (tvb, offset+4); + addrfmt = evtype & 0x0F; + evtype = evtype >> 4; + + proto_tree_add_item (rscn_tree, hf_swils_rscn_evtype, tvb, offset+4, + 1, 0); + proto_tree_add_item (rscn_tree, hf_swils_rscn_addrfmt, tvb, offset+4, + 1, 0); + proto_tree_add_string (rscn_tree, hf_swils_rscn_affectedport, tvb, + offset+5, 3, fc_to_str (tvb_get_ptr (tvb, + offset+5, 3))); + proto_tree_add_item (rscn_tree, hf_swils_rscn_detectfn, tvb, + offset+8, 4, 0); + numrec = tvb_get_ntohl (tvb, offset+12); + proto_tree_add_text (rscn_tree, tvb, offset+12, 4, "Num Entries: %d", + numrec); + + offset = 16; + for (i = 0; i < numrec; i++) { + subti = proto_tree_add_text (rscn_tree, tvb, offset, 20, + "Device Entry %d", i); + dev_tree = proto_item_add_subtree (rscn_tree, ett_fcswils_rscn_dev); + + proto_tree_add_item (dev_tree, hf_swils_rscn_portstate, tvb, offset, 1, 0); + proto_tree_add_string (dev_tree, hf_swils_rscn_portid, tvb, offset+1, 3, + fc_to_str (tvb_get_ptr (tvb, offset+1, 3))); + proto_tree_add_string (dev_tree, hf_swils_rscn_pwwn, tvb, offset+4, 8, + fcwwn_to_str (tvb_get_ptr (tvb, offset+4, 8))); + proto_tree_add_string (dev_tree, hf_swils_rscn_nwwn, tvb, offset+4, 8, + fcwwn_to_str (tvb_get_ptr (tvb, offset+4, 8))); + offset += 20; + } + } +} + +/* + * Merge Request contains zoning objects organized in the following format: + * + * Zone Set Object + * | + * +---------------- Zone Object + * | | + * +-- +---------------- Zone Member + * | | | + * +-- +---- +----- + * + * So the decoding of the zone merge request is based on this structure + */ + +static void +dissect_swils_zone_mbr (tvbuff_t *tvb, proto_tree *zmbr_tree, int offset) +{ + int mbrlen = 4 + tvb_get_guint8 (tvb, offset+3); + + proto_tree_add_item (zmbr_tree, hf_swils_zone_mbrtype, tvb, + offset, 1, 0); + proto_tree_add_text (zmbr_tree, tvb, offset+2, 1, "Flags: 0x%x", + tvb_get_guint8 (tvb, offset+2)); + proto_tree_add_text (zmbr_tree, tvb, offset+3, 1, + "Identifier Length: %d", + tvb_get_guint8 (tvb, offset+3)); + switch (tvb_get_guint8 (tvb, offset)) { + case FC_SWILS_ZONEMBR_WWN: + proto_tree_add_string (zmbr_tree, hf_swils_zone_mbrid, tvb, + offset+4, 8, + fcwwn_to_str (tvb_get_ptr (tvb, + offset+4, + 8))); + break; + case FC_SWILS_ZONEMBR_DP: + proto_tree_add_string_format (zmbr_tree, + hf_swils_zone_mbrid, + tvb, offset+4, 4, " ", + "0x%x", + tvb_get_ntohl (tvb, + offset+4)); + break; + case FC_SWILS_ZONEMBR_FCID: + proto_tree_add_string (zmbr_tree, hf_swils_zone_mbrid, tvb, + offset+4, 4, + fc_to_str (tvb_get_ptr (tvb, + offset+5, + 3))); + break; + case FC_SWILS_ZONEMBR_ALIAS: + proto_tree_add_string (zmbr_tree, hf_swils_zone_mbrid, tvb, + offset+4, + tvb_get_guint8 (tvb, offset+3), + zonenm_to_str (tvb, offset+4)); + break; + case FC_SWILS_ZONEMBR_WWN_LUN: + proto_tree_add_string (zmbr_tree, hf_swils_zone_mbrid, tvb, + offset+4, 8, + fcwwn_to_str (tvb_get_ptr (tvb, + offset+4, + 8))); + proto_tree_add_item (zmbr_tree, hf_swils_zone_mbrid_lun, tvb, + offset+12, 8, 0); + break; + case FC_SWILS_ZONEMBR_DP_LUN: + proto_tree_add_string_format (zmbr_tree, + hf_swils_zone_mbrid, + tvb, offset+4, 4, " ", + "0x%x", + tvb_get_ntohl (tvb, + offset+4)); + proto_tree_add_item (zmbr_tree, hf_swils_zone_mbrid_lun, tvb, + offset+8, 8, 0); + break; + case FC_SWILS_ZONEMBR_FCID_LUN: + proto_tree_add_string (zmbr_tree, hf_swils_zone_mbrid, tvb, + offset+4, 4, + fc_to_str (tvb_get_ptr (tvb, + offset+5, + 3))); + proto_tree_add_item (zmbr_tree, hf_swils_zone_mbrid_lun, tvb, + offset+8, 8, 0); + break; + default: + proto_tree_add_string (zmbr_tree, hf_swils_zone_mbrid, tvb, + offset+4, mbrlen, + "Unknown member type format"); + + } +} + +static void +dissect_swils_zone_obj (tvbuff_t *tvb, proto_tree *zobj_tree, int offset) +{ + proto_tree *zmbr_tree; + int mbrlen, numrec, i, objtype; + proto_item *subti; + + objtype = tvb_get_guint8 (tvb, offset); + + proto_tree_add_item (zobj_tree, hf_swils_zone_objtype, tvb, offset, + 1, 0); + proto_tree_add_item (zobj_tree, hf_swils_zone_protocol, tvb, + offset+1, 1, 0); + proto_tree_add_string (zobj_tree, hf_swils_zone_objname, tvb, + offset+4, ZONENAME_LEN (tvb, offset+4), + zonenm_to_str (tvb, offset+4)); + + numrec = tvb_get_ntohl (tvb, offset+4+ZONENAME_LEN (tvb, offset+4)); + proto_tree_add_text (zobj_tree, tvb, + offset+4+ZONENAME_LEN (tvb, offset+4), 4, + "Number of Zone Members: %d", numrec); + + offset += 8 + ZONENAME_LEN (tvb, offset+4); + for (i = 0; i < numrec; i++) { + if (objtype == FC_SWILS_ZONEOBJ_ZONESET) { + dissect_swils_zone_obj (tvb, zobj_tree, offset); + offset += get_zoneobj_len (tvb, offset); + } + else { + mbrlen = 4 + tvb_get_guint8 (tvb, offset+3); + subti = proto_tree_add_text (zobj_tree, tvb, offset, mbrlen, + "Zone Member %d", i); + zmbr_tree = proto_item_add_subtree (zobj_tree, + ett_fcswils_zonembr); + dissect_swils_zone_mbr (tvb, zmbr_tree, offset); + offset += mbrlen; + } + } +} + +static void +dissect_swils_mergereq (tvbuff_t *tvb, proto_tree *mr_tree, guint8 isreq) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 0; + proto_tree *zobjlist_tree, *zobj_tree; + int numrec, i, zonesetlen, objlistlen, objlen; + proto_item *subti; + + if (mr_tree) { + if (isreq) { + /* zonesetlen is the size of the zoneset including the zone name */ + zonesetlen = tvb_get_ntohs (tvb, offset+2); + proto_tree_add_text (mr_tree, tvb, offset+2, 2, + "Active ZoneSet Length: %d", zonesetlen); + + if (zonesetlen) { + proto_tree_add_string (mr_tree, hf_swils_zone_activezonenm, tvb, + offset+4, ZONENAME_LEN (tvb, offset+4), + zonenm_to_str (tvb, offset+4)); + + /* objlistlen gives the size of the active zoneset object list */ + objlistlen = zonesetlen - ZONENAME_LEN (tvb, offset+4); + /* Offset = start of the active zoneset zoning object list */ + offset = offset + (4 + ZONENAME_LEN (tvb, offset+4)); + numrec = tvb_get_ntohl (tvb, offset); + + subti = proto_tree_add_text (mr_tree, tvb, offset, objlistlen, + "Active Zone Set"); + zobjlist_tree = proto_item_add_subtree (subti, + ett_fcswils_zoneobjlist); + + proto_tree_add_text (zobjlist_tree, tvb, offset, 4, + "Number of zoning objects: %d", numrec); + + offset += 4; + for (i = 0; i < numrec; i++) { + objlen = get_zoneobj_len (tvb, offset); + subti = proto_tree_add_text (zobjlist_tree, tvb, offset+4, + objlen, "Zone Object %d", i); + zobj_tree = proto_item_add_subtree (subti, ett_fcswils_zoneobj); + dissect_swils_zone_obj (tvb, zobj_tree, offset); + offset += objlen; + } + } + else { + offset += 4; + } + + zonesetlen = tvb_get_ntohl (tvb, offset); + proto_tree_add_text (mr_tree, tvb, offset, 4, + "Full Zone Set Length: %d", zonesetlen); + + if (zonesetlen) { + objlistlen = zonesetlen; + /* Offset = start of the active zoneset zoning object list */ + offset += 4; + numrec = tvb_get_ntohl (tvb, offset); + + subti = proto_tree_add_text (mr_tree, tvb, offset, objlistlen, + "Full Zone Set"); + + zobjlist_tree = proto_item_add_subtree (subti, + ett_fcswils_zoneobjlist); + proto_tree_add_text (zobjlist_tree, tvb, offset, 4, + "Number of zoning objects: %d", numrec); + offset += 4; + for (i = 0; i < numrec; i++) { + objlen = get_zoneobj_len (tvb, offset); + subti = proto_tree_add_text (zobjlist_tree, tvb, offset, + objlen, "Zone Object %d", i); + zobj_tree = proto_item_add_subtree (subti, ett_fcswils_zoneobj); + dissect_swils_zone_obj (tvb, zobj_tree, offset); + offset += objlen; + } + } + } + else { + proto_tree_add_item (mr_tree, hf_swils_zone_status, tvb, + offset+5, 1, 0); + proto_tree_add_item (mr_tree, hf_swils_zone_reason, tvb, + offset+6, 1, 0); + proto_tree_add_text (mr_tree, tvb, offset+7, 1, + "Vendor Unique: 0x%x", + tvb_get_guint8 (tvb, offset+7)); + } + } +} + +static void +dissect_swils_aca (tvbuff_t *tvb, proto_tree *aca_tree, guint8 isreq) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 0; + int numrec, plen, i; + + if (aca_tree) { + if (isreq) { + plen = tvb_get_ntohs (tvb, offset+2); + proto_tree_add_text (aca_tree, tvb, offset+2, 2, + "Domain ID List Length: %d", plen); + numrec = plen/4; + offset = 4; + + for (i = 0; i < numrec; i++) { + proto_tree_add_uint_format (aca_tree, hf_swils_aca_domainid, + tvb, offset+3, 1, + tvb_get_guint8 (tvb, offset+3), + "Domain ID %d: %d", i, + tvb_get_guint8 (tvb, offset+3)); + offset += 4; + } + } + else { + proto_tree_add_item (aca_tree, hf_swils_zone_status, tvb, + offset+5, 1, 0); + proto_tree_add_item (aca_tree, hf_swils_zone_reason, tvb, + offset+6, 1, 0); + proto_tree_add_text (aca_tree, tvb, offset+7, 1, + "Vendor Unique: 0x%x", + tvb_get_guint8 (tvb, offset+7)); + } + } +} + +static void +dissect_swils_rca (tvbuff_t *tvb, proto_tree *rca_tree, guint8 isreq) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 0; + + if (rca_tree) { + if (!isreq) { + proto_tree_add_item (rca_tree, hf_swils_zone_status, tvb, + offset+5, 1, 0); + proto_tree_add_item (rca_tree, hf_swils_zone_reason, tvb, + offset+6, 1, 0); + proto_tree_add_text (rca_tree, tvb, offset+7, 1, + "Vendor Unique: 0x%x", + tvb_get_guint8 (tvb, offset+7)); + } + } +} + +static void +dissect_swils_sfc (tvbuff_t *tvb, proto_tree *sfc_tree, guint8 isreq) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 0; + proto_tree *zobjlist_tree, *zobj_tree; + int numrec, i, zonesetlen, objlistlen, objlen; + proto_item *subti; + + if (sfc_tree) { + if (isreq) { + proto_tree_add_item (sfc_tree, hf_swils_sfc_opcode, tvb, offset+1, 1, 0); + + zonesetlen = tvb_get_ntohs (tvb, offset+2); + proto_tree_add_text (sfc_tree, tvb, offset+2, 2, + "ZoneSet Length: %d", zonesetlen); + + if (zonesetlen) { + proto_tree_add_string (sfc_tree, hf_swils_sfc_zonenm, tvb, + offset+4, ZONENAME_LEN (tvb, offset+4), + zonenm_to_str (tvb, offset+4)); + + /* objlistlen gives the size of the active zoneset object list */ + objlistlen = zonesetlen - ZONENAME_LEN (tvb, offset+4); + /* Offset = start of the active zoneset zoning object list */ + offset = offset + (4 + ZONENAME_LEN (tvb, offset+4)); + numrec = tvb_get_ntohl (tvb, offset); + + subti = proto_tree_add_text (sfc_tree, tvb, offset, objlistlen, + "Zone Set"); + zobjlist_tree = proto_item_add_subtree (subti, + ett_fcswils_zoneobjlist); + + proto_tree_add_text (zobjlist_tree, tvb, offset, 4, + "Number of zoning objects: %d", numrec); + + offset += 4; + for (i = 0; i < numrec; i++) { + objlen = get_zoneobj_len (tvb, offset); + subti = proto_tree_add_text (zobjlist_tree, tvb, offset, + objlen, "Zone Object %d", i); + zobj_tree = proto_item_add_subtree (subti, ett_fcswils_zoneobj); + dissect_swils_zone_obj (tvb, zobj_tree, offset); + offset += objlen; + } + } + else { + offset += 4; + } + + zonesetlen = tvb_get_ntohl (tvb, offset); + proto_tree_add_text (sfc_tree, tvb, offset, 4, + "Full Zone Set Length: %d", zonesetlen); + + if (zonesetlen) { + objlistlen = zonesetlen; + /* Offset = start of the active zoneset zoning object list */ + offset += 4; + numrec = tvb_get_ntohl (tvb, offset); + + subti = proto_tree_add_text (sfc_tree, tvb, offset, objlistlen, + "Full Zone Set"); + + zobjlist_tree = proto_item_add_subtree (subti, + ett_fcswils_zoneobjlist); + proto_tree_add_text (zobjlist_tree, tvb, offset, 4, + "Number of zoning objects: %d", numrec); + offset += 4; + for (i = 0; i < numrec; i++) { + objlen = get_zoneobj_len (tvb, offset); + subti = proto_tree_add_text (zobjlist_tree, tvb, offset, + objlen, "Zone Object %d", i); + zobj_tree = proto_item_add_subtree (subti, ett_fcswils_zoneobj); + dissect_swils_zone_obj (tvb, zobj_tree, offset); + offset += objlen; + } + } + } + else { + proto_tree_add_item (sfc_tree, hf_swils_zone_status, tvb, + offset+5, 1, 0); + proto_tree_add_item (sfc_tree, hf_swils_zone_reason, tvb, + offset+6, 1, 0); + proto_tree_add_text (sfc_tree, tvb, offset+7, 1, + "Vendor Unique: 0x%x", + tvb_get_guint8 (tvb, offset+7)); + } + } +} + +static void +dissect_swils_ufc (tvbuff_t *tvb, proto_tree *ufc_tree, guint8 isreq) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 0; + + if (ufc_tree) { + if (!isreq) { + proto_tree_add_item (ufc_tree, hf_swils_zone_status, tvb, + offset+5, 1, 0); + proto_tree_add_item (ufc_tree, hf_swils_zone_reason, tvb, + offset+6, 1, 0); + proto_tree_add_text (ufc_tree, tvb, offset+7, 1, + "Vendor Unique: 0x%x", + tvb_get_guint8 (tvb, offset+7)); + } + } +} + +static void +dissect_swils_esc (tvbuff_t *tvb, proto_tree *esc_tree, guint8 isreq) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 0; + int i, numrec, plen; + proto_tree *pdesc_tree; + proto_item *subti; + + if (esc_tree) { + if (isreq) { + plen = tvb_get_ntohs (tvb, offset+2); + proto_tree_add_text (esc_tree, tvb, offset+2, 2, + "Payload Length: %d", plen); + proto_tree_add_item (esc_tree, hf_swils_esc_swvendorid, tvb, + offset+4, 8, 0); + numrec = (plen - 12)/12; + offset = 12; + + for (i = 0; i < numrec; i++) { + subti = proto_tree_add_text (esc_tree, tvb, offset, 12, + "Protocol Descriptor %d", i); + pdesc_tree = proto_item_add_subtree (subti, + ett_fcswils_esc_pdesc); + proto_tree_add_item (pdesc_tree, hf_swils_esc_pdesc_vendorid, tvb, + offset, 8, 0); + proto_tree_add_item (pdesc_tree, hf_swils_esc_protocolid, + tvb, offset+10, 2, 0); + offset += 12; + } + } + else { + proto_tree_add_item (esc_tree, hf_swils_esc_swvendorid, tvb, + offset+4, 8, 0); + subti = proto_tree_add_text (esc_tree, tvb, offset+12, 12, + "Accepted Protocol Descriptor"); + pdesc_tree = proto_item_add_subtree (subti, ett_fcswils_esc_pdesc); + + proto_tree_add_item (pdesc_tree, hf_swils_esc_pdesc_vendorid, tvb, + offset+12, 8, 0); + proto_tree_add_item (pdesc_tree, hf_swils_esc_protocolid, + tvb, offset+22, 2, 0); + } + } +} + +static void +dissect_swils_drlir (tvbuff_t *tvb _U_, proto_tree *drlir_tree _U_, + guint8 isreq _U_) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + return; +} + +static void +dissect_swils_swrjt (tvbuff_t *tvb, proto_tree *swrjt_tree) +{ + /* Set up structures needed to add the protocol subtree and manage it */ + int offset = 0; + + if (swrjt_tree) { + proto_tree_add_item (swrjt_tree, hf_swils_rjt, tvb, offset+5, 1, 0); + proto_tree_add_item (swrjt_tree, hf_swils_rjtdet, tvb, offset+6, 1, 0); + proto_tree_add_item (swrjt_tree, hf_swils_rjtvendor, tvb, offset+7, + 1, 0); + } +} + +/* Code to actually dissect the packets */ +static void +dissect_fcswils (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + proto_item *ti = NULL; + guint8 opcode, + failed_opcode = 0; + int offset = 0; + conversation_t *conversation; + fcswils_conv_data_t *cdata; + fcswils_conv_key_t ckey, *req_key; + proto_tree *swils_tree = NULL; + guint8 isreq = FC_SWILS_REQ; + + /* Make entries in Protocol column and Info column on summary display */ + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "SW_ILS"); + + /* decoding of this is done by each individual opcode handler */ + opcode = tvb_get_guint8 (tvb, 0); + + if (tree) { + ti = proto_tree_add_protocol_format (tree, proto_fcswils, tvb, 0, + tvb_length (tvb), "SW_ILS"); + swils_tree = proto_item_add_subtree (ti, ett_fcswils); + } + + /* Register conversation if this is not a response */ + if ((opcode != FC_SWILS_SWACC) && (opcode != FC_SWILS_SWRJT)) { + conversation = find_conversation (&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->oxid, + pinfo->rxid, NO_PORT2); + if (!conversation) { + conversation = conversation_new (&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->oxid, + pinfo->rxid, NO_PORT2); + } + + ckey.conv_idx = conversation->index; + + cdata = (fcswils_conv_data_t *)g_hash_table_lookup (fcswils_req_hash, + &ckey); + if (cdata) { + /* Since we never free the memory used by an exchange, this maybe a + * case of another request using the same exchange as a previous + * req. + */ + cdata->opcode = opcode; + } + else { + req_key = g_mem_chunk_alloc (fcswils_req_keys); + req_key->conv_idx = conversation->index; + + cdata = g_mem_chunk_alloc (fcswils_req_vals); + cdata->opcode = opcode; + + g_hash_table_insert (fcswils_req_hash, req_key, cdata); + } + } + else { + /* Opcode is ACC or RJT */ + conversation = find_conversation (&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->oxid, + pinfo->rxid, NO_PORT2); + isreq = FC_SWILS_RPLY; + if (!conversation) { + if (tree && (opcode == FC_SWILS_SWACC)) { + /* No record of what this accept is for. Can't decode */ + proto_tree_add_text (swils_tree, tvb, 0, tvb_length (tvb), + "No record of Exchg. Unable to decode SW_ACC"); + return; + } + } + else { + ckey.conv_idx = conversation->index; + + cdata = (fcswils_conv_data_t *)g_hash_table_lookup (fcswils_req_hash, &ckey); + + if (cdata != NULL) { + if (opcode == FC_SWILS_SWACC) + opcode = cdata->opcode; + else + failed_opcode = cdata->opcode; + } + + if (tree) { + if ((cdata == NULL) && (opcode != FC_SWILS_SWRJT)) { + /* No record of what this accept is for. Can't decode */ + proto_tree_add_text (swils_tree, tvb, 0, tvb_length (tvb), + "No record of SW_ILS Req. Unable to decode SW_ACC"); + return; + } + } + } + } + + if (check_col (pinfo->cinfo, COL_INFO)) { + if (isreq == FC_SWILS_REQ) { + col_add_str (pinfo->cinfo, COL_INFO, + val_to_str (opcode, fc_swils_opcode_key_val, "0x%x")); + } + else if (opcode == FC_SWILS_SWRJT) { + col_add_fstr (pinfo->cinfo, COL_INFO, "SW_RJT (%s)", + val_to_str (failed_opcode, fc_swils_opcode_key_val, "0x%x")); + } + else { + col_add_fstr (pinfo->cinfo, COL_INFO, "SW_ACC (%s)", + val_to_str (opcode, fc_swils_opcode_key_val, "0x%x")); + } + } + + if (tree) { + proto_tree_add_item (swils_tree, hf_swils_opcode, tvb, offset, 1, 0); + } + + switch (opcode) { + case FC_SWILS_SWRJT: + dissect_swils_swrjt (tvb, swils_tree); + break; + case FC_SWILS_ELP: + dissect_swils_elp (tvb, swils_tree, isreq); + break; + case FC_SWILS_EFP: + dissect_swils_efp (tvb, swils_tree, isreq); + break; + case FC_SWILS_DIA: + dissect_swils_dia (tvb, swils_tree, isreq); + break; + case FC_SWILS_RDI: + dissect_swils_rdi (tvb, swils_tree, isreq); + break; + case FC_SWILS_HLO: + dissect_swils_hello (tvb, swils_tree, isreq); + break; + case FC_SWILS_LSU: + dissect_swils_lsupdate (tvb, swils_tree, isreq); + break; + case FC_SWILS_LSA: + dissect_swils_lsack (tvb, swils_tree, isreq); + break; + case FC_SWILS_BF: + case FC_SWILS_RCF: + /* Nothing to be displayed for these two commands */ + break; + case FC_SWILS_RSCN: + dissect_swils_rscn (tvb, swils_tree, isreq); + break; + case FC_SWILS_DRLIR: + dissect_swils_drlir (tvb, swils_tree, isreq); + break; + case FC_SWILS_MR: + dissect_swils_mergereq (tvb, swils_tree, isreq); + break; + case FC_SWILS_ACA: + dissect_swils_aca (tvb, swils_tree, isreq); + break; + case FC_SWILS_RCA: + dissect_swils_rca (tvb, swils_tree, isreq); + break; + case FC_SWILS_SFC: + dissect_swils_sfc (tvb, swils_tree, isreq); + break; + case FC_SWILS_UFC: + dissect_swils_ufc (tvb, swils_tree, isreq); + break; + case FC_SWILS_ESC: + dissect_swils_esc (tvb, swils_tree, isreq); + break; + default: + call_dissector (data_handle, tvb, pinfo, tree); + } + +} + +/* Register the protocol with Ethereal */ + +/* this format is require because a script is used to build the C function + that calls all the protocol registration. +*/ + +void +proto_register_fcswils (void) +{ +/* Setup list of header fields See Section 1.6.1 for details*/ + static hf_register_info hf[] = { + { &hf_swils_opcode, + {"Cmd Code", "swils.opcode", FT_UINT8, BASE_HEX, + VALS (fc_swils_opcode_key_val), 0x0, "", HFILL}}, + { &hf_swils_elp_rev, + {"Revision", "swils.elp.rev", FT_UINT8, BASE_DEC, NULL, + 0x0, "", HFILL}}, + { &hf_swils_elp_flags, + {"Flag", "swils.elp.flag", FT_BYTES, BASE_HEX, NULL, 0x0, "", HFILL}}, + { &hf_swils_elp_r_a_tov, + {"R_A_TOV", "swils.elp.ratov", FT_UINT32, BASE_DEC, NULL, + 0x0, "", HFILL}}, + { &hf_swils_elp_e_d_tov, + {"E_D_TOV", "swils.elp.edtov", FT_UINT32, BASE_DEC, NULL, 0x0, "", + HFILL}}, + { &hf_swils_elp_req_epn, + {"Req Eport Name", "swils.elp.reqepn", FT_STRING, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_swils_elp_req_esn, + {"Req Switch Name", "swils.elp.reqesn", FT_STRING, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_swils_elp_clsf_svcp, + {"Class F Svc Param", "swils.elp.clsfp", FT_BYTES, BASE_NONE, NULL, 0x0, + "", HFILL}}, + { &hf_swils_elp_clsf_rcvsz, + {"Max Class F Frame Size", "swils.elp.clsfrsz", FT_UINT16, BASE_DEC, + NULL, 0x0, "", HFILL}}, + { &hf_swils_elp_clsf_conseq, + {"Class F Max Concurrent Seq", "swils.elp.clsfcs", FT_UINT16, BASE_DEC, + NULL, 0x0, "", HFILL}}, + { &hf_swils_elp_clsf_e2e, + {"Class F E2E Credit", "swils.elp.cfe2e", FT_UINT16, BASE_DEC, NULL, + 0x0, "", HFILL}}, + { &hf_swils_elp_clsf_openseq, + {"Class F Max Open Seq", "swils.elp.oseq", FT_UINT16, BASE_DEC, NULL, + 0x0, "", HFILL}}, + { &hf_swils_elp_cls1_svcp, + {"Class 1 Svc Param", "swils.elp.cls1p", FT_BYTES, BASE_NONE, NULL, 0x0, + "", HFILL}}, + { &hf_swils_elp_cls1_rcvsz, + {"Class 1 Frame Size", "swils.elp.cls1rsz", FT_UINT16, BASE_DEC, NULL, + 0x0, "", HFILL}}, + { &hf_swils_elp_cls2_svcp, + {"Class 2 Svc Param", "swils.elp.cls2p", FT_BYTES, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_swils_elp_cls2_rcvsz, + {"Class 2 Frame Size", "swils.elp.cls1rsz", FT_UINT16, BASE_DEC, NULL, + 0x0, "", HFILL}}, + { &hf_swils_elp_cls3_svcp, + {"Class 3 Svc Param", "swils.elp.cls3p", FT_BYTES, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_swils_elp_cls3_rcvsz, + {"Class 3 Frame Size", "swils.elp.cls1rsz", FT_UINT16, BASE_DEC, NULL, + 0x0, "", HFILL}}, + { &hf_swils_elp_isl_fc_mode, + {"ISL Flow Ctrl Mode", "swils.elp.fcmode", FT_STRING, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_swils_elp_fcplen, + {"Flow Ctrl Param Len", "swils.elp.fcplen", FT_UINT16, BASE_DEC, NULL, + 0x0, "", HFILL}}, + { &hf_swils_elp_b2bcredit, + {"B2B Credit", "swils.elp.b2b", FT_UINT32, BASE_DEC, NULL, 0x0, "", + HFILL}}, + { &hf_swils_elp_compat1, + {"Compatability Param 1", "swils.elp.compat1", FT_UINT32, BASE_DEC, NULL, + 0x0, "", HFILL}}, + { &hf_swils_elp_compat2, + {"Compatability Param 2", "swils.elp.compat2", FT_UINT32, BASE_DEC, NULL, + 0x0, "", HFILL}}, + { &hf_swils_elp_compat3, + {"Compatability Param 3", "swils.elp.compat3", FT_UINT32, BASE_DEC, NULL, + 0x0, "", HFILL}}, + { &hf_swils_elp_compat4, + {"Compatability Param 4", "swils.elp.compat4", FT_UINT32, BASE_DEC, NULL, + 0x0, "", HFILL}}, + { &hf_swils_efp_rec_type, + {"Record Type", "swils.efp.rectype", FT_UINT8, BASE_HEX, + VALS (fcswils_rectype_val), 0x0, "", HFILL}}, + { &hf_swils_efp_dom_id, + {"Domain ID", "swils.efp.domid", FT_UINT8, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_swils_efp_switch_name, + {"Switch Name", "swils.efp.sname", FT_STRING, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_swils_efp_mcast_grpno, + {"Mcast Grp#", "swils.efp.mcastno", FT_UINT8, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_swils_efp_alias_token, + {"Alias Token", "swils.efp.aliastok", FT_BYTES, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_swils_efp_payload_len, + {"Payload Len", "swils.efp.payloadlen", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL}}, + { &hf_swils_efp_pswitch_pri, + {"Principal Switch Priority", "swils.efp.psprio", FT_UINT8, BASE_DEC, + NULL, 0x0, "", HFILL}}, + { &hf_swils_efp_pswitch_name, + {"Principal Switch Name", "swils.efp.psname", FT_STRING, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_swils_dia_switch_name, + {"Switch Name", "swils.dia.sname", FT_STRING, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_swils_rdi_payload_len, + {"Payload Len", "swils.rdi.len", FT_UINT16, BASE_DEC, NULL, 0x0, "", + HFILL}}, + { &hf_swils_rdi_req_sname, + {"Req Switch Name", "swils.rdi.reqsn", FT_STRING, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_swils_fspfh_cmd, + {"Command: ", "swils.fspf.cmd", FT_UINT8, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_swils_fspfh_rev, + {"Version", "swils.fspf.ver", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL}}, + { &hf_swils_fspfh_ar_num, + {"AR Number", "swils.fspf.arnum", FT_UINT8, BASE_HEX, NULL, 0x0, "", + HFILL}}, + { &hf_swils_fspfh_auth_type, + {"Authentication Type", "swils.fspf.authtype", FT_UINT8, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_swils_fspfh_dom_id, + {"Originating Domain ID", "swils.fspf.origdomid", FT_UINT8, BASE_DEC, + NULL, 0x0, "", HFILL}}, + { &hf_swils_fspfh_auth, + {"Authentication", "swils.fspf.auth", FT_BYTES, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_swils_hlo_options, + {"Options", "swils.hlo.options", FT_BYTES, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_swils_hlo_hloint, + {"Hello Interval (secs)", "swils.hlo.hloint", FT_UINT32, BASE_DEC, + NULL, 0x0, "", HFILL}}, + { &hf_swils_hlo_deadint, + {"Dead Interval (secs)", "swils.hlo.deadint", FT_UINT32, BASE_DEC, + NULL, 0x0, "", HFILL}}, + { &hf_swils_hlo_rcv_domid, + {"Recipient Domain ID", "swils.hlo.rcvdomid", FT_UINT8, BASE_DEC, + NULL, 0x0, "", HFILL}}, + { &hf_swils_hlo_orig_pidx, + {"Originating Port Idx", "swils.hlo.origpidx", FT_UINT24, BASE_HEX, + NULL, 0x0, "", HFILL}}, + { &hf_swils_lsrh_lsr_type, + {"LSR Type", "swils.lsr.type", FT_UINT8, BASE_HEX, + VALS (fc_swils_fspf_linkrec_val), 0x0, "", HFILL}}, + { &hf_swils_lsrh_lsid, + {"Link State Id", "swils.ls.id", FT_UINT8, BASE_DEC, NULL, 0x0, + "", HFILL}}, + { &hf_swils_lsrh_adv_domid, + {"Advertising Domain Id", "swils.lsr.advdomid", FT_UINT8, BASE_DEC, + NULL, 0x0, "", HFILL}}, + { &hf_swils_lsrh_ls_incid, + {"LS Incarnation Number", "swils.lsr.incid", FT_UINT32, BASE_DEC, + NULL, 0x0, "", HFILL}}, + { &hf_swils_ldrec_linkid, + {"Link ID", "swils.ldr.linkid", FT_STRING, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_swils_ldrec_out_pidx, + {"Output Port Idx", "swils.ldr.out_portidx", FT_UINT24, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_swils_ldrec_nbr_pidx, + {"Neighbor Port Idx", "swils.ldr.nbr_portidx", FT_UINT24, BASE_HEX, + NULL, 0x0, "", HFILL}}, + { &hf_swils_ldrec_link_type, + {"Link Type", "swils.ldr.linktype", FT_UINT8, BASE_HEX, + VALS (fc_swils_link_type_val), 0x0, "", HFILL}}, + { &hf_swils_ldrec_link_cost, + {"Link Cost", "swils.ldr.linkcost", FT_UINT16, BASE_DEC, NULL, 0x0, + "", HFILL}}, + { &hf_swils_rscn_evtype, + {"Event Type", "swils.rscn.evtype", FT_UINT8, BASE_DEC, + VALS (fc_swils_rscn_portstate_val), 0xF0, "", HFILL}}, + { &hf_swils_rscn_addrfmt, + {"Address Format", "swils.rscn.addrfmt", FT_UINT8, BASE_DEC, + VALS (fc_swils_rscn_addrfmt_val), 0x0F, "", HFILL}}, + { &hf_swils_rscn_affectedport, + {"Affected Port ID", "swils.rscn.affectedport", FT_STRING, BASE_HEX, + NULL, 0x0, "", HFILL}}, + { &hf_swils_rscn_detectfn, + {"Detection Function", "swils.rscn.detectfn", FT_UINT32, BASE_HEX, + VALS (fc_swils_rscn_detectfn_val), 0x0, "", HFILL}}, + { &hf_swils_rscn_portstate, + {"Port State", "swils.rscn.portstate", FT_UINT8, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_swils_rscn_portid, + {"Port Id", "swils.rscn.portid", FT_STRING, BASE_HEX, NULL, 0x0, "", + HFILL}}, + { &hf_swils_rscn_pwwn, + {"Port WWN", "swils.rscn.pwwn", FT_STRING, BASE_HEX, NULL, 0x0, "", + HFILL}}, + { &hf_swils_rscn_nwwn, + {"Node WWN", "swils.rscn.nwwn", FT_STRING, BASE_HEX, NULL, 0x0, "", + HFILL}}, + { &hf_swils_esc_swvendorid, + {"Switch Vendor ID", "swils.esc.swvendor", FT_STRING, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_swils_esc_pdesc_vendorid, + {"Vendor ID", "swils.esc.vendorid", FT_STRING, BASE_HEX, NULL, 0x0, + "", HFILL}}, + { &hf_swils_esc_protocolid, + {"Protocol ID", "swils.esc.protocol", FT_UINT16, BASE_HEX, + VALS (fc_swils_esc_protocol_val), 0x0, "", HFILL}}, + { &hf_swils_zone_activezonenm, + {"Active Zoneset Name", "swils.mr.activezonesetname", FT_STRING, + BASE_HEX, NULL, 0x0, "", HFILL}}, + { &hf_swils_zone_objname, + {"Zone Object Name", "swils.zone.zoneobjname", FT_STRING, BASE_HEX, + NULL, 0x0, "", HFILL}}, + { &hf_swils_zone_objtype, + {"Zone Object Type", "swils.zone.zoneobjtype", FT_UINT8, BASE_HEX, + VALS (fc_swils_zoneobj_type_val), 0x0, "", HFILL}}, + { &hf_swils_zone_mbrtype, + {"Zone Member Type", "swils.zone.mbrtype", FT_UINT8, BASE_HEX, + VALS (fc_swils_zonembr_type_val), 0x0, "", HFILL}}, + { &hf_swils_zone_protocol, + {"Zone Protocol", "swils.zone.protocol", FT_UINT8, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_swils_zone_mbrid, + {"Member Identifier", "swils.zone.mbrid", FT_STRING, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_swils_zone_status, + {"Zone Command Status", "swils.zone.status", FT_UINT8, BASE_HEX, + VALS (fc_swils_mr_rsp_val), 0x0, "Applies to MR, ACA, RCA, SFC, UFC", + HFILL}}, + { &hf_swils_zone_reason, + {"Zone Command Reason Code", "swils.zone.reason", FT_UINT8, BASE_HEX, + VALS (fc_swils_mr_reason_val), 0x0, "Applies to MR, ACA, RCA, SFC, UFC", + HFILL}}, + { &hf_swils_aca_domainid, + {"Known Domain ID", "swils.aca.domainid", FT_UINT8, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_swils_sfc_opcode, + {"Operation Request", "swils.sfc.opcode", FT_UINT8, BASE_HEX, + VALS (fc_swils_sfc_op_val), 0x0, "", HFILL}}, + { &hf_swils_sfc_zonenm, + {"Zone Set Name", "swils.sfc.zonename", FT_STRING, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_swils_rjt, + {"Reason Code", "swils.rjt.reason", FT_UINT8, BASE_HEX, + VALS (fc_swils_rjt_val), 0x0, "", HFILL}}, + { &hf_swils_rjtdet, + {"Reason Code Explanantion", "swils.rjt.reasonexpl", FT_UINT8, + BASE_HEX, VALS (fc_swils_deterr_val), 0x0, "", HFILL}}, + { &hf_swils_rjtvendor, + {"Vendor Unique Error Code", "swils.rjt.vendor", FT_UINT8, BASE_HEX, + NULL, 0x0, "", HFILL}}, + { &hf_swils_zone_mbrid_lun, + {"LUN", "swils.zone.lun", FT_BYTES, BASE_HEX, NULL, 0x0, "", + HFILL}}, + }; + + /* Setup protocol subtree array */ + static gint *ett[] = { + &ett_fcswils, + &ett_fcswils_swacc, + &ett_fcswils_swrjt, + &ett_fcswils_elp, + &ett_fcswils_efp, + &ett_fcswils_efplist, + &ett_fcswils_dia, + &ett_fcswils_rdi, + &ett_fcswils_fspfhdr, + &ett_fcswils_hlo, + &ett_fcswils_lsrec, + &ett_fcswils_lsrechdr, + &ett_fcswils_ldrec, + &ett_fcswils_lsu, + &ett_fcswils_lsa, + &ett_fcswils_bf, + &ett_fcswils_rcf, + &ett_fcswils_rscn, + &ett_fcswils_rscn_dev, + &ett_fcswils_drlir, + &ett_fcswils_mr, + &ett_fcswils_zoneobjlist, + &ett_fcswils_zoneobj, + &ett_fcswils_zonembr, + &ett_fcswils_aca, + &ett_fcswils_rca, + &ett_fcswils_sfc, + &ett_fcswils_ufc, + &ett_fcswils_esc, + &ett_fcswils_esc_pdesc, + }; + + /* Register the protocol name and description */ + proto_fcswils = proto_register_protocol("Fibre Channel SW_ILS", "FC-SWILS", "swils"); + + /* Required function calls to register the header fields and subtrees used */ + proto_register_field_array(proto_fcswils, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + register_init_routine(&fcswils_init_protocol); +} + +/* If this dissector uses sub-dissector registration add a registration routine. + This format is required because a script is used to find these routines and + create the code that calls these routines. +*/ +void +proto_reg_handoff_fcswils (void) +{ + dissector_handle_t swils_handle; + + swils_handle = create_dissector_handle (dissect_fcswils, proto_fcswils); + dissector_add("fc.ftype", FC_FTYPE_SWILS, swils_handle); + + data_handle = find_dissector ("data"); +} + + diff --git a/packet-fcswils.h b/packet-fcswils.h new file mode 100644 index 0000000000..365304d8b4 --- /dev/null +++ b/packet-fcswils.h @@ -0,0 +1,389 @@ +/* packet-fcswils.h + * Fibre Channel Switch InterLink Services Definitions + * Copyright 2001 Dinesh G Dutt (ddutt@cisco.com) + * + * $Id: packet-fcswils.h,v 1.1 2002/12/08 02:32:17 gerald Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * 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. + */ + +#ifndef __PACKET_FCSWILS_H_ +#define __PACKET_FCSWILS_H_ + +/* Command codes */ +#define FC_SWILS_SWRJT 0x01 +#define FC_SWILS_SWACC 0x02 +#define FC_SWILS_ELP 0x10 +#define FC_SWILS_EFP 0x11 +#define FC_SWILS_DIA 0x12 +#define FC_SWILS_RDI 0x13 +#define FC_SWILS_HLO 0x14 +#define FC_SWILS_LSU 0x15 +#define FC_SWILS_LSA 0x16 +#define FC_SWILS_BF 0x17 +#define FC_SWILS_RCF 0x18 +#define FC_SWILS_RSCN 0x1B +#define FC_SWILS_DRLIR 0x1E +#define FC_SWILS_DSCN 0x20 +#define FC_SWILS_LOOPD 0x21 +#define FC_SWILS_MR 0x22 +#define FC_SWILS_ACA 0x23 +#define FC_SWILS_RCA 0x24 +#define FC_SWILS_SFC 0x25 +#define FC_SWILS_UFC 0x26 +#define FC_SWILS_ESC 0x30 + +/* Used in filters */ +static const value_string fc_swils_opcode_key_val[] = { + {FC_SWILS_SWRJT , "SW_RJT"}, + {FC_SWILS_SWACC , "SW_ACC"}, + {FC_SWILS_ELP , "ELP"}, + {FC_SWILS_EFP , "EFP"}, + {FC_SWILS_DIA , "DIA"}, + {FC_SWILS_RDI , "RDI"}, + {FC_SWILS_HLO , "HLO"}, + {FC_SWILS_LSU , "LSU"}, + {FC_SWILS_LSA , "LSA"}, + {FC_SWILS_BF , "BF"}, + {FC_SWILS_RCF , "RCF"}, + {FC_SWILS_RSCN , "SW_RSCN"}, + {FC_SWILS_DRLIR , "DRLIR"}, + {FC_SWILS_DSCN , "DSCN"}, + {FC_SWILS_LOOPD , "LOOPD"}, + {FC_SWILS_MR , "MR"}, + {FC_SWILS_ACA , "ACA"}, + {FC_SWILS_RCA , "RCA"}, + {FC_SWILS_SFC , "SFC"}, + {FC_SWILS_UFC , "UFC"}, + {FC_SWILS_ESC , "ESC"}, + {0, NULL}, +}; + +/* Used in Info field */ +static const value_string fc_swils_opcode_val[] = { + {FC_SWILS_SWRJT , "SW_RJT"}, + {FC_SWILS_SWACC , "SW_ACC"}, + {FC_SWILS_ELP , "ELP"}, + {FC_SWILS_EFP , "EFP"}, + {FC_SWILS_DIA , "Domain ID Assigned"}, + {FC_SWILS_RDI , "Request Domain ID"}, + {FC_SWILS_HLO , "Hello"}, + {FC_SWILS_LSU , "Link State Update"}, + {FC_SWILS_LSA , "Link State Ack"}, + {FC_SWILS_BF , "Build Fabric"}, + {FC_SWILS_RCF , "Reconfigure Fabric"}, + {FC_SWILS_RSCN , "Interswitch RSCN"}, + {FC_SWILS_DRLIR , "DRLIR"}, + {FC_SWILS_DSCN , "SW_RSCN"}, + {FC_SWILS_LOOPD , "LOOPD"}, + {FC_SWILS_MR , "Merge Req"}, + {FC_SWILS_ACA , "Acquire Change Auth"}, + {FC_SWILS_RCA , "Release Change Auth"}, + {FC_SWILS_SFC , "Stage Fabric Conf"}, + {FC_SWILS_UFC , "Update Fabric Conf"}, + {FC_SWILS_ESC , "ESC"}, + {0, NULL}, +}; + +/* Reject reason codes */ + +#define FC_SWILS_RJT_INVCODE 0x01 +#define FC_SWILS_RJT_INVVER 0x02 +#define FC_SWILS_RJT_LOGERR 0x03 +#define FC_SWILS_RJT_INVSIZE 0x04 +#define FC_SWILS_RJT_LOGBSY 0x05 +#define FC_SWILS_RJT_PROTERR 0x07 +#define FC_SWILS_RJT_GENFAIL 0x09 +#define FC_SWILS_RJT_CMDNOTSUPP 0x0B +#define FC_SWILS_RJT_VENDUNIQ 0xFF + +static const value_string fc_swils_rjt_val [] = { + {FC_SWILS_RJT_INVCODE , "Invalid Cmd Code"}, + {FC_SWILS_RJT_INVVER , "Invalid Revision"}, + {FC_SWILS_RJT_LOGERR , "Logical Error"}, + {FC_SWILS_RJT_INVSIZE , "Invalid Size"}, + {FC_SWILS_RJT_LOGBSY , "Logical Busy"}, + {FC_SWILS_RJT_PROTERR , "Protocol Error"}, + {FC_SWILS_RJT_GENFAIL , "Unable to Perform"}, + {FC_SWILS_RJT_CMDNOTSUPP, "Unsupported Cmd"}, + {FC_SWILS_RJT_VENDUNIQ , "Vendor Unique Err"}, + {0, NULL}, +}; + +/* Detailed reason code defines */ +#define FC_SWILS_RJT_NODET 0x0 +#define FC_SWILS_RJT_CLSF_ERR 0x1 +#define FC_SWILS_RJT_CLSN_ERR 0x3 +#define FC_SWILS_RJT_INVFC_CODE 0x4 +#define FC_SWILS_RJT_INVFC_PARM 0x5 +#define FC_SWILS_RJT_INV_PNAME 0xD +#define FC_SWILS_RJT_INV_SNAME 0xE +#define FC_SWILS_RJT_TOV_MSMTCH 0xF +#define FC_SWILS_RJT_INV_DIDLST 0x10 +#define FC_SWILS_RJT_CMD_INPROG 0x19 +#define FC_SWILS_RJT_OORSRC 0x29 +#define FC_SWILS_RJT_NO_DID 0x2A +#define FC_SWILS_RJT_INV_DID 0x2B +#define FC_SWILS_RJT_NO_REQ 0x2C +#define FC_SWILS_RJT_NOLNK_PARM 0x2D +#define FC_SWILS_RJT_NO_REQDID 0x2E +#define FC_SWILS_RJT_EP_ISOL 0x2F + +static const value_string fc_swils_deterr_val [] = { + {FC_SWILS_RJT_NODET , "No Additional Details"}, + {FC_SWILS_RJT_CLSF_ERR , "Class F Svc Param Err"}, + {FC_SWILS_RJT_CLSN_ERR , "Class N Svc Param Err"}, + {FC_SWILS_RJT_INVFC_CODE , "Unknown Flow Ctrl Code"}, + {FC_SWILS_RJT_INVFC_PARM , "Invalid Flow Ctrl Parm"}, + {FC_SWILS_RJT_INV_PNAME , "Invalid Port Name"}, + {FC_SWILS_RJT_INV_SNAME , "Invalid Switch Name"}, + {FC_SWILS_RJT_TOV_MSMTCH , "R_A_/E_D_TOV Mismatch"}, + {FC_SWILS_RJT_INV_DIDLST, "Invalid Domain ID List"}, + {FC_SWILS_RJT_CMD_INPROG , "Cmd Already in Progress"}, + {FC_SWILS_RJT_OORSRC , "Insufficient Resources"}, + {FC_SWILS_RJT_NO_DID , "Domain ID Unavailable"}, + {FC_SWILS_RJT_INV_DID, "Invalid Domain ID"}, + {FC_SWILS_RJT_NO_REQ , "Request Not Supported"}, + {FC_SWILS_RJT_NOLNK_PARM , "Link Parm Not Estd."}, + {FC_SWILS_RJT_NO_REQDID , "Group of Domain IDs Unavail"}, + {FC_SWILS_RJT_EP_ISOL , "E_Port Isolated"}, + {0, NULL} +}; + +typedef struct _fcswils_elp { + guint8 revision; + guint8 flags[2]; + guint8 rsvd1; + guint32 r_a_tov; + guint32 e_d_tov; + guint8 req_epname[8]; + guint8 req_sname[8]; + guint8 clsf_svcparm[6]; + guint16 clsf_rcvsize; + guint16 clsf_conseq; + guint16 clsf_e2e; + guint16 clsf_openseq; + guint16 rsvd; + guint8 cls1_svcparm[2]; + guint16 cls1_rcvsize; + guint8 cls2_svcparm[2]; + guint16 cls2_rcvsize; + guint8 cls3_svcparm[2]; + guint16 cls3_rcvsize; + guint8 rsvd2[20]; + guint16 isl_flwctrl_mode; + guint16 flw_ctrl_parmlen; + guint32 b2b_credit; + guint32 compat_p1; + guint32 compat_p2; + guint32 compat_p3; + guint32 compat_p4; +} fcswils_elp; +#define FC_SWILS_ELP_SIZE 100 + +#define FC_SWILS_ELP_FC_VENDOR 0x1 +#define FC_SWILS_ELP_FC_RRDY 0x2 + +static const value_string fcswils_elp_fc_val[] = { + {FC_SWILS_ELP_FC_VENDOR, "Vendor Unique"}, + {FC_SWILS_ELP_FC_RRDY, "R_RDY Flow Ctrl"}, + {0, NULL}, +}; + +struct _fcswils_efp_didrec { + guint8 rec_type; + guint8 dom_id; + guint16 rsvd1; + guint32 rsvd2; + guint8 sname[8]; +}; +struct _fcswils_efp_mcastrec { + guint8 rec_type; + guint8 mcast_grpnum; + guint8 rsvd[14]; +}; + +typedef union _fcswils_efp_listrec { + struct _fcswils_efp_didrec didrec; + struct _fcswils_efp_mcastrec mcastrec; +} fcswils_efp_listrec; + +#define FC_SWILS_LRECTYPE_DOMAIN 0x1 +#define FC_SWILS_LRECTYPE_MCAST 0x2 + +static const value_string fcswils_rectype_val[] = { + {FC_SWILS_LRECTYPE_DOMAIN, "Domain ID List Rec"}, + {FC_SWILS_LRECTYPE_MCAST, "Multicast ID List Rec"}, + {0, NULL}, +}; + +typedef struct _fcswils_efp { + guint8 opcode; + guint8 reclen; + guint16 payload_len; + guint8 rsvd1[3]; + guint8 pswitch_prio; + guint8 pswitch_name[8]; + fcswils_efp_listrec *listrec; +} fcswils_efp; +#define FC_SWILS_EFP_SIZE 16 /* not including listrec */ + +typedef struct _fcswils_dia { + guint8 switch_name[8]; + guint8 rsvd[4]; +} fcswils_dia; + +typedef struct _fcswils_rdi_req { + guint8 rsvd[3]; + guint8 domain_id; +} fcswils_rdi_req; +#define FC_SWILS_RDIREQ_SIZE 4 + +static const value_string fc_swils_link_type_val[] = { + {0x01, "P2P Link"}, + {0xF0, "Vendor Specific"}, + {0xF1, "Vendor Specific"}, + {0xF2, "Vendor Specific"}, + {0xF3, "Vendor Specific"}, + {0xF4, "Vendor Specific"}, + {0xF5, "Vendor Specific"}, + {0xF6, "Vendor Specific"}, + {0xF7, "Vendor Specific"}, + {0xF8, "Vendor Specific"}, + {0xF9, "Vendor Specific"}, + {0xFA, "Vendor Specific"}, + {0xFB, "Vendor Specific"}, + {0xFC, "Vendor Specific"}, + {0xFD, "Vendor Specific"}, + {0xFE, "Vendor Specific"}, + {0xFF, "Vendor Specific"}, + {0, NULL}, +}; + +#define FC_SWILS_LSR_SLR 0x1 /* switch link record */ +#define FC_SWILS_LSR_ARS 0x2 /* AR Summary record */ + +static const value_string fc_swils_fspf_linkrec_val[] = { + {FC_SWILS_LSR_SLR, "Switch Link Record"}, + {FC_SWILS_LSR_ARS, "AR Summary Record"}, + {0, NULL}, +}; + +static const value_string fc_swils_fspf_lsrflags_val[] = { + {0x0, "LSR is for a Topology Update"}, + {0x1, "LSR is for Initial DB Sync | Not the last seq in DB sync"}, + {0x2, "Last Seq in DB Sync. LSU has no LSRs"}, + {0x3, "LSR is for Initial DB Sync | Last Seq in DB Sync"}, + {0, NULL}, +}; + +#define FC_SWILS_PDESC_FSPF_BB 0x01 +#define FC_SWILS_PDESC_FSPF 0x02 + +static const value_string fc_swils_rscn_portstate_val[] = { + {0, "No Additional Info"}, + {1, "Port is online"}, + {2, "Port is offline"}, + {0, NULL}, +}; + +static const value_string fc_swils_rscn_addrfmt_val[] = { + {0, "Port Addr Format"}, + {1, "Area Addr Format"}, + {2, "Domain Addr Format"}, + {3, "Fabric Addr Format"}, +}; + +static const value_string fc_swils_rscn_detectfn_val[] = { + {1, "Fabric Detected"}, + {2, "N_Port Detected"}, + {0, NULL}, +}; + +static const value_string fc_swils_esc_protocol_val[] = { + {0, "Reserved"}, + {1, "FSPF-Backbone Protocol"}, + {2, "FSPF Protocol"}, + {0, NULL}, +}; + +#define FC_SWILS_ZONEOBJ_ZONESET 1 +#define FC_SWILS_ZONEOBJ_ZONE 2 +#define FC_SWILS_ZONEOBJ_ZONEALIAS 3 + +static const value_string fc_swils_zoneobj_type_val[] = { + {0, "Reserved"}, + {FC_SWILS_ZONEOBJ_ZONESET , "Zone Set"}, + {FC_SWILS_ZONEOBJ_ZONE , "Zone"}, + {FC_SWILS_ZONEOBJ_ZONEALIAS, "Zone Alias"}, + {0, NULL}, +}; + +#define FC_SWILS_ZONEMBR_WWN 1 +#define FC_SWILS_ZONEMBR_DP 2 +#define FC_SWILS_ZONEMBR_FCID 3 +#define FC_SWILS_ZONEMBR_ALIAS 4 +#define FC_SWILS_ZONEMBR_WWN_LUN 0xE1 +#define FC_SWILS_ZONEMBR_DP_LUN 0xE2 +#define FC_SWILS_ZONEMBR_FCID_LUN 0xE3 + +static const value_string fc_swils_zonembr_type_val[] = { + {0, "Reserved"}, + {FC_SWILS_ZONEMBR_WWN, "WWN"}, + {FC_SWILS_ZONEMBR_DP, "Domain/Physical Port (0x00ddpppp)"}, + {FC_SWILS_ZONEMBR_FCID, "FC Address"}, + {FC_SWILS_ZONEMBR_ALIAS, "Zone Alias"}, + {FC_SWILS_ZONEMBR_WWN_LUN, "WWN+LUN"}, + {FC_SWILS_ZONEMBR_DP_LUN, "Domain/Physical Port+LUN"}, + {FC_SWILS_ZONEMBR_FCID_LUN, "FCID+LUN"}, + {0, NULL}, +}; + +static const value_string fc_swils_mr_rsp_val[] = { + {0, "Successful"}, + {1, "Fabric Busy"}, + {2, "Failed"}, + {0, NULL}, +}; + +static const value_string fc_swils_mr_reason_val[] = { + {0x0, "No Reason"}, + {0x1, "Invalid Data Length"}, + {0x2, "Unsupported Command"}, + {0x3, "Reserved"}, + {0x4, "Not Authorized"}, + {0x5, "Invalid Request"}, + {0x6, "Fabric Changing"}, + {0x7, "Update Not Staged"}, + {0x8, "Invalid Zone Set Format"}, + {0x9, "Invalid Data"}, + {0xA, "Cannot Merge"}, + {0, NULL}, +}; + +static const value_string fc_swils_sfc_op_val[] = { + {0, "Reserved"}, + {1, "Reserved"}, + {2, "Reserved"}, + {3, "Activate Zone Set"}, + {4, "Deactivate Zone Set"}, + {0, NULL}, +}; + +#endif diff --git a/packet-ipfc.c b/packet-ipfc.c new file mode 100644 index 0000000000..2c2b91135f --- /dev/null +++ b/packet-ipfc.c @@ -0,0 +1,155 @@ +/* packet-ipfc.c + * Routines for Decoding FC header for IP/FC + * Copyright 2001, Dinesh G Dutt <ddutt@cisco.com> + * + * $Id: packet-ipfc.c,v 1.1 2002/12/08 02:32:17 gerald Exp $ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * Copyright 1998 Gerald Combs + * + * Copied from WHATEVER_FILE_YOU_USED (where "WHATEVER_FILE_YOU_USED" + * is a dissector file; if you just copied this from README.developer, + * don't bother with the "Copied from" - you don't even need to put + * in a "Copied from" if you copied an existing dissector, especially + * if the bulk of the code in the new dissector is your code) + * + * 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 + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#ifdef HAVE_NETINET_IN_H +# include <netinet/in.h> +#endif + +#include <glib.h> + +#ifdef NEED_SNPRINTF_H +# include "snprintf.h" +#endif + +#include <epan/packet.h> +#include "etypes.h" +#include "packet-fc.h" + +/* Initialize the protocol and registered fields */ +static int proto_ipfc = -1; +static int hf_ipfc_network_da = -1; +static int hf_ipfc_network_sa = -1; +static int hf_ipfc_llc = -1; + +/* Initialize the subtree pointers */ +static gint ett_ipfc = -1; +static dissector_table_t ipfc_dissector_table; +static dissector_handle_t data_handle; + +static void +dissect_ipfc (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + +/* Set up structures needed to add the protocol subtree and manage it */ + proto_item *ti; + proto_tree *ipfc_tree; + int offset = 0; + tvbuff_t *next_tvb; + + /* Make entries in Protocol column and Info column on summary display */ + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "IP/FC"); + + if (tree) { + ti = proto_tree_add_text (tree, tvb, offset, 16, + "Network Header"); + ipfc_tree = proto_item_add_subtree (ti, ett_ipfc); + + proto_tree_add_string (ipfc_tree, hf_ipfc_network_da, tvb, offset, 8, + fcwwn_to_str (tvb_get_ptr (tvb, offset, 8))); + proto_tree_add_string (ipfc_tree, hf_ipfc_network_sa, tvb, offset+8, 8, + fcwwn_to_str (tvb_get_ptr (tvb, offset+8, 8))); + /* This is a dummy add to just switch to llc */ + proto_tree_add_uint_hidden (ipfc_tree, hf_ipfc_llc, tvb, offset, 16, 0); + } + + next_tvb = tvb_new_subset (tvb, 16, -1, -1); + if (!dissector_try_port (ipfc_dissector_table, 0, next_tvb, pinfo, tree)) { + call_dissector (data_handle, next_tvb, pinfo, tree); + } +} + +/* Register the protocol with Ethereal */ + +/* this format is require because a script is used to build the C function + that calls all the protocol registration. +*/ + +void +proto_register_ipfc (void) +{ + +/* Setup list of header fields See Section 1.6.1 for details*/ + static hf_register_info hf[] = { + { &hf_ipfc_network_da, + {"Network DA", "ipfc.nethdr.da", FT_STRING, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_ipfc_network_sa, + {"Network SA", "ipfc.nethdr.sa", FT_STRING, BASE_HEX, NULL, + 0x0, "", HFILL}}, + { &hf_ipfc_llc, + {"LLC/SNAP", "ipfc.llc", FT_UINT8, BASE_HEX, NULL, 0x0, "", + HFILL}}, + }; + + /* Setup protocol subtree array */ + static gint *ett[] = { + &ett_ipfc, + }; + + /* Register the protocol name and description */ + proto_ipfc = proto_register_protocol("IP Over FC", "IPFC", "ipfc"); + + /* Required function calls to register the header fields and subtrees used */ + proto_register_field_array(proto_ipfc, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + + ipfc_dissector_table = register_dissector_table ("ipfc.llc", "IPFC", + FT_UINT8, BASE_HEX); +} + +/* If this dissector uses sub-dissector registration add a registration routine. + This format is required because a script is used to find these routines and + create the code that calls these routines. +*/ +void +proto_reg_handoff_ipfc (void) +{ + dissector_handle_t ipfc_handle; + + ipfc_handle = create_dissector_handle (dissect_ipfc, proto_ipfc); + dissector_add("fc.ftype", FC_FTYPE_IP, ipfc_handle); + + data_handle = find_dissector ("data"); +} + diff --git a/packet-llc.c b/packet-llc.c index 0371f768a1..bb1054a6a3 100644 --- a/packet-llc.c +++ b/packet-llc.c @@ -2,7 +2,7 @@ * Routines for IEEE 802.2 LLC layer * Gilbert Ramirez <gram@alumni.rice.edu> * - * $Id: packet-llc.c,v 1.102 2002/11/16 08:55:11 guy Exp $ + * $Id: packet-llc.c,v 1.103 2002/12/08 02:32:17 gerald Exp $ * * Ethereal - Network traffic analyzer * By Gerald Combs <gerald@ethereal.com> @@ -560,4 +560,5 @@ proto_reg_handoff_llc(void) dissector_add("udp.port", UDP_PORT_LLC3, llc_handle); dissector_add("udp.port", UDP_PORT_LLC4, llc_handle); dissector_add("udp.port", UDP_PORT_LLC5, llc_handle); + dissector_add("ipfc.llc", 0, llc_handle); } |