Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-fcp.c
blob5c3aa13ca4388f2652445f503134a1480630e2bf
1 /* packet-fcp.c
2 * Routines for Fibre Channel Protocol for SCSI (FCP)
3 * Copyright 2001, Dinesh G Dutt <ddutt@cisco.com>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
12 #include "config.h"
14 #include <epan/packet.h>
15 #include <epan/conversation.h>
16 #include <epan/proto_data.h>
17 #include <epan/tfs.h>
18 #include "packet-scsi.h"
19 #include "packet-fc.h"
20 #include "packet-fcels.h"
22 void proto_register_fcp(void);
23 void proto_reg_handoff_fcp(void);
25 static dissector_handle_t fcp_handle;
27 typedef struct _fcp_proto_data_t {
28 uint16_t lun;
29 } fcp_proto_data_t;
31 /* Initialize the protocol and registered fields */
32 static int proto_fcp;
33 static int hf_fcp_multilun;
34 static int hf_fcp_singlelun;
35 static int hf_fcp_crn;
36 static int hf_fcp_taskattr;
37 static int hf_fcp_taskmgmt;
38 static int hf_fcp_addlcdblen;
39 static int hf_fcp_rddata;
40 static int hf_fcp_wrdata;
41 static int hf_fcp_dl;
42 static int hf_fcp_bidir_dl;
43 static int hf_fcp_data_ro;
44 static int hf_fcp_r_ctl;
45 static int hf_fcp_burstlen;
46 static int hf_fcp_rspflags;
47 static int hf_fcp_retry_delay_timer;
48 static int hf_fcp_resid;
49 static int hf_fcp_bidir_resid;
50 static int hf_fcp_snslen;
51 static int hf_fcp_rsplen;
52 static int hf_fcp_rspcode;
53 static int hf_fcp_scsistatus;
54 static int hf_fcp_mgmt_flags_obsolete;
55 static int hf_fcp_mgmt_flags_clear_aca;
56 static int hf_fcp_mgmt_flags_target_reset;
57 static int hf_fcp_mgmt_flags_lu_reset;
58 static int hf_fcp_mgmt_flags_rsvd;
59 static int hf_fcp_mgmt_flags_clear_task_set;
60 static int hf_fcp_mgmt_flags_abort_task_set;
61 static int hf_fcp_rsp_flags_bidi;
62 static int hf_fcp_rsp_flags_bidi_rru;
63 static int hf_fcp_rsp_flags_bidi_rro;
64 static int hf_fcp_rsp_flags_conf_req;
65 static int hf_fcp_rsp_flags_resid_under;
66 static int hf_fcp_rsp_flags_resid_over;
67 static int hf_fcp_rsp_flags_sns_vld;
68 static int hf_fcp_rsp_flags_res_vld;
69 static int hf_fcp_request_in;
70 static int hf_fcp_response_in;
71 static int hf_fcp_time;
72 static int hf_fcp_els_op;
73 static int hf_fcp_srr_ox_id;
74 static int hf_fcp_srr_rx_id;
76 /* Initialize the subtree pointers */
77 static int ett_fcp;
78 static int ett_fcp_taskmgmt;
79 static int ett_fcp_rsp_flags;
81 typedef struct _fcp_conv_data_t {
82 wmem_map_t *luns;
83 } fcp_conv_data_t;
85 typedef struct fcp_request_data {
86 uint32_t request_frame;
87 uint32_t response_frame;
88 nstime_t request_time;
89 itlq_nexus_t *itlq;
90 } fcp_request_data_t;
92 #define FCP_DEF_CMND_LEN 32 /* by default cmnd is 32 bytes */
93 #define FCP_DEF_RSP_LEN 24 /* default FCP_RSP len */
95 /* Information Categories based on lower 4 bits of R_CTL */
96 #define FCP_IU_DATA 0x1
97 #define FCP_IU_UNSOL_CTL 0x2
98 #define FCP_IU_SOL_CTL 0x3
99 #define FCP_IU_CONFIRM 0x3
100 #define FCP_IU_XFER_RDY 0x5
101 #define FCP_IU_CMD 0x6
102 #define FCP_IU_RSP 0x7
104 static const value_string fcp_iu_val[] = {
105 {FCP_IU_DATA , "FCP_DATA"},
106 {FCP_IU_UNSOL_CTL , "Control"},
107 {FCP_IU_CONFIRM , "Confirm"},
108 {FCP_IU_XFER_RDY , "XFER_RDY"},
109 {FCP_IU_CMD , "FCP_CMND"},
110 {FCP_IU_RSP , "FCP_RSP"},
111 {0, NULL},
115 /* Task Attribute Values */
116 static const value_string fcp_task_attr_val[] = {
117 {0, "Simple"},
118 {1, "Head of Queue"},
119 {2, "Ordered"},
120 {4, "ACA"},
121 {5, "Untagged"},
122 {0, NULL},
125 /* RSP Code Definitions (from FCP_RSP_INFO) */
126 static const value_string fcp_rsp_code_val[] = {
127 {0, "Task Management Function Complete"},
128 {1, "FCP_DATA length Different from FCP_BURST_LEN"},
129 {2, "FCP_CMND Fields Invalid"},
130 {3, "FCP_DATA Parameter Mismatch With FCP_DATA_RO"},
131 {4, "Task Management Function Rejected"},
132 {5, "Task Management Function Failed"},
133 {9, "Task Management Function Incorrect LUN"},
134 {0, NULL},
139 static const true_false_string fcp_mgmt_flags_obsolete_tfs = {
140 "OBSOLETE BIT is SET",
141 "OBSOLETE BIT is NOT set",
143 static const true_false_string fcp_mgmt_flags_clear_aca_tfs = {
144 "CLEAR ACA is SET",
145 "Clear aca is NOT set",
147 static const true_false_string fcp_mgmt_flags_target_reset_tfs = {
148 "TARGET RESET is SET",
149 "Target reset is NOT set",
151 static const true_false_string fcp_mgmt_flags_lu_reset_tfs = {
152 "LU RESET is SET",
153 "Lu reset is NOT set",
155 static const true_false_string fcp_mgmt_flags_rsvd_tfs = {
156 "RSVD is SET",
157 "Rsvd is NOT set",
159 static const true_false_string fcp_mgmt_flags_clear_task_set_tfs = {
160 "CLEAR TASK SET is SET",
161 "Clear task set is NOT set",
163 static const true_false_string fcp_mgmt_flags_abort_task_set_tfs = {
164 "ABORT TASK SET is SET",
165 "Abort task set is NOT set",
168 static void
169 dissect_task_mgmt_flags(packet_info *pinfo, proto_tree *parent_tree, tvbuff_t *tvb, int offset)
171 proto_item *item;
172 static int * const mgmt_flags[] = {
173 &hf_fcp_mgmt_flags_obsolete,
174 &hf_fcp_mgmt_flags_clear_aca,
175 &hf_fcp_mgmt_flags_target_reset,
176 &hf_fcp_mgmt_flags_lu_reset,
177 &hf_fcp_mgmt_flags_rsvd,
178 &hf_fcp_mgmt_flags_clear_task_set,
179 &hf_fcp_mgmt_flags_abort_task_set,
180 NULL
183 uint8_t flags;
185 flags = tvb_get_uint8(tvb, offset);
186 item = proto_tree_add_bitmask_with_flags(parent_tree, tvb, offset, hf_fcp_taskmgmt,
187 ett_fcp_taskmgmt, mgmt_flags, ENC_NA, BMT_NO_FALSE|BMT_NO_TFS);
189 if (!flags)
190 proto_item_append_text(item, " (No values set)");
192 if (flags & 0x80) {
193 col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[FCP OBSOLETE] ");
196 if (flags & 0x40) {
197 col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[FCP CLEAR_ACA] ");
200 if (flags & 0x20) {
201 col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[FCP TARGET_RESET] ");
204 if (flags & 0x10) {
205 col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[FCP LU_RESET] ");
208 if (flags & 0x08) {
209 col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[FCP RSVD] ");
212 if (flags & 0x04) {
213 col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[FCP CLEAR_TASK_SET] ");
216 if (flags & 0x02) {
217 col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[FCP ABORT_TASK_SET] ");
221 static const true_false_string fcp_rsp_flags_bidi_tfs = {
222 "Bidirectional residual fields are PRESENT",
223 "Bidirectional residual fields are NOT present",
225 static const true_false_string fcp_rsp_flags_bidi_rru_tfs = {
226 "Bidirectional residual underflow is PRESENT",
227 "Bidirectional residual underflow is NOT present",
229 static const true_false_string fcp_rsp_flags_bidi_rro_tfs = {
230 "Bidirectional residual overflow is PRESENT",
231 "Bidirectional residual overflow is NOT present",
233 static const true_false_string fcp_rsp_flags_conf_req_tfs = {
234 "CONF REQ is SET",
235 "Conf req set is NOT set",
237 static const true_false_string fcp_rsp_flags_resid_under_tfs = {
238 "RESID UNDER is SET",
239 "Resid under is NOT set",
241 static const true_false_string fcp_rsp_flags_resid_over_tfs = {
242 "RESID OVER is SET",
243 "Resid over is NOT set",
245 static const true_false_string fcp_rsp_flags_sns_vld_tfs = {
246 "SNS VLD is SET",
247 "Sns vld is NOT set",
249 static const true_false_string fcp_rsp_flags_res_vld_tfs = {
250 "RES VLD is SET",
251 "Res vld is NOT set",
254 static void
255 dissect_rsp_flags(proto_tree *parent_tree, tvbuff_t *tvb, int offset)
257 proto_item *item;
258 uint8_t flags;
259 static int * const resid_present_flags[] = {
260 &hf_fcp_rsp_flags_bidi,
261 &hf_fcp_rsp_flags_bidi_rru,
262 &hf_fcp_rsp_flags_bidi_rro,
263 &hf_fcp_rsp_flags_conf_req,
264 &hf_fcp_rsp_flags_resid_under,
265 &hf_fcp_rsp_flags_resid_over,
266 &hf_fcp_rsp_flags_sns_vld,
267 &hf_fcp_rsp_flags_res_vld,
268 NULL
271 static int * const no_resid_flags[] = {
272 &hf_fcp_rsp_flags_bidi,
273 &hf_fcp_rsp_flags_conf_req,
274 &hf_fcp_rsp_flags_resid_under,
275 &hf_fcp_rsp_flags_resid_over,
276 &hf_fcp_rsp_flags_sns_vld,
277 &hf_fcp_rsp_flags_res_vld,
278 NULL
281 flags = tvb_get_uint8(tvb, offset);
282 if (flags & 0x80) {
283 item = proto_tree_add_bitmask_with_flags(parent_tree, tvb, offset, hf_fcp_rspflags,
284 ett_fcp_rsp_flags, resid_present_flags, ENC_NA, BMT_NO_FALSE|BMT_NO_TFS);
285 } else {
286 item = proto_tree_add_bitmask_with_flags(parent_tree, tvb, offset, hf_fcp_rspflags,
287 ett_fcp_rsp_flags, no_resid_flags, ENC_NA, BMT_NO_FALSE|BMT_NO_TFS);
290 if (!flags)
291 proto_item_append_text(item, " (No values set)");
294 static void
295 dissect_fcp_cmnd(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, proto_tree *tree, conversation_t *conversation, fc_hdr *fchdr, fcp_conv_data_t *fcp_conv_data)
297 int offset = 0;
298 int add_len = 0;
299 uint8_t flags, rwflags, lun0;
300 uint16_t lun = 0xffff;
301 tvbuff_t *cdb_tvb;
302 int tvb_len;
303 fcp_request_data_t *request_data = NULL;
304 itl_nexus_t itl;
305 fcp_proto_data_t *proto_data;
307 /* Determine the length of the FCP part of the packet */
308 flags = tvb_get_uint8(tvb, offset+10);
309 if (flags) {
310 add_len = tvb_get_uint8(tvb, offset+11) & 0x7C;
311 add_len = add_len >> 2;
314 lun0 = tvb_get_uint8(tvb, offset);
316 /* Display single-level LUNs in decimal for clarity */
317 /* I'm taking a shortcut here by assuming that if the first byte of the
318 * LUN field is 0, it is a single-level LUN. This is not true. For a
319 * real single-level LUN, all 8 bytes except byte 1 must be 0.
321 if (lun0) {
322 proto_tree_add_item(tree, hf_fcp_multilun, tvb, offset, 8, ENC_NA);
323 lun = tvb_get_uint8(tvb, offset) & 0x3f;
324 lun <<= 8;
325 lun |= tvb_get_uint8(tvb, offset+1);
326 } else {
327 proto_tree_add_item(tree, hf_fcp_singlelun, tvb, offset+1,
328 1, ENC_BIG_ENDIAN);
329 lun = tvb_get_uint8(tvb, offset+1);
332 if (!pinfo->fd->visited) {
333 proto_data = wmem_new(wmem_file_scope(), fcp_proto_data_t);
334 proto_data->lun = lun;
335 p_add_proto_data(wmem_file_scope(), pinfo, proto_fcp, 0, proto_data);
338 request_data = (fcp_request_data_t*)wmem_map_lookup(fcp_conv_data->luns, GUINT_TO_POINTER((unsigned)lun));
339 if (!request_data) {
340 request_data = wmem_new(wmem_file_scope(), fcp_request_data_t);
341 request_data->request_frame = pinfo->num;
342 request_data->response_frame = 0;
343 request_data->request_time = pinfo->abs_ts;
345 request_data->itlq = wmem_new(wmem_file_scope(), itlq_nexus_t);
346 request_data->itlq->first_exchange_frame=0;
347 request_data->itlq->last_exchange_frame=0;
348 request_data->itlq->lun=lun;
349 request_data->itlq->scsi_opcode=0xffff;
350 request_data->itlq->task_flags=0;
351 request_data->itlq->data_length=0;
352 request_data->itlq->bidir_data_length=0;
353 request_data->itlq->fc_time=pinfo->abs_ts;
354 request_data->itlq->flags=0;
355 request_data->itlq->alloc_len=0;
356 request_data->itlq->extra_data=NULL;
358 wmem_map_insert(fcp_conv_data->luns, GUINT_TO_POINTER((unsigned)lun), request_data);
361 /* populate the exchange struct */
362 if(!pinfo->fd->visited){
363 if(fchdr->fctl&FC_FCTL_EXCHANGE_FIRST){
364 request_data->itlq->first_exchange_frame=pinfo->num;
365 request_data->itlq->fc_time = pinfo->abs_ts;
367 if(fchdr->fctl&FC_FCTL_EXCHANGE_LAST){
368 request_data->itlq->last_exchange_frame=pinfo->num;
372 if (request_data->itlq)
373 request_data->itlq->lun = lun;
375 fchdr->lun = lun;
377 proto_tree_add_item(tree, hf_fcp_crn, tvb, offset+8, 1, ENC_BIG_ENDIAN);
378 proto_tree_add_item(tree, hf_fcp_taskattr, tvb, offset+9, 1, ENC_BIG_ENDIAN);
379 dissect_task_mgmt_flags(pinfo, tree, tvb, offset+10);
380 proto_tree_add_item(tree, hf_fcp_addlcdblen, tvb, offset+11, 1, ENC_BIG_ENDIAN);
381 rwflags = tvb_get_uint8(tvb, offset+11);
382 if (request_data->itlq) {
383 if (rwflags & 0x02) {
384 request_data->itlq->task_flags |= SCSI_DATA_READ;
386 if (rwflags & 0x01) {
387 request_data->itlq->task_flags |= SCSI_DATA_WRITE;
390 proto_tree_add_item(tree, hf_fcp_rddata, tvb, offset+11, 1, ENC_BIG_ENDIAN);
391 proto_tree_add_item(tree, hf_fcp_wrdata, tvb, offset+11, 1, ENC_BIG_ENDIAN);
393 tvb_len = tvb_captured_length_remaining(tvb, offset+12);
394 if (tvb_len > (16 + add_len))
395 tvb_len = 16 + add_len;
397 itl.cmdset = 0xff;
398 itl.conversation = conversation;
400 cdb_tvb = tvb_new_subset_length(tvb, offset+12, tvb_len);
401 dissect_scsi_cdb(cdb_tvb, pinfo, parent_tree, SCSI_DEV_UNKNOWN, request_data->itlq, &itl);
403 proto_tree_add_item(tree, hf_fcp_dl, tvb, offset+12+16+add_len,
404 4, ENC_BIG_ENDIAN);
405 if (request_data->itlq) {
406 request_data->itlq->data_length = tvb_get_ntohl(tvb, offset+12+16+add_len);
409 if ( ((rwflags & 0x03) == 0x03)
410 && tvb_reported_length_remaining(tvb, offset+12+16+add_len+4) >= 4) {
411 proto_tree_add_item(tree, hf_fcp_bidir_dl, tvb, offset+12+16+add_len+4,
412 4, ENC_BIG_ENDIAN);
413 if (request_data->itlq) {
414 request_data->itlq->bidir_data_length = tvb_get_ntohl(tvb, offset+12+16+add_len+4);
421 static void
422 dissect_fcp_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, conversation_t *conversation, itlq_nexus_t *itlq, uint32_t relative_offset)
424 itl_nexus_t itl;
425 itlq_nexus_t empty_itlq;
427 itl.cmdset = 0xff;
428 itl.conversation = conversation;
430 if (itlq == NULL)
432 /* Provide "default" itlq */
433 memset(&empty_itlq, 0, sizeof(empty_itlq));
434 empty_itlq.lun=0xffff;
435 empty_itlq.scsi_opcode=0xffff;
436 itlq = &empty_itlq;
439 dissect_scsi_payload(tvb, pinfo, parent_tree, false, itlq, &itl, relative_offset);
442 /* fcp-3 9.5 table 24 */
443 static int
444 dissect_fcp_rspinfo(tvbuff_t *tvb, proto_tree *tree, int offset)
446 /* 3 reserved bytes */
447 offset += 3;
449 /* rsp code */
450 proto_tree_add_item(tree, hf_fcp_rspcode, tvb, offset, 1, ENC_BIG_ENDIAN);
451 offset += 1;
453 /* 4 reserved bytes */
454 offset += 4;
456 return offset;
459 static void
460 dissect_fcp_rsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, proto_tree *tree, conversation_t *conversation, fc_hdr *fchdr, fcp_request_data_t *request_data)
462 uint32_t offset = 0;
463 int32_t snslen = 0;
464 int32_t rsplen = 0;
465 uint8_t flags;
466 uint8_t status;
467 itl_nexus_t itl;
468 itlq_nexus_t empty_itlq;
470 status = tvb_get_uint8(tvb, offset+11);
472 col_append_fstr(pinfo->cinfo, COL_INFO, ":%s",
473 val_to_str(status, scsi_status_val, "0x%x"));
475 /* Save the response frame */
476 if (request_data != NULL) {
477 request_data->response_frame = pinfo->num;
479 /* populate the exchange struct */
480 if(!pinfo->fd->visited){
481 if(fchdr->fctl&FC_FCTL_EXCHANGE_FIRST){
482 request_data->itlq->first_exchange_frame=pinfo->num;
483 request_data->itlq->fc_time = pinfo->abs_ts;
485 if(fchdr->fctl&FC_FCTL_EXCHANGE_LAST){
486 request_data->itlq->last_exchange_frame=pinfo->num;
489 } else {
490 /* Provide "default" itlq */
491 memset(&empty_itlq, 0, sizeof(empty_itlq));
492 empty_itlq.lun=0xffff;
493 empty_itlq.scsi_opcode=0xffff;
496 /* 8 reserved bytes */
497 offset += 8;
499 /* retry delay timer */
500 proto_tree_add_item(tree, hf_fcp_retry_delay_timer, tvb, offset, 2, ENC_BIG_ENDIAN);
501 offset += 2;
503 /* flags */
504 flags = tvb_get_uint8(tvb, offset);
505 dissect_rsp_flags(tree, tvb, offset);
506 offset += 1;
508 itl.cmdset = 0xff;
509 itl.conversation = conversation;
511 /* scsi status code */
512 proto_tree_add_item(tree, hf_fcp_scsistatus, tvb, offset, 1, ENC_BIG_ENDIAN);
513 dissect_scsi_rsp(tvb, pinfo, parent_tree, (request_data != NULL) ? request_data->itlq : &empty_itlq, &itl, tvb_get_uint8(tvb, offset));
514 offset += 1;
516 /* residual count */
517 if (flags & 0x0e) {
518 proto_tree_add_item(tree, hf_fcp_resid, tvb, offset, 4, ENC_BIG_ENDIAN);
520 offset += 4;
522 /* sense length */
523 if (flags & 0x2) {
524 snslen = tvb_get_ntohl(tvb, offset);
525 proto_tree_add_uint(tree, hf_fcp_snslen, tvb, offset, 4,
526 snslen);
528 offset += 4;
530 /* response length */
531 if (flags & 0x1) {
532 rsplen = tvb_get_ntohl(tvb, offset);
533 proto_tree_add_uint(tree, hf_fcp_rsplen, tvb, offset, 4,
534 rsplen);
536 offset += 4;
538 /* rsp_info */
539 if (rsplen) {
540 tvbuff_t *rspinfo_tvb;
542 rspinfo_tvb = tvb_new_subset_length_caplen(tvb, offset, MIN(rsplen, tvb_captured_length_remaining(tvb, offset)), rsplen);
543 dissect_fcp_rspinfo(rspinfo_tvb, tree, 0);
545 offset += rsplen;
548 /* sense info */
549 if (snslen) {
550 tvbuff_t *sns_tvb;
552 sns_tvb = tvb_new_subset_length_caplen(tvb, offset, MIN(snslen, tvb_captured_length_remaining(tvb, offset)), snslen);
553 dissect_scsi_snsinfo(sns_tvb, pinfo, parent_tree, 0,
554 snslen,
555 (request_data != NULL) ? request_data->itlq : &empty_itlq, &itl);
557 offset += snslen;
560 /* bidir read resid (only present for bidirectional responses) */
561 if (flags & 0x80) {
562 if (flags & 0x60) {
563 proto_tree_add_item(tree, hf_fcp_bidir_resid, tvb, offset, 4, ENC_BIG_ENDIAN);
565 /*offset += 4;*/
569 static void
570 dissect_fcp_xfer_rdy(tvbuff_t *tvb, proto_tree *tree)
572 int offset = 0;
574 proto_tree_add_item(tree, hf_fcp_data_ro, tvb, offset, 4, ENC_BIG_ENDIAN);
575 proto_tree_add_item(tree, hf_fcp_burstlen, tvb, offset+4, 4, ENC_BIG_ENDIAN);
578 static void
579 dissect_fcp_srr(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, fc_hdr *fchdr)
581 uint8_t r_ctl;
583 r_ctl = fchdr->r_ctl & 0xf;
584 if (r_ctl == FCP_IU_UNSOL_CTL) { /* request */
585 proto_tree_add_item(tree, hf_fcp_srr_ox_id, tvb, 4, 2, ENC_BIG_ENDIAN);
586 proto_tree_add_item(tree, hf_fcp_srr_rx_id, tvb, 6, 2, ENC_BIG_ENDIAN);
587 proto_tree_add_item(tree, hf_fcp_data_ro, tvb, 8, 4, ENC_BIG_ENDIAN);
588 proto_tree_add_item(tree, hf_fcp_r_ctl, tvb, 12, 1, ENC_NA);
592 static const value_string fcp_els_iu_val[] = {
593 {FCP_IU_UNSOL_CTL , "FCP ELS Request"},
594 {FCP_IU_SOL_CTL , "FCP ELS Response"},
595 {0, NULL},
599 * Dissect FC-4 ELS for FCP.
601 static void
602 dissect_fcp_els(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, fc_hdr *fchdr)
604 uint8_t op;
606 op = tvb_get_uint8(tvb, 0);
607 col_add_str(pinfo->cinfo, COL_INFO, val_to_str_ext(op, &fc_els_proto_val_ext, "0x%x"));
608 proto_tree_add_item(tree, hf_fcp_els_op, tvb, 0, 1, ENC_NA);
610 switch (op) { /* XXX should switch based on conv for LS_ACC */
611 case FC_ELS_SRR:
612 dissect_fcp_srr(tvb, pinfo, tree, fchdr);
613 break;
614 default:
615 call_data_dissector(tvb, pinfo, tree);
616 break;
620 static int
621 dissect_fcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
623 proto_item *ti = NULL;
624 proto_tree *fcp_tree = NULL;
625 fc_hdr *fchdr;
626 uint8_t r_ctl;
627 conversation_t *fc_conv;
628 fcp_conv_data_t *fcp_conv_data = NULL;
629 fcp_request_data_t *request_data = NULL;
630 bool els;
631 fcp_proto_data_t *proto_data;
633 /* Reject the packet if data is NULL */
634 if (data == NULL)
635 return 0;
636 fchdr = (fc_hdr *)data;
638 /* Make entries in Protocol column and Info column on summary display */
639 col_set_str(pinfo->cinfo, COL_PROTOCOL, "FCP");
641 r_ctl = fchdr->r_ctl;
642 els = (r_ctl & 0xf0) == FC_RCTL_LINK_DATA;
643 r_ctl &= 0xF;
645 col_add_str(pinfo->cinfo, COL_INFO,
646 val_to_str(r_ctl, els ? fcp_els_iu_val : fcp_iu_val,
647 "0x%x"));
649 ti = proto_tree_add_protocol_format(tree, proto_fcp, tvb, 0, -1,
650 "FCP: %s",
651 val_to_str(r_ctl,
652 els ? fcp_els_iu_val :
653 fcp_iu_val, "Unknown 0x%02x"));
654 fcp_tree = proto_item_add_subtree(ti, ett_fcp);
656 fc_conv = find_or_create_conversation(pinfo);
657 fcp_conv_data = (fcp_conv_data_t *)conversation_get_proto_data(fc_conv, proto_fcp);
659 if (!fcp_conv_data) {
660 fcp_conv_data = wmem_new(wmem_file_scope(), fcp_conv_data_t);
661 fcp_conv_data->luns = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
662 conversation_add_proto_data(fc_conv, proto_fcp, fcp_conv_data);
665 /* Lun is only populated by FCP_IU_CMD, and subsequent packets assume the same lun.
666 The only way that consistently works is to save the lun on the first pass when packets
667 are guaranteed to be parsed consecutively */
668 if (!pinfo->fd->visited) {
669 proto_data = wmem_new(wmem_file_scope(), fcp_proto_data_t);
670 proto_data->lun = fchdr->lun;
671 p_add_proto_data(wmem_file_scope(), pinfo, proto_fcp, 0, proto_data);
672 } else {
673 proto_data = (fcp_proto_data_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_fcp, 0);
676 if ((r_ctl != FCP_IU_CMD) && (r_ctl != FCP_IU_UNSOL_CTL) && (proto_data != NULL)) {
677 request_data = (fcp_request_data_t *)wmem_map_lookup(fcp_conv_data->luns, GUINT_TO_POINTER((unsigned)(proto_data->lun)));
680 /* put a request_in in all frames except the command frame */
681 if ((r_ctl != FCP_IU_CMD) && (r_ctl != FCP_IU_UNSOL_CTL) &&
682 (request_data != NULL) && (request_data->itlq->first_exchange_frame)) {
683 proto_item *it;
684 it = proto_tree_add_uint(fcp_tree, hf_fcp_singlelun, tvb, 0, 0, proto_data->lun);
685 proto_item_set_generated(it);
686 if (request_data != NULL) {
687 it = proto_tree_add_uint(fcp_tree, hf_fcp_request_in, tvb, 0, 0, request_data->request_frame);
688 proto_item_set_generated(it);
689 /* only put the response time in the actual response frame */
690 if (r_ctl == FCP_IU_RSP) {
691 nstime_t delta_ts;
692 nstime_delta(&delta_ts, &pinfo->abs_ts, &request_data->request_time);
693 it = proto_tree_add_time(ti, hf_fcp_time, tvb, 0, 0, &delta_ts);
694 proto_item_set_generated(it);
698 /* put a response_in in all frames except the response frame */
699 if ((r_ctl != FCP_IU_RSP) && (r_ctl != FCP_IU_SOL_CTL) &&
700 (request_data != NULL) && (request_data->response_frame)) {
701 proto_item *it;
702 it = proto_tree_add_uint(fcp_tree, hf_fcp_response_in, tvb, 0, 0, request_data->response_frame);
703 proto_item_set_generated(it);
706 if (els) {
707 dissect_fcp_els(tvb, pinfo, fcp_tree, fchdr);
708 return tvb_captured_length(tvb);
711 switch (r_ctl) {
712 case FCP_IU_DATA:
713 dissect_fcp_data(tvb, pinfo, tree, fc_conv, (request_data != NULL) ? request_data->itlq : NULL, fchdr->relative_offset);
714 break;
715 case FCP_IU_CONFIRM:
716 /* Nothing to be done here */
717 break;
718 case FCP_IU_XFER_RDY:
719 dissect_fcp_xfer_rdy(tvb, fcp_tree);
720 break;
721 case FCP_IU_CMD:
722 dissect_fcp_cmnd(tvb, pinfo, tree, fcp_tree, fc_conv, fchdr, fcp_conv_data);
723 break;
724 case FCP_IU_RSP:
725 dissect_fcp_rsp(tvb, pinfo, tree, fcp_tree, fc_conv, fchdr, request_data);
726 break;
727 default:
728 call_data_dissector(tvb, pinfo, tree);
729 break;
731 /*xxx once the subdissectors return bytes consumed: proto_item_set_end(ti, tvb, offset);*/
732 return tvb_captured_length(tvb);
735 /* Register the protocol with Wireshark */
737 void
738 proto_register_fcp(void)
741 /* Setup list of header fields See Section 1.6.1 for details*/
742 static hf_register_info hf[] = {
743 { &hf_fcp_multilun,
744 {"Multi-Level LUN", "fcp.multilun",
745 FT_BYTES, BASE_NONE, NULL, 0x0,
746 NULL, HFILL}},
748 { &hf_fcp_singlelun,
749 {"LUN", "fcp.lun",
750 FT_UINT8, BASE_HEX, NULL, 0x0,
751 NULL, HFILL}},
753 { &hf_fcp_crn,
754 {"Command Ref Num", "fcp.crn",
755 FT_UINT8, BASE_DEC, NULL, 0x0,
756 NULL, HFILL}},
758 { &hf_fcp_taskattr,
759 {"Task Attribute", "fcp.taskattr",
760 FT_UINT8, BASE_HEX, VALS(fcp_task_attr_val), 0x7,
761 NULL, HFILL}},
763 { &hf_fcp_taskmgmt,
764 {"Task Management Flags", "fcp.taskmgmt",
765 FT_UINT8, BASE_HEX, NULL, 0x0,
766 NULL, HFILL}},
768 { &hf_fcp_addlcdblen,
769 {"Additional CDB Length", "fcp.addlcdblen",
770 FT_UINT8, BASE_DEC, NULL, 0xFC,
771 NULL, HFILL}},
773 { &hf_fcp_rddata,
774 {"RDDATA", "fcp.rddata",
775 FT_BOOLEAN, 8, NULL, 0x02,
776 NULL, HFILL}},
778 { &hf_fcp_wrdata,
779 {"WRDATA", "fcp.wrdata",
780 FT_BOOLEAN, 8, NULL, 0x01,
781 NULL, HFILL}},
783 { &hf_fcp_dl,
784 {"FCP_DL", "fcp.dl",
785 FT_UINT32, BASE_DEC, NULL, 0x0,
786 NULL, HFILL}},
788 { &hf_fcp_bidir_dl,
789 {"FCP_BIDIRECTIONAL_READ_DL", "fcp.bidir_dl",
790 FT_UINT32, BASE_DEC, NULL, 0x0,
791 NULL, HFILL}},
793 { &hf_fcp_data_ro,
794 {"FCP_DATA_RO", "fcp.data_ro",
795 FT_UINT32, BASE_DEC, VALS(fcp_iu_val), 0x0,
796 NULL, HFILL}},
798 { &hf_fcp_r_ctl,
799 {"R_CTL", "fcp.r_ctl",
800 FT_UINT8, BASE_HEX, NULL, 0x0,
801 NULL, HFILL}},
803 { &hf_fcp_burstlen,
804 {"Burst Length", "fcp.burstlen",
805 FT_UINT32, BASE_DEC, NULL, 0x0,
806 NULL, HFILL}},
808 { &hf_fcp_retry_delay_timer,
809 {"Retry Delay Timer", "fcp.rsp.retry_delay_timer",
810 FT_UINT16, BASE_DEC, NULL, 0x0,
811 NULL, HFILL}},
813 { &hf_fcp_rspflags,
814 {"FCP_RSP Flags", "fcp.rspflags",
815 FT_UINT8, BASE_HEX, NULL, 0x0,
816 NULL, HFILL}},
818 { &hf_fcp_resid,
819 {"FCP_RESID", "fcp.resid",
820 FT_UINT32, BASE_DEC, NULL, 0x0,
821 NULL, HFILL}},
823 { &hf_fcp_bidir_resid,
824 {"Bidirectional Read Resid", "fcp.bidir_resid",
825 FT_UINT32, BASE_DEC, NULL, 0x0,
826 NULL, HFILL}},
828 { &hf_fcp_snslen,
829 {"FCP_SNS_LEN", "fcp.snslen",
830 FT_UINT32, BASE_DEC, NULL, 0x0,
831 NULL, HFILL}},
833 { &hf_fcp_rsplen,
834 {"FCP_RSP_LEN", "fcp.rsplen",
835 FT_UINT32, BASE_DEC, NULL, 0x0,
836 NULL, HFILL}},
838 { &hf_fcp_rspcode,
839 {"RSP_CODE", "fcp.rspcode",
840 FT_UINT8, BASE_HEX, VALS(fcp_rsp_code_val), 0x0,
841 NULL, HFILL}},
843 { &hf_fcp_scsistatus,
844 {"SCSI Status", "fcp.status",
845 FT_UINT8, BASE_HEX, VALS(scsi_status_val), 0x0,
846 NULL, HFILL}},
848 { &hf_fcp_mgmt_flags_obsolete,
849 { "Obsolete", "fcp.mgmt.flags.obsolete",
850 FT_BOOLEAN, 8, TFS(&fcp_mgmt_flags_obsolete_tfs), 0x80,
851 NULL, HFILL }},
853 { &hf_fcp_mgmt_flags_clear_aca,
854 { "Clear ACA", "fcp.mgmt.flags.clear_aca",
855 FT_BOOLEAN, 8, TFS(&fcp_mgmt_flags_clear_aca_tfs), 0x40,
856 NULL, HFILL }},
858 { &hf_fcp_mgmt_flags_target_reset,
859 { "Target Reset", "fcp.mgmt.flags.target_reset",
860 FT_BOOLEAN, 8, TFS(&fcp_mgmt_flags_target_reset_tfs), 0x20,
861 NULL, HFILL }},
863 { &hf_fcp_mgmt_flags_lu_reset,
864 { "LU Reset", "fcp.mgmt.flags.lu_reset",
865 FT_BOOLEAN, 8, TFS(&fcp_mgmt_flags_lu_reset_tfs), 0x10,
866 NULL, HFILL }},
868 { &hf_fcp_mgmt_flags_rsvd,
869 { "Rsvd", "fcp.mgmt.flags.rsvd",
870 FT_BOOLEAN, 8, TFS(&fcp_mgmt_flags_rsvd_tfs), 0x08,
871 NULL, HFILL }},
873 { &hf_fcp_mgmt_flags_clear_task_set,
874 { "Clear Task Set", "fcp.mgmt.flags.clear_task_set",
875 FT_BOOLEAN, 8, TFS(&fcp_mgmt_flags_clear_task_set_tfs), 0x04,
876 NULL, HFILL }},
878 { &hf_fcp_mgmt_flags_abort_task_set,
879 { "Abort Task Set", "fcp.mgmt.flags.abort_task_set",
880 FT_BOOLEAN, 8, TFS(&fcp_mgmt_flags_abort_task_set_tfs), 0x02,
881 NULL, HFILL }},
883 { &hf_fcp_rsp_flags_bidi,
884 { "Bidi Rsp", "fcp.rsp.flags.bidi",
885 FT_BOOLEAN, 8, TFS(&fcp_rsp_flags_bidi_tfs), 0x80,
886 NULL, HFILL }},
888 { &hf_fcp_rsp_flags_bidi_rru,
889 { "Bidi Read Resid Under", "fcp.rsp.flags.bidi_rru",
890 FT_BOOLEAN, 8, TFS(&fcp_rsp_flags_bidi_rru_tfs), 0x40,
891 NULL, HFILL }},
893 { &hf_fcp_rsp_flags_bidi_rro,
894 { "Bidi Read Resid Over", "fcp.rsp.flags.bidi_rro",
895 FT_BOOLEAN, 8, TFS(&fcp_rsp_flags_bidi_rro_tfs), 0x20,
896 NULL, HFILL }},
898 { &hf_fcp_rsp_flags_conf_req,
899 { "Conf Req", "fcp.rsp.flags.conf_req",
900 FT_BOOLEAN, 8, TFS(&fcp_rsp_flags_conf_req_tfs), 0x10,
901 NULL, HFILL }},
903 { &hf_fcp_rsp_flags_resid_under,
904 { "Resid Under", "fcp.rsp.flags.resid_under",
905 FT_BOOLEAN, 8, TFS(&fcp_rsp_flags_resid_under_tfs), 0x08,
906 NULL, HFILL }},
908 { &hf_fcp_rsp_flags_resid_over,
909 { "Resid Over", "fcp.rsp.flags.resid_over",
910 FT_BOOLEAN, 8, TFS(&fcp_rsp_flags_resid_over_tfs), 0x04,
911 NULL, HFILL }},
913 { &hf_fcp_rsp_flags_sns_vld,
914 { "SNS Vld", "fcp.rsp.flags.sns_vld",
915 FT_BOOLEAN, 8, TFS(&fcp_rsp_flags_sns_vld_tfs), 0x02,
916 NULL, HFILL }},
918 { &hf_fcp_rsp_flags_res_vld,
919 { "RES Vld", "fcp.rsp.flags.res_vld",
920 FT_BOOLEAN, 8, TFS(&fcp_rsp_flags_res_vld_tfs), 0x01,
921 NULL, HFILL }},
923 { &hf_fcp_request_in,
924 { "Request In", "fcp.request_in",
925 FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0,
926 "The frame number for the request", HFILL }},
928 { &hf_fcp_response_in,
929 { "Response In", "fcp.response_in",
930 FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0,
931 "The frame number of the response", HFILL }},
933 { &hf_fcp_time,
934 { "Time from FCP_CMND", "fcp.time",
935 FT_RELATIVE_TIME, BASE_NONE, NULL, 0,
936 "Time since the FCP_CMND frame", HFILL }},
938 { &hf_fcp_els_op,
939 {"Opcode", "fcp.els.op",
940 FT_UINT8, BASE_HEX|BASE_EXT_STRING, &fc_els_proto_val_ext, 0x0,
941 NULL, HFILL}},
943 { &hf_fcp_srr_ox_id,
944 {"OX_ID", "fcp.els.srr.ox_id",
945 FT_UINT16, BASE_HEX, NULL, 0x0,
946 NULL, HFILL}},
948 { &hf_fcp_srr_rx_id,
949 {"RX_ID", "fcp.els.srr.rx_id",
950 FT_UINT16, BASE_HEX, NULL, 0x0,
951 NULL, HFILL}},
954 /* Setup protocol subtree array */
955 static int *ett[] = {
956 &ett_fcp,
957 &ett_fcp_taskmgmt,
958 &ett_fcp_rsp_flags,
961 /* Register the protocol name and description */
962 proto_fcp = proto_register_protocol("Fibre Channel Protocol for SCSI",
963 "FCP", "fcp");
965 /* Required function calls to register the header fields and subtrees used */
966 proto_register_field_array(proto_fcp, hf, array_length(hf));
967 proto_register_subtree_array(ett, array_length(ett));
969 fcp_handle = register_dissector("fcp", dissect_fcp, proto_fcp);
972 void
973 proto_reg_handoff_fcp(void)
975 dissector_add_uint("fc.ftype", FC_FTYPE_SCSI, fcp_handle);
979 * Editor modelines - https://www.wireshark.org/tools/modelines.html
981 * Local variables:
982 * c-basic-offset: 4
983 * tab-width: 8
984 * indent-tabs-mode: nil
985 * End:
987 * vi: set shiftwidth=4 tabstop=8 expandtab:
988 * :indentSize=4:tabSize=8:noTabs=true: