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