/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 | | */ |