/src/wireshark/epan/dissectors/packet-fcoe.c
Line | Count | Source |
1 | | /* |
2 | | * packet-fcoe.c |
3 | | * Routines for FCoE dissection - Fibre Channel over Ethernet |
4 | | * Copyright (c) 2006 Nuova Systems, Inc. (jre@nuovasystems.com) |
5 | | * |
6 | | * Wireshark - Network traffic analyzer |
7 | | * By Gerald Combs <gerald@wireshark.org> |
8 | | * Copyright 1998 Gerald Combs |
9 | | * |
10 | | * Based on packet-fcip.c, Copyright 2001, Dinesh G Dutt (ddutt@cisco.com) |
11 | | * |
12 | | * SPDX-License-Identifier: GPL-2.0-or-later |
13 | | */ |
14 | | |
15 | | /* |
16 | | * For FCoE protocol details, see http://fcoe.com. |
17 | | */ |
18 | | |
19 | | #include "config.h" |
20 | | |
21 | | #include <epan/packet.h> |
22 | | #include <epan/prefs.h> |
23 | | #include <epan/crc32-tvb.h> |
24 | | #include <epan/expert.h> |
25 | | #include "packet-fc.h" |
26 | | |
27 | | void proto_register_fcoe(void); |
28 | | void proto_reg_handoff_fcoe(void); |
29 | | |
30 | 140 | #define FCOE_HEADER_LEN 14 /* header: version, SOF, and padding */ |
31 | 28 | #define FCOE_TRAILER_LEN 8 /* trailer: CRC, EOF, and padding */ |
32 | | |
33 | | typedef enum { |
34 | | FCOE_EOFn = 0x41, |
35 | | FCOE_EOFt = 0x42, |
36 | | FCOE_EOFrt = 0x44, |
37 | | FCOE_EOFdt = 0x46, |
38 | | FCOE_EOFni = 0x49, |
39 | | FCOE_EOFdti = 0x4E, |
40 | | FCOE_EOFrti = 0x4F, |
41 | | FCOE_EOFa = 0x50 |
42 | | } fcoe_eof_t; |
43 | | |
44 | | typedef enum { |
45 | | FCOE_SOFf = 0x28, |
46 | | FCOE_SOFi4 = 0x29, |
47 | | FCOE_SOFi2 = 0x2D, |
48 | | FCOE_SOFi3 = 0x2E, |
49 | | FCOE_SOFn4 = 0x31, |
50 | | FCOE_SOFn2 = 0x35, |
51 | | FCOE_SOFn3 = 0x36, |
52 | | FCOE_SOFc4 = 0x39 |
53 | | } fcoe_sof_t; |
54 | | |
55 | | static const value_string fcoe_eof_vals[] = { |
56 | | {FCOE_EOFn, "EOFn" }, |
57 | | {FCOE_EOFt, "EOFt" }, |
58 | | {FCOE_EOFrt, "EOFrt" }, |
59 | | {FCOE_EOFdt, "EOFdt" }, |
60 | | {FCOE_EOFni, "EOFni" }, |
61 | | {FCOE_EOFdti, "EOFdti" }, |
62 | | {FCOE_EOFrti, "EOFrti" }, |
63 | | {FCOE_EOFa, "EOFa" }, |
64 | | {0, NULL} |
65 | | }; |
66 | | |
67 | | static const value_string fcoe_sof_vals[] = { |
68 | | {FCOE_SOFf, "SOFf" }, |
69 | | {FCOE_SOFi4, "SOFi4" }, |
70 | | {FCOE_SOFi2, "SOFi2" }, |
71 | | {FCOE_SOFi3, "SOFi3" }, |
72 | | {FCOE_SOFn4, "SOFn4" }, |
73 | | {FCOE_SOFn2, "SOFn2" }, |
74 | | {FCOE_SOFn3, "SOFn3" }, |
75 | | {FCOE_SOFc4, "SOFc4" }, |
76 | | {0, NULL} |
77 | | }; |
78 | | |
79 | | static int proto_fcoe; |
80 | | static int hf_fcoe_ver; |
81 | | static int hf_fcoe_len; |
82 | | static int hf_fcoe_sof; |
83 | | static int hf_fcoe_eof; |
84 | | static int hf_fcoe_crc; |
85 | | static int hf_fcoe_crc_status; |
86 | | |
87 | | static int ett_fcoe; |
88 | | |
89 | | static expert_field ei_fcoe_crc; |
90 | | |
91 | | static dissector_handle_t fc_handle; |
92 | | static dissector_handle_t fcoe_handle; |
93 | | |
94 | | |
95 | | /* Looks for the EOF at a given offset. Returns NULL if the EOF is not |
96 | | * present, is not one of the known values, or if the next three bytes, if |
97 | | * present, are not padding. Otherwise returns the entry from the EOF |
98 | | * value_string. Intended for use with the newer T11 version, where the frame |
99 | | * length is not explicitly set (and padding is used). */ |
100 | | static const char * |
101 | | fcoe_get_eof(tvbuff_t *tvb, int eof_offset) |
102 | 29 | { |
103 | 29 | uint8_t eof = 0; |
104 | 29 | const char *eof_str; |
105 | 29 | int padding_remaining; |
106 | | |
107 | 29 | if (!tvb_bytes_exist(tvb, eof_offset, 1)) { |
108 | 4 | return NULL; |
109 | 4 | } |
110 | | |
111 | 25 | padding_remaining = MIN(tvb_captured_length_remaining(tvb, eof_offset+1),3); |
112 | 25 | if (tvb_memeql(tvb, eof_offset+1, (const uint8_t*)"\x00\x00\x00", padding_remaining)) { |
113 | 18 | return NULL; |
114 | 18 | } |
115 | | |
116 | 7 | eof = tvb_get_uint8(tvb, eof_offset); |
117 | 7 | eof_str = try_val_to_str(eof, fcoe_eof_vals); |
118 | 7 | return eof_str; |
119 | 25 | } |
120 | | |
121 | | static int |
122 | | dissect_fcoe(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) |
123 | 110 | { |
124 | 110 | int crc_offset; |
125 | 110 | int eof_offset; |
126 | 110 | int frame_len = 0; |
127 | 110 | int header_len = FCOE_HEADER_LEN; |
128 | 110 | unsigned version; |
129 | 110 | const char *ver; |
130 | 110 | uint16_t len_sof; |
131 | 110 | uint8_t sof = 0; |
132 | 110 | uint8_t eof = 0; |
133 | 110 | const char *eof_str; |
134 | 110 | const char *crc_msg; |
135 | 110 | const char *len_msg; |
136 | 110 | proto_item *ti; |
137 | 110 | proto_tree *fcoe_tree; |
138 | 110 | tvbuff_t *next_tvb; |
139 | 110 | bool crc_exists; |
140 | 110 | uint32_t crc_computed = 0; |
141 | 110 | uint32_t crc = 0; |
142 | 110 | fc_data_t fc_data; |
143 | | |
144 | | /* |
145 | | * For now, handle both the version defined before and after August 2007. |
146 | | * In the newer version, byte 1 is reserved and always zero. In the old |
147 | | * version, it'll never be zero. |
148 | | */ |
149 | 110 | if (tvb_get_uint8(tvb, 1)) { |
150 | 95 | header_len = 2; |
151 | 95 | len_sof = tvb_get_ntohs(tvb, 0); |
152 | 95 | frame_len = (len_sof & 0x3ff0) >> 2; |
153 | 95 | if (len_sof >= 4) { |
154 | | /* This length includes the CRC; subtract it off. |
155 | | * If it's less than 4, that's bogus; we'll warn |
156 | | * about an invalid length below and not try to |
157 | | * dissect the CRC, so we don't have to throw a |
158 | | * ReportedBoundsError here (but don't want to |
159 | | * overflow.) |
160 | | */ |
161 | 93 | len_sof -= 4; |
162 | 93 | } |
163 | 95 | sof = len_sof & 0xf; |
164 | 95 | sof |= (sof < 8) ? 0x30 : 0x20; |
165 | 95 | version = len_sof >> 14; |
166 | 95 | ver = "pre-T11 "; |
167 | 95 | if (version != 0) |
168 | 78 | ver = wmem_strdup_printf(pinfo->pool, ver, "pre-T11 ver %d ", version); |
169 | 95 | eof_offset = header_len + frame_len + 4; |
170 | 95 | eof_str = "none"; |
171 | 95 | if (tvb_bytes_exist(tvb, eof_offset, 1)) { |
172 | 9 | eof = tvb_get_uint8(tvb, eof_offset); |
173 | 9 | eof_str = val_to_str(pinfo->pool, eof, fcoe_eof_vals, "0x%x"); |
174 | 9 | } |
175 | | /* Old format has a length field, so we can help the Ethernet dissector |
176 | | * guess about the FCS; note this format does not pad after the EOF */ |
177 | 95 | set_actual_length(tvb, eof_offset+1); |
178 | 95 | } else { |
179 | 15 | sof = tvb_get_uint8(tvb, FCOE_HEADER_LEN - 1); |
180 | | |
181 | | /* |
182 | | * Only version 0 is defined at this point. |
183 | | * Don't print the version in the short summary if it is zero. |
184 | | */ |
185 | 15 | ver = ""; |
186 | 15 | version = tvb_get_uint8(tvb, 0) >> 4; |
187 | 15 | if (version != 0) |
188 | 9 | ver = wmem_strdup_printf(pinfo->pool, ver, "ver %d ", version); |
189 | | |
190 | 15 | frame_len = tvb_reported_length_remaining(tvb, FCOE_HEADER_LEN); |
191 | 15 | if (frame_len >= FCOE_TRAILER_LEN) { |
192 | | /* The frame is claimed to be long enough for a trailer. |
193 | | * Slice it off. Otherwise (which doesn't make sense), just |
194 | | * ignore it and the eof_offset will be off the edge and the |
195 | | * various tvb_bytes_exist checks will prevent exceptions. |
196 | | */ |
197 | 13 | frame_len -= FCOE_TRAILER_LEN; |
198 | 13 | } |
199 | 15 | eof_offset = header_len + frame_len + 4; |
200 | 15 | if (NULL == (eof_str = fcoe_get_eof(tvb, eof_offset))) { |
201 | | /* We didn't find the EOF, look 4 bytes earlier */ |
202 | 14 | if (NULL != (eof_str = fcoe_get_eof(tvb, eof_offset-4))) { |
203 | | /* Found it, so it seems there's an Ethernet FCS. */ |
204 | 0 | frame_len -= 4; |
205 | 0 | set_actual_length(tvb, eof_offset); |
206 | 0 | eof_offset -= 4; |
207 | 14 | } else { |
208 | 14 | if (tvb_bytes_exist(tvb, eof_offset, 1)) { |
209 | | /* Hmm, we have enough bytes to look for the EOF |
210 | | * but it's an unexpected value. */ |
211 | 12 | eof = tvb_get_uint8(tvb, eof_offset); |
212 | 12 | eof_str = wmem_strdup_printf(pinfo->pool, "0x%x", eof); |
213 | 12 | } else { |
214 | | /* We just didn't capture enough to get the EOF */ |
215 | 2 | eof_str = "none"; |
216 | 2 | } |
217 | 14 | } |
218 | 14 | } |
219 | | |
220 | 15 | } |
221 | 110 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "FCoE"); |
222 | 110 | crc_offset = header_len + frame_len; |
223 | | |
224 | 110 | next_tvb = tvb_new_subset_length(tvb, header_len, frame_len); |
225 | | |
226 | | /* |
227 | | * Check the CRC. |
228 | | */ |
229 | 110 | crc_msg = ""; |
230 | 110 | crc_exists = tvb_bytes_exist(tvb, crc_offset, 4); |
231 | 110 | if (crc_exists) { |
232 | 22 | crc = tvb_get_ntohl(tvb, crc_offset); |
233 | 22 | crc_computed = crc32_802_tvb(next_tvb, frame_len); |
234 | 22 | if (crc != crc_computed) { |
235 | 21 | crc_msg = " [bad FC CRC]"; |
236 | 21 | } |
237 | 22 | } |
238 | 110 | len_msg = ""; |
239 | 110 | if ((frame_len % 4) != 0 || frame_len < 24) { |
240 | 20 | len_msg = " [invalid length]"; |
241 | 20 | } |
242 | | |
243 | 110 | ti = proto_tree_add_protocol_format(tree, proto_fcoe, tvb, 0, |
244 | 110 | header_len, |
245 | 110 | "FCoE %s(%s/%s) %d bytes%s%s", ver, |
246 | 110 | val_to_str(pinfo->pool, sof, fcoe_sof_vals, |
247 | 110 | "0x%x"), |
248 | 110 | eof_str, frame_len, crc_msg, |
249 | 110 | len_msg); |
250 | | |
251 | | /* Dissect the FCoE header */ |
252 | | |
253 | 110 | fcoe_tree = proto_item_add_subtree(ti, ett_fcoe); |
254 | 110 | proto_tree_add_uint(fcoe_tree, hf_fcoe_ver, tvb, 0, 1, version); |
255 | 110 | if (tvb_get_uint8(tvb, 1)) { |
256 | 95 | proto_tree_add_uint(fcoe_tree, hf_fcoe_len, tvb, 0, 2, frame_len); |
257 | 95 | } |
258 | 110 | proto_tree_add_uint(fcoe_tree, hf_fcoe_sof, tvb, |
259 | 110 | header_len - 1, 1, sof); |
260 | | |
261 | | /* |
262 | | * Create the CRC information. |
263 | | */ |
264 | 110 | if (crc_exists) { |
265 | 22 | proto_tree_add_checksum(fcoe_tree, tvb, crc_offset, hf_fcoe_crc, hf_fcoe_crc_status, &ei_fcoe_crc, pinfo, crc_computed, ENC_BIG_ENDIAN, PROTO_CHECKSUM_VERIFY); |
266 | 22 | proto_tree_set_appendix(fcoe_tree, tvb, crc_offset, |
267 | 22 | tvb_captured_length_remaining (tvb, crc_offset)); |
268 | 88 | } else { |
269 | 88 | proto_tree_add_checksum(fcoe_tree, tvb, crc_offset, hf_fcoe_crc, hf_fcoe_crc_status, &ei_fcoe_crc, pinfo, 0, ENC_BIG_ENDIAN, PROTO_CHECKSUM_NOT_PRESENT); |
270 | 88 | } |
271 | | |
272 | | /* |
273 | | * Interpret the EOF. |
274 | | */ |
275 | 110 | if (tvb_bytes_exist(tvb, eof_offset, 1)) { |
276 | 22 | proto_tree_add_item(fcoe_tree, hf_fcoe_eof, tvb, eof_offset, 1, ENC_BIG_ENDIAN); |
277 | 22 | } |
278 | | |
279 | | /* Set the SOF/EOF flags in the packet_info header */ |
280 | 110 | fc_data.sof_eof = 0; |
281 | 110 | if (sof == FCOE_SOFi3 || sof == FCOE_SOFi2 || sof == FCOE_SOFi4) { |
282 | 23 | fc_data.sof_eof = FC_DATA_SOF_FIRST_FRAME; |
283 | 87 | } else if (sof == FCOE_SOFf) { |
284 | 2 | fc_data.sof_eof = FC_DATA_SOF_SOFF; |
285 | 2 | } |
286 | | |
287 | 110 | if (eof != FCOE_EOFn) { |
288 | 108 | fc_data.sof_eof |= FC_DATA_EOF_LAST_FRAME; |
289 | 108 | } else if (eof != FCOE_EOFt) { |
290 | 2 | fc_data.sof_eof |= FC_DATA_EOF_INVALID; |
291 | 2 | } |
292 | | |
293 | | /* Call the FC Dissector if this is carrying an FC frame */ |
294 | 110 | fc_data.ethertype = ETHERTYPE_UNK; |
295 | | |
296 | 110 | if (fc_handle) { |
297 | 110 | call_dissector_with_data(fc_handle, next_tvb, pinfo, tree, &fc_data); |
298 | 110 | } else { |
299 | 0 | call_data_dissector(next_tvb, pinfo, tree); |
300 | 0 | } |
301 | 110 | return tvb_captured_length(tvb); |
302 | 110 | } |
303 | | |
304 | | void |
305 | | proto_register_fcoe(void) |
306 | 14 | { |
307 | 14 | module_t *fcoe_module; |
308 | | |
309 | | /* Setup list of header fields See Section 1.6.1 for details*/ |
310 | 14 | static hf_register_info hf[] = { |
311 | 14 | { &hf_fcoe_sof, |
312 | 14 | {"SOF", "fcoe.sof", FT_UINT8, BASE_HEX, VALS(fcoe_sof_vals), 0, |
313 | 14 | NULL, HFILL}}, |
314 | 14 | { &hf_fcoe_eof, |
315 | 14 | {"EOF", "fcoe.eof", FT_UINT8, BASE_HEX, VALS(fcoe_eof_vals), 0, |
316 | 14 | NULL, HFILL}}, |
317 | 14 | { &hf_fcoe_ver, |
318 | 14 | {"Version", "fcoe.ver", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL}}, |
319 | 14 | { &hf_fcoe_len, |
320 | 14 | {"Frame length", "fcoe.len", FT_UINT32, |
321 | 14 | BASE_DEC, NULL, 0, NULL, HFILL}}, |
322 | 14 | { &hf_fcoe_crc, |
323 | 14 | {"CRC", "fcoe.crc", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL}}, |
324 | 14 | { &hf_fcoe_crc_status, |
325 | 14 | {"CRC Status", "fcoe.crc.status", FT_UINT8, BASE_NONE, VALS(proto_checksum_vals), 0x0, |
326 | 14 | NULL, HFILL }} |
327 | 14 | }; |
328 | 14 | static int *ett[] = { |
329 | 14 | &ett_fcoe, |
330 | 14 | }; |
331 | | |
332 | 14 | static ei_register_info ei[] = { |
333 | 14 | { &ei_fcoe_crc, { "fcoe.crc.bad", PI_CHECKSUM, PI_ERROR, "Bad checksum", EXPFILL }}, |
334 | 14 | }; |
335 | | |
336 | 14 | expert_module_t* expert_fcoe; |
337 | | |
338 | | /* Register the protocol name and description */ |
339 | 14 | proto_fcoe = proto_register_protocol("Fibre Channel over Ethernet", |
340 | 14 | "FCoE", "fcoe"); |
341 | 14 | fcoe_handle = register_dissector("fcoe", dissect_fcoe, proto_fcoe); |
342 | | |
343 | | /* Required function calls to register the header fields and |
344 | | * subtrees used */ |
345 | 14 | proto_register_field_array(proto_fcoe, hf, array_length(hf)); |
346 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
347 | 14 | expert_fcoe = expert_register_protocol(proto_fcoe); |
348 | 14 | expert_register_field_array(expert_fcoe, ei, array_length(ei)); |
349 | | |
350 | 14 | fcoe_module = prefs_register_protocol_obsolete(proto_fcoe); |
351 | | |
352 | 14 | prefs_register_obsolete_preference(fcoe_module, "ethertype"); |
353 | 14 | } |
354 | | |
355 | | void |
356 | | proto_reg_handoff_fcoe(void) |
357 | 14 | { |
358 | 14 | dissector_add_uint("ethertype", ETHERTYPE_FCOE, fcoe_handle); |
359 | 14 | fc_handle = find_dissector_add_dependency("fc", proto_fcoe); |
360 | 14 | } |
361 | | |
362 | | /* |
363 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
364 | | * |
365 | | * Local variables: |
366 | | * c-basic-offset: 4 |
367 | | * tab-width: 8 |
368 | | * indent-tabs-mode: nil |
369 | | * End: |
370 | | * |
371 | | * vi: set shiftwidth=4 tabstop=8 expandtab: |
372 | | * :indentSize=4:tabSize=8:noTabs=true: |
373 | | */ |