Coverage Report

Created: 2026-08-14 06:45

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-fcp.c
Line
Count
Source
1
/* packet-fcp.c
2
 * Routines for Fibre Channel Protocol for SCSI (FCP)
3
 * Copyright 2001, Dinesh G Dutt <ddutt@cisco.com>
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
#include "config.h"
13
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"
21
22
void proto_register_fcp(void);
23
void proto_reg_handoff_fcp(void);
24
25
static dissector_handle_t fcp_handle;
26
27
typedef struct _fcp_proto_data_t {
28
    uint16_t lun;
29
} fcp_proto_data_t;
30
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;
75
76
/* Initialize the subtree pointers */
77
static int ett_fcp;
78
static int ett_fcp_taskmgmt;
79
static int ett_fcp_rsp_flags;
80
81
typedef struct _fcp_conv_data_t {
82
    wmem_map_t *luns;
83
} fcp_conv_data_t;
84
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;
91
92
#define FCP_DEF_CMND_LEN         32 /* by default cmnd is 32 bytes */
93
#define FCP_DEF_RSP_LEN          24 /* default FCP_RSP len */
94
95
/* Information Categories based on lower 4 bits of R_CTL */
96
11
#define FCP_IU_DATA              0x1
97
166
#define FCP_IU_UNSOL_CTL         0x2
98
384
#define FCP_IU_SOL_CTL           0x3
99
0
#define FCP_IU_CONFIRM           0x3
100
1
#define FCP_IU_XFER_RDY          0x5
101
1.24k
#define FCP_IU_CMD               0x6
102
504
#define FCP_IU_RSP               0x7
103
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},
112
};
113
114
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},
123
};
124
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},
135
};
136
137
138
139
static const true_false_string fcp_mgmt_flags_obsolete_tfs = {
140
   "OBSOLETE BIT is SET",
141
   "OBSOLETE BIT is NOT set",
142
};
143
static const true_false_string fcp_mgmt_flags_clear_aca_tfs = {
144
   "CLEAR ACA is SET",
145
   "Clear aca is NOT set",
146
};
147
static const true_false_string fcp_mgmt_flags_target_reset_tfs = {
148
   "TARGET RESET is SET",
149
   "Target reset is NOT set",
150
};
151
static const true_false_string fcp_mgmt_flags_lu_reset_tfs = {
152
   "LU RESET is SET",
153
   "Lu reset is NOT set",
154
};
155
static const true_false_string fcp_mgmt_flags_rsvd_tfs = {
156
   "RSVD is SET",
157
   "Rsvd is NOT set",
158
};
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",
162
};
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",
166
};
167
168
static void
169
dissect_task_mgmt_flags(packet_info *pinfo, proto_tree *parent_tree, tvbuff_t *tvb, int offset)
170
359
{
171
359
    proto_item *item;
172
359
    static int * const mgmt_flags[] = {
173
359
        &hf_fcp_mgmt_flags_obsolete,
174
359
        &hf_fcp_mgmt_flags_clear_aca,
175
359
        &hf_fcp_mgmt_flags_target_reset,
176
359
        &hf_fcp_mgmt_flags_lu_reset,
177
359
        &hf_fcp_mgmt_flags_rsvd,
178
359
        &hf_fcp_mgmt_flags_clear_task_set,
179
359
        &hf_fcp_mgmt_flags_abort_task_set,
180
359
        NULL
181
359
    };
182
183
359
    uint8_t flags;
184
185
359
    flags = tvb_get_uint8(tvb, offset);
186
359
    item = proto_tree_add_bitmask_with_flags(parent_tree, tvb, offset, hf_fcp_taskmgmt,
187
359
                                   ett_fcp_taskmgmt, mgmt_flags, ENC_NA, BMT_NO_FALSE|BMT_NO_TFS);
188
189
359
    if (!flags)
190
77
        proto_item_append_text(item, " (No values set)");
191
192
359
    if (flags & 0x80) {
193
152
        col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[FCP OBSOLETE] ");
194
152
    }
195
196
359
    if (flags & 0x40) {
197
106
        col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[FCP CLEAR_ACA] ");
198
106
    }
199
200
359
    if (flags & 0x20) {
201
144
        col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[FCP TARGET_RESET] ");
202
144
    }
203
204
359
    if (flags & 0x10) {
205
133
        col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[FCP LU_RESET] ");
206
133
    }
207
208
359
    if (flags & 0x08) {
209
101
        col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[FCP RSVD] ");
210
101
    }
211
212
359
    if (flags & 0x04) {
213
146
        col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[FCP CLEAR_TASK_SET] ");
214
146
    }
215
216
359
    if (flags & 0x02) {
217
167
        col_prepend_fence_fstr(pinfo->cinfo, COL_INFO, "[FCP ABORT_TASK_SET] ");
218
167
    }
219
359
}
220
221
static const true_false_string fcp_rsp_flags_bidi_tfs = {
222
   "Bidirectional residual fields are PRESENT",
223
   "Bidirectional residual fields are NOT present",
224
};
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",
228
};
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",
232
};
233
static const true_false_string fcp_rsp_flags_conf_req_tfs = {
234
   "CONF REQ is SET",
235
   "Conf req set is NOT set",
236
};
237
static const true_false_string fcp_rsp_flags_resid_under_tfs = {
238
   "RESID UNDER is SET",
239
   "Resid under is NOT set",
240
};
241
static const true_false_string fcp_rsp_flags_resid_over_tfs = {
242
   "RESID OVER is SET",
243
   "Resid over is NOT set",
244
};
245
static const true_false_string fcp_rsp_flags_sns_vld_tfs = {
246
   "SNS VLD is SET",
247
   "Sns vld is NOT set",
248
};
249
static const true_false_string fcp_rsp_flags_res_vld_tfs = {
250
   "RES VLD is SET",
251
   "Res vld is NOT set",
252
};
253
254
static void
255
dissect_rsp_flags(proto_tree *parent_tree, tvbuff_t *tvb, int offset)
256
57
{
257
57
    proto_item *item;
258
57
    uint8_t     flags;
259
57
    static int * const resid_present_flags[] = {
260
57
        &hf_fcp_rsp_flags_bidi,
261
57
        &hf_fcp_rsp_flags_bidi_rru,
262
57
        &hf_fcp_rsp_flags_bidi_rro,
263
57
        &hf_fcp_rsp_flags_conf_req,
264
57
        &hf_fcp_rsp_flags_resid_under,
265
57
        &hf_fcp_rsp_flags_resid_over,
266
57
        &hf_fcp_rsp_flags_sns_vld,
267
57
        &hf_fcp_rsp_flags_res_vld,
268
57
        NULL
269
57
    };
270
271
57
    static int * const no_resid_flags[] = {
272
57
        &hf_fcp_rsp_flags_bidi,
273
57
        &hf_fcp_rsp_flags_conf_req,
274
57
        &hf_fcp_rsp_flags_resid_under,
275
57
        &hf_fcp_rsp_flags_resid_over,
276
57
        &hf_fcp_rsp_flags_sns_vld,
277
57
        &hf_fcp_rsp_flags_res_vld,
278
57
        NULL
279
57
    };
280
281
57
    flags = tvb_get_uint8(tvb, offset);
282
57
    if (flags & 0x80) {
283
33
        item = proto_tree_add_bitmask_with_flags(parent_tree, tvb, offset, hf_fcp_rspflags,
284
33
                                   ett_fcp_rsp_flags, resid_present_flags, ENC_NA, BMT_NO_FALSE|BMT_NO_TFS);
285
33
    } else {
286
24
        item = proto_tree_add_bitmask_with_flags(parent_tree, tvb, offset, hf_fcp_rspflags,
287
24
                                   ett_fcp_rsp_flags, no_resid_flags, ENC_NA, BMT_NO_FALSE|BMT_NO_TFS);
288
24
    }
289
290
57
    if (!flags)
291
11
        proto_item_append_text(item, " (No values set)");
292
57
}
293
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)
296
361
{
297
361
    int          offset  = 0;
298
361
    int          add_len = 0;
299
361
    uint8_t      flags, rwflags, lun0;
300
361
    uint16_t     lun     = 0xffff;
301
361
    tvbuff_t    *cdb_tvb;
302
361
    int          tvb_len;
303
361
    fcp_request_data_t *request_data = NULL;
304
361
    itl_nexus_t itl;
305
361
    fcp_proto_data_t *proto_data;
306
307
    /* Determine the length of the FCP part of the packet */
308
361
    flags = tvb_get_uint8(tvb, offset+10);
309
361
    if (flags) {
310
284
        add_len = tvb_get_uint8(tvb, offset+11) & 0x7C;
311
284
        add_len = add_len >> 2;
312
284
    }
313
314
361
    lun0 = tvb_get_uint8(tvb, offset);
315
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.
320
     */
321
361
    if (lun0) {
322
264
      proto_tree_add_item(tree, hf_fcp_multilun, tvb, offset, 8, ENC_NA);
323
264
      lun = tvb_get_uint8(tvb, offset) & 0x3f;
324
264
      lun <<= 8;
325
264
      lun |= tvb_get_uint8(tvb, offset+1);
326
264
    } else {
327
97
      proto_tree_add_item(tree, hf_fcp_singlelun, tvb, offset+1,
328
97
                          1, ENC_BIG_ENDIAN);
329
97
      lun = tvb_get_uint8(tvb, offset+1);
330
97
    }
331
332
361
    if (!pinfo->fd->visited) {
333
359
        proto_data = wmem_new(wmem_file_scope(), fcp_proto_data_t);
334
359
        proto_data->lun = lun;
335
359
        p_add_proto_data(wmem_file_scope(), pinfo, proto_fcp, 0, proto_data);
336
359
    }
337
338
361
    request_data = (fcp_request_data_t*)wmem_map_lookup(fcp_conv_data->luns, GUINT_TO_POINTER((unsigned)lun));
339
361
    if (!request_data) {
340
106
        request_data = wmem_new(wmem_file_scope(), fcp_request_data_t);
341
106
        request_data->request_frame = pinfo->num;
342
106
        request_data->response_frame = 0;
343
106
        request_data->request_time = pinfo->abs_ts;
344
345
106
        request_data->itlq = wmem_new(wmem_file_scope(), itlq_nexus_t);
346
106
        request_data->itlq->first_exchange_frame=0;
347
106
        request_data->itlq->last_exchange_frame=0;
348
106
        request_data->itlq->lun=lun;
349
106
        request_data->itlq->scsi_opcode=0xffff;
350
106
        request_data->itlq->task_flags=0;
351
106
        request_data->itlq->data_length=0;
352
106
        request_data->itlq->bidir_data_length=0;
353
106
        request_data->itlq->fc_time=pinfo->abs_ts;
354
106
        request_data->itlq->flags=0;
355
106
        request_data->itlq->alloc_len=0;
356
106
        request_data->itlq->extra_data=NULL;
357
358
106
        wmem_map_insert(fcp_conv_data->luns, GUINT_TO_POINTER((unsigned)lun), request_data);
359
106
    }
360
361
    /* populate the exchange struct */
362
361
    if(!pinfo->fd->visited){
363
359
        if(fchdr->fctl&FC_FCTL_EXCHANGE_FIRST){
364
122
            request_data->itlq->first_exchange_frame=pinfo->num;
365
122
            request_data->itlq->fc_time = pinfo->abs_ts;
366
122
        }
367
359
        if(fchdr->fctl&FC_FCTL_EXCHANGE_LAST){
368
114
            request_data->itlq->last_exchange_frame=pinfo->num;
369
114
        }
370
359
    }
371
372
361
    if (request_data->itlq)
373
359
        request_data->itlq->lun = lun;
374
375
361
    fchdr->lun = lun;
376
377
361
    proto_tree_add_item(tree, hf_fcp_crn, tvb, offset+8, 1, ENC_BIG_ENDIAN);
378
361
    proto_tree_add_item(tree, hf_fcp_taskattr, tvb, offset+9, 1, ENC_BIG_ENDIAN);
379
361
    dissect_task_mgmt_flags(pinfo, tree, tvb, offset+10);
380
361
    proto_tree_add_item(tree, hf_fcp_addlcdblen, tvb, offset+11, 1, ENC_BIG_ENDIAN);
381
361
    rwflags = tvb_get_uint8(tvb, offset+11);
382
361
    if (request_data->itlq) {
383
359
        if (rwflags & 0x02) {
384
179
            request_data->itlq->task_flags |= SCSI_DATA_READ;
385
179
        }
386
359
        if (rwflags & 0x01) {
387
177
            request_data->itlq->task_flags |= SCSI_DATA_WRITE;
388
177
        }
389
359
    }
390
361
    proto_tree_add_item(tree, hf_fcp_rddata, tvb, offset+11, 1, ENC_BIG_ENDIAN);
391
361
    proto_tree_add_item(tree, hf_fcp_wrdata, tvb, offset+11, 1, ENC_BIG_ENDIAN);
392
393
361
    tvb_len = tvb_captured_length_remaining(tvb, offset+12);
394
361
    if (tvb_len > (16 + add_len))
395
199
      tvb_len = 16 + add_len;
396
397
361
    itl.cmdset = 0xff;
398
361
    itl.conversation = conversation;
399
400
361
    cdb_tvb = tvb_new_subset_length(tvb, offset+12, tvb_len);
401
361
    dissect_scsi_cdb(cdb_tvb, pinfo, parent_tree, SCSI_DEV_UNKNOWN, request_data->itlq, &itl);
402
403
361
    proto_tree_add_item(tree, hf_fcp_dl, tvb, offset+12+16+add_len,
404
361
                        4, ENC_BIG_ENDIAN);
405
361
    if (request_data->itlq) {
406
186
        request_data->itlq->data_length = tvb_get_ntohl(tvb, offset+12+16+add_len);
407
186
    }
408
409
361
    if ( ((rwflags & 0x03) == 0x03)
410
48
    &&  tvb_reported_length_remaining(tvb, offset+12+16+add_len+4) >= 4) {
411
39
        proto_tree_add_item(tree, hf_fcp_bidir_dl, tvb, offset+12+16+add_len+4,
412
39
                            4, ENC_BIG_ENDIAN);
413
39
        if (request_data->itlq) {
414
38
            request_data->itlq->bidir_data_length = tvb_get_ntohl(tvb, offset+12+16+add_len+4);
415
38
        }
416
417
39
    }
418
419
361
}
420
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)
423
11
{
424
11
    itl_nexus_t itl;
425
11
    itlq_nexus_t empty_itlq;
426
427
11
    itl.cmdset = 0xff;
428
11
    itl.conversation = conversation;
429
430
11
    if (itlq == NULL)
431
11
    {
432
        /* Provide "default" itlq */
433
11
        memset(&empty_itlq, 0, sizeof(empty_itlq));
434
11
        empty_itlq.lun=0xffff;
435
11
        empty_itlq.scsi_opcode=0xffff;
436
11
        itlq = &empty_itlq;
437
11
    }
438
439
11
    dissect_scsi_payload(tvb, pinfo, parent_tree, false, itlq, &itl, relative_offset);
440
11
}
441
442
/* fcp-3  9.5 table 24 */
443
static int
444
dissect_fcp_rspinfo(tvbuff_t *tvb, proto_tree *tree, int offset)
445
29
{
446
    /* 3 reserved bytes */
447
29
    offset += 3;
448
449
    /* rsp code */
450
29
    proto_tree_add_item(tree, hf_fcp_rspcode, tvb, offset, 1, ENC_BIG_ENDIAN);
451
29
    offset += 1;
452
453
    /* 4 reserved bytes */
454
29
    offset += 4;
455
456
29
    return offset;
457
29
}
458
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)
461
60
{
462
60
    uint32_t    offset = 0;
463
60
    int32_t     snslen = 0;
464
60
    int32_t     rsplen = 0;
465
60
    uint8_t     flags;
466
60
    uint8_t     status;
467
60
    itl_nexus_t itl;
468
60
    itlq_nexus_t empty_itlq;
469
470
60
    status = tvb_get_uint8(tvb, offset+11);
471
472
60
    col_append_fstr(pinfo->cinfo, COL_INFO, ":%s",
473
60
                         val_to_str(pinfo->pool, status, scsi_status_val, "0x%x"));
474
475
    /* Save the response frame */
476
60
    if (request_data != NULL) {
477
0
        request_data->response_frame = pinfo->num;
478
479
        /* populate the exchange struct */
480
0
        if(!pinfo->fd->visited){
481
0
            if(fchdr->fctl&FC_FCTL_EXCHANGE_FIRST){
482
0
                request_data->itlq->first_exchange_frame=pinfo->num;
483
0
                request_data->itlq->fc_time = pinfo->abs_ts;
484
0
            }
485
0
            if(fchdr->fctl&FC_FCTL_EXCHANGE_LAST){
486
0
                request_data->itlq->last_exchange_frame=pinfo->num;
487
0
            }
488
0
        }
489
60
    } else {
490
        /* Provide "default" itlq */
491
60
        memset(&empty_itlq, 0, sizeof(empty_itlq));
492
60
        empty_itlq.lun=0xffff;
493
60
        empty_itlq.scsi_opcode=0xffff;
494
60
    }
495
496
    /* 8 reserved bytes */
497
60
    offset += 8;
498
499
    /* retry delay timer */
500
60
    proto_tree_add_item(tree, hf_fcp_retry_delay_timer, tvb, offset, 2, ENC_BIG_ENDIAN);
501
60
    offset += 2;
502
503
    /* flags */
504
60
    flags = tvb_get_uint8(tvb, offset);
505
60
    dissect_rsp_flags(tree, tvb, offset);
506
60
    offset += 1;
507
508
60
    itl.cmdset = 0xff;
509
60
    itl.conversation = conversation;
510
511
    /* scsi status code */
512
60
    proto_tree_add_item(tree, hf_fcp_scsistatus, tvb, offset, 1, ENC_BIG_ENDIAN);
513
60
    dissect_scsi_rsp(tvb, pinfo, parent_tree, (request_data != NULL) ? request_data->itlq : &empty_itlq, &itl, tvb_get_uint8(tvb, offset));
514
60
    offset += 1;
515
516
    /* residual count */
517
60
    if (flags & 0x0e) {
518
34
        proto_tree_add_item(tree, hf_fcp_resid, tvb, offset, 4, ENC_BIG_ENDIAN);
519
34
    }
520
60
    offset += 4;
521
522
    /* sense length */
523
60
    if (flags & 0x2) {
524
24
        snslen = tvb_get_ntohl(tvb, offset);
525
24
        proto_tree_add_uint(tree, hf_fcp_snslen, tvb, offset, 4,
526
24
                            snslen);
527
24
    }
528
60
    offset += 4;
529
530
    /* response length */
531
60
    if (flags & 0x1) {
532
31
        rsplen = tvb_get_ntohl(tvb, offset);
533
31
        proto_tree_add_uint(tree, hf_fcp_rsplen, tvb, offset, 4,
534
31
                            rsplen);
535
31
    }
536
60
    offset += 4;
537
538
    /* rsp_info */
539
60
    if (rsplen) {
540
29
        tvbuff_t *rspinfo_tvb;
541
542
29
        rspinfo_tvb = tvb_new_subset_length(tvb, offset, rsplen);
543
29
        dissect_fcp_rspinfo(rspinfo_tvb, tree, 0);
544
545
29
        offset += rsplen;
546
29
    }
547
548
    /* sense info */
549
60
    if (snslen) {
550
24
        tvbuff_t *sns_tvb;
551
552
24
        sns_tvb = tvb_new_subset_length(tvb, offset, snslen);
553
24
        dissect_scsi_snsinfo(sns_tvb, pinfo, parent_tree, 0,
554
24
                              snslen,
555
24
                              (request_data != NULL) ? request_data->itlq : &empty_itlq, &itl);
556
557
24
        offset += snslen;
558
24
    }
559
560
    /* bidir read resid (only present for bidirectional responses) */
561
60
    if (flags & 0x80) {
562
25
        if (flags & 0x60) {
563
23
            proto_tree_add_item(tree, hf_fcp_bidir_resid, tvb, offset, 4, ENC_BIG_ENDIAN);
564
23
        }
565
        /*offset += 4;*/
566
25
    }
567
60
}
568
569
static void
570
dissect_fcp_xfer_rdy(tvbuff_t *tvb, proto_tree *tree)
571
1
{
572
1
    int         offset = 0;
573
574
1
    proto_tree_add_item(tree, hf_fcp_data_ro, tvb, offset, 4, ENC_BIG_ENDIAN);
575
1
    proto_tree_add_item(tree, hf_fcp_burstlen, tvb, offset+4, 4, ENC_BIG_ENDIAN);
576
1
}
577
578
static void
579
dissect_fcp_srr(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, fc_hdr *fchdr)
580
0
{
581
0
    uint8_t r_ctl;
582
583
0
    r_ctl = fchdr->r_ctl & 0xf;
584
0
    if (r_ctl == FCP_IU_UNSOL_CTL) {            /* request */
585
0
        proto_tree_add_item(tree, hf_fcp_srr_ox_id, tvb, 4, 2, ENC_BIG_ENDIAN);
586
0
        proto_tree_add_item(tree, hf_fcp_srr_rx_id, tvb, 6, 2, ENC_BIG_ENDIAN);
587
0
        proto_tree_add_item(tree, hf_fcp_data_ro, tvb, 8, 4, ENC_BIG_ENDIAN);
588
0
        proto_tree_add_item(tree, hf_fcp_r_ctl, tvb, 12, 1, ENC_NA);
589
0
    }
590
0
}
591
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},
596
};
597
598
/*
599
 * Dissect FC-4 ELS for FCP.
600
 */
601
static void
602
dissect_fcp_els(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, fc_hdr *fchdr)
603
3
{
604
3
    uint8_t op;
605
606
3
    op = tvb_get_uint8(tvb, 0);
607
3
    col_add_str(pinfo->cinfo, COL_INFO, val_to_str_ext(pinfo->pool, op, &fc_els_proto_val_ext, "0x%x"));
608
3
    proto_tree_add_item(tree, hf_fcp_els_op, tvb, 0, 1, ENC_NA);
609
610
3
    switch (op) {   /* XXX should switch based on conv for LS_ACC */
611
0
    case FC_ELS_SRR:
612
0
        dissect_fcp_srr(tvb, pinfo, tree, fchdr);
613
0
        break;
614
3
    default:
615
3
        call_data_dissector(tvb, pinfo, tree);
616
3
        break;
617
3
    }
618
3
}
619
620
static int
621
dissect_fcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
622
444
{
623
444
    proto_item      *ti            = NULL;
624
444
    proto_tree      *fcp_tree      = NULL;
625
444
    fc_hdr          *fchdr;
626
444
    uint8_t          r_ctl;
627
444
    conversation_t  *fc_conv;
628
444
    fcp_conv_data_t *fcp_conv_data = NULL;
629
444
    fcp_request_data_t *request_data = NULL;
630
444
    bool             els;
631
444
    fcp_proto_data_t *proto_data;
632
633
    /* Reject the packet if data is NULL */
634
444
    if (data == NULL)
635
0
        return 0;
636
444
    fchdr = (fc_hdr *)data;
637
638
    /* Make entries in Protocol column and Info column on summary display */
639
444
    col_set_str(pinfo->cinfo, COL_PROTOCOL, "FCP");
640
641
444
    r_ctl = fchdr->r_ctl;
642
444
    els = (r_ctl & 0xf0) == FC_RCTL_LINK_DATA;
643
444
    r_ctl &= 0xF;
644
645
444
    col_add_str(pinfo->cinfo, COL_INFO,
646
444
                     val_to_str(pinfo->pool, r_ctl, els ? fcp_els_iu_val : fcp_iu_val,
647
444
                                 "0x%x"));
648
649
444
    ti = proto_tree_add_protocol_format(tree, proto_fcp, tvb, 0, -1,
650
444
                                            "FCP: %s",
651
444
                                            val_to_str(pinfo->pool, r_ctl,
652
444
                                            els ? fcp_els_iu_val :
653
444
                                            fcp_iu_val, "Unknown 0x%02x"));
654
444
    fcp_tree = proto_item_add_subtree(ti, ett_fcp);
655
656
444
    fc_conv = find_or_create_conversation(pinfo);
657
444
    fcp_conv_data = (fcp_conv_data_t *)conversation_get_proto_data(fc_conv, proto_fcp);
658
659
444
    if (!fcp_conv_data) {
660
31
        fcp_conv_data = wmem_new(wmem_file_scope(), fcp_conv_data_t);
661
31
        fcp_conv_data->luns = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
662
31
        conversation_add_proto_data(fc_conv, proto_fcp, fcp_conv_data);
663
31
    }
664
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
444
    if (!pinfo->fd->visited) {
669
444
        proto_data = wmem_new(wmem_file_scope(), fcp_proto_data_t);
670
444
        proto_data->lun = fchdr->lun;
671
444
        p_add_proto_data(wmem_file_scope(), pinfo, proto_fcp, 0, proto_data);
672
444
    } else {
673
0
        proto_data = (fcp_proto_data_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_fcp, 0);
674
0
    }
675
676
444
    if ((r_ctl != FCP_IU_CMD) && (r_ctl != FCP_IU_UNSOL_CTL) && (proto_data != NULL)) {
677
81
        request_data = (fcp_request_data_t *)wmem_map_lookup(fcp_conv_data->luns, GUINT_TO_POINTER((unsigned)(proto_data->lun)));
678
81
    }
679
680
    /* put a request_in in all frames except the command frame */
681
444
    if ((r_ctl != FCP_IU_CMD) && (r_ctl != FCP_IU_UNSOL_CTL) &&
682
81
        (request_data != NULL) && (request_data->itlq->first_exchange_frame)) {
683
0
        proto_item *it;
684
0
        it = proto_tree_add_uint(fcp_tree, hf_fcp_singlelun, tvb, 0, 0, proto_data->lun);
685
0
        proto_item_set_generated(it);
686
0
        if (request_data != NULL) {
687
0
            it = proto_tree_add_uint(fcp_tree, hf_fcp_request_in, tvb, 0, 0, request_data->request_frame);
688
0
            proto_item_set_generated(it);
689
            /* only put the response time in the actual response frame */
690
0
            if (r_ctl == FCP_IU_RSP) {
691
0
                nstime_t delta_ts;
692
0
                nstime_delta(&delta_ts, &pinfo->abs_ts, &request_data->request_time);
693
0
                it = proto_tree_add_time(ti, hf_fcp_time, tvb, 0, 0, &delta_ts);
694
0
                proto_item_set_generated(it);
695
0
            }
696
0
        }
697
0
    }
698
    /* put a response_in in all frames except the response frame */
699
444
    if ((r_ctl != FCP_IU_RSP) && (r_ctl != FCP_IU_SOL_CTL) &&
700
384
        (request_data != NULL) && (request_data->response_frame)) {
701
0
        proto_item *it;
702
0
        it = proto_tree_add_uint(fcp_tree, hf_fcp_response_in, tvb, 0, 0, request_data->response_frame);
703
0
        proto_item_set_generated(it);
704
0
    }
705
706
444
    if (els) {
707
3
        dissect_fcp_els(tvb, pinfo, fcp_tree, fchdr);
708
3
        return tvb_captured_length(tvb);
709
3
    }
710
711
441
    switch (r_ctl) {
712
11
    case FCP_IU_DATA:
713
11
        dissect_fcp_data(tvb, pinfo, tree, fc_conv, (request_data != NULL) ? request_data->itlq : NULL, fchdr->relative_offset);
714
11
        break;
715
0
    case FCP_IU_CONFIRM:
716
        /* Nothing to be done here */
717
0
        break;
718
1
    case FCP_IU_XFER_RDY:
719
1
        dissect_fcp_xfer_rdy(tvb, fcp_tree);
720
1
        break;
721
361
    case FCP_IU_CMD:
722
361
        dissect_fcp_cmnd(tvb, pinfo, tree, fcp_tree, fc_conv, fchdr, fcp_conv_data);
723
361
        break;
724
60
    case FCP_IU_RSP:
725
60
        dissect_fcp_rsp(tvb, pinfo, tree, fcp_tree, fc_conv, fchdr, request_data);
726
60
        break;
727
8
    default:
728
8
        call_data_dissector(tvb, pinfo, tree);
729
8
        break;
730
441
    }
731
/*xxx once the subdissectors return bytes consumed:  proto_item_set_end(ti, tvb, offset);*/
732
229
    return tvb_captured_length(tvb);
733
441
}
734
735
/* Register the protocol with Wireshark */
736
737
void
738
proto_register_fcp(void)
739
16
{
740
741
    /* Setup list of header fields  See Section 1.6.1 for details*/
742
16
    static hf_register_info hf[] = {
743
16
        { &hf_fcp_multilun,
744
16
          {"Multi-Level LUN", "fcp.multilun",
745
16
           FT_BYTES, BASE_NONE, NULL, 0x0,
746
16
           NULL, HFILL}},
747
748
16
        { &hf_fcp_singlelun,
749
16
          {"LUN", "fcp.lun",
750
16
           FT_UINT8, BASE_HEX, NULL, 0x0,
751
16
           NULL, HFILL}},
752
753
16
        { &hf_fcp_crn,
754
16
          {"Command Ref Num", "fcp.crn",
755
16
           FT_UINT8, BASE_DEC, NULL, 0x0,
756
16
           NULL, HFILL}},
757
758
16
        { &hf_fcp_taskattr,
759
16
          {"Task Attribute", "fcp.taskattr",
760
16
           FT_UINT8, BASE_HEX, VALS(fcp_task_attr_val), 0x7,
761
16
           NULL, HFILL}},
762
763
16
        { &hf_fcp_taskmgmt,
764
16
          {"Task Management Flags", "fcp.taskmgmt",
765
16
           FT_UINT8, BASE_HEX, NULL, 0x0,
766
16
           NULL, HFILL}},
767
768
16
        { &hf_fcp_addlcdblen,
769
16
          {"Additional CDB Length", "fcp.addlcdblen",
770
16
           FT_UINT8, BASE_DEC, NULL, 0xFC,
771
16
           NULL, HFILL}},
772
773
16
        { &hf_fcp_rddata,
774
16
          {"RDDATA", "fcp.rddata",
775
16
           FT_BOOLEAN, 8, NULL, 0x02,
776
16
           NULL, HFILL}},
777
778
16
        { &hf_fcp_wrdata,
779
16
          {"WRDATA", "fcp.wrdata",
780
16
           FT_BOOLEAN, 8, NULL, 0x01,
781
16
           NULL, HFILL}},
782
783
16
        { &hf_fcp_dl,
784
16
          {"FCP_DL", "fcp.dl",
785
16
           FT_UINT32, BASE_DEC, NULL, 0x0,
786
16
           NULL, HFILL}},
787
788
16
        { &hf_fcp_bidir_dl,
789
16
          {"FCP_BIDIRECTIONAL_READ_DL", "fcp.bidir_dl",
790
16
           FT_UINT32, BASE_DEC, NULL, 0x0,
791
16
           NULL, HFILL}},
792
793
16
        { &hf_fcp_data_ro,
794
16
          {"FCP_DATA_RO", "fcp.data_ro",
795
16
           FT_UINT32, BASE_DEC, VALS(fcp_iu_val), 0x0,
796
16
           NULL, HFILL}},
797
798
16
        { &hf_fcp_r_ctl,
799
16
          {"R_CTL", "fcp.r_ctl",
800
16
           FT_UINT8, BASE_HEX, NULL, 0x0,
801
16
           NULL, HFILL}},
802
803
16
        { &hf_fcp_burstlen,
804
16
          {"Burst Length", "fcp.burstlen",
805
16
           FT_UINT32, BASE_DEC, NULL, 0x0,
806
16
           NULL, HFILL}},
807
808
16
        { &hf_fcp_retry_delay_timer,
809
16
          {"Retry Delay Timer", "fcp.rsp.retry_delay_timer",
810
16
           FT_UINT16, BASE_DEC, NULL, 0x0,
811
16
           NULL, HFILL}},
812
813
16
        { &hf_fcp_rspflags,
814
16
          {"FCP_RSP Flags", "fcp.rspflags",
815
16
           FT_UINT8, BASE_HEX, NULL, 0x0,
816
16
           NULL, HFILL}},
817
818
16
        { &hf_fcp_resid,
819
16
          {"FCP_RESID", "fcp.resid",
820
16
           FT_UINT32, BASE_DEC, NULL, 0x0,
821
16
           NULL, HFILL}},
822
823
16
        { &hf_fcp_bidir_resid,
824
16
          {"Bidirectional Read Resid", "fcp.bidir_resid",
825
16
           FT_UINT32, BASE_DEC, NULL, 0x0,
826
16
           NULL, HFILL}},
827
828
16
        { &hf_fcp_snslen,
829
16
          {"FCP_SNS_LEN", "fcp.snslen",
830
16
           FT_UINT32, BASE_DEC, NULL, 0x0,
831
16
           NULL, HFILL}},
832
833
16
        { &hf_fcp_rsplen,
834
16
          {"FCP_RSP_LEN", "fcp.rsplen",
835
16
           FT_UINT32, BASE_DEC, NULL, 0x0,
836
16
           NULL, HFILL}},
837
838
16
        { &hf_fcp_rspcode,
839
16
          {"RSP_CODE", "fcp.rspcode",
840
16
           FT_UINT8, BASE_HEX, VALS(fcp_rsp_code_val), 0x0,
841
16
           NULL, HFILL}},
842
843
16
        { &hf_fcp_scsistatus,
844
16
          {"SCSI Status", "fcp.status",
845
16
           FT_UINT8, BASE_HEX, VALS(scsi_status_val), 0x0,
846
16
           NULL, HFILL}},
847
848
16
        { &hf_fcp_mgmt_flags_obsolete,
849
16
          { "Obsolete", "fcp.mgmt.flags.obsolete",
850
16
            FT_BOOLEAN, 8, TFS(&fcp_mgmt_flags_obsolete_tfs), 0x80,
851
16
            NULL, HFILL }},
852
853
16
        { &hf_fcp_mgmt_flags_clear_aca,
854
16
          { "Clear ACA", "fcp.mgmt.flags.clear_aca",
855
16
            FT_BOOLEAN, 8, TFS(&fcp_mgmt_flags_clear_aca_tfs), 0x40,
856
16
            NULL, HFILL }},
857
858
16
        { &hf_fcp_mgmt_flags_target_reset,
859
16
          { "Target Reset", "fcp.mgmt.flags.target_reset",
860
16
            FT_BOOLEAN, 8, TFS(&fcp_mgmt_flags_target_reset_tfs), 0x20,
861
16
            NULL, HFILL }},
862
863
16
        { &hf_fcp_mgmt_flags_lu_reset,
864
16
          { "LU Reset", "fcp.mgmt.flags.lu_reset",
865
16
            FT_BOOLEAN, 8, TFS(&fcp_mgmt_flags_lu_reset_tfs), 0x10,
866
16
            NULL, HFILL }},
867
868
16
        { &hf_fcp_mgmt_flags_rsvd,
869
16
          { "Rsvd", "fcp.mgmt.flags.rsvd",
870
16
            FT_BOOLEAN, 8, TFS(&fcp_mgmt_flags_rsvd_tfs), 0x08,
871
16
            NULL, HFILL }},
872
873
16
        { &hf_fcp_mgmt_flags_clear_task_set,
874
16
          { "Clear Task Set", "fcp.mgmt.flags.clear_task_set",
875
16
            FT_BOOLEAN, 8, TFS(&fcp_mgmt_flags_clear_task_set_tfs), 0x04,
876
16
            NULL, HFILL }},
877
878
16
        { &hf_fcp_mgmt_flags_abort_task_set,
879
16
          { "Abort Task Set", "fcp.mgmt.flags.abort_task_set",
880
16
            FT_BOOLEAN, 8, TFS(&fcp_mgmt_flags_abort_task_set_tfs), 0x02,
881
16
            NULL, HFILL }},
882
883
16
        { &hf_fcp_rsp_flags_bidi,
884
16
          { "Bidi Rsp", "fcp.rsp.flags.bidi",
885
16
            FT_BOOLEAN, 8, TFS(&fcp_rsp_flags_bidi_tfs), 0x80,
886
16
            NULL, HFILL }},
887
888
16
        { &hf_fcp_rsp_flags_bidi_rru,
889
16
          { "Bidi Read Resid Under", "fcp.rsp.flags.bidi_rru",
890
16
            FT_BOOLEAN, 8, TFS(&fcp_rsp_flags_bidi_rru_tfs), 0x40,
891
16
            NULL, HFILL }},
892
893
16
        { &hf_fcp_rsp_flags_bidi_rro,
894
16
          { "Bidi Read Resid Over", "fcp.rsp.flags.bidi_rro",
895
16
            FT_BOOLEAN, 8, TFS(&fcp_rsp_flags_bidi_rro_tfs), 0x20,
896
16
            NULL, HFILL }},
897
898
16
        { &hf_fcp_rsp_flags_conf_req,
899
16
          { "Conf Req", "fcp.rsp.flags.conf_req",
900
16
            FT_BOOLEAN, 8, TFS(&fcp_rsp_flags_conf_req_tfs), 0x10,
901
16
            NULL, HFILL }},
902
903
16
        { &hf_fcp_rsp_flags_resid_under,
904
16
          { "Resid Under", "fcp.rsp.flags.resid_under",
905
16
            FT_BOOLEAN, 8, TFS(&fcp_rsp_flags_resid_under_tfs), 0x08,
906
16
            NULL, HFILL }},
907
908
16
        { &hf_fcp_rsp_flags_resid_over,
909
16
          { "Resid Over", "fcp.rsp.flags.resid_over",
910
16
            FT_BOOLEAN, 8, TFS(&fcp_rsp_flags_resid_over_tfs), 0x04,
911
16
            NULL, HFILL }},
912
913
16
        { &hf_fcp_rsp_flags_sns_vld,
914
16
          { "SNS Vld", "fcp.rsp.flags.sns_vld",
915
16
            FT_BOOLEAN, 8, TFS(&fcp_rsp_flags_sns_vld_tfs), 0x02,
916
16
            NULL, HFILL }},
917
918
16
        { &hf_fcp_rsp_flags_res_vld,
919
16
          { "RES Vld", "fcp.rsp.flags.res_vld",
920
16
            FT_BOOLEAN, 8, TFS(&fcp_rsp_flags_res_vld_tfs), 0x01,
921
16
            NULL, HFILL }},
922
923
16
        { &hf_fcp_request_in,
924
16
          { "Request In", "fcp.request_in",
925
16
            FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0,
926
16
            "The frame number for the request", HFILL }},
927
928
16
        { &hf_fcp_response_in,
929
16
          { "Response In", "fcp.response_in",
930
16
            FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0,
931
16
            "The frame number of the response", HFILL }},
932
933
16
        { &hf_fcp_time,
934
16
          { "Time from FCP_CMND", "fcp.time",
935
16
            FT_RELATIVE_TIME, BASE_NONE, NULL, 0,
936
16
            "Time since the FCP_CMND frame", HFILL }},
937
938
16
        { &hf_fcp_els_op,
939
16
          {"Opcode", "fcp.els.op",
940
16
           FT_UINT8, BASE_HEX|BASE_EXT_STRING, &fc_els_proto_val_ext, 0x0,
941
16
           NULL, HFILL}},
942
943
16
        { &hf_fcp_srr_ox_id,
944
16
          {"OX_ID", "fcp.els.srr.ox_id",
945
16
           FT_UINT16, BASE_HEX, NULL, 0x0,
946
16
           NULL, HFILL}},
947
948
16
        { &hf_fcp_srr_rx_id,
949
16
          {"RX_ID", "fcp.els.srr.rx_id",
950
16
           FT_UINT16, BASE_HEX, NULL, 0x0,
951
16
           NULL, HFILL}},
952
16
    };
953
954
    /* Setup protocol subtree array */
955
16
    static int *ett[] = {
956
16
        &ett_fcp,
957
16
        &ett_fcp_taskmgmt,
958
16
        &ett_fcp_rsp_flags,
959
16
    };
960
961
    /* Register the protocol name and description */
962
16
    proto_fcp = proto_register_protocol("Fibre Channel Protocol for SCSI",
963
16
                                        "FCP", "fcp");
964
965
    /* Required function calls to register the header fields and subtrees used */
966
16
    proto_register_field_array(proto_fcp, hf, array_length(hf));
967
16
    proto_register_subtree_array(ett, array_length(ett));
968
969
16
    fcp_handle = register_dissector("fcp", dissect_fcp, proto_fcp);
970
16
}
971
972
void
973
proto_reg_handoff_fcp(void)
974
16
{
975
16
    dissector_add_uint("fc.ftype", FC_FTYPE_SCSI, fcp_handle);
976
16
}
977
978
/*
979
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
980
 *
981
 * Local variables:
982
 * c-basic-offset: 4
983
 * tab-width: 8
984
 * indent-tabs-mode: nil
985
 * End:
986
 *
987
 * vi: set shiftwidth=4 tabstop=8 expandtab:
988
 * :indentSize=4:tabSize=8:noTabs=true:
989
 */