/src/wireshark/epan/dissectors/packet-fc.c
Line | Count | Source |
1 | | /* packet-fc.c |
2 | | * Routines for Fibre Channel Decoding (FC Header, Link Ctl & Basic Link Svc) |
3 | | * Copyright 2001, Dinesh G Dutt <ddutt@cisco.com> |
4 | | * Copyright 2003 Ronnie Sahlberg, exchange first/last matching and |
5 | | * tap listener and misc updates |
6 | | * |
7 | | * Wireshark - Network traffic analyzer |
8 | | * By Gerald Combs <gerald@wireshark.org> |
9 | | * Copyright 1998 Gerald Combs |
10 | | * |
11 | | * SPDX-License-Identifier: GPL-2.0-or-later |
12 | | */ |
13 | | |
14 | | #include "config.h" |
15 | | |
16 | | #include <epan/packet.h> |
17 | | #include <epan/prefs.h> |
18 | | #include <epan/tfs.h> |
19 | | #include <wiretap/wtap.h> |
20 | | #include <epan/reassemble.h> |
21 | | #include <epan/conversation_table.h> |
22 | | #include <epan/srt_table.h> |
23 | | #include "packet-fc.h" |
24 | | #include "packet-fclctl.h" |
25 | | #include "packet-fcbls.h" |
26 | | #include <epan/crc32-tvb.h> |
27 | | #include <epan/expert.h> |
28 | | |
29 | | void proto_register_fc(void); |
30 | | void proto_reg_handoff_fc(void); |
31 | | |
32 | 7.07k | #define FC_HEADER_SIZE 24 |
33 | 1.65k | #define FC_RCTL_VFT 0x50 |
34 | 855 | #define MDSHDR_TRAILER_SIZE 6 |
35 | | |
36 | | /* Size of various fields in FC header in bytes */ |
37 | 1.52k | #define FC_RCTL_SIZE 1 |
38 | | #define FC_DID_SIZE 3 |
39 | | #define FC_CSCTL_SIZE 1 |
40 | | #define FC_SID_SIZE 3 |
41 | 4 | #define FC_TYPE_SIZE 1 |
42 | | #define FC_FCTL_SIZE 3 |
43 | | #define FC_SEQID_SIZE 1 |
44 | | #define FC_DFCTL_SIZE 1 |
45 | | #define FC_SEQCNT_SIZE 2 |
46 | | #define FC_OXID_SIZE 2 |
47 | | #define FC_RXID_SIZE 2 |
48 | | #define FC_PARAM_SIZE 4 |
49 | | |
50 | | /* Initialize the protocol and registered fields */ |
51 | | static int proto_fc; |
52 | | static int hf_fc_time; |
53 | | static int hf_fc_exchange_first_frame; |
54 | | static int hf_fc_exchange_last_frame; |
55 | | static int hf_fc_rctl; |
56 | | static int hf_fc_did; |
57 | | static int hf_fc_csctl; |
58 | | static int hf_fc_sid; |
59 | | static int hf_fc_id; |
60 | | static int hf_fc_type; |
61 | | static int hf_fc_fctl; |
62 | | static int hf_fc_fctl_exchange_responder; |
63 | | static int hf_fc_fctl_seq_recipient; |
64 | | static int hf_fc_fctl_exchange_first; |
65 | | static int hf_fc_fctl_exchange_last; |
66 | | static int hf_fc_fctl_seq_last; |
67 | | static int hf_fc_fctl_priority; |
68 | | static int hf_fc_fctl_transfer_seq_initiative; |
69 | | static int hf_fc_fctl_rexmitted_seq; |
70 | | static int hf_fc_fctl_rel_offset; |
71 | | static int hf_fc_fctl_abts_ack; |
72 | | /* static int hf_fc_fctl_abts_not_ack; */ |
73 | | static int hf_fc_fctl_last_data_frame; |
74 | | static int hf_fc_fctl_ack_0_1; |
75 | | static int hf_fc_seqid; |
76 | | static int hf_fc_dfctl; |
77 | | static int hf_fc_seqcnt; |
78 | | static int hf_fc_oxid; |
79 | | static int hf_fc_rxid; |
80 | | static int hf_fc_param; |
81 | | static int hf_fc_ftype; /* Derived field, non-existent in FC hdr */ |
82 | | static int hf_fc_reassembled; |
83 | | static int hf_fc_relative_offset; |
84 | | |
85 | | /* VFT fields */ |
86 | | static int hf_fc_vft; |
87 | | static int hf_fc_vft_rctl; |
88 | | static int hf_fc_vft_ver; |
89 | | static int hf_fc_vft_type; |
90 | | static int hf_fc_vft_pri; |
91 | | static int hf_fc_vft_vf_id; |
92 | | static int hf_fc_vft_hop_ct; |
93 | | |
94 | | /* Network_Header fields */ |
95 | | static int hf_fc_nh_da; |
96 | | static int hf_fc_nh_sa; |
97 | | |
98 | | /* For Basic Link Svc */ |
99 | | static int hf_fc_bls_seqid_vld; |
100 | | static int hf_fc_bls_lastvld_seqid; |
101 | | static int hf_fc_bls_oxid; |
102 | | static int hf_fc_bls_rxid; |
103 | | static int hf_fc_bls_lowseqcnt; |
104 | | static int hf_fc_bls_hiseqcnt; |
105 | | static int hf_fc_bls_rjtcode; |
106 | | static int hf_fc_bls_rjtdetail; |
107 | | static int hf_fc_bls_vendor; |
108 | | |
109 | | /* For FC SOF */ |
110 | | static int proto_fcsof; |
111 | | |
112 | | static int hf_fcsof; |
113 | | static int hf_fceof; |
114 | | static int hf_fccrc; |
115 | | static int hf_fccrc_status; |
116 | | |
117 | | static int ett_fcsof; |
118 | | static int ett_fceof; |
119 | | static int ett_fccrc; |
120 | | |
121 | | |
122 | | /* Initialize the subtree pointers */ |
123 | | static int ett_fc; |
124 | | static int ett_fctl; |
125 | | static int ett_fcbls; |
126 | | static int ett_fc_vft; |
127 | | |
128 | | static expert_field ei_fccrc; |
129 | | static expert_field ei_short_hdr; |
130 | | /* static expert_field ei_frag_size; */ |
131 | | |
132 | | static dissector_handle_t fc_handle, fcsof_handle; |
133 | | static dissector_table_t fcftype_dissector_table; |
134 | | |
135 | | static int fc_tap; |
136 | | |
137 | | typedef struct _fc_conv_data_t { |
138 | | wmem_tree_t *exchanges; |
139 | | wmem_tree_t *luns; |
140 | | } fc_conv_data_t; |
141 | | |
142 | | /* Reassembly stuff */ |
143 | | static bool fc_reassemble = true; |
144 | | static uint32_t fc_max_frame_size = 1024; |
145 | | static reassembly_table fc_reassembly_table; |
146 | | |
147 | | typedef struct _fcseq_conv_key { |
148 | | uint32_t conv_idx; |
149 | | } fcseq_conv_key_t; |
150 | | |
151 | | typedef struct _fcseq_conv_data { |
152 | | uint32_t seq_cnt; |
153 | | } fcseq_conv_data_t; |
154 | | |
155 | | static wmem_map_t *fcseq_req_hash; |
156 | | |
157 | | /* |
158 | | * Hash Functions |
159 | | */ |
160 | | static int |
161 | | fcseq_equal(const void *v, const void *w) |
162 | 3 | { |
163 | 3 | const fcseq_conv_key_t *v1 = (const fcseq_conv_key_t *)v; |
164 | 3 | const fcseq_conv_key_t *v2 = (const fcseq_conv_key_t *)w; |
165 | | |
166 | 3 | return (v1->conv_idx == v2->conv_idx); |
167 | 3 | } |
168 | | |
169 | | static unsigned |
170 | | fcseq_hash (const void *v) |
171 | 498 | { |
172 | 498 | const fcseq_conv_key_t *key = (const fcseq_conv_key_t *)v; |
173 | 498 | unsigned val; |
174 | | |
175 | 498 | val = key->conv_idx; |
176 | | |
177 | 498 | return val; |
178 | 498 | } |
179 | | |
180 | | static const char* fc_conv_get_filter_type(conv_item_t* conv, conv_filter_type_e filter) |
181 | 0 | { |
182 | 0 | if ((filter == CONV_FT_SRC_ADDRESS) && (conv->src_address.type == AT_FC)) |
183 | 0 | return "fc.s_id"; |
184 | | |
185 | 0 | if ((filter == CONV_FT_DST_ADDRESS) && (conv->dst_address.type == AT_FC)) |
186 | 0 | return "fc.d_id"; |
187 | | |
188 | 0 | if ((filter == CONV_FT_ANY_ADDRESS) && (conv->src_address.type == AT_FC)) |
189 | 0 | return "fc.id"; |
190 | | |
191 | 0 | return CONV_FILTER_INVALID; |
192 | 0 | } |
193 | | |
194 | | static ct_dissector_info_t fc_ct_dissector_info = {&fc_conv_get_filter_type}; |
195 | | |
196 | | static tap_packet_status |
197 | | fc_conversation_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip, tap_flags_t flags) |
198 | 0 | { |
199 | 0 | conv_hash_t *hash = (conv_hash_t*) pct; |
200 | 0 | hash->flags = flags; |
201 | 0 | const fc_hdr *fchdr=(const fc_hdr *)vip; |
202 | |
|
203 | 0 | add_conversation_table_data(hash, &fchdr->s_id, &fchdr->d_id, 0, 0, 1, pinfo->fd->pkt_len, &pinfo->rel_ts, &pinfo->abs_ts, &fc_ct_dissector_info, CONVERSATION_NONE); |
204 | |
|
205 | 0 | return TAP_PACKET_REDRAW; |
206 | 0 | } |
207 | | |
208 | | static const char* fc_endpoint_get_filter_type(endpoint_item_t* endpoint, conv_filter_type_e filter) |
209 | 0 | { |
210 | 0 | if ((filter == CONV_FT_ANY_ADDRESS) && (endpoint->myaddress.type == AT_FC)) |
211 | 0 | return "fc.id"; |
212 | | |
213 | 0 | return CONV_FILTER_INVALID; |
214 | 0 | } |
215 | | |
216 | | static et_dissector_info_t fc_endpoint_dissector_info = {&fc_endpoint_get_filter_type}; |
217 | | |
218 | | static tap_packet_status |
219 | | fc_endpoint_packet(void *pit, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip, tap_flags_t flags) |
220 | 0 | { |
221 | 0 | conv_hash_t *hash = (conv_hash_t*) pit; |
222 | 0 | hash->flags = flags; |
223 | 0 | const fc_hdr *fchdr=(const fc_hdr *)vip; |
224 | | |
225 | | /* Take two "add" passes per packet, adding for each direction, ensures that all |
226 | | packets are counted properly (even if address is sending to itself) |
227 | | XXX - this could probably be done more efficiently inside endpoint_table */ |
228 | 0 | add_endpoint_table_data(hash, &fchdr->s_id, 0, true, 1, pinfo->fd->pkt_len, &fc_endpoint_dissector_info, ENDPOINT_NONE); |
229 | 0 | add_endpoint_table_data(hash, &fchdr->d_id, 0, false, 1, pinfo->fd->pkt_len, &fc_endpoint_dissector_info, ENDPOINT_NONE); |
230 | |
|
231 | 0 | return TAP_PACKET_REDRAW; |
232 | 0 | } |
233 | | |
234 | 0 | #define FC_NUM_PROCEDURES 256 |
235 | | |
236 | | static void |
237 | | fcstat_init(struct register_srt* srt _U_, GArray* srt_array) |
238 | 0 | { |
239 | 0 | srt_stat_table *fc_srt_table; |
240 | 0 | uint32_t i; |
241 | |
|
242 | 0 | fc_srt_table = init_srt_table("Fibre Channel Types", NULL, srt_array, FC_NUM_PROCEDURES, NULL, "fc.type", NULL); |
243 | 0 | for (i = 0; i < FC_NUM_PROCEDURES; i++) |
244 | 0 | { |
245 | 0 | char* tmp_str = val_to_str(NULL, i, fc_fc4_val, "Unknown(0x%02x)"); |
246 | 0 | init_srt_table_row(fc_srt_table, i, tmp_str); |
247 | 0 | wmem_free(NULL, tmp_str); |
248 | 0 | } |
249 | 0 | } |
250 | | |
251 | | static tap_packet_status |
252 | | fcstat_packet(void *pss, packet_info *pinfo, epan_dissect_t *edt _U_, const void *prv, tap_flags_t flags _U_) |
253 | 0 | { |
254 | 0 | unsigned i = 0; |
255 | 0 | srt_stat_table *fc_srt_table; |
256 | 0 | srt_data_t *data = (srt_data_t *)pss; |
257 | 0 | const fc_hdr *fc=(const fc_hdr *)prv; |
258 | | |
259 | | /* we are only interested in reply packets */ |
260 | 0 | if(!(fc->fctl&FC_FCTL_EXCHANGE_RESPONDER)){ |
261 | 0 | return TAP_PACKET_DONT_REDRAW; |
262 | 0 | } |
263 | | /* if we haven't seen the request, just ignore it */ |
264 | 0 | if ( (!fc->fc_ex) || (fc->fc_ex->first_exchange_frame==0) ){ |
265 | 0 | return TAP_PACKET_DONT_REDRAW; |
266 | 0 | } |
267 | | |
268 | 0 | fc_srt_table = g_array_index(data->srt_array, srt_stat_table*, i); |
269 | 0 | add_srt_table_data(fc_srt_table, fc->type, &fc->fc_ex->fc_time, pinfo); |
270 | |
|
271 | 0 | return TAP_PACKET_REDRAW; |
272 | 0 | } |
273 | | |
274 | | |
275 | | const value_string fc_fc4_val[] = { |
276 | | {FC_TYPE_BLS, "Basic Link Svc"}, |
277 | | {FC_TYPE_ELS, "Ext Link Svc"}, |
278 | | {FC_TYPE_LLCSNAP, "LLC_SNAP"}, |
279 | | {FC_TYPE_IP, "IP/FC"}, |
280 | | {FC_TYPE_SCSI, "FCP"}, |
281 | | {FC_TYPE_FCCT, "FC_CT"}, |
282 | | {FC_TYPE_SWILS, "SW_ILS"}, |
283 | | {FC_TYPE_AL, "AL"}, |
284 | | {FC_TYPE_SNMP, "SNMP"}, |
285 | | {FC_TYPE_SB_FROM_CU, "SB-3(CU->Channel)"}, |
286 | | {FC_TYPE_SB_TO_CU, "SB-3(Channel->CU)"}, |
287 | | {0, NULL} |
288 | | }; |
289 | | |
290 | | static const value_string fc_ftype_vals [] = { |
291 | | {FC_FTYPE_UNDEF , "Unknown frame"}, |
292 | | {FC_FTYPE_SWILS, "SW_ILS"}, |
293 | | {FC_FTYPE_IP , "IP/FC"}, |
294 | | {FC_FTYPE_SCSI , "FCP"}, |
295 | | {FC_FTYPE_BLS , "Basic Link Svc"}, |
296 | | {FC_FTYPE_ELS , "ELS"}, |
297 | | {FC_FTYPE_FCCT , "FC_CT"}, |
298 | | {FC_FTYPE_LINKDATA, "Link Data"}, |
299 | | {FC_FTYPE_VDO, "Video Data"}, |
300 | | {FC_FTYPE_LINKCTL, "Link Ctl"}, |
301 | | {FC_FTYPE_SBCCS, "SBCCS"}, |
302 | | {FC_FTYPE_OHMS, "OHMS(Cisco MDS)"}, |
303 | | {0, NULL} |
304 | | }; |
305 | | |
306 | | static const value_string fc_wka_vals[] _U_ = { |
307 | | {FC_WKA_MULTICAST, "Multicast Server"}, |
308 | | {FC_WKA_CLKSYNC, "Clock Sync Server"}, |
309 | | {FC_WKA_KEYDIST, "Key Distribution Server"}, |
310 | | {FC_WKA_ALIAS, "Alias Server"}, |
311 | | {FC_WKA_QOSF, "QoS Facilitator"}, |
312 | | {FC_WKA_MGMT, "Management Server"}, |
313 | | {FC_WKA_TIME, "Time Server"}, |
314 | | {FC_WKA_DNS, "Directory Server"}, |
315 | | {FC_WKA_FABRIC_CTRLR, "Fabric Ctlr"}, |
316 | | {FC_WKA_FPORT, "F_Port Server"}, |
317 | | {FC_WKA_BCAST, "Broadcast ID"}, |
318 | | {0, NULL} |
319 | | }; |
320 | | |
321 | | static const value_string fc_routing_val[] = { |
322 | | {FC_RCTL_DEV_DATA, "Device_Data"}, |
323 | | {FC_RCTL_ELS, "Extended Link Services"}, |
324 | | {FC_RCTL_LINK_DATA, "FC-4 Link_Data"}, |
325 | | {FC_RCTL_VIDEO, "Video_Data"}, |
326 | | {FC_RCTL_BLS, "Basic Link Services"}, |
327 | | {FC_RCTL_LINK_CTL, "Link_Control Frame"}, |
328 | | {0, NULL} |
329 | | }; |
330 | | |
331 | | static const value_string fc_iu_val[] = { |
332 | | {FC_IU_UNCATEGORIZED , "Uncategorized Data"}, |
333 | | {FC_IU_SOLICITED_DATA , "Solicited Data"}, |
334 | | {FC_IU_UNSOLICITED_CTL , "Unsolicited Control"}, |
335 | | {FC_IU_SOLICITED_CTL , "Solicited Control"}, |
336 | | {FC_IU_UNSOLICITED_DATA, "Solicited Data"}, |
337 | | {FC_IU_DATA_DESCRIPTOR , "Data Descriptor"}, |
338 | | {FC_IU_UNSOLICITED_CMD , "Unsolicited Command"}, |
339 | | {FC_IU_CMD_STATUS , "Command Status"}, |
340 | | {0, NULL} |
341 | | }; |
342 | | |
343 | | |
344 | | /* For FC SOF */ |
345 | | #define FC_SOFC1 0xBCB51717 |
346 | | #define FC_SOFI1 0xBCB55757 |
347 | | #define FC_SOFN1 0xBCB53737 |
348 | 0 | #define FC_SOFI2 0xBCB55555 |
349 | | #define FC_SOFN2 0xBCB53535 |
350 | 0 | #define FC_SOFI3 0xBCB55656 |
351 | | #define FC_SOFN3 0xBCB53636 |
352 | | #define FC_SOFC4 0xBCB51919 |
353 | | #define FC_SOFI4 0xBCB55959 |
354 | | #define FC_SOFN4 0xBCB53939 |
355 | 0 | #define FC_SOFF 0xBCB55858 |
356 | | |
357 | 0 | #define EOFT_NEG 0xBC957575 |
358 | 0 | #define EOFT_POS 0xBCB57575 |
359 | | #define EOFDT_NEG 0xBC959595 |
360 | | #define EOFDT_POS 0xBCB59595 |
361 | | #define EOFA_NEG 0xBC95F5F5 |
362 | | #define EOFA_POS 0xBCB5F5F5 |
363 | | #define EOFN_NEG 0xBC95D5D5 |
364 | | #define EOFN_POS 0xBCB5D5D5 |
365 | | #define EOFNI_NEG 0xBC8AD5D5 |
366 | | #define EOFNI_POS 0xBCAAD5D5 |
367 | 0 | #define EOFDTI_NEG 0xBC8A9595 |
368 | 0 | #define EOFDTI_POS 0xBCAA9595 |
369 | | #define EOFRT_NEG 0xBC959999 |
370 | | #define EOFRT_POS 0xBCB59999 |
371 | | #define EOFRTI_NEG 0xBC8A9999 |
372 | | #define EOFRTI_POS 0xBCAA9999 |
373 | | |
374 | | static const value_string fc_sof_vals[] = { |
375 | | {FC_SOFC1, "SOFc1 - SOF Connect Class 1 (Obsolete)" }, |
376 | | {FC_SOFI1, "SOFi1 - SOF Initiate Class 1 (Obsolete)" }, |
377 | | {FC_SOFN1, "SOFn1 - SOF Normal Class 1 (Obsolete)" }, |
378 | | {FC_SOFI2, "SOFi2 - SOF Initiate Class 2" }, |
379 | | {FC_SOFN2, "SOFn2 - SOF Normal Class 2" }, |
380 | | {FC_SOFI3, "SOFi3 - SOF Initiate Class 3" }, |
381 | | {FC_SOFN3, "SOFn3 - SOF Normal Class 3" }, |
382 | | {FC_SOFC4, "SOFc4 - SOF Activate Class 4 (Obsolete)" }, |
383 | | {FC_SOFI4, "SOFi4 - SOF Initiate Class 4 (Obsolete)" }, |
384 | | {FC_SOFN4, "SOFn4 - SOF Normal Class 4 (Obsolete)" }, |
385 | | {FC_SOFF, "SOFf - SOF Fabric" }, |
386 | | {0, NULL} |
387 | | }; |
388 | | |
389 | | static const value_string fc_eof_vals[] = { |
390 | | {EOFT_NEG, "EOFt- - EOF Terminate" }, |
391 | | {EOFT_POS, "EOFt+ - EOF Terminate" }, |
392 | | {EOFDT_NEG, "EOFdt- - EOF Disconnect-Terminate-Class 1 (Obsolete)" }, |
393 | | {EOFDT_POS, "EOFdt+ - EOF Disconnect-Terminate-Class 1 (Obsolete)" }, |
394 | | {EOFA_NEG, "EOFa- - EOF Abort" }, |
395 | | {EOFA_POS, "EOFa+ - EOF Abort" }, |
396 | | {EOFN_NEG, "EOFn- - EOF Normal" }, |
397 | | {EOFN_POS, "EOFn+ - EOF Normal" }, |
398 | | {EOFNI_NEG, "EOFni- - EOF Normal Invalid" }, |
399 | | {EOFNI_POS, "EOFni+ - EOF Normal Invalid" }, |
400 | | {EOFDTI_NEG, "EOFdti- - EOF Disconnect-Terminate-Invalid Class 1 (Obsolete)" }, |
401 | | {EOFDTI_POS, "EOFdti+ - EOF Disconnect-Terminate-Invalid Class 1 (Obsolete)" }, |
402 | | {EOFRT_NEG, "EOFrt- - EOF Remove-Terminate Class 4 (Obsolete)" }, |
403 | | {EOFRT_POS, "EOFrt+ - EOF Remove-Terminate Class 4 (Obsolete)" }, |
404 | | {EOFRTI_NEG, "EOFrti- - EOF Remove-Terminate Invalid Class 4 (Obsolete)" }, |
405 | | {EOFRTI_POS, "EOFrti+ - EOF Remove-Terminate Invalid Class 4 (Obsolete)" }, |
406 | | {0, NULL} |
407 | | }; |
408 | | |
409 | | /* BA_ACC & BA_RJT are decoded in this file itself instead of a traditional |
410 | | * dedicated file and dissector format because the dissector would require some |
411 | | * fields of the FC_HDR such as param in some cases, type in some others, the |
412 | | * lower 4 bits of r_ctl in some other cases etc. So, we decode BLS & Link Ctl |
413 | | * in this file itself. |
414 | | */ |
415 | | static void |
416 | | dissect_fc_ba_acc (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) |
417 | 1 | { |
418 | | /* Set up structures needed to add the protocol subtree and manage it */ |
419 | 1 | proto_tree *acc_tree; |
420 | 1 | int offset = 0; |
421 | | |
422 | | /* Make entries in Protocol column and Info column on summary display */ |
423 | 1 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "BLS"); |
424 | | |
425 | 1 | col_set_str(pinfo->cinfo, COL_INFO, "BA_ACC"); |
426 | | |
427 | 1 | if (tree) { |
428 | 1 | acc_tree = proto_tree_add_subtree(tree, tvb, 0, -1, ett_fcbls, NULL, "Basic Link Svc"); |
429 | | |
430 | 1 | proto_tree_add_item (acc_tree, hf_fc_bls_seqid_vld, tvb, offset++, 1, ENC_BIG_ENDIAN); |
431 | 1 | proto_tree_add_item (acc_tree, hf_fc_bls_lastvld_seqid, tvb, offset++, 1, ENC_BIG_ENDIAN); |
432 | 1 | offset += 2; /* Skip reserved field */ |
433 | 1 | proto_tree_add_item (acc_tree, hf_fc_bls_oxid, tvb, offset, 2, ENC_BIG_ENDIAN); |
434 | 1 | offset += 2; |
435 | 1 | proto_tree_add_item (acc_tree, hf_fc_bls_rxid, tvb, offset, 2, ENC_BIG_ENDIAN); |
436 | 1 | offset += 2; |
437 | 1 | proto_tree_add_item (acc_tree, hf_fc_bls_lowseqcnt, tvb, offset, 2, ENC_BIG_ENDIAN); |
438 | 1 | offset += 2; |
439 | 1 | proto_tree_add_item (acc_tree, hf_fc_bls_hiseqcnt, tvb, offset, 2, ENC_BIG_ENDIAN); |
440 | 1 | } |
441 | 1 | } |
442 | | |
443 | | static void |
444 | | dissect_fc_ba_rjt (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) |
445 | 3 | { |
446 | | /* Set up structures needed to add the protocol subtree and manage it */ |
447 | 3 | proto_tree *rjt_tree; |
448 | 3 | int offset = 0; |
449 | | |
450 | | /* Make entries in Protocol column and Info column on summary display */ |
451 | 3 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "BLS"); |
452 | | |
453 | 3 | col_set_str(pinfo->cinfo, COL_INFO, "BA_RJT"); |
454 | | |
455 | 3 | if (tree) { |
456 | 3 | rjt_tree = proto_tree_add_subtree(tree, tvb, 0, -1, ett_fcbls, NULL, "Basic Link Svc"); |
457 | | |
458 | 3 | proto_tree_add_item (rjt_tree, hf_fc_bls_rjtcode, tvb, offset+1, 1, ENC_BIG_ENDIAN); |
459 | 3 | proto_tree_add_item (rjt_tree, hf_fc_bls_rjtdetail, tvb, offset+2, 1, ENC_BIG_ENDIAN); |
460 | 3 | proto_tree_add_item (rjt_tree, hf_fc_bls_vendor, tvb, offset+3, 1, ENC_BIG_ENDIAN); |
461 | 3 | } |
462 | 3 | } |
463 | | |
464 | | static uint8_t |
465 | | fc_get_ftype (uint8_t r_ctl, uint8_t type) |
466 | 1.52k | { |
467 | | /* A simple attempt to determine the upper level protocol based on the |
468 | | * r_ctl & type fields. |
469 | | */ |
470 | 1.52k | switch (r_ctl & 0xF0) { |
471 | 1.04k | case FC_RCTL_DEV_DATA: |
472 | 1.04k | switch (type) { |
473 | 190 | case FC_TYPE_SWILS: |
474 | 190 | if ((r_ctl == 0x2) || (r_ctl == 0x3)) |
475 | 181 | return FC_FTYPE_SWILS; |
476 | 9 | else |
477 | 9 | return FC_FTYPE_UNDEF; |
478 | 5 | case FC_TYPE_IP: |
479 | 5 | return FC_FTYPE_IP; |
480 | 519 | case FC_TYPE_SCSI: |
481 | 519 | return FC_FTYPE_SCSI; |
482 | 76 | case FC_TYPE_FCCT: |
483 | 76 | return FC_FTYPE_FCCT; |
484 | 12 | case FC_TYPE_SB_FROM_CU: |
485 | 65 | case FC_TYPE_SB_TO_CU: |
486 | 65 | return FC_FTYPE_SBCCS; |
487 | 34 | case FC_TYPE_VENDOR: |
488 | 34 | return FC_FTYPE_OHMS; |
489 | 154 | default: |
490 | 154 | return FC_FTYPE_UNDEF; |
491 | 1.04k | } |
492 | 226 | case FC_RCTL_ELS: |
493 | 226 | if (((r_ctl & 0x0F) == 0x2) || ((r_ctl & 0x0F) == 0x3)) |
494 | 209 | return FC_FTYPE_ELS; |
495 | 17 | else if (type == FC_TYPE_ELS) |
496 | 2 | return FC_FTYPE_OHMS; |
497 | 15 | else |
498 | 15 | return FC_FTYPE_UNDEF; |
499 | 28 | case FC_RCTL_LINK_DATA: |
500 | 28 | switch (type) { |
501 | 3 | case FC_TYPE_SCSI: |
502 | 3 | return FC_FTYPE_SCSI; |
503 | 25 | default: |
504 | 25 | return FC_FTYPE_LINKDATA; |
505 | 28 | } |
506 | 17 | case FC_RCTL_VIDEO: |
507 | 17 | return FC_FTYPE_VDO; |
508 | 43 | case FC_RCTL_BLS: |
509 | 43 | if (type == 0) |
510 | 28 | return FC_FTYPE_BLS; |
511 | 15 | else |
512 | 15 | return FC_FTYPE_UNDEF; |
513 | 15 | case FC_RCTL_LINK_CTL: |
514 | 15 | return FC_FTYPE_LINKCTL; |
515 | 149 | default: |
516 | 149 | return FC_FTYPE_UNDEF; |
517 | 1.52k | } |
518 | 1.52k | } |
519 | | |
520 | | static const value_string abts_ack_vals[] = { |
521 | | {0, "ABTS - Cont"}, |
522 | | {1, "ABTS - Abort"}, |
523 | | {2, "ABTS - Stop"}, |
524 | | {3, "ABTS - Imm Seq Retx"}, |
525 | | {0,NULL} |
526 | | }; |
527 | | #if 0 |
528 | | static const value_string abts_not_ack_vals[] = { |
529 | | {0x000000, "ABTS - Abort/MS"}, |
530 | | {0x000010, "ABTS - Abort/SS"}, |
531 | | {0x000020, "ABTS - Process/IB"}, |
532 | | {0x000030, "ABTS - Discard/MS/Imm Retx"}, |
533 | | {0,NULL} |
534 | | }; |
535 | | #endif |
536 | | static const value_string last_data_frame_vals[] = { |
537 | | {0, "Last Data Frame - No Info"}, |
538 | | {1, "Last Data Frame - Seq Imm"}, |
539 | | {2, "Last Data Frame - Seq Soon"}, |
540 | | {3, "Last Data Frame - Seq Delyd"}, |
541 | | {0,NULL} |
542 | | }; |
543 | | static const value_string ack_0_1_vals[] = { |
544 | | {3, "ACK_0 Required"}, |
545 | | {2, "ACK_0 Required"}, |
546 | | {1, "ACK_1 Required"}, |
547 | | {0, "no ack required"}, |
548 | | {0,NULL} |
549 | | }; |
550 | | static const true_false_string tfs_fc_fctl_exchange_responder = { |
551 | | "Exchange Responder", |
552 | | "Exchange Originator" |
553 | | }; |
554 | | static const true_false_string tfs_fc_fctl_seq_recipient = { |
555 | | "Seq Recipient", |
556 | | "Seq Initiator" |
557 | | }; |
558 | | static const true_false_string tfs_fc_fctl_exchange_first = { |
559 | | "Exchg First", |
560 | | "NOT exchg first" |
561 | | }; |
562 | | static const true_false_string tfs_fc_fctl_exchange_last = { |
563 | | "Exchg Last", |
564 | | "NOT exchg last" |
565 | | }; |
566 | | static const true_false_string tfs_fc_fctl_seq_last = { |
567 | | "Seq Last", |
568 | | "NOT seq last" |
569 | | }; |
570 | | static const true_false_string tfs_fc_fctl_priority = { |
571 | | "Priority", |
572 | | "CS_CTL" |
573 | | }; |
574 | | static const true_false_string tfs_fc_fctl_transfer_seq_initiative = { |
575 | | "Transfer Seq Initiative", |
576 | | "NOT transfer seq initiative" |
577 | | }; |
578 | | static const true_false_string tfs_fc_fctl_rexmitted_seq = { |
579 | | "Retransmitted Sequence", |
580 | | "NOT retransmitted sequence" |
581 | | }; |
582 | | static const true_false_string tfs_fc_fctl_rel_offset = { |
583 | | "Rel Offset SET", |
584 | | "Rel Offset NOT set" |
585 | | }; |
586 | | |
587 | | /* |
588 | | * Dissect the VFT header. |
589 | | */ |
590 | | static void |
591 | | dissect_fc_vft(proto_tree *parent_tree, |
592 | | tvbuff_t *tvb, int offset) |
593 | 45 | { |
594 | 45 | proto_item *item; |
595 | 45 | proto_tree *tree; |
596 | 45 | uint8_t rctl; |
597 | 45 | uint8_t ver; |
598 | 45 | uint8_t type; |
599 | 45 | uint8_t pri; |
600 | 45 | uint16_t vf_id; |
601 | 45 | uint8_t hop_ct; |
602 | | |
603 | 45 | rctl = tvb_get_uint8(tvb, offset); |
604 | 45 | type = tvb_get_uint8(tvb, offset + 1); |
605 | 45 | ver = (type >> 6) & 3; |
606 | 45 | type = (type >> 2) & 0xf; |
607 | 45 | vf_id = tvb_get_ntohs(tvb, offset + 2); |
608 | 45 | pri = (vf_id >> 13) & 7; |
609 | 45 | vf_id = (vf_id >> 1) & 0xfff; |
610 | 45 | hop_ct = tvb_get_uint8(tvb, offset + 4); |
611 | | |
612 | 45 | item = proto_tree_add_uint_format_value(parent_tree, hf_fc_vft, tvb, offset, |
613 | 45 | 8, vf_id, "VF_ID %d Pri %d Hop Count %d", |
614 | 45 | vf_id, pri, hop_ct); |
615 | 45 | tree = proto_item_add_subtree(item, ett_fc_vft); |
616 | 45 | proto_tree_add_uint(tree, hf_fc_vft_rctl, tvb, offset, 1, rctl); |
617 | 45 | proto_tree_add_uint(tree, hf_fc_vft_ver, tvb, offset + 1, 1, ver); |
618 | 45 | proto_tree_add_uint(tree, hf_fc_vft_type, tvb, offset + 1, 1, type); |
619 | 45 | proto_tree_add_uint(tree, hf_fc_vft_pri, tvb, offset + 2, 1, pri); |
620 | 45 | proto_tree_add_uint(tree, hf_fc_vft_vf_id, tvb, offset + 2, 2, vf_id); |
621 | 45 | proto_tree_add_uint(tree, hf_fc_vft_hop_ct, tvb, offset + 4, 1, hop_ct); |
622 | 45 | } |
623 | | |
624 | | /* code to dissect the F_CTL bitmask */ |
625 | | static void |
626 | | dissect_fc_fctl(packet_info *pinfo _U_, proto_tree *parent_tree, tvbuff_t *tvb, int offset) |
627 | 1.52k | { |
628 | 1.52k | static int * const flags[] = { |
629 | 1.52k | &hf_fc_fctl_exchange_responder, |
630 | 1.52k | &hf_fc_fctl_seq_recipient, |
631 | 1.52k | &hf_fc_fctl_exchange_first, |
632 | 1.52k | &hf_fc_fctl_exchange_last, |
633 | 1.52k | &hf_fc_fctl_seq_last, |
634 | 1.52k | &hf_fc_fctl_priority, |
635 | 1.52k | &hf_fc_fctl_transfer_seq_initiative, |
636 | 1.52k | &hf_fc_fctl_last_data_frame, |
637 | 1.52k | &hf_fc_fctl_ack_0_1, |
638 | 1.52k | &hf_fc_fctl_rexmitted_seq, |
639 | 1.52k | &hf_fc_fctl_abts_ack, |
640 | 1.52k | &hf_fc_fctl_rel_offset, |
641 | 1.52k | NULL |
642 | 1.52k | }; |
643 | | |
644 | 1.52k | proto_tree_add_bitmask_with_flags(parent_tree, tvb, offset, hf_fc_fctl, |
645 | 1.52k | ett_fctl, flags, ENC_BIG_ENDIAN, BMT_NO_INT); |
646 | 1.52k | } |
647 | | |
648 | | static const value_string fc_bls_proto_val[] = { |
649 | | {FC_BLS_NOP , "NOP"}, |
650 | | {FC_BLS_ABTS , "ABTS"}, |
651 | | {FC_BLS_RMC , "RMC"}, |
652 | | {FC_BLS_BAACC , "BA_ACC"}, |
653 | | {FC_BLS_BARJT , "BA_RJT"}, |
654 | | {FC_BLS_PRMT , "PRMT"}, |
655 | | {0, NULL} |
656 | | }; |
657 | | |
658 | | static const value_string fc_els_proto_val[] = { |
659 | | {0x01 , "Solicited Data"}, |
660 | | {0x02 , "Request"}, |
661 | | {0x03 , "Reply"}, |
662 | | {0, NULL} |
663 | | }; |
664 | | |
665 | | /* Code to actually dissect the packets */ |
666 | | static void |
667 | | dissect_fc_helper (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, bool is_ifcp, fc_data_t* fc_data) |
668 | 1.65k | { |
669 | | /* Set up structures needed to add the protocol subtree and manage it */ |
670 | 1.65k | proto_item *ti, *hidden_item; |
671 | 1.65k | proto_tree *fc_tree; |
672 | 1.65k | tvbuff_t *next_tvb; |
673 | 1.65k | int offset = 0, next_offset; |
674 | 1.65k | int vft_offset = -1; |
675 | 1.65k | bool is_lastframe_inseq, is_1frame_inseq, is_exchg_resp = 0; |
676 | 1.65k | fragment_head *fcfrag_head; |
677 | 1.65k | uint32_t frag_id, frag_size; |
678 | 1.65k | uint8_t df_ctl, seq_id; |
679 | 1.65k | uint32_t f_ctl; |
680 | 1.65k | address addr; |
681 | | |
682 | 1.65k | uint32_t param, exchange_key; |
683 | 1.65k | uint16_t real_seqcnt; |
684 | 1.65k | uint8_t ftype; |
685 | | |
686 | 1.65k | fc_hdr* fchdr = wmem_new(pinfo->pool, fc_hdr); /* Needed by conversations, not just tap */ |
687 | 1.65k | fc_exchange_t *fc_ex; |
688 | 1.65k | fc_conv_data_t *fc_conv_data=NULL; |
689 | | |
690 | 1.65k | conversation_t *conversation; |
691 | 1.65k | fcseq_conv_data_t *cdata; |
692 | 1.65k | fcseq_conv_key_t ckey, *req_key; |
693 | | |
694 | | /* Make entries in Protocol column and Info column on summary display */ |
695 | 1.65k | col_set_str(pinfo->cinfo, COL_PROTOCOL, "FC"); |
696 | | |
697 | 1.65k | fchdr->r_ctl = tvb_get_uint8 (tvb, offset); |
698 | 1.65k | fchdr->fc_ex = NULL; |
699 | | |
700 | | /* |
701 | | * If the frame contains a VFT (virtual fabric tag), decode it |
702 | | * as a separate header before the FC frame header. |
703 | | * |
704 | | * This used to be called the Cisco-proprietary EISL field, but is now |
705 | | * standardized in FC-FS-2. See section 10.2.4. |
706 | | */ |
707 | 1.65k | if (fchdr->r_ctl == FC_RCTL_VFT) { |
708 | 48 | vft_offset = offset; |
709 | 48 | offset += 8; |
710 | 48 | fchdr->r_ctl = tvb_get_uint8 (tvb, offset); |
711 | 48 | } |
712 | | |
713 | | /* Each fc endpoint pair gets its own TCP session in iFCP but |
714 | | * the src/dst ids are undefined(==semi-random) in the FC header. |
715 | | * This means we can no track conversations for FC over iFCP by using |
716 | | * the FC src/dst addresses. |
717 | | * For iFCP: Do not update the pinfo src/dst struct and let it remain |
718 | | * being tcpip src/dst so that request/response matching in the FCP layer |
719 | | * will use ip addresses instead and still work. |
720 | | */ |
721 | 1.65k | if(!is_ifcp){ |
722 | 1.63k | set_address_tvb (&pinfo->dst, AT_FC, 3, tvb, offset+1); |
723 | 1.63k | set_address_tvb (&pinfo->src, AT_FC, 3, tvb, offset+5); |
724 | 1.63k | conversation_set_conv_addr_port_endpoints(pinfo, &pinfo->src, &pinfo->dst, CONVERSATION_EXCHG, 0, 0); |
725 | 1.63k | } else { |
726 | 24 | conversation_set_conv_addr_port_endpoints(pinfo, &pinfo->src, &pinfo->dst, CONVERSATION_EXCHG, pinfo->srcport, pinfo->destport); |
727 | 24 | } |
728 | 1.65k | set_address(&fchdr->d_id, pinfo->dst.type, pinfo->dst.len, pinfo->dst.data); |
729 | 1.65k | set_address(&fchdr->s_id, pinfo->src.type, pinfo->src.len, pinfo->src.data); |
730 | | |
731 | 1.65k | fchdr->cs_ctl = tvb_get_uint8 (tvb, offset+4); |
732 | 1.65k | fchdr->type = tvb_get_uint8 (tvb, offset+8); |
733 | 1.65k | fchdr->fctl=tvb_get_ntoh24(tvb,offset+9); |
734 | 1.65k | fchdr->seqcnt = tvb_get_ntohs (tvb, offset+14); |
735 | 1.65k | fchdr->oxid=tvb_get_ntohs(tvb,offset+16); |
736 | 1.65k | fchdr->rxid=tvb_get_ntohs(tvb,offset+18); |
737 | 1.65k | fchdr->relative_offset=0; |
738 | 1.65k | param = tvb_get_ntohl (tvb, offset+20); |
739 | 1.65k | seq_id = tvb_get_uint8 (tvb, offset+12); |
740 | | |
741 | | /* set up a conversation and conversation data */ |
742 | | /* TODO treat the fc address s_id==00.00.00 as a wildcard matching anything */ |
743 | 1.65k | conversation=find_or_create_conversation(pinfo); |
744 | 1.65k | fc_conv_data=(fc_conv_data_t *)conversation_get_proto_data(conversation, proto_fc); |
745 | 1.65k | if(!fc_conv_data){ |
746 | 321 | fc_conv_data=wmem_new(wmem_file_scope(), fc_conv_data_t); |
747 | 321 | fc_conv_data->exchanges=wmem_tree_new(wmem_file_scope()); |
748 | 321 | fc_conv_data->luns=wmem_tree_new(wmem_file_scope()); |
749 | 321 | conversation_add_proto_data(conversation, proto_fc, fc_conv_data); |
750 | 321 | } |
751 | | |
752 | | /* Set up LUN data. OXID + LUN make up unique exchanges, but LUN is populated in subdissectors |
753 | | and not necessarily in every frame. Stub it here for now */ |
754 | 1.65k | fchdr->lun = 0xFFFF; |
755 | 1.65k | if (pinfo->fd->visited) { |
756 | 0 | fchdr->lun = (uint16_t)GPOINTER_TO_UINT(wmem_tree_lookup32(fc_conv_data->luns, fchdr->oxid)); |
757 | 0 | } |
758 | | |
759 | | /* In the interest of speed, if "tree" is NULL, don't do any work not |
760 | | necessary to generate protocol tree items. */ |
761 | 1.65k | ti = proto_tree_add_protocol_format (tree, proto_fc, tvb, offset, FC_HEADER_SIZE, "Fibre Channel"); |
762 | 1.65k | fc_tree = proto_item_add_subtree (ti, ett_fc); |
763 | | |
764 | | /*is_ack = ((fchdr->r_ctl == 0xC0) || (fchdr->r_ctl == 0xC1));*/ |
765 | | |
766 | | /* There are two ways to determine if this is the first frame of a |
767 | | * sequence. Either: |
768 | | * (i) The SOF bits indicate that this is the first frame OR |
769 | | * (ii) This is an SOFf frame and seqcnt is 0. |
770 | | */ |
771 | 1.65k | is_1frame_inseq = (((fc_data->sof_eof & FC_DATA_SOF_FIRST_FRAME) == FC_DATA_SOF_FIRST_FRAME) || |
772 | 755 | (((fc_data->sof_eof & FC_DATA_SOF_SOFF) == FC_DATA_SOF_SOFF) && |
773 | 41 | (fchdr->seqcnt == 0))); |
774 | | |
775 | 1.65k | is_lastframe_inseq = ((fc_data->sof_eof & FC_DATA_EOF_LAST_FRAME) == FC_DATA_EOF_LAST_FRAME); |
776 | | |
777 | 1.65k | is_lastframe_inseq |= fchdr->fctl & FC_FCTL_SEQ_LAST; |
778 | | /*is_valid_frame = ((pinfo->sof_eof & 0x40) == 0x40);*/ |
779 | | |
780 | 1.65k | ftype = fc_get_ftype (fchdr->r_ctl, fchdr->type); |
781 | | |
782 | 1.65k | col_add_str (pinfo->cinfo, COL_INFO, val_to_str(pinfo->pool, ftype, fc_ftype_vals, |
783 | 1.65k | "Unknown Type (0x%x)")); |
784 | | |
785 | 1.65k | if (ftype == FC_FTYPE_LINKCTL) |
786 | 15 | col_append_fstr (pinfo->cinfo, COL_INFO, ", %s", |
787 | 15 | val_to_str(pinfo->pool, (fchdr->r_ctl & 0x0F), |
788 | 15 | fc_lctl_proto_val, |
789 | 15 | "LCTL 0x%x")); |
790 | | |
791 | 1.65k | if (vft_offset >= 0) { |
792 | 45 | dissect_fc_vft(fc_tree, tvb, vft_offset); |
793 | 45 | } |
794 | 1.65k | switch (fchdr->r_ctl & 0xF0) { |
795 | | |
796 | 1.04k | case FC_RCTL_DEV_DATA: |
797 | 1.07k | case FC_RCTL_LINK_DATA: |
798 | 1.08k | case FC_RCTL_VIDEO: |
799 | | /* the lower 4 bits of R_CTL are the information category */ |
800 | 1.08k | proto_tree_add_uint_format_value(fc_tree, hf_fc_rctl, tvb, offset, |
801 | 1.08k | FC_RCTL_SIZE, fchdr->r_ctl, |
802 | 1.08k | "0x%x(%s/%s)", |
803 | 1.08k | fchdr->r_ctl, |
804 | 1.08k | val_to_str(pinfo->pool, (fchdr->r_ctl & 0xF0), |
805 | 1.08k | fc_routing_val, "0x%x"), |
806 | 1.08k | val_to_str(pinfo->pool, (fchdr->r_ctl & 0x0F), |
807 | 1.08k | fc_iu_val, "0x%x")); |
808 | 1.08k | break; |
809 | | |
810 | 15 | case FC_RCTL_LINK_CTL: |
811 | | /* the lower 4 bits of R_CTL indicate the type of link ctl frame */ |
812 | 15 | proto_tree_add_uint_format_value(fc_tree, hf_fc_rctl, tvb, offset, |
813 | 15 | FC_RCTL_SIZE, fchdr->r_ctl, |
814 | 15 | "0x%x(%s/%s)", |
815 | 15 | fchdr->r_ctl, |
816 | 15 | val_to_str(pinfo->pool, (fchdr->r_ctl & 0xF0), |
817 | 15 | fc_routing_val, "0x%x"), |
818 | 15 | val_to_str(pinfo->pool, (fchdr->r_ctl & 0x0F), |
819 | 15 | fc_lctl_proto_val, "0x%x")); |
820 | 15 | break; |
821 | | |
822 | 43 | case FC_RCTL_BLS: |
823 | 43 | switch (fchdr->type) { |
824 | | |
825 | 28 | case 0x00: |
826 | | /* the lower 4 bits of R_CTL indicate the type of BLS frame */ |
827 | 28 | proto_tree_add_uint_format_value(fc_tree, hf_fc_rctl, tvb, offset, |
828 | 28 | FC_RCTL_SIZE, fchdr->r_ctl, |
829 | 28 | "0x%x(%s/%s)", |
830 | 28 | fchdr->r_ctl, |
831 | 28 | val_to_str(pinfo->pool, (fchdr->r_ctl & 0xF0), |
832 | 28 | fc_routing_val, "0x%x"), |
833 | 28 | val_to_str(pinfo->pool, (fchdr->r_ctl & 0x0F), |
834 | 28 | fc_bls_proto_val, "0x%x")); |
835 | 28 | break; |
836 | | |
837 | 15 | default: |
838 | 15 | proto_tree_add_uint_format_value(fc_tree, hf_fc_rctl, tvb, offset, |
839 | 15 | FC_RCTL_SIZE, fchdr->r_ctl, |
840 | 15 | "0x%x(%s/0x%x)", |
841 | 15 | fchdr->r_ctl, |
842 | 15 | val_to_str(pinfo->pool, (fchdr->r_ctl & 0xF0), |
843 | 15 | fc_routing_val, "0x%x"), |
844 | 15 | fchdr->r_ctl & 0x0F); |
845 | 15 | break; |
846 | 43 | } |
847 | 43 | break; |
848 | | |
849 | 226 | case FC_RCTL_ELS: |
850 | 226 | switch (fchdr->type) { |
851 | | |
852 | 5 | case 0x01: |
853 | | /* the lower 4 bits of R_CTL indicate the type of ELS frame */ |
854 | 5 | proto_tree_add_uint_format_value(fc_tree, hf_fc_rctl, tvb, offset, |
855 | 5 | FC_RCTL_SIZE, fchdr->r_ctl, |
856 | 5 | "0x%x(%s/%s)", |
857 | 5 | fchdr->r_ctl, |
858 | 5 | val_to_str(pinfo->pool, (fchdr->r_ctl & 0xF0), |
859 | 5 | fc_routing_val, "0x%x"), |
860 | 5 | val_to_str(pinfo->pool, (fchdr->r_ctl & 0x0F), |
861 | 5 | fc_els_proto_val, "0x%x")); |
862 | 5 | break; |
863 | | |
864 | 221 | default: |
865 | 221 | proto_tree_add_uint_format_value(fc_tree, hf_fc_rctl, tvb, offset, |
866 | 221 | FC_RCTL_SIZE, fchdr->r_ctl, |
867 | 221 | "0x%x(%s/0x%x)", |
868 | 221 | fchdr->r_ctl, |
869 | 221 | val_to_str(pinfo->pool, (fchdr->r_ctl & 0xF0), |
870 | 221 | fc_routing_val, "0x%x"), |
871 | 221 | fchdr->r_ctl & 0x0F); |
872 | 221 | break; |
873 | 226 | } |
874 | 226 | break; |
875 | | |
876 | 226 | default: |
877 | 149 | proto_tree_add_uint_format_value(fc_tree, hf_fc_rctl, tvb, offset, |
878 | 149 | FC_RCTL_SIZE, fchdr->r_ctl, |
879 | 149 | "0x%x(%s/0x%x)", |
880 | 149 | fchdr->r_ctl, |
881 | 149 | val_to_str(pinfo->pool, (fchdr->r_ctl & 0xF0), |
882 | 149 | fc_routing_val, "0x%x"), |
883 | 149 | fchdr->r_ctl & 0x0F); |
884 | 149 | break; |
885 | 1.65k | } |
886 | | |
887 | 1.52k | hidden_item = proto_tree_add_uint (fc_tree, hf_fc_ftype, tvb, offset, 1, |
888 | 1.52k | ftype); |
889 | 1.52k | proto_item_set_hidden(hidden_item); |
890 | | |
891 | | /* XXX - use "fc_wka_vals[]" on this? */ |
892 | 1.52k | set_address(&addr, AT_FC, 3, fchdr->d_id.data); |
893 | 1.52k | proto_tree_add_item(fc_tree, hf_fc_did, tvb, offset+1, 3, ENC_NA); |
894 | 1.52k | hidden_item = proto_tree_add_item (fc_tree, hf_fc_id, tvb, offset+1, 3, ENC_NA); |
895 | 1.52k | proto_item_set_hidden(hidden_item); |
896 | | |
897 | 1.52k | proto_tree_add_uint (fc_tree, hf_fc_csctl, tvb, offset+4, 1, fchdr->cs_ctl); |
898 | | |
899 | | /* XXX - use "fc_wka_vals[]" on this? */ |
900 | 1.52k | set_address(&addr, AT_FC, 3, fchdr->s_id.data); |
901 | 1.52k | proto_tree_add_item(fc_tree, hf_fc_sid, tvb, offset+5, 3, ENC_NA); |
902 | 1.52k | hidden_item = proto_tree_add_item (fc_tree, hf_fc_id, tvb, offset+5, 3, ENC_NA); |
903 | 1.52k | proto_item_set_hidden(hidden_item); |
904 | | |
905 | 1.52k | if (ftype == FC_FTYPE_LINKCTL) { |
906 | 15 | if (((fchdr->r_ctl & 0x0F) == FC_LCTL_FBSYB) || |
907 | 13 | ((fchdr->r_ctl & 0x0F) == FC_LCTL_FBSYL)) { |
908 | | /* for F_BSY frames, the upper 4 bits of the type field specify the |
909 | | * reason for the BSY. |
910 | | */ |
911 | 4 | proto_tree_add_uint_format_value(fc_tree, hf_fc_type, tvb, |
912 | 4 | offset+8, FC_TYPE_SIZE, |
913 | 4 | fchdr->type,"0x%x(%s)", fchdr->type, |
914 | 4 | fclctl_get_typestr (pinfo->pool, (uint8_t) (fchdr->r_ctl & 0x0F), |
915 | 4 | fchdr->type)); |
916 | 11 | } else { |
917 | 11 | proto_tree_add_item (fc_tree, hf_fc_type, tvb, offset+8, 1, ENC_BIG_ENDIAN); |
918 | 11 | } |
919 | 1.50k | } else { |
920 | 1.50k | proto_tree_add_item (fc_tree, hf_fc_type, tvb, offset+8, 1, ENC_BIG_ENDIAN); |
921 | 1.50k | } |
922 | | |
923 | | |
924 | 1.52k | dissect_fc_fctl(pinfo, fc_tree, tvb, offset+9); |
925 | 1.52k | f_ctl = tvb_get_ntoh24(tvb, offset+9); |
926 | | |
927 | | |
928 | 1.52k | proto_tree_add_item (fc_tree, hf_fc_seqid, tvb, offset+12, 1, ENC_BIG_ENDIAN); |
929 | | |
930 | 1.52k | df_ctl = tvb_get_uint8(tvb, offset+13); |
931 | | |
932 | 1.52k | proto_tree_add_uint (fc_tree, hf_fc_dfctl, tvb, offset+13, 1, df_ctl); |
933 | 1.52k | proto_tree_add_uint (fc_tree, hf_fc_seqcnt, tvb, offset+14, 2, fchdr->seqcnt); |
934 | 1.52k | proto_tree_add_uint (fc_tree, hf_fc_oxid, tvb, offset+16, 2, fchdr->oxid); |
935 | 1.52k | proto_tree_add_uint (fc_tree, hf_fc_rxid, tvb, offset+18, 2, fchdr->rxid); |
936 | | |
937 | 1.52k | if (ftype == FC_FTYPE_LINKCTL) { |
938 | 15 | if (((fchdr->r_ctl & 0x0F) == FC_LCTL_FRJT) || |
939 | 13 | ((fchdr->r_ctl & 0x0F) == FC_LCTL_PRJT) || |
940 | 11 | ((fchdr->r_ctl & 0x0F) == FC_LCTL_PBSY)) { |
941 | | /* In all these cases of Link Ctl frame, the parameter field |
942 | | * encodes the detailed error message |
943 | | */ |
944 | 6 | proto_tree_add_uint_format_value(fc_tree, hf_fc_param, tvb, |
945 | 6 | offset+20, 4, param, |
946 | 6 | "0x%x(%s)", param, |
947 | 6 | fclctl_get_paramstr (pinfo->pool, (fchdr->r_ctl & 0x0F), |
948 | 6 | param)); |
949 | 9 | } else { |
950 | 9 | proto_tree_add_item (fc_tree, hf_fc_param, tvb, offset+20, 4, ENC_BIG_ENDIAN); |
951 | 9 | } |
952 | 1.50k | } else if (ftype == FC_FTYPE_BLS) { |
953 | 28 | if ((fchdr->r_ctl & 0x0F) == FC_BLS_ABTS) { |
954 | 4 | proto_tree_add_uint_format_value(fc_tree, hf_fc_param, tvb, |
955 | 4 | offset+20, 4, param, |
956 | 4 | "0x%x(%s)", param, |
957 | 4 | ((param & 0x0F) == 1 ? "Abort Sequence" : |
958 | 4 | "Abort Exchange")); |
959 | 24 | } else { |
960 | 24 | proto_tree_add_item (fc_tree, hf_fc_param, tvb, offset+20, |
961 | 24 | 4, ENC_BIG_ENDIAN); |
962 | 24 | } |
963 | 1.47k | } else if (ftype == FC_FTYPE_SCSI ) { |
964 | 522 | if (f_ctl&FC_FCTL_REL_OFFSET){ |
965 | 13 | proto_tree_add_item (fc_tree, hf_fc_relative_offset, tvb, offset+20, 4, ENC_BIG_ENDIAN); |
966 | 13 | fchdr->relative_offset=tvb_get_ntohl(tvb, offset+20); |
967 | 509 | } else { |
968 | 509 | proto_tree_add_item (fc_tree, hf_fc_param, tvb, offset+20, 4, ENC_BIG_ENDIAN); |
969 | 509 | } |
970 | 956 | } else { |
971 | 956 | proto_tree_add_item (fc_tree, hf_fc_param, tvb, offset+20, 4, ENC_BIG_ENDIAN); |
972 | 956 | } |
973 | | |
974 | | /* Skip the Frame_Header */ |
975 | 1.52k | next_offset = offset + FC_HEADER_SIZE; |
976 | | |
977 | | /* Network_Header present? */ |
978 | 1.52k | if (df_ctl & FC_DFCTL_NH) { |
979 | 339 | proto_tree_add_item(fc_tree, hf_fc_nh_da, tvb, next_offset, 8, ENC_NA); |
980 | 339 | proto_tree_add_item(fc_tree, hf_fc_nh_sa, tvb, next_offset+8, 8, ENC_NA); |
981 | 339 | next_offset += 16; |
982 | 339 | } |
983 | | |
984 | | /* XXX - handle Association_Header and Device_Header here */ |
985 | | |
986 | 1.52k | if (ftype == FC_FTYPE_LINKCTL) { |
987 | | /* ACK_1 frames and other LINK_CTL frames echo the last seq bit if the |
988 | | * packet they're ack'ing did not have it set. So, we'll incorrectly |
989 | | * flag them as being fragmented when they're not. This fixes the |
990 | | * problem |
991 | | */ |
992 | 13 | is_lastframe_inseq = true; |
993 | 1.50k | } else { |
994 | 1.50k | is_exchg_resp = (f_ctl & FC_FCTL_EXCHANGE_RESPONDER) != 0; |
995 | 1.50k | } |
996 | | |
997 | 1.52k | if (tvb_reported_length (tvb) < FC_HEADER_SIZE) { |
998 | 0 | proto_tree_add_expert(fc_tree, pinfo, &ei_short_hdr, |
999 | 0 | tvb, 0, tvb_reported_length(tvb)); |
1000 | 0 | return; |
1001 | 0 | } |
1002 | | |
1003 | 1.52k | frag_size = tvb_reported_length (tvb)-FC_HEADER_SIZE; |
1004 | | |
1005 | | /* If there is an MDS header, we need to subtract the MDS trailer size |
1006 | | * Link Ctl, BLS & OHMS are all (encap header + FC Header + encap trailer) |
1007 | | * and are never fragmented and so we ignore the frag_size assertion for |
1008 | | * these frames. |
1009 | | */ |
1010 | 1.52k | if (fc_data->ethertype == ETHERTYPE_FCFT) { |
1011 | 303 | if ((frag_size < MDSHDR_TRAILER_SIZE) || |
1012 | 279 | ((frag_size == MDSHDR_TRAILER_SIZE) && (ftype != FC_FTYPE_LINKCTL) && |
1013 | 30 | (ftype != FC_FTYPE_BLS) && (ftype != FC_FTYPE_OHMS))) { |
1014 | 30 | proto_tree_add_expert(fc_tree, pinfo, &ei_short_hdr, |
1015 | 30 | tvb, FC_HEADER_SIZE, frag_size); |
1016 | 30 | return; |
1017 | 30 | } |
1018 | 273 | frag_size -= MDSHDR_TRAILER_SIZE; |
1019 | 1.21k | } else if (fc_data->ethertype == ETHERTYPE_BRDWALK) { |
1020 | 1.06k | if (frag_size <= 8) { |
1021 | 26 | proto_tree_add_expert(fc_tree, pinfo, &ei_short_hdr, |
1022 | 26 | tvb, FC_HEADER_SIZE, frag_size); |
1023 | 26 | return; |
1024 | 26 | } |
1025 | 1.03k | frag_size -= 8; /* 4 byte of FC CRC + |
1026 | | 4 bytes of error+EOF = 8 bytes */ |
1027 | 1.03k | } |
1028 | | |
1029 | 1.46k | if (!is_lastframe_inseq) { |
1030 | | /* Show this only as a fragmented FC frame */ |
1031 | 14 | col_append_str (pinfo->cinfo, COL_INFO, " (Fragmented)"); |
1032 | 14 | } |
1033 | | |
1034 | | /* If this is a fragment, attempt to check if fully reassembled frame is |
1035 | | * present, if we're configured to reassemble. |
1036 | | */ |
1037 | 1.46k | if ((ftype != FC_FTYPE_LINKCTL) && (ftype != FC_FTYPE_BLS) && |
1038 | 1.36k | (ftype != FC_FTYPE_OHMS) && |
1039 | 1.33k | (!is_lastframe_inseq || !is_1frame_inseq) && fc_reassemble && |
1040 | 611 | tvb_bytes_exist(tvb, FC_HEADER_SIZE, frag_size) && tree) { |
1041 | | /* Add this to the list of fragments */ |
1042 | | |
1043 | | /* In certain cases such as FICON, the SEQ_CNT is streaming |
1044 | | * i.e. continuously increasing. So, zero does not signify the |
1045 | | * first frame of the sequence. To fix this, we need to save the |
1046 | | * SEQ_CNT of the first frame in sequence and use this value to |
1047 | | * determine the actual offset into a frame. |
1048 | | */ |
1049 | 514 | ckey.conv_idx = conversation->conv_index; |
1050 | | |
1051 | 514 | cdata = (fcseq_conv_data_t *)wmem_map_lookup (fcseq_req_hash, |
1052 | 514 | &ckey); |
1053 | | |
1054 | 514 | if (is_1frame_inseq) { |
1055 | 7 | if (cdata) { |
1056 | | /* Since we never free the memory used by an exchange, this maybe a |
1057 | | * case of another request using the same exchange as a previous |
1058 | | * req. |
1059 | | */ |
1060 | 3 | cdata->seq_cnt = fchdr->seqcnt; |
1061 | 3 | } |
1062 | 4 | else { |
1063 | 4 | req_key = wmem_new(wmem_file_scope(), fcseq_conv_key_t); |
1064 | 4 | req_key->conv_idx = conversation->conv_index; |
1065 | | |
1066 | 4 | cdata = wmem_new(wmem_file_scope(), fcseq_conv_data_t); |
1067 | 4 | cdata->seq_cnt = fchdr->seqcnt; |
1068 | | |
1069 | 4 | wmem_map_insert (fcseq_req_hash, req_key, cdata); |
1070 | 4 | } |
1071 | 7 | real_seqcnt = 0; |
1072 | 7 | } |
1073 | 507 | else if (cdata != NULL) { |
1074 | 0 | real_seqcnt = fchdr->seqcnt - cdata->seq_cnt ; |
1075 | 0 | } |
1076 | 507 | else { |
1077 | 507 | real_seqcnt = fchdr->seqcnt; |
1078 | 507 | } |
1079 | | |
1080 | | /* Verify that this is a valid fragment */ |
1081 | 514 | if (is_lastframe_inseq && !is_1frame_inseq && !real_seqcnt) { |
1082 | | /* This is a frame that purports to be the last frame in a |
1083 | | * sequence, is not the first frame, but has a seqcnt that is |
1084 | | * 0. This is a bogus frame, don't attempt to reassemble it. |
1085 | | */ |
1086 | 329 | next_tvb = tvb_new_subset_remaining (tvb, next_offset); |
1087 | 329 | col_append_str (pinfo->cinfo, COL_INFO, " (Bogus Fragment)"); |
1088 | 329 | } else { |
1089 | | |
1090 | 185 | frag_id = ((fchdr->oxid << 16) ^ seq_id) | is_exchg_resp ; |
1091 | | |
1092 | | /* We assume that all frames are of the same max size */ |
1093 | 185 | fcfrag_head = fragment_add (&fc_reassembly_table, |
1094 | 185 | tvb, FC_HEADER_SIZE, |
1095 | 185 | pinfo, frag_id, NULL, |
1096 | 185 | real_seqcnt * fc_max_frame_size, |
1097 | 185 | frag_size, |
1098 | 185 | !is_lastframe_inseq); |
1099 | | |
1100 | 185 | if (fcfrag_head) { |
1101 | 0 | next_tvb = tvb_new_chain(tvb, fcfrag_head->tvb_data); |
1102 | | |
1103 | | /* Add the defragmented data to the data source list. */ |
1104 | 0 | add_new_data_source(pinfo, next_tvb, "Reassembled FC"); |
1105 | |
|
1106 | 0 | hidden_item = proto_tree_add_boolean (fc_tree, hf_fc_reassembled, |
1107 | 0 | tvb, offset+9, 1, 1); |
1108 | 0 | proto_item_set_hidden(hidden_item); |
1109 | 0 | } |
1110 | 185 | else { |
1111 | 185 | hidden_item = proto_tree_add_boolean (fc_tree, hf_fc_reassembled, |
1112 | 185 | tvb, offset+9, 1, 0); |
1113 | 185 | proto_item_set_hidden(hidden_item); |
1114 | 185 | next_tvb = tvb_new_subset_remaining (tvb, next_offset); |
1115 | 185 | call_data_dissector(next_tvb, pinfo, tree); |
1116 | 185 | return; |
1117 | 185 | } |
1118 | 185 | } |
1119 | 951 | } else { |
1120 | 951 | hidden_item = proto_tree_add_boolean (fc_tree, hf_fc_reassembled, |
1121 | 951 | tvb, offset+9, 1, 0); |
1122 | 951 | proto_item_set_hidden(hidden_item); |
1123 | 951 | next_tvb = tvb_new_subset_remaining (tvb, next_offset); |
1124 | 951 | } |
1125 | | |
1126 | 1.28k | if ((ftype != FC_FTYPE_LINKCTL) && (ftype != FC_FTYPE_BLS)) { |
1127 | | /* If relative offset is used, only dissect the pdu with |
1128 | | * offset 0 (param) */ |
1129 | 1.18k | if( (fchdr->fctl&FC_FCTL_REL_OFFSET) && param ){ |
1130 | 80 | call_data_dissector(next_tvb, pinfo, tree); |
1131 | 1.10k | } else { |
1132 | 1.10k | if (!dissector_try_uint_with_data (fcftype_dissector_table, ftype, |
1133 | 1.10k | next_tvb, pinfo, tree, false, fchdr)) { |
1134 | 120 | call_data_dissector(next_tvb, pinfo, tree); |
1135 | 120 | } |
1136 | 1.10k | } |
1137 | 1.18k | } else if (ftype == FC_FTYPE_BLS) { |
1138 | 28 | if ((fchdr->r_ctl & 0x0F) == FC_BLS_BAACC) { |
1139 | 1 | dissect_fc_ba_acc (next_tvb, pinfo, tree); |
1140 | 27 | } else if ((fchdr->r_ctl & 0x0F) == FC_BLS_BARJT) { |
1141 | 3 | dissect_fc_ba_rjt (next_tvb, pinfo, tree); |
1142 | 24 | } else if ((fchdr->r_ctl & 0x0F) == FC_BLS_ABTS) { |
1143 | 4 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "BLS"); |
1144 | 4 | col_set_str(pinfo->cinfo, COL_INFO, "ABTS"); |
1145 | 4 | } |
1146 | 28 | } |
1147 | | |
1148 | | /* Lun is only populated by subdissectors, and subsequent packets assume the same lun. |
1149 | | The only way that consistently works is to save the lun on the first pass (with OXID as |
1150 | | key) when packets are guaranteed to be parsed consecutively */ |
1151 | | |
1152 | | /* Set up LUN data */ |
1153 | 1.28k | if (!pinfo->fd->visited) { |
1154 | 750 | wmem_tree_insert32(fc_conv_data->luns, fchdr->oxid, GUINT_TO_POINTER((unsigned)fchdr->lun)); |
1155 | 750 | } |
1156 | | |
1157 | 1.28k | exchange_key = ((fchdr->oxid & 0xFFFF) | (((uint32_t)fchdr->lun << 16) & 0xFFFF0000)); |
1158 | | |
1159 | | /* set up the exchange data */ |
1160 | | /* XXX we should come up with a way to handle when the 16bit oxid wraps |
1161 | | * so that large traces will work |
1162 | | */ |
1163 | 1.28k | fc_ex=(fc_exchange_t*)wmem_tree_lookup32(fc_conv_data->exchanges, exchange_key); |
1164 | 1.28k | if(!fc_ex){ |
1165 | 269 | fc_ex=wmem_new(wmem_file_scope(), fc_exchange_t); |
1166 | 269 | fc_ex->first_exchange_frame=0; |
1167 | 269 | fc_ex->last_exchange_frame=0; |
1168 | 269 | fc_ex->fc_time=pinfo->abs_ts; |
1169 | | |
1170 | 269 | wmem_tree_insert32(fc_conv_data->exchanges, exchange_key, fc_ex); |
1171 | 269 | } |
1172 | | |
1173 | 1.28k | fchdr->fc_ex = fc_ex; |
1174 | | |
1175 | | /* XXX: The ACK_1 frames (and other LINK_CONTROL frames) should |
1176 | | * probably be ignored (or treated specially) for SRT purposes, |
1177 | | * and not used to change the first exchange frame or start time |
1178 | | * of an exchange. |
1179 | | */ |
1180 | | |
1181 | | /* populate the exchange struct */ |
1182 | 1.28k | if(!pinfo->fd->visited){ |
1183 | 750 | if(fchdr->fctl&FC_FCTL_EXCHANGE_FIRST){ |
1184 | 378 | fc_ex->first_exchange_frame=pinfo->num; |
1185 | 378 | fc_ex->fc_time = pinfo->abs_ts; |
1186 | 378 | } |
1187 | 750 | if(fchdr->fctl&FC_FCTL_EXCHANGE_LAST){ |
1188 | 277 | fc_ex->last_exchange_frame=pinfo->num; |
1189 | 277 | } |
1190 | 750 | } |
1191 | | |
1192 | | /* put some nice exchange data in the tree */ |
1193 | 1.28k | if(!(fchdr->fctl&FC_FCTL_EXCHANGE_FIRST)){ |
1194 | 372 | proto_item *it; |
1195 | 372 | it=proto_tree_add_uint(fc_tree, hf_fc_exchange_first_frame, tvb, 0, 0, fc_ex->first_exchange_frame); |
1196 | 372 | proto_item_set_generated(it); |
1197 | 372 | if(fchdr->fctl&FC_FCTL_EXCHANGE_LAST){ |
1198 | 38 | nstime_t delta_ts; |
1199 | 38 | nstime_delta(&delta_ts, &pinfo->abs_ts, &fc_ex->fc_time); |
1200 | 38 | it=proto_tree_add_time(ti, hf_fc_time, tvb, 0, 0, &delta_ts); |
1201 | 38 | proto_item_set_generated(it); |
1202 | 38 | } |
1203 | 372 | } |
1204 | 1.28k | if(!(fchdr->fctl&FC_FCTL_EXCHANGE_LAST)){ |
1205 | 473 | proto_item *it; |
1206 | 473 | it=proto_tree_add_uint(fc_tree, hf_fc_exchange_last_frame, tvb, 0, 0, fc_ex->last_exchange_frame); |
1207 | 473 | proto_item_set_generated(it); |
1208 | 473 | } |
1209 | | |
1210 | 1.28k | tap_queue_packet(fc_tap, pinfo, fchdr); |
1211 | 1.28k | } |
1212 | | |
1213 | | static int |
1214 | | dissect_fc (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) |
1215 | 1.65k | { |
1216 | 1.65k | fc_data_t* fc_data = (fc_data_t*)data; |
1217 | | |
1218 | 1.65k | if (!fc_data) |
1219 | 0 | return 0; |
1220 | | |
1221 | 1.65k | dissect_fc_helper (tvb, pinfo, tree, false, fc_data); |
1222 | 1.65k | return tvb_captured_length(tvb); |
1223 | 1.65k | } |
1224 | | |
1225 | | static int |
1226 | | dissect_fc_wtap (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
1227 | 0 | { |
1228 | 0 | fc_data_t fc_data; |
1229 | |
|
1230 | 0 | fc_data.ethertype = ETHERTYPE_UNK; |
1231 | 0 | fc_data.sof_eof = 0; |
1232 | |
|
1233 | 0 | dissect_fc_helper (tvb, pinfo, tree, false, &fc_data); |
1234 | 0 | return tvb_captured_length(tvb); |
1235 | 0 | } |
1236 | | |
1237 | | static int |
1238 | | dissect_fc_ifcp (tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) |
1239 | 3 | { |
1240 | 3 | fc_data_t* fc_data = (fc_data_t*)data; |
1241 | | |
1242 | 3 | if (!fc_data) |
1243 | 0 | return 0; |
1244 | | |
1245 | 3 | dissect_fc_helper (tvb, pinfo, tree, true, fc_data); |
1246 | 3 | return tvb_captured_length(tvb); |
1247 | 3 | } |
1248 | | |
1249 | | static int |
1250 | 0 | dissect_fcsof(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) { |
1251 | |
|
1252 | 0 | proto_item *it; |
1253 | 0 | proto_tree *fcsof_tree; |
1254 | 0 | tvbuff_t *next_tvb; |
1255 | 0 | uint32_t sof; |
1256 | 0 | uint32_t crc_computed; |
1257 | 0 | uint32_t eof; |
1258 | 0 | const int FCSOF_TRAILER_LEN = 8; |
1259 | 0 | const int FCSOF_HEADER_LEN = 4; |
1260 | 0 | int crc_offset = tvb_reported_length(tvb) - FCSOF_TRAILER_LEN; |
1261 | 0 | int eof_offset = crc_offset + 4; |
1262 | 0 | int sof_offset = 0; |
1263 | 0 | int frame_len_for_checksum; |
1264 | 0 | fc_data_t fc_data; |
1265 | |
|
1266 | 0 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "FC"); |
1267 | | |
1268 | | /* Get SOF */ |
1269 | 0 | sof = tvb_get_ntohl(tvb, 0); |
1270 | | |
1271 | | /* GET Computed CRC */ |
1272 | 0 | frame_len_for_checksum = crc_offset - FCSOF_HEADER_LEN; |
1273 | 0 | crc_computed = crc32_802_tvb(tvb_new_subset_length(tvb, 4, frame_len_for_checksum), frame_len_for_checksum); |
1274 | | |
1275 | | /* Get EOF */ |
1276 | 0 | eof = tvb_get_ntohl(tvb, eof_offset); |
1277 | |
|
1278 | 0 | it = proto_tree_add_protocol_format(tree, proto_fcsof, tvb, 0, |
1279 | 0 | 4, "Fibre Channel Delimiter: SOF: %s EOF: %s", |
1280 | 0 | val_to_str(pinfo->pool, sof, fc_sof_vals, "0x%x"), |
1281 | 0 | val_to_str(pinfo->pool, eof, fc_eof_vals, "0x%x")); |
1282 | |
|
1283 | 0 | fcsof_tree = proto_item_add_subtree(it, ett_fcsof); |
1284 | |
|
1285 | 0 | proto_tree_add_uint(fcsof_tree, hf_fcsof, tvb, sof_offset, 4, sof); |
1286 | |
|
1287 | 0 | proto_tree_add_checksum(fcsof_tree, tvb, crc_offset, hf_fccrc, hf_fccrc_status, &ei_fccrc, pinfo, crc_computed, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY); |
1288 | |
|
1289 | 0 | proto_tree_add_uint(fcsof_tree, hf_fceof, tvb, eof_offset, 4, eof); |
1290 | |
|
1291 | 0 | next_tvb = tvb_new_subset_length(tvb, 4, crc_offset-4); |
1292 | |
|
1293 | 0 | fc_data.ethertype = ETHERTYPE_UNK; |
1294 | 0 | fc_data.sof_eof = 0; |
1295 | 0 | if (sof == FC_SOFI2 || sof == FC_SOFI3) { |
1296 | 0 | fc_data.sof_eof = FC_DATA_SOF_FIRST_FRAME; |
1297 | 0 | } else if (sof == FC_SOFF) { |
1298 | 0 | fc_data.sof_eof = FC_DATA_SOF_SOFF; |
1299 | 0 | } |
1300 | |
|
1301 | 0 | if (eof == EOFT_POS || eof == EOFT_NEG) { |
1302 | 0 | fc_data.sof_eof |= FC_DATA_EOF_LAST_FRAME; |
1303 | 0 | } else if (eof == EOFDTI_NEG || eof == EOFDTI_POS) { |
1304 | 0 | fc_data.sof_eof |= FC_DATA_EOF_INVALID; |
1305 | 0 | } |
1306 | | |
1307 | | /* Call FC dissector */ |
1308 | 0 | call_dissector_with_data(fc_handle, next_tvb, pinfo, tree, &fc_data); |
1309 | 0 | return tvb_captured_length(tvb); |
1310 | 0 | } |
1311 | | |
1312 | | /* Register the protocol with Wireshark */ |
1313 | | |
1314 | | /* this format is require because a script is used to build the C function |
1315 | | that calls all the protocol registration. |
1316 | | */ |
1317 | | |
1318 | | void |
1319 | | proto_register_fc(void) |
1320 | 14 | { |
1321 | | |
1322 | | /* Setup list of header fields See Section 1.6.1 for details*/ |
1323 | 14 | static hf_register_info hf[] = { |
1324 | 14 | { &hf_fc_rctl, |
1325 | 14 | { "R_CTL", "fc.r_ctl", FT_UINT8, BASE_HEX, NULL, 0x0, |
1326 | 14 | NULL, HFILL }}, |
1327 | 14 | { &hf_fc_ftype, |
1328 | 14 | {"Frame type", "fc.ftype", FT_UINT8, BASE_HEX, VALS(fc_ftype_vals), |
1329 | 14 | 0x0, "Derived Type", HFILL}}, |
1330 | 14 | { &hf_fc_did, |
1331 | 14 | { "Dest Addr", "fc.d_id", FT_BYTES, SEP_DOT, NULL, 0x0, |
1332 | 14 | "Destination Address", HFILL}}, |
1333 | 14 | { &hf_fc_csctl, |
1334 | 14 | {"CS_CTL", "fc.cs_ctl", FT_UINT8, BASE_HEX, NULL, 0x0, |
1335 | 14 | NULL, HFILL}}, |
1336 | 14 | { &hf_fc_sid, |
1337 | 14 | {"Src Addr", "fc.s_id", FT_BYTES, SEP_DOT, NULL, 0x0, |
1338 | 14 | "Source Address", HFILL}}, |
1339 | 14 | { &hf_fc_id, |
1340 | 14 | {"Addr", "fc.id", FT_BYTES, SEP_DOT, NULL, 0x0, |
1341 | 14 | "Source or Destination Address", HFILL}}, |
1342 | 14 | { &hf_fc_type, |
1343 | 14 | {"Type", "fc.type", FT_UINT8, BASE_HEX, VALS (fc_fc4_val), 0x0, |
1344 | 14 | NULL, HFILL}}, |
1345 | 14 | { &hf_fc_fctl, |
1346 | 14 | {"F_CTL", "fc.f_ctl", FT_UINT24, BASE_HEX, NULL, 0x0, NULL, HFILL}}, |
1347 | 14 | { &hf_fc_seqid, |
1348 | 14 | {"SEQ_ID", "fc.seq_id", FT_UINT8, BASE_HEX, NULL, 0x0, |
1349 | 14 | "Sequence ID", HFILL}}, |
1350 | 14 | { &hf_fc_dfctl, |
1351 | 14 | {"DF_CTL", "fc.df_ctl", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL}}, |
1352 | 14 | { &hf_fc_seqcnt, |
1353 | 14 | {"SEQ_CNT", "fc.seq_cnt", FT_UINT16, BASE_DEC, NULL, 0x0, |
1354 | 14 | "Sequence Count", HFILL}}, |
1355 | 14 | { &hf_fc_oxid, |
1356 | 14 | {"OX_ID", "fc.ox_id", FT_UINT16, BASE_HEX, NULL, 0x0, "Originator ID", |
1357 | 14 | HFILL}}, |
1358 | 14 | { &hf_fc_rxid, |
1359 | 14 | {"RX_ID", "fc.rx_id", FT_UINT16, BASE_HEX, NULL, 0x0, "Receiver ID", |
1360 | 14 | HFILL}}, |
1361 | 14 | { &hf_fc_param, |
1362 | 14 | {"Parameter", "fc.parameter", FT_UINT32, BASE_HEX, NULL, 0x0, NULL, |
1363 | 14 | HFILL}}, |
1364 | | |
1365 | 14 | { &hf_fc_reassembled, |
1366 | 14 | {"Reassembled Frame", "fc.reassembled", FT_BOOLEAN, BASE_NONE, NULL, |
1367 | 14 | 0x0, NULL, HFILL}}, |
1368 | 14 | { &hf_fc_nh_da, |
1369 | 14 | {"Network DA", "fc.nethdr.da", FT_FCWWN, BASE_NONE, NULL, |
1370 | 14 | 0x0, NULL, HFILL}}, |
1371 | 14 | { &hf_fc_nh_sa, |
1372 | 14 | {"Network SA", "fc.nethdr.sa", FT_FCWWN, BASE_NONE, NULL, |
1373 | 14 | 0x0, NULL, HFILL}}, |
1374 | | |
1375 | | /* Basic Link Svc field definitions */ |
1376 | 14 | { &hf_fc_bls_seqid_vld, |
1377 | 14 | {"SEQID Valid", "fc.bls_seqidvld", FT_UINT8, BASE_HEX, |
1378 | 14 | VALS (fc_bls_seqid_val), 0x0, NULL, HFILL}}, |
1379 | 14 | { &hf_fc_bls_lastvld_seqid, |
1380 | 14 | {"Last Valid SEQID", "fc.bls_lastseqid", FT_UINT8, BASE_HEX, NULL, |
1381 | 14 | 0x0, NULL, HFILL}}, |
1382 | 14 | { &hf_fc_bls_oxid, |
1383 | 14 | {"OXID", "fc.bls_oxid", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}}, |
1384 | 14 | { &hf_fc_bls_rxid, |
1385 | 14 | {"RXID", "fc.bls_rxid", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL}}, |
1386 | 14 | { &hf_fc_bls_lowseqcnt, |
1387 | 14 | {"Low SEQCNT", "fc.bls_lseqcnt", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, |
1388 | 14 | HFILL}}, |
1389 | 14 | { &hf_fc_bls_hiseqcnt, |
1390 | 14 | {"High SEQCNT", "fc.bls_hseqcnt", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, |
1391 | 14 | HFILL}}, |
1392 | 14 | { &hf_fc_bls_rjtcode, |
1393 | 14 | {"Reason", "fc.bls_reason", FT_UINT8, BASE_HEX, VALS(fc_bls_barjt_val), |
1394 | 14 | 0x0, NULL, HFILL}}, |
1395 | 14 | { &hf_fc_bls_rjtdetail, |
1396 | 14 | {"Reason Explanation", "fc.bls_rjtdetail", FT_UINT8, BASE_HEX, |
1397 | 14 | VALS (fc_bls_barjt_det_val), 0x0, NULL, HFILL}}, |
1398 | 14 | { &hf_fc_bls_vendor, |
1399 | 14 | {"Vendor Unique Reason", "fc.bls_vnduniq", FT_UINT8, BASE_HEX, NULL, |
1400 | 14 | 0x0, NULL, HFILL}}, |
1401 | 14 | { &hf_fc_fctl_exchange_responder, |
1402 | 14 | {"ExgRpd", "fc.fctl.exchange_responder", FT_BOOLEAN, 24, TFS(&tfs_fc_fctl_exchange_responder), |
1403 | 14 | FC_FCTL_EXCHANGE_RESPONDER, "Exchange Responder?", HFILL}}, |
1404 | 14 | { &hf_fc_fctl_seq_recipient, |
1405 | 14 | {"SeqRec", "fc.fctl.seq_recipient", FT_BOOLEAN, 24, TFS(&tfs_fc_fctl_seq_recipient), |
1406 | 14 | FC_FCTL_SEQ_RECIPIENT, "Seq Recipient?", HFILL}}, |
1407 | 14 | { &hf_fc_fctl_exchange_first, |
1408 | 14 | {"ExgFst", "fc.fctl.exchange_first", FT_BOOLEAN, 24, TFS(&tfs_fc_fctl_exchange_first), |
1409 | 14 | FC_FCTL_EXCHANGE_FIRST, "First Exchange?", HFILL}}, |
1410 | 14 | { &hf_fc_fctl_exchange_last, |
1411 | 14 | {"ExgLst", "fc.fctl.exchange_last", FT_BOOLEAN, 24, TFS(&tfs_fc_fctl_exchange_last), |
1412 | 14 | FC_FCTL_EXCHANGE_LAST, "Last Exchange?", HFILL}}, |
1413 | 14 | { &hf_fc_fctl_seq_last, |
1414 | 14 | {"SeqLst", "fc.fctl.seq_last", FT_BOOLEAN, 24, TFS(&tfs_fc_fctl_seq_last), |
1415 | 14 | FC_FCTL_SEQ_LAST, "Last Sequence?", HFILL}}, |
1416 | 14 | { &hf_fc_fctl_priority, |
1417 | 14 | {"Pri", "fc.fctl.priority", FT_BOOLEAN, 24, TFS(&tfs_fc_fctl_priority), |
1418 | 14 | FC_FCTL_PRIORITY, "Priority", HFILL}}, |
1419 | 14 | { &hf_fc_fctl_transfer_seq_initiative, |
1420 | 14 | {"TSI", "fc.fctl.transfer_seq_initiative", FT_BOOLEAN, 24, TFS(&tfs_fc_fctl_transfer_seq_initiative), |
1421 | 14 | FC_FCTL_TRANSFER_SEQ_INITIATIVE, "Transfer Seq Initiative", HFILL}}, |
1422 | 14 | { &hf_fc_fctl_rexmitted_seq, |
1423 | 14 | {"RetSeq", "fc.fctl.rexmitted_seq", FT_BOOLEAN, 24, TFS(&tfs_fc_fctl_rexmitted_seq), |
1424 | 14 | FC_FCTL_REXMITTED_SEQ, "Retransmitted Sequence", HFILL}}, |
1425 | 14 | { &hf_fc_fctl_rel_offset, |
1426 | 14 | {"RelOff", "fc.fctl.rel_offset", FT_BOOLEAN, 24, TFS(&tfs_fc_fctl_rel_offset), |
1427 | 14 | FC_FCTL_REL_OFFSET, "rel offset", HFILL}}, |
1428 | 14 | { &hf_fc_fctl_last_data_frame, |
1429 | 14 | {"LDF", "fc.fctl.last_data_frame", FT_UINT24, BASE_HEX, VALS(last_data_frame_vals), |
1430 | 14 | FC_FCTL_LAST_DATA_FRAME_MASK, "Last Data Frame?", HFILL}}, |
1431 | 14 | { &hf_fc_fctl_ack_0_1, |
1432 | 14 | {"A01", "fc.fctl.ack_0_1", FT_UINT24, BASE_HEX, VALS(ack_0_1_vals), |
1433 | 14 | FC_FCTL_ACK_0_1_MASK, "Ack 0/1 value", HFILL}}, |
1434 | 14 | { &hf_fc_fctl_abts_ack, |
1435 | 14 | {"AA", "fc.fctl.abts_ack", FT_UINT24, BASE_HEX, VALS(abts_ack_vals), |
1436 | 14 | FC_FCTL_ABTS_MASK, "ABTS ACK values", HFILL}}, |
1437 | | #if 0 |
1438 | | { &hf_fc_fctl_abts_not_ack, |
1439 | | {"AnA", "fc.fctl.abts_not_ack", FT_UINT24, BASE_HEX, VALS(abts_not_ack_vals), |
1440 | | FC_FCTL_ABTS_MASK, "ABTS not ACK vals", HFILL}}, |
1441 | | #endif |
1442 | 14 | { &hf_fc_exchange_first_frame, |
1443 | 14 | { "Exchange First In", "fc.exchange_first_frame", FT_FRAMENUM, BASE_NONE, NULL, |
1444 | 14 | 0, "The first frame of this exchange is in this frame", HFILL }}, |
1445 | 14 | { &hf_fc_exchange_last_frame, |
1446 | 14 | { "Exchange Last In", "fc.exchange_last_frame", FT_FRAMENUM, BASE_NONE, NULL, |
1447 | 14 | 0, "The last frame of this exchange is in this frame", HFILL }}, |
1448 | 14 | { &hf_fc_time, |
1449 | 14 | { "Time from Exchange First", "fc.time", FT_RELATIVE_TIME, BASE_NONE, NULL, |
1450 | 14 | 0, "Time since the first frame of the Exchange", HFILL }}, |
1451 | 14 | { &hf_fc_relative_offset, |
1452 | 14 | {"Relative Offset", "fc.relative_offset", FT_UINT32, BASE_DEC, NULL, |
1453 | 14 | 0, "Relative offset of data", HFILL}}, |
1454 | 14 | { &hf_fc_vft, |
1455 | 14 | {"VFT Header", "fc.vft", FT_UINT16, BASE_DEC, NULL, |
1456 | 14 | 0, NULL, HFILL}}, |
1457 | 14 | { &hf_fc_vft_rctl, |
1458 | 14 | {"R_CTL", "fc.vft.rctl", FT_UINT8, BASE_HEX, NULL, |
1459 | 14 | 0, NULL, HFILL}}, |
1460 | 14 | { &hf_fc_vft_ver, |
1461 | 14 | {"Version", "fc.vft.ver", FT_UINT8, BASE_DEC, NULL, |
1462 | 14 | 0, "Version of VFT header", HFILL}}, |
1463 | 14 | { &hf_fc_vft_type, |
1464 | 14 | {"Type", "fc.vft.type", FT_UINT8, BASE_DEC, NULL, |
1465 | 14 | 0, "Type of tagged frame", HFILL}}, |
1466 | 14 | { &hf_fc_vft_pri, |
1467 | 14 | {"Priority", "fc.vft.pri", FT_UINT8, BASE_DEC, NULL, |
1468 | 14 | 0, "QoS Priority", HFILL}}, |
1469 | 14 | { &hf_fc_vft_vf_id, |
1470 | 14 | {"VF_ID", "fc.vft.vf_id", FT_UINT16, BASE_DEC, NULL, |
1471 | 14 | 0, "Virtual Fabric ID", HFILL}}, |
1472 | 14 | { &hf_fc_vft_hop_ct, |
1473 | 14 | {"HopCT", "fc.vft.hop_ct", FT_UINT8, BASE_DEC, NULL, |
1474 | 14 | 0, "Hop Count", HFILL}}, |
1475 | 14 | }; |
1476 | | |
1477 | | /* Setup protocol subtree array */ |
1478 | 14 | static int *ett[] = { |
1479 | 14 | &ett_fc, |
1480 | 14 | &ett_fcbls, |
1481 | 14 | &ett_fc_vft, |
1482 | 14 | &ett_fctl |
1483 | 14 | }; |
1484 | | |
1485 | 14 | static ei_register_info ei[] = { |
1486 | 14 | { &ei_fccrc, |
1487 | 14 | { "fc.crc.bad", PI_CHECKSUM, PI_ERROR, "Bad checksum", EXPFILL }}, |
1488 | 14 | { &ei_short_hdr, |
1489 | 14 | { "fc.short_hdr", PI_MALFORMED, PI_ERROR, |
1490 | 14 | "Packet length is shorter than the required header", EXPFILL }}, |
1491 | | #if 0 |
1492 | | { &ei_frag_size, |
1493 | | { "fc.frag_size", PI_MALFORMED, PI_ERROR, |
1494 | | "Invalid fragment size", EXPFILL }} |
1495 | | #endif |
1496 | 14 | }; |
1497 | | |
1498 | 14 | module_t *fc_module; |
1499 | 14 | expert_module_t* expert_fc; |
1500 | | |
1501 | | /* FC SOF */ |
1502 | | |
1503 | 14 | static hf_register_info sof_hf[] = { |
1504 | 14 | { &hf_fcsof, |
1505 | 14 | { "SOF", "fc.sof", FT_UINT32, BASE_HEX, VALS(fc_sof_vals), 0, |
1506 | 14 | NULL, HFILL }}, |
1507 | 14 | { &hf_fceof, |
1508 | 14 | { "EOF", "fc.eof", FT_UINT32, BASE_HEX, VALS(fc_eof_vals), 0, |
1509 | 14 | NULL, HFILL }}, |
1510 | 14 | { &hf_fccrc, |
1511 | 14 | { "CRC", "fc.crc", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }}, |
1512 | 14 | { &hf_fccrc_status, |
1513 | 14 | { "CRC Status", "fc.crc.status", FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0, NULL, HFILL }}, |
1514 | 14 | }; |
1515 | | |
1516 | 14 | static int *sof_ett[] = { |
1517 | 14 | &ett_fcsof, |
1518 | 14 | &ett_fceof, |
1519 | 14 | &ett_fccrc |
1520 | 14 | }; |
1521 | | |
1522 | | |
1523 | | /* Register the protocol name and description */ |
1524 | 14 | proto_fc = proto_register_protocol ("Fibre Channel", "FC", "fc"); |
1525 | 14 | fc_handle = register_dissector ("fc", dissect_fc, proto_fc); |
1526 | 14 | register_dissector ("fc_ifcp", dissect_fc_ifcp, proto_fc); |
1527 | 14 | fc_tap = register_tap("fc"); |
1528 | | |
1529 | | /* Required function calls to register the header fields and subtrees used */ |
1530 | 14 | proto_register_field_array(proto_fc, hf, array_length(hf)); |
1531 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
1532 | 14 | expert_fc = expert_register_protocol(proto_fc); |
1533 | 14 | expert_register_field_array(expert_fc, ei, array_length(ei)); |
1534 | | |
1535 | | /* subdissectors called through this table will find the fchdr structure |
1536 | | * through data parameter of dissector |
1537 | | */ |
1538 | 14 | fcftype_dissector_table = register_dissector_table ("fc.ftype", |
1539 | 14 | "FC Frame Type", |
1540 | 14 | proto_fc, FT_UINT8, BASE_HEX); |
1541 | | |
1542 | | /* Register preferences */ |
1543 | 14 | fc_module = prefs_register_protocol (proto_fc, NULL); |
1544 | 14 | prefs_register_bool_preference (fc_module, |
1545 | 14 | "reassemble", |
1546 | 14 | "Reassemble multi-frame sequences", |
1547 | 14 | "If enabled, reassembly of multi-frame " |
1548 | 14 | "sequences is done", |
1549 | 14 | &fc_reassemble); |
1550 | 14 | prefs_register_uint_preference (fc_module, |
1551 | 14 | "max_frame_size", "Max FC Frame Size", |
1552 | 14 | "This is the size of non-last frames in a " |
1553 | 14 | "multi-frame sequence", 10, |
1554 | 14 | &fc_max_frame_size); |
1555 | | |
1556 | 14 | fcseq_req_hash = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), fcseq_hash, fcseq_equal); |
1557 | | |
1558 | 14 | reassembly_table_register(&fc_reassembly_table, |
1559 | 14 | &addresses_reassembly_table_functions); |
1560 | | |
1561 | | |
1562 | | /* Register FC SOF/EOF */ |
1563 | 14 | proto_fcsof = proto_register_protocol("Fibre Channel Delimiters", "FCSoF", "fcsof"); |
1564 | | |
1565 | 14 | proto_register_field_array(proto_fcsof, sof_hf, array_length(sof_hf)); |
1566 | 14 | proto_register_subtree_array(sof_ett, array_length(sof_ett)); |
1567 | | |
1568 | 14 | fcsof_handle = register_dissector("fcsof", dissect_fcsof, proto_fcsof); |
1569 | | |
1570 | 14 | register_conversation_table(proto_fc, true, fc_conversation_packet, fc_endpoint_packet); |
1571 | 14 | register_srt_table(proto_fc, NULL, 1, fcstat_packet, fcstat_init, NULL); |
1572 | 14 | } |
1573 | | |
1574 | | |
1575 | | /* If this dissector uses sub-dissector registration add a registration routine. |
1576 | | This format is required because a script is used to find these routines and |
1577 | | create the code that calls these routines. |
1578 | | */ |
1579 | | void |
1580 | | proto_reg_handoff_fc (void) |
1581 | 14 | { |
1582 | 14 | dissector_add_uint("wtap_encap", WTAP_ENCAP_FIBRE_CHANNEL_FC2, |
1583 | 14 | create_dissector_handle(dissect_fc_wtap, proto_fc)); |
1584 | | |
1585 | 14 | dissector_add_uint("wtap_encap", WTAP_ENCAP_FIBRE_CHANNEL_FC2_WITH_FRAME_DELIMS, fcsof_handle); |
1586 | 14 | } |
1587 | | |
1588 | | /* |
1589 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
1590 | | * |
1591 | | * Local variables: |
1592 | | * c-basic-offset: 4 |
1593 | | * tab-width: 8 |
1594 | | * indent-tabs-mode: nil |
1595 | | * End: |
1596 | | * |
1597 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
1598 | | * :indentSize=4:tabSize=8:noTabs=true: |
1599 | | */ |