/src/wireshark/epan/dissectors/packet-ipmi.c
Line | Count | Source |
1 | | /* packet-ipmi.c |
2 | | * Routines for IPMI dissection |
3 | | * Copyright 2002-2008, Alexey Neyman, Pigeon Point Systems <avn@pigeonpoint.com> |
4 | | * |
5 | | * Wireshark - Network traffic analyzer |
6 | | * By Gerald Combs <gerald@wireshark.org> |
7 | | * Copyright 1998 Gerald Combs |
8 | | * |
9 | | * SPDX-License-Identifier: GPL-2.0-or-later |
10 | | */ |
11 | | |
12 | | #include "config.h" |
13 | 0 | #define WS_LOG_DOMAIN "packet-ipmi" |
14 | | #include <wireshark.h> |
15 | | |
16 | | #include <epan/packet.h> |
17 | | #include <epan/conversation.h> |
18 | | #include <epan/prefs.h> |
19 | | #include <epan/addr_resolv.h> |
20 | | |
21 | | #include "packet-ipmi.h" |
22 | | |
23 | | static dissector_handle_t ipmi_i2c_handle; |
24 | | |
25 | | void proto_register_ipmi(void); |
26 | | void proto_reg_handoff_ipmi(void); |
27 | | |
28 | | /* |
29 | | * See the IPMI specifications at |
30 | | * |
31 | | * http://www.intel.com/design/servers/ipmi/ |
32 | | */ |
33 | | |
34 | | /* Top-level search structure: list of registered handlers for a given netFn */ |
35 | | struct ipmi_netfn_root { |
36 | | ipmi_netfn_t *list; |
37 | | const char *desc; |
38 | | uint32_t siglen; |
39 | | }; |
40 | | |
41 | | enum { |
42 | | MSGFMT_NONE = 0, |
43 | | MSGFMT_IPMB, |
44 | | MSGFMT_LAN, |
45 | | MSGFMT_GUESS |
46 | | }; |
47 | | |
48 | | struct ipmi_parse_typelen { |
49 | | void (*get_len)(unsigned *, unsigned *, tvbuff_t *, unsigned, unsigned, bool); |
50 | | void (*parse)(char *, tvbuff_t *, unsigned, unsigned); |
51 | | const char *desc; |
52 | | }; |
53 | | |
54 | | /* IPMI parsing context */ |
55 | | typedef struct { |
56 | | ipmi_header_t hdr; |
57 | | unsigned hdr_len; |
58 | | unsigned flags; |
59 | | uint8_t cks1; |
60 | | uint8_t cks2; |
61 | | } ipmi_context_t; |
62 | | |
63 | | /* Temporary request-response matching data. */ |
64 | | typedef struct { |
65 | | /* Request header */ |
66 | | ipmi_header_t hdr; |
67 | | /* Frame number where the request resides */ |
68 | | uint32_t frame_num; |
69 | | /* Nest level of the request in the frame */ |
70 | | uint8_t nest_level; |
71 | | } ipmi_request_t; |
72 | | |
73 | | /* List of request-response matching data */ |
74 | | typedef wmem_list_t ipmi_request_list_t; |
75 | | |
76 | 0 | #define NSAVED_DATA 2 |
77 | | |
78 | | /* Per-command data */ |
79 | | typedef struct { |
80 | | uint32_t matched_frame_num; |
81 | | uint32_t saved_data[NSAVED_DATA]; |
82 | | } ipmi_cmd_data_t; |
83 | | |
84 | | /* Per-frame data */ |
85 | | typedef struct { |
86 | | ipmi_cmd_data_t * cmd_data[3]; |
87 | | nstime_t ts; |
88 | | } ipmi_frame_data_t; |
89 | | |
90 | | /* RB tree of frame data */ |
91 | | typedef wmem_tree_t ipmi_frame_tree_t; |
92 | | |
93 | | /* cached dissector data */ |
94 | | typedef struct { |
95 | | /* tree of cached frame data */ |
96 | | ipmi_frame_tree_t * frame_tree; |
97 | | /* list of cached requests */ |
98 | | ipmi_request_list_t * request_list; |
99 | | /* currently dissected frame number */ |
100 | | uint32_t curr_frame_num; |
101 | | /* currently dissected frame */ |
102 | | ipmi_frame_data_t * curr_frame; |
103 | | /* current nesting level */ |
104 | | uint8_t curr_level; |
105 | | /* subsequent nesting level */ |
106 | | uint8_t next_level; |
107 | | /* top level message channel */ |
108 | | uint8_t curr_channel; |
109 | | /* top level message direction */ |
110 | | uint8_t curr_dir; |
111 | | /* pointer to current command */ |
112 | | const ipmi_header_t * curr_hdr; |
113 | | /* current completion code */ |
114 | | uint8_t curr_ccode; |
115 | | } ipmi_packet_data_t; |
116 | | |
117 | | /* Maximum nest level where it worth caching data */ |
118 | 0 | #define MAX_NEST_LEVEL 3 |
119 | | |
120 | | int proto_ipmi; |
121 | | static int proto_ipmb; |
122 | | static int proto_kcs; |
123 | | static int proto_tmode; |
124 | | |
125 | | /* WARNING: Setting this to true might result in the entire dissector being |
126 | | disabled by default or removed completely. */ |
127 | | static bool dissect_bus_commands; |
128 | | static bool fru_langcode_is_english = true; |
129 | | static unsigned response_after_req = 5000; |
130 | | static unsigned response_before_req; |
131 | | static int message_format = MSGFMT_GUESS; |
132 | | static int selected_oem = IPMI_OEM_NONE; |
133 | | |
134 | | static int hf_ipmi_command_data; |
135 | | static int hf_ipmi_session_handle; |
136 | | static int hf_ipmi_header_trg; |
137 | | static int hf_ipmi_header_trg_lun; |
138 | | static int hf_ipmi_header_netfn; |
139 | | static int hf_ipmi_header_crc; |
140 | | static int hf_ipmi_header_src; |
141 | | static int hf_ipmi_header_src_lun; |
142 | | static int hf_ipmi_header_bridged; |
143 | | static int hf_ipmi_header_sequence; |
144 | | static int hf_ipmi_header_command; |
145 | | static int hf_ipmi_header_completion; |
146 | | static int hf_ipmi_header_sig; |
147 | | static int hf_ipmi_data_crc; |
148 | | static int hf_ipmi_response_to; |
149 | | static int hf_ipmi_response_in; |
150 | | static int hf_ipmi_response_time; |
151 | | |
152 | | static int ett_ipmi; |
153 | | static int ett_header; |
154 | | static int ett_header_byte_1; |
155 | | static int ett_header_byte_4; |
156 | | static int ett_data; |
157 | | static int ett_typelen; |
158 | | |
159 | | static expert_field ei_impi_parser_not_implemented; |
160 | | |
161 | | static struct ipmi_netfn_root ipmi_cmd_tab[IPMI_NETFN_MAX]; |
162 | | |
163 | | static ipmi_packet_data_t * |
164 | | get_packet_data(packet_info * pinfo) |
165 | 0 | { |
166 | 0 | ipmi_packet_data_t * data; |
167 | | |
168 | | /* get conversation data */ |
169 | 0 | conversation_t * conv = find_or_create_conversation(pinfo); |
170 | | |
171 | | /* get protocol-specific data */ |
172 | 0 | data = (ipmi_packet_data_t *) |
173 | 0 | conversation_get_proto_data(conv, proto_ipmi); |
174 | |
|
175 | 0 | if (!data) { |
176 | | /* allocate per-packet data */ |
177 | 0 | data = wmem_new0(wmem_file_scope(), ipmi_packet_data_t); |
178 | | |
179 | | /* allocate request list and frame tree */ |
180 | 0 | data->frame_tree = wmem_tree_new(wmem_file_scope()); |
181 | 0 | data->request_list = wmem_list_new(wmem_file_scope()); |
182 | | |
183 | | /* add protocol data */ |
184 | 0 | conversation_add_proto_data(conv, proto_ipmi, data); |
185 | 0 | } |
186 | | |
187 | | /* check if packet has changed */ |
188 | 0 | if (pinfo->num != data->curr_frame_num) { |
189 | 0 | data->curr_level = 0; |
190 | 0 | data->next_level = 0; |
191 | 0 | } |
192 | |
|
193 | 0 | return data; |
194 | 0 | } |
195 | | |
196 | | static ipmi_frame_data_t * |
197 | | get_frame_data(ipmi_packet_data_t * data, uint32_t frame_num) |
198 | 0 | { |
199 | 0 | ipmi_frame_data_t * frame = (ipmi_frame_data_t *) |
200 | 0 | wmem_tree_lookup32(data->frame_tree, frame_num); |
201 | |
|
202 | 0 | if (frame == NULL) { |
203 | 0 | frame = wmem_new0(wmem_file_scope(), ipmi_frame_data_t); |
204 | |
|
205 | 0 | wmem_tree_insert32(data->frame_tree, frame_num, frame); |
206 | 0 | } |
207 | 0 | return frame; |
208 | 0 | } |
209 | | |
210 | | static ipmi_request_t * |
211 | | get_matched_request(ipmi_packet_data_t * data, const ipmi_header_t * rs_hdr, |
212 | | unsigned flags) |
213 | 0 | { |
214 | 0 | wmem_list_frame_t * iter = wmem_list_head(data->request_list); |
215 | 0 | ipmi_header_t rq_hdr; |
216 | | |
217 | | /* reset message context */ |
218 | 0 | rq_hdr.context = 0; |
219 | | |
220 | | /* copy channel */ |
221 | 0 | rq_hdr.channel = data->curr_channel; |
222 | | |
223 | | /* toggle packet direction */ |
224 | 0 | rq_hdr.dir = rs_hdr->dir ^ 1; |
225 | |
|
226 | 0 | rq_hdr.session = rs_hdr->session; |
227 | | |
228 | | /* swap responder address/lun */ |
229 | 0 | rq_hdr.rs_sa = rs_hdr->rq_sa; |
230 | 0 | rq_hdr.rs_lun = rs_hdr->rq_lun; |
231 | | |
232 | | /* remove reply flag */ |
233 | 0 | rq_hdr.netfn = rs_hdr->netfn & ~1; |
234 | | |
235 | | /* swap requester address/lun */ |
236 | 0 | rq_hdr.rq_sa = rs_hdr->rs_sa; |
237 | 0 | rq_hdr.rq_lun = rs_hdr->rs_lun; |
238 | | |
239 | | /* copy sequence */ |
240 | 0 | rq_hdr.rq_seq = rs_hdr->rq_seq; |
241 | | |
242 | | /* copy command */ |
243 | 0 | rq_hdr.cmd = rs_hdr->cmd; |
244 | | |
245 | | /* TODO: copy prefix bytes */ |
246 | |
|
247 | 0 | ws_debug("%d, %d: rq_hdr : {" |
248 | 0 | "\tchannel=%d" |
249 | 0 | "\tdir=%d" |
250 | 0 | "\trs_sa=%x" |
251 | 0 | "\trs_lun=%d" |
252 | 0 | "\tnetfn=%x" |
253 | 0 | "\trq_sa=%x" |
254 | 0 | "\trq_lun=%d" |
255 | 0 | "\trq_seq=%x" |
256 | 0 | "\tcmd=%x }", |
257 | 0 | data->curr_frame_num, data->curr_level, |
258 | 0 | rq_hdr.channel, rq_hdr.dir, rq_hdr.rs_sa, rq_hdr.rs_lun, |
259 | 0 | rq_hdr.netfn, rq_hdr.rq_sa, rq_hdr.rq_lun, rq_hdr.rq_seq, |
260 | 0 | rq_hdr.cmd); |
261 | |
|
262 | 0 | while (iter) { |
263 | 0 | ipmi_request_t * rq = (ipmi_request_t *) wmem_list_frame_data(iter); |
264 | | |
265 | | /* check if in Get Message context */ |
266 | 0 | if (rs_hdr->context == IPMI_E_GETMSG && !(flags & IPMI_D_TRG_SA)) { |
267 | | /* diregard rsSA */ |
268 | 0 | rq_hdr.rq_sa = rq->hdr.rq_sa; |
269 | 0 | } |
270 | | |
271 | | /* compare command headers */ |
272 | 0 | if (!memcmp(&rq_hdr, &rq->hdr, sizeof(rq_hdr))) { |
273 | 0 | return rq; |
274 | 0 | } |
275 | | |
276 | | /* proceed to next request */ |
277 | 0 | iter = wmem_list_frame_next(iter); |
278 | 0 | } |
279 | | |
280 | 0 | return NULL; |
281 | 0 | } |
282 | | |
283 | | static void |
284 | | remove_old_requests(ipmi_packet_data_t * data, const nstime_t * curr_time) |
285 | 0 | { |
286 | 0 | wmem_list_frame_t * iter = wmem_list_head(data->request_list); |
287 | |
|
288 | 0 | while (iter) { |
289 | 0 | ipmi_request_t * rq = (ipmi_request_t *) wmem_list_frame_data(iter); |
290 | 0 | ipmi_frame_data_t * frame = get_frame_data(data, rq->frame_num); |
291 | 0 | nstime_t delta; |
292 | | |
293 | | /* calculate time delta */ |
294 | 0 | nstime_delta(&delta, curr_time, &frame->ts); |
295 | |
|
296 | 0 | if (nstime_to_msec(&delta) > response_after_req) { |
297 | 0 | wmem_list_frame_t * del = iter; |
298 | | |
299 | | /* proceed to next request */ |
300 | 0 | iter = wmem_list_frame_next(iter); |
301 | | |
302 | | /* free request data */ |
303 | 0 | wmem_free(wmem_file_scope(), rq); |
304 | | |
305 | | /* remove list item */ |
306 | 0 | wmem_list_remove_frame(data->request_list, del); |
307 | 0 | } else { |
308 | 0 | break; |
309 | 0 | } |
310 | 0 | } |
311 | 0 | } |
312 | | |
313 | | static void |
314 | | match_request_response(ipmi_packet_data_t * data, const ipmi_header_t * hdr, |
315 | | unsigned flags) |
316 | 0 | { |
317 | | /* get current frame */ |
318 | 0 | ipmi_frame_data_t * rs_frame = data->curr_frame; |
319 | | |
320 | | /* get current command data */ |
321 | 0 | ipmi_cmd_data_t * rs_data = rs_frame->cmd_data[data->curr_level]; |
322 | | |
323 | | /* check if parse response for the first time */ |
324 | 0 | if (!rs_data) { |
325 | 0 | ipmi_request_t * rq; |
326 | | |
327 | | /* allocate command data */ |
328 | 0 | rs_data = wmem_new0(wmem_file_scope(), ipmi_cmd_data_t); |
329 | | |
330 | | /* search for matching request */ |
331 | 0 | rq = get_matched_request(data, hdr, flags); |
332 | | |
333 | | /* check if matching request is found */ |
334 | 0 | if (rq) { |
335 | | /* get request frame data */ |
336 | 0 | ipmi_frame_data_t * rq_frame = |
337 | 0 | get_frame_data(data, rq->frame_num); |
338 | | |
339 | | /* get command data */ |
340 | 0 | ipmi_cmd_data_t * rq_data = rq_frame->cmd_data[rq->nest_level]; |
341 | | |
342 | | /* save matched frame numbers */ |
343 | 0 | rq_data->matched_frame_num = data->curr_frame_num; |
344 | 0 | rs_data->matched_frame_num = rq->frame_num; |
345 | | |
346 | | /* copy saved command data information */ |
347 | 0 | rs_data->saved_data[0] = rq_data->saved_data[0]; |
348 | 0 | rs_data->saved_data[1] = rq_data->saved_data[1]; |
349 | | |
350 | | /* remove request from the list */ |
351 | 0 | wmem_list_remove(data->request_list, rq); |
352 | | |
353 | | /* delete request data */ |
354 | 0 | wmem_free(wmem_file_scope(), rq); |
355 | 0 | } |
356 | | |
357 | | /* save command data pointer in frame */ |
358 | 0 | rs_frame->cmd_data[data->curr_level] = rs_data; |
359 | 0 | } |
360 | 0 | } |
361 | | |
362 | | static void |
363 | | add_request(ipmi_packet_data_t * data, const ipmi_header_t * hdr) |
364 | 0 | { |
365 | | /* get current frame */ |
366 | 0 | ipmi_frame_data_t * rq_frame = data->curr_frame; |
367 | | |
368 | | /* get current command data */ |
369 | 0 | ipmi_cmd_data_t * rq_data = rq_frame->cmd_data[data->curr_level]; |
370 | | |
371 | | /* check if parse response for the first time */ |
372 | 0 | if (!rq_data) { |
373 | 0 | ipmi_request_t * rq; |
374 | | |
375 | | /* allocate command data */ |
376 | 0 | rq_data = wmem_new0(wmem_file_scope(), ipmi_cmd_data_t); |
377 | | |
378 | | /* set command data pointer */ |
379 | 0 | rq_frame->cmd_data[data->curr_level] = rq_data; |
380 | | |
381 | | /* allocate request data */ |
382 | 0 | rq = wmem_new0(wmem_file_scope(), ipmi_request_t); |
383 | | |
384 | | /* copy request header */ |
385 | 0 | memcpy(&rq->hdr, hdr, sizeof(rq->hdr)); |
386 | | |
387 | | /* override context, channel and direction */ |
388 | 0 | rq->hdr.context = 0; |
389 | 0 | rq->hdr.channel = data->curr_channel; |
390 | 0 | rq->hdr.dir = data->curr_dir; |
391 | | |
392 | | /* set request frame number */ |
393 | 0 | rq->frame_num = data->curr_frame_num; |
394 | | |
395 | | /* set command nest level */ |
396 | 0 | rq->nest_level = data->curr_level; |
397 | | |
398 | | /* append request to list */ |
399 | 0 | wmem_list_append(data->request_list, rq); |
400 | |
|
401 | 0 | ws_debug("%d, %d: rq_hdr : {" |
402 | 0 | "\tchannel=%d" |
403 | 0 | "\tdir=%d" |
404 | 0 | "\trs_sa=%x" |
405 | 0 | "\trs_lun=%d" |
406 | 0 | "\tnetfn=%x" |
407 | 0 | "\trq_sa=%x" |
408 | 0 | "\trq_lun=%d" |
409 | 0 | "\trq_seq=%x" |
410 | 0 | "\tcmd=%x }", |
411 | 0 | data->curr_frame_num, data->curr_level, |
412 | 0 | rq->hdr.channel, rq->hdr.dir, rq->hdr.rs_sa, rq->hdr.rs_lun, |
413 | 0 | rq->hdr.netfn, rq->hdr.rq_sa, rq->hdr.rq_lun, rq->hdr.rq_seq, |
414 | 0 | rq->hdr.cmd); |
415 | 0 | } |
416 | 0 | } |
417 | | |
418 | | static void |
419 | | add_command_info(packet_info *pinfo, const ipmi_cmd_t * cmd, |
420 | | bool resp, uint8_t cc_val, const char * cc_str, bool broadcast) |
421 | 0 | { |
422 | 0 | if (resp) { |
423 | 0 | col_add_fstr(pinfo->cinfo, COL_INFO, "Rsp, %s, %s (%02xh)", |
424 | 0 | cmd->desc, cc_str, cc_val); |
425 | 0 | } else { |
426 | 0 | col_add_fstr(pinfo->cinfo, COL_INFO, "Req, %s%s", |
427 | 0 | broadcast ? "Broadcast " : "", cmd->desc); |
428 | 0 | } |
429 | 0 | } |
430 | | |
431 | | static int |
432 | | dissect_ipmi_cmd(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, |
433 | | int hf_parent_item, int ett_tree, const ipmi_context_t * ctx) |
434 | 5 | { |
435 | 5 | ipmi_packet_data_t * data; |
436 | 5 | ipmi_netfn_t * cmd_list; |
437 | 5 | const ipmi_cmd_t * cmd; |
438 | 5 | proto_item * ti; |
439 | 5 | proto_tree * cmd_tree = NULL, * tmp_tree; |
440 | 5 | uint8_t prev_level, cc_val; |
441 | 5 | unsigned offset, siglen, is_resp; |
442 | 5 | const char * cc_str, * netfn_str; |
443 | | |
444 | 5 | if (!dissect_bus_commands) { |
445 | 5 | ti = proto_tree_add_item(tree, hf_parent_item, tvb, 0, -1, ENC_NA); |
446 | 5 | cmd_tree = proto_item_add_subtree(ti, ett_tree); |
447 | 5 | proto_tree_add_item(cmd_tree, hf_ipmi_command_data, tvb, 0, -1, ENC_NA); |
448 | 5 | return 0; |
449 | 5 | } |
450 | | |
451 | | /* get packet data */ |
452 | 0 | data = get_packet_data(pinfo); |
453 | 0 | if (!data) { |
454 | 0 | return 0; |
455 | 0 | } |
456 | | |
457 | | /* get prefix length */ |
458 | 0 | siglen = ipmi_getsiglen(ctx->hdr.netfn); |
459 | | |
460 | | /* get response flag */ |
461 | 0 | is_resp = ctx->hdr.netfn & 1; |
462 | | |
463 | | /* check message length */ |
464 | 0 | if (tvb_captured_length(tvb) < ctx->hdr_len + siglen + is_resp |
465 | 0 | + !(ctx->flags & IPMI_D_NO_CKS)) { |
466 | | /* don bother with anything */ |
467 | 0 | return call_data_dissector(tvb, pinfo, tree); |
468 | 0 | } |
469 | | |
470 | | /* save nest level */ |
471 | 0 | prev_level = data->curr_level; |
472 | | |
473 | | /* assign next nest level */ |
474 | 0 | data->curr_level = data->next_level; |
475 | | |
476 | | /* increment next nest level */ |
477 | 0 | data->next_level++; |
478 | | |
479 | | /* check for the first invocation */ |
480 | 0 | if (!data->curr_level) { |
481 | | /* get current frame data */ |
482 | 0 | data->curr_frame = get_frame_data(data, pinfo->num); |
483 | 0 | data->curr_frame_num = pinfo->num; |
484 | | |
485 | | /* copy frame timestamp */ |
486 | 0 | memcpy(&data->curr_frame->ts, &pinfo->abs_ts, sizeof(nstime_t)); |
487 | | |
488 | | /* cache channel and direction */ |
489 | 0 | data->curr_channel = ctx->hdr.channel; |
490 | 0 | data->curr_dir = ctx->hdr.dir; |
491 | | |
492 | | /* remove requests which are too old */ |
493 | 0 | remove_old_requests(data, &pinfo->abs_ts); |
494 | 0 | } |
495 | |
|
496 | 0 | if (data->curr_level < MAX_NEST_LEVEL) { |
497 | 0 | if (ctx->hdr.netfn & 1) { |
498 | | /* perform request/response matching */ |
499 | 0 | match_request_response(data, &ctx->hdr, ctx->flags); |
500 | 0 | } else { |
501 | | /* add request to the list for later matching */ |
502 | 0 | add_request(data, &ctx->hdr); |
503 | 0 | } |
504 | 0 | } |
505 | | |
506 | | /* get command list by network function code */ |
507 | 0 | cmd_list = ipmi_getnetfn(ctx->hdr.netfn, |
508 | 0 | tvb_get_ptr(tvb, ctx->hdr_len + is_resp, siglen)); |
509 | | |
510 | | /* get command descriptor */ |
511 | 0 | cmd = ipmi_getcmd(cmd_list, ctx->hdr.cmd); |
512 | | |
513 | | /* check if response */ |
514 | 0 | if (is_resp) { |
515 | | /* get completion code */ |
516 | 0 | cc_val = tvb_get_uint8(tvb, ctx->hdr_len); |
517 | | |
518 | | /* get completion code desc */ |
519 | 0 | cc_str = ipmi_get_completion_code(cc_val, cmd); |
520 | 0 | } else { |
521 | 0 | cc_val = 0; |
522 | 0 | cc_str = NULL; |
523 | 0 | } |
524 | | |
525 | | /* check if not inside a message */ |
526 | 0 | if (!data->curr_level) { |
527 | | /* add packet info */ |
528 | 0 | add_command_info(pinfo, cmd, is_resp, cc_val, cc_str, |
529 | 0 | ctx->flags & IPMI_D_BROADCAST ? true : false); |
530 | 0 | } |
531 | |
|
532 | 0 | if (tree) { |
533 | | /* add parent node */ |
534 | 0 | if (!data->curr_level) { |
535 | 0 | ti = proto_tree_add_item(tree, hf_parent_item, tvb, 0, -1, ENC_NA); |
536 | 0 | cmd_tree = proto_item_add_subtree(ti, ett_tree); |
537 | 0 | } else { |
538 | 0 | char str[ITEM_LABEL_LENGTH]; |
539 | |
|
540 | 0 | if (is_resp) { |
541 | 0 | snprintf(str, ITEM_LABEL_LENGTH, "Rsp, %s, %s", |
542 | 0 | cmd->desc, cc_str); |
543 | 0 | } else { |
544 | 0 | snprintf(str, ITEM_LABEL_LENGTH, "Req, %s", cmd->desc); |
545 | 0 | } |
546 | 0 | if (proto_registrar_get_ftype(hf_parent_item) == FT_STRING) { |
547 | 0 | ti = proto_tree_add_string(tree, hf_parent_item, tvb, 0, -1, str); |
548 | 0 | cmd_tree = proto_item_add_subtree(ti, ett_tree); |
549 | 0 | } |
550 | 0 | else |
551 | 0 | cmd_tree = proto_tree_add_subtree(tree, tvb, 0, -1, ett_tree, NULL, str); |
552 | 0 | } |
553 | |
|
554 | 0 | if (data->curr_level < MAX_NEST_LEVEL) { |
555 | | /* check if response */ |
556 | 0 | if (ctx->hdr.netfn & 1) { |
557 | | /* get current command data */ |
558 | 0 | ipmi_cmd_data_t * rs_data = |
559 | 0 | data->curr_frame->cmd_data[data->curr_level]; |
560 | |
|
561 | 0 | if (rs_data->matched_frame_num) { |
562 | 0 | nstime_t ns; |
563 | | |
564 | | /* add "Request to:" field */ |
565 | 0 | ti = proto_tree_add_uint(cmd_tree, hf_ipmi_response_to, |
566 | 0 | tvb, 0, 0, rs_data->matched_frame_num); |
567 | | |
568 | | /* mark field as a generated one */ |
569 | 0 | proto_item_set_generated(ti); |
570 | | |
571 | | /* calculate delta time */ |
572 | 0 | nstime_delta(&ns, &pinfo->abs_ts, |
573 | 0 | &get_frame_data(data, |
574 | 0 | rs_data->matched_frame_num)->ts); |
575 | | |
576 | | /* add "Response time" field */ |
577 | 0 | ti = proto_tree_add_time(cmd_tree, hf_ipmi_response_time, |
578 | 0 | tvb, 0, 0, &ns); |
579 | | |
580 | | /* mark field as a generated one */ |
581 | 0 | proto_item_set_generated(ti); |
582 | 0 | } |
583 | 0 | } else { |
584 | | /* get current command data */ |
585 | 0 | ipmi_cmd_data_t * rq_data = |
586 | 0 | data->curr_frame->cmd_data[data->curr_level]; |
587 | |
|
588 | 0 | if (rq_data->matched_frame_num) { |
589 | | /* add "Response in:" field */ |
590 | 0 | ti = proto_tree_add_uint(cmd_tree, hf_ipmi_response_in, |
591 | 0 | tvb, 0, 0, rq_data->matched_frame_num); |
592 | | |
593 | | /* mark field as a generated one */ |
594 | 0 | proto_item_set_generated(ti); |
595 | 0 | } |
596 | 0 | } |
597 | 0 | } |
598 | | |
599 | | /* set starting offset */ |
600 | 0 | offset = 0; |
601 | | |
602 | | /* check if message is broadcast */ |
603 | 0 | if (ctx->flags & IPMI_D_BROADCAST) { |
604 | | /* skip first byte */ |
605 | 0 | offset++; |
606 | 0 | } |
607 | | |
608 | | /* check if session handle is specified */ |
609 | 0 | if (ctx->flags & IPMI_D_SESSION_HANDLE) { |
610 | | /* add session handle field */ |
611 | 0 | proto_tree_add_item(cmd_tree, hf_ipmi_session_handle, |
612 | 0 | tvb, offset++, 1, ENC_LITTLE_ENDIAN); |
613 | 0 | } |
614 | | |
615 | | /* check if responder address is specified */ |
616 | 0 | if (ctx->flags & IPMI_D_TRG_SA) { |
617 | | /* add response address field */ |
618 | 0 | proto_tree_add_item(cmd_tree, hf_ipmi_header_trg, tvb, |
619 | 0 | offset++, 1, ENC_LITTLE_ENDIAN); |
620 | 0 | } |
621 | | |
622 | | /* get NetFn string */ |
623 | 0 | netfn_str = ipmi_getnetfnname(pinfo->pool, ctx->hdr.netfn, cmd_list); |
624 | | |
625 | | /* Network function + target LUN */ |
626 | 0 | tmp_tree = proto_tree_add_subtree_format(cmd_tree, tvb, offset, 1, |
627 | 0 | ett_header_byte_1, NULL, "Target LUN: 0x%02x, NetFN: %s %s (0x%02x)", |
628 | 0 | ctx->hdr.rs_lun, netfn_str, |
629 | 0 | is_resp ? "Response" : "Request", ctx->hdr.netfn); |
630 | | |
631 | | /* add Net Fn */ |
632 | 0 | proto_tree_add_uint_format(tmp_tree, hf_ipmi_header_netfn, tvb, |
633 | 0 | offset, 1, ctx->hdr.netfn << 2, |
634 | 0 | "NetFn: %s %s (0x%02x)", netfn_str, |
635 | 0 | is_resp ? "Response" : "Request", ctx->hdr.netfn); |
636 | |
|
637 | 0 | proto_tree_add_item(tmp_tree, hf_ipmi_header_trg_lun, tvb, |
638 | 0 | offset++, 1, ENC_LITTLE_ENDIAN); |
639 | | |
640 | | /* check if cks1 is specified */ |
641 | 0 | if (!(ctx->flags & IPMI_D_NO_CKS)) { |
642 | 0 | uint8_t cks = tvb_get_uint8(tvb, offset); |
643 | | |
644 | | /* Header checksum */ |
645 | 0 | if (ctx->cks1) { |
646 | 0 | uint8_t correct = cks - ctx->cks1; |
647 | |
|
648 | 0 | proto_tree_add_uint_format_value(cmd_tree, hf_ipmi_header_crc, |
649 | 0 | tvb, offset++, 1, cks, |
650 | 0 | "0x%02x (incorrect, expected 0x%02x)", cks, correct); |
651 | 0 | } else { |
652 | 0 | proto_tree_add_uint_format_value(cmd_tree, hf_ipmi_header_crc, |
653 | 0 | tvb, offset++, 1, cks, |
654 | 0 | "0x%02x (correct)", cks); |
655 | 0 | } |
656 | 0 | } |
657 | | |
658 | | /* check if request address is specified */ |
659 | 0 | if (!(ctx->flags & IPMI_D_NO_RQ_SA)) { |
660 | | /* add request address field */ |
661 | 0 | proto_tree_add_item(cmd_tree, hf_ipmi_header_src, tvb, |
662 | 0 | offset++, 1, ENC_LITTLE_ENDIAN); |
663 | 0 | } |
664 | | |
665 | | /* check if request sequence is specified */ |
666 | 0 | if (!(ctx->flags & IPMI_D_NO_SEQ)) { |
667 | | /* Sequence number + source LUN */ |
668 | 0 | tmp_tree = proto_tree_add_subtree_format(cmd_tree, tvb, offset, 1, |
669 | 0 | ett_header_byte_4, NULL, "%s: 0x%02x, SeqNo: 0x%02x", |
670 | 0 | (ctx->flags & IPMI_D_TMODE) ? "Bridged" : "Source LUN", |
671 | 0 | ctx->hdr.rq_lun, ctx->hdr.rq_seq); |
672 | |
|
673 | 0 | if (ctx->flags & IPMI_D_TMODE) { |
674 | 0 | proto_tree_add_item(tmp_tree, hf_ipmi_header_bridged, |
675 | 0 | tvb, offset, 1, ENC_LITTLE_ENDIAN); |
676 | 0 | } else { |
677 | 0 | proto_tree_add_item(tmp_tree, hf_ipmi_header_src_lun, |
678 | 0 | tvb, offset, 1, ENC_LITTLE_ENDIAN); |
679 | 0 | } |
680 | | |
681 | | /* print seq no */ |
682 | 0 | proto_tree_add_item(tmp_tree, hf_ipmi_header_sequence, tvb, |
683 | 0 | offset++, 1, ENC_LITTLE_ENDIAN); |
684 | 0 | } |
685 | | |
686 | | /* command code */ |
687 | 0 | proto_tree_add_uint_format_value(cmd_tree, hf_ipmi_header_command, |
688 | 0 | tvb, offset++, 1, ctx->hdr.cmd, "%s (0x%02x)", |
689 | 0 | cmd->desc, ctx->hdr.cmd); |
690 | |
|
691 | 0 | if (is_resp) { |
692 | | /* completion code */ |
693 | 0 | proto_tree_add_uint_format_value(cmd_tree, |
694 | 0 | hf_ipmi_header_completion, tvb, offset++, 1, |
695 | 0 | cc_val, "%s (0x%02x)", cc_str, cc_val); |
696 | 0 | } |
697 | |
|
698 | 0 | if (siglen) { |
699 | | /* command prefix (if present) */ |
700 | 0 | ti = proto_tree_add_item(cmd_tree, hf_ipmi_header_sig, tvb, |
701 | 0 | offset, siglen, ENC_NA); |
702 | 0 | proto_item_append_text(ti, " (%s)", netfn_str); |
703 | 0 | } |
704 | 0 | } |
705 | |
|
706 | 0 | if (tree || (cmd->flags & CMD_CALLRQ)) { |
707 | | /* calculate message data length */ |
708 | 0 | unsigned data_len = tvb_captured_length(tvb) |
709 | 0 | - ctx->hdr_len |
710 | 0 | - siglen |
711 | 0 | - (is_resp ? 1 : 0) |
712 | 0 | - !(ctx->flags & IPMI_D_NO_CKS); |
713 | | |
714 | | /* create data subset */ |
715 | 0 | tvbuff_t * data_tvb = tvb_new_subset_length(tvb, |
716 | 0 | ctx->hdr_len + siglen + (is_resp ? 1 : 0), data_len); |
717 | | |
718 | | /* Select sub-handler */ |
719 | 0 | ipmi_cmd_handler_t hnd = is_resp ? cmd->parse_resp : cmd->parse_req; |
720 | |
|
721 | 0 | if (hnd && tvb_captured_length(data_tvb)) { |
722 | | /* create data field */ |
723 | 0 | tmp_tree = proto_tree_add_subtree(cmd_tree, data_tvb, 0, -1, ett_data, NULL, "Data"); |
724 | | |
725 | | /* save current command */ |
726 | 0 | data->curr_hdr = &ctx->hdr; |
727 | | |
728 | | /* save current completion code */ |
729 | 0 | data->curr_ccode = cc_val; |
730 | | |
731 | | /* call command parser */ |
732 | 0 | hnd(data_tvb, pinfo, tmp_tree); |
733 | 0 | } |
734 | 0 | } |
735 | | |
736 | | /* check if cks2 is specified */ |
737 | 0 | if (tree && !(ctx->flags & IPMI_D_NO_CKS)) { |
738 | 0 | uint8_t cks; |
739 | | |
740 | | /* get cks2 offset */ |
741 | 0 | offset = tvb_captured_length(tvb) - 1; |
742 | | |
743 | | /* get cks2 */ |
744 | 0 | cks = tvb_get_uint8(tvb, offset); |
745 | | |
746 | | /* Header checksum */ |
747 | 0 | if (ctx->cks2) { |
748 | 0 | uint8_t correct = cks - ctx->cks2; |
749 | |
|
750 | 0 | proto_tree_add_uint_format_value(cmd_tree, hf_ipmi_data_crc, |
751 | 0 | tvb, offset, 1, cks, |
752 | 0 | "0x%02x (incorrect, expected 0x%02x)", cks, correct); |
753 | 0 | } else { |
754 | 0 | proto_tree_add_uint_format_value(cmd_tree, hf_ipmi_data_crc, |
755 | 0 | tvb, offset, 1, cks, |
756 | 0 | "0x%02x (correct)", cks); |
757 | 0 | } |
758 | 0 | } |
759 | | |
760 | | /* decrement next nest level */ |
761 | 0 | data->next_level = data->curr_level; |
762 | | |
763 | | /* restore previous nest level */ |
764 | 0 | data->curr_level = prev_level; |
765 | |
|
766 | 0 | return tvb_captured_length(tvb); |
767 | 0 | } |
768 | | |
769 | | /* Get currently parsed message header */ |
770 | | const ipmi_header_t * ipmi_get_hdr(packet_info * pinfo) |
771 | 0 | { |
772 | 0 | ipmi_packet_data_t * data = get_packet_data(pinfo); |
773 | 0 | return data->curr_hdr; |
774 | 0 | } |
775 | | |
776 | | /* Get completion code for currently parsed message */ |
777 | | uint8_t ipmi_get_ccode(packet_info * pinfo) |
778 | 0 | { |
779 | 0 | ipmi_packet_data_t * data = get_packet_data(pinfo); |
780 | 0 | return data->curr_ccode; |
781 | 0 | } |
782 | | |
783 | | /* Save request data for later use in response */ |
784 | | void ipmi_set_data(packet_info *pinfo, unsigned idx, uint32_t value) |
785 | 0 | { |
786 | 0 | ipmi_packet_data_t * data = get_packet_data(pinfo); |
787 | | |
788 | | /* check bounds */ |
789 | 0 | if (data->curr_level >= MAX_NEST_LEVEL || idx >= NSAVED_DATA || !data->curr_frame ) { |
790 | 0 | return; |
791 | 0 | } |
792 | | |
793 | | /* save data */ |
794 | 0 | data->curr_frame->cmd_data[data->curr_level]->saved_data[idx] = value; |
795 | 0 | } |
796 | | |
797 | | /* Get saved request data */ |
798 | | bool ipmi_get_data(packet_info *pinfo, unsigned idx, uint32_t * value) |
799 | 0 | { |
800 | 0 | ipmi_packet_data_t * data = get_packet_data(pinfo); |
801 | | |
802 | | /* check bounds */ |
803 | 0 | if (data->curr_level >= MAX_NEST_LEVEL || idx >= NSAVED_DATA || !data->curr_frame ) { |
804 | 0 | return false; |
805 | 0 | } |
806 | | |
807 | | /* get data */ |
808 | 0 | *value = data->curr_frame->cmd_data[data->curr_level]->saved_data[idx]; |
809 | 0 | return true; |
810 | 0 | } |
811 | | |
812 | | /* ---------------------------------------------------------------- |
813 | | Support for Type/Length fields parsing. |
814 | | ---------------------------------------------------------------- */ |
815 | | |
816 | | static void |
817 | | get_len_binary(unsigned *clen, unsigned *blen, tvbuff_t *tvb _U_, unsigned offs _U_, |
818 | | unsigned len, bool len_is_bytes _U_) |
819 | 0 | { |
820 | 0 | *clen = len * 3; |
821 | 0 | *blen = len; |
822 | 0 | } |
823 | | |
824 | | static void |
825 | | parse_binary(char *p, tvbuff_t *tvb, unsigned offs, unsigned len) |
826 | 0 | { |
827 | 0 | static const char hex[] = "0123456789ABCDEF"; |
828 | 0 | uint8_t v; |
829 | 0 | unsigned i; |
830 | |
|
831 | 0 | for (i = 0; i < len / 3; i++) { |
832 | 0 | v = tvb_get_uint8(tvb, offs + i); |
833 | 0 | *p++ = hex[v >> 4]; |
834 | 0 | *p++ = hex[v & 0xf]; |
835 | 0 | *p++ = ' '; |
836 | 0 | } |
837 | |
|
838 | 0 | if (i) { |
839 | 0 | *--p = '\0'; |
840 | 0 | } |
841 | 0 | } |
842 | | |
843 | | static const struct ipmi_parse_typelen ptl_binary = { |
844 | | get_len_binary, parse_binary, "Binary" |
845 | | }; |
846 | | |
847 | | static void |
848 | | get_len_bcdplus(unsigned *clen, unsigned *blen, tvbuff_t *tvb _U_, unsigned offs _U_, |
849 | | unsigned len, bool len_is_bytes) |
850 | 0 | { |
851 | 0 | if (len_is_bytes) { |
852 | 0 | *clen = len * 2; |
853 | 0 | *blen = len; |
854 | 0 | } else { |
855 | 0 | *blen = (len + 1) / 2; |
856 | 0 | *clen = len; |
857 | 0 | } |
858 | 0 | } |
859 | | |
860 | | static void |
861 | | parse_bcdplus(char *p, tvbuff_t *tvb, unsigned offs, unsigned len) |
862 | 0 | { |
863 | 0 | static const char bcd[] = "0123456789 -.:,_"; |
864 | 0 | unsigned i, msk = 0xf0, shft = 4; |
865 | 0 | uint8_t v; |
866 | |
|
867 | 0 | for (i = 0; i < len; i++) { |
868 | 0 | v = (tvb_get_uint8(tvb, offs + i / 2) & msk) >> shft; |
869 | 0 | *p++ = bcd[v]; |
870 | 0 | msk ^= 0xff; |
871 | 0 | shft = 4 - shft; |
872 | 0 | } |
873 | 0 | } |
874 | | |
875 | | static const struct ipmi_parse_typelen ptl_bcdplus = { |
876 | | get_len_bcdplus, parse_bcdplus, "BCD+" |
877 | | }; |
878 | | |
879 | | static void |
880 | | get_len_6bit_ascii(unsigned *clen, unsigned *blen, tvbuff_t *tvb _U_, unsigned offs _U_, |
881 | | unsigned len, bool len_is_bytes) |
882 | 0 | { |
883 | 0 | if (len_is_bytes) { |
884 | 0 | *clen = len * 4 / 3; |
885 | 0 | *blen = len; |
886 | 0 | } else { |
887 | 0 | *blen = (len * 3 + 3) / 4; |
888 | 0 | *clen = len; |
889 | 0 | } |
890 | 0 | } |
891 | | |
892 | | static void |
893 | | parse_6bit_ascii(char *p, tvbuff_t *tvb, unsigned offs, unsigned len) |
894 | 0 | { |
895 | 0 | uint32_t v; |
896 | 0 | unsigned i; |
897 | | |
898 | | /* First, handle "full" triplets of bytes, 4 characters each */ |
899 | 0 | for (i = 0; i < len / 4; i++) { |
900 | 0 | v = tvb_get_letoh24(tvb, offs + i * 3); |
901 | 0 | p[0] = ' ' + (v & 0x3f); |
902 | 0 | p[1] = ' ' + ((v >> 6) & 0x3f); |
903 | 0 | p[2] = ' ' + ((v >> 12) & 0x3f); |
904 | 0 | p[3] = ' ' + ((v >> 18) & 0x3f); |
905 | 0 | p += 4; |
906 | 0 | } |
907 | | |
908 | | /* Do we have any characters left? */ |
909 | 0 | offs += len / 4; |
910 | 0 | len &= 0x3; |
911 | 0 | switch (len) { |
912 | 0 | case 3: |
913 | 0 | v = (tvb_get_uint8(tvb, offs + 2) << 4) | (tvb_get_uint8(tvb, offs + 1) >> 4); |
914 | 0 | p[2] = ' ' + (v & 0x3f); |
915 | | /* Fall thru */ |
916 | 0 | case 2: |
917 | 0 | v = (tvb_get_uint8(tvb, offs + 1) << 2) | (tvb_get_uint8(tvb, offs) >> 6); |
918 | 0 | p[1] = ' ' + (v & 0x3f); |
919 | | /* Fall thru */ |
920 | 0 | case 1: |
921 | 0 | v = tvb_get_uint8(tvb, offs) & 0x3f; |
922 | 0 | p[0] = ' ' + (v & 0x3f); |
923 | 0 | } |
924 | 0 | } |
925 | | |
926 | | static const struct ipmi_parse_typelen ptl_6bit_ascii = { |
927 | | get_len_6bit_ascii, parse_6bit_ascii, "6-bit ASCII" |
928 | | }; |
929 | | |
930 | | static void |
931 | | get_len_8bit_ascii(unsigned *clen, unsigned *blen, tvbuff_t *tvb, unsigned offs, |
932 | | unsigned len, bool len_is_bytes _U_) |
933 | 0 | { |
934 | 0 | unsigned i; |
935 | 0 | uint8_t ch; |
936 | |
|
937 | 0 | *blen = len; /* One byte is one character */ |
938 | 0 | *clen = 0; |
939 | 0 | for (i = 0; i < len; i++) { |
940 | 0 | ch = tvb_get_uint8(tvb, offs + i); |
941 | 0 | *clen += (ch >= 0x20 && ch <= 0x7f) ? 1 : 4; |
942 | 0 | } |
943 | 0 | } |
944 | | |
945 | | static void |
946 | | parse_8bit_ascii(char *p, tvbuff_t *tvb, unsigned offs, unsigned len) |
947 | 0 | { |
948 | 0 | uint8_t ch; |
949 | 0 | char *pmax; |
950 | |
|
951 | 0 | pmax = p + len; |
952 | 0 | while (p < pmax) { |
953 | 0 | ch = tvb_get_uint8(tvb, offs++); |
954 | 0 | if (ch >= 0x20 && ch <= 0x7f) { |
955 | 0 | *p++ = ch; |
956 | 0 | } else { |
957 | 0 | snprintf(p, 5, "\\x%02x", ch); |
958 | 0 | p += 4; |
959 | 0 | } |
960 | 0 | } |
961 | 0 | } |
962 | | |
963 | | static const struct ipmi_parse_typelen ptl_8bit_ascii = { |
964 | | get_len_8bit_ascii, parse_8bit_ascii, "ASCII+Latin1" |
965 | | }; |
966 | | |
967 | | static void |
968 | | get_len_unicode(unsigned *clen, unsigned *blen, tvbuff_t *tvb _U_, unsigned offs _U_, |
969 | | unsigned len _U_, bool len_is_bytes) |
970 | 0 | { |
971 | 0 | if (len_is_bytes) { |
972 | 0 | *clen = len * 3; /* Each 2 bytes result in 6 chars printed: \Uxxxx */ |
973 | 0 | *blen = len; |
974 | 0 | } else { |
975 | 0 | *clen = len * 6; |
976 | 0 | *blen = len * 2; |
977 | 0 | } |
978 | 0 | } |
979 | | |
980 | | static void |
981 | | parse_unicode(char *p, tvbuff_t *tvb, unsigned offs, unsigned len) |
982 | 0 | { |
983 | 0 | char *pmax = p + len; |
984 | 0 | uint8_t ch0, ch1; |
985 | |
|
986 | 0 | while (p < pmax) { |
987 | 0 | ch0 = tvb_get_uint8(tvb, offs++); |
988 | 0 | ch1 = tvb_get_uint8(tvb, offs++); |
989 | 0 | snprintf(p, 7, "\\U%02x%02x", ch0, ch1); |
990 | 0 | p += 6; |
991 | 0 | } |
992 | 0 | } |
993 | | |
994 | | static const struct ipmi_parse_typelen ptl_unicode = { |
995 | | get_len_unicode, parse_unicode, "Unicode" |
996 | | }; |
997 | | |
998 | | void |
999 | | ipmi_add_typelen(packet_info *pinfo, proto_tree *tree, int hf_string, int hf_type, int hf_length, tvbuff_t *tvb, |
1000 | | unsigned offs, bool is_fru) |
1001 | 0 | { |
1002 | 0 | static const struct ipmi_parse_typelen * const fru_eng[4] = { |
1003 | 0 | &ptl_binary, &ptl_bcdplus, &ptl_6bit_ascii, &ptl_8bit_ascii |
1004 | 0 | }; |
1005 | 0 | static const struct ipmi_parse_typelen * const fru_noneng[4] = { |
1006 | 0 | &ptl_binary, &ptl_bcdplus, &ptl_6bit_ascii, &ptl_unicode |
1007 | 0 | }; |
1008 | 0 | static const struct ipmi_parse_typelen * const ipmi[4] = { |
1009 | 0 | &ptl_unicode, &ptl_bcdplus, &ptl_6bit_ascii, &ptl_8bit_ascii |
1010 | 0 | }; |
1011 | 0 | const struct ipmi_parse_typelen *ptr; |
1012 | 0 | proto_tree *s_tree; |
1013 | 0 | unsigned type, msk, clen, blen, len; |
1014 | 0 | const char *unit; |
1015 | 0 | char *str; |
1016 | 0 | uint8_t typelen; |
1017 | |
|
1018 | 0 | typelen = tvb_get_uint8(tvb, offs); |
1019 | 0 | type = typelen >> 6; |
1020 | 0 | if (is_fru) { |
1021 | 0 | msk = 0x3f; |
1022 | 0 | ptr = (fru_langcode_is_english ? fru_eng : fru_noneng)[type]; |
1023 | 0 | unit = "bytes"; |
1024 | 0 | } else { |
1025 | 0 | msk = 0x1f; |
1026 | 0 | ptr = ipmi[type]; |
1027 | 0 | unit = "characters"; |
1028 | 0 | } |
1029 | |
|
1030 | 0 | len = typelen & msk; |
1031 | 0 | ptr->get_len(&clen, &blen, tvb, offs + 1, len, is_fru); |
1032 | |
|
1033 | 0 | str = (char *)wmem_alloc(pinfo->pool, clen + 1); |
1034 | 0 | ptr->parse(str, tvb, offs + 1, clen); |
1035 | 0 | str[clen] = '\0'; |
1036 | |
|
1037 | 0 | s_tree = proto_tree_add_subtree_format(tree, tvb, offs, 1, ett_typelen, NULL, |
1038 | 0 | "%s Type/Length byte: %s, %d %s", (proto_registrar_get_nth(hf_string))->name, ptr->desc, len, unit); |
1039 | 0 | proto_tree_add_uint_format_value(s_tree, hf_type, tvb, offs, 1, type, "%s (0x%02x)", |
1040 | 0 | ptr->desc, type); |
1041 | 0 | proto_tree_add_uint_format_value(s_tree, hf_length, tvb, offs, 1, len, "%d %s", |
1042 | 0 | len, unit); |
1043 | |
|
1044 | 0 | proto_tree_add_string_format_value(tree, hf_string, tvb, offs + 1, blen, str, |
1045 | 0 | "[%s] '%s'", ptr->desc, str); |
1046 | 0 | } |
1047 | | |
1048 | | /* ---------------------------------------------------------------- |
1049 | | Timestamp, IPMI-style. |
1050 | | ---------------------------------------------------------------- */ |
1051 | | void |
1052 | | ipmi_add_timestamp(packet_info *pinfo, proto_tree *tree, int hf, tvbuff_t *tvb, unsigned offset) |
1053 | 0 | { |
1054 | 0 | uint32_t ts = tvb_get_letohl(tvb, offset); |
1055 | |
|
1056 | 0 | if (ts == 0xffffffff) { |
1057 | 0 | proto_tree_add_uint_format_value(tree, hf, tvb, offset, 4, |
1058 | 0 | ts, "Unspecified/Invalid"); |
1059 | 0 | } else if (ts <= 0x20000000) { |
1060 | 0 | proto_tree_add_uint_format_value(tree, hf, tvb, offset, 4, |
1061 | 0 | ts, "%s since SEL device's initialization", |
1062 | 0 | unsigned_time_secs_to_str(pinfo->pool, ts)); |
1063 | 0 | } else { |
1064 | 0 | proto_tree_add_uint_format_value(tree, hf, tvb, offset, 4, |
1065 | 0 | ts, "%s", abs_time_secs_to_str(pinfo->pool, ts, ABSOLUTE_TIME_UTC, true)); |
1066 | 0 | } |
1067 | 0 | } |
1068 | | |
1069 | | /* ---------------------------------------------------------------- |
1070 | | GUID, IPMI-style. |
1071 | | ---------------------------------------------------------------- */ |
1072 | | |
1073 | | void |
1074 | | ipmi_add_guid(proto_tree *tree, int hf, tvbuff_t *tvb, unsigned offset) |
1075 | 0 | { |
1076 | 0 | e_guid_t guid; |
1077 | 0 | int i; |
1078 | |
|
1079 | 0 | guid.data1 = tvb_get_letohl(tvb, offset + 12); |
1080 | 0 | guid.data2 = tvb_get_letohs(tvb, offset + 10); |
1081 | 0 | guid.data3 = tvb_get_letohs(tvb, offset + 8); |
1082 | 0 | for (i = 0; i < 8; i++) { |
1083 | 0 | guid.data4[i] = tvb_get_uint8(tvb, offset + 7 - i); |
1084 | 0 | } |
1085 | 0 | proto_tree_add_guid(tree, hf, tvb, offset, 16, &guid); |
1086 | 0 | } |
1087 | | |
1088 | | /* ---------------------------------------------------------------- |
1089 | | Routines for registering/looking up command parsers. |
1090 | | ---------------------------------------------------------------- */ |
1091 | | |
1092 | | static void |
1093 | | ipmi_netfn_setdesc(uint32_t netfn, const char *desc, uint32_t siglen) |
1094 | 238 | { |
1095 | 238 | struct ipmi_netfn_root *inr; |
1096 | | |
1097 | 238 | inr = &ipmi_cmd_tab[netfn >> 1]; |
1098 | 238 | inr->desc = desc; |
1099 | 238 | inr->siglen = siglen; |
1100 | 238 | } |
1101 | | |
1102 | | void |
1103 | | ipmi_register_netfn_cmdtab(uint32_t netfn, unsigned oem_selector, |
1104 | | const uint8_t *sig, uint32_t siglen, const char *desc, |
1105 | | const ipmi_cmd_t *cmdtab, uint32_t cmdtablen) |
1106 | 154 | { |
1107 | 154 | struct ipmi_netfn_root *inr; |
1108 | 154 | ipmi_netfn_t *inh; |
1109 | | |
1110 | 154 | netfn >>= 1; /* Requests and responses grouped together */ |
1111 | 154 | if (netfn >= IPMI_NETFN_MAX) { |
1112 | 0 | return; |
1113 | 0 | } |
1114 | | |
1115 | 154 | inr = &ipmi_cmd_tab[netfn]; |
1116 | 154 | if (inr->siglen != siglen) { |
1117 | 0 | return; |
1118 | 0 | } |
1119 | | |
1120 | 154 | inh = wmem_new(wmem_epan_scope(), struct ipmi_netfn_handler); |
1121 | 154 | inh->desc = desc; |
1122 | 154 | inh->oem_selector = oem_selector; |
1123 | 154 | inh->sig = sig; |
1124 | 154 | inh->cmdtab = cmdtab; |
1125 | 154 | inh->cmdtablen = cmdtablen; |
1126 | | |
1127 | 154 | inh->next = inr->list; |
1128 | 154 | inr->list = inh; |
1129 | 154 | } |
1130 | | |
1131 | | uint32_t |
1132 | | ipmi_getsiglen(uint32_t netfn) |
1133 | 0 | { |
1134 | 0 | return ipmi_cmd_tab[netfn >> 1].siglen; |
1135 | 0 | } |
1136 | | |
1137 | | const char * |
1138 | | ipmi_getnetfnname(wmem_allocator_t *pool, uint32_t netfn, ipmi_netfn_t *nf) |
1139 | 0 | { |
1140 | 0 | const char *dn, *db; |
1141 | |
|
1142 | 0 | dn = ipmi_cmd_tab[netfn >> 1].desc ? |
1143 | 0 | ipmi_cmd_tab[netfn >> 1].desc : "Reserved"; |
1144 | 0 | db = nf ? nf->desc : NULL; |
1145 | 0 | if (db) { |
1146 | 0 | return wmem_strdup_printf(pool, "%s (%s)", db, dn); |
1147 | 0 | } else { |
1148 | 0 | return dn; |
1149 | 0 | } |
1150 | 0 | } |
1151 | | |
1152 | | ipmi_netfn_t * |
1153 | | ipmi_getnetfn(uint32_t netfn, const uint8_t *sig) |
1154 | 0 | { |
1155 | 0 | struct ipmi_netfn_root *inr; |
1156 | 0 | ipmi_netfn_t *inh; |
1157 | |
|
1158 | 0 | inr = &ipmi_cmd_tab[netfn >> 1]; |
1159 | 0 | for (inh = inr->list; inh; inh = inh->next) { |
1160 | 0 | if ((inh->oem_selector == (unsigned)selected_oem || inh->oem_selector == IPMI_OEM_NONE) |
1161 | 0 | && (!inr->siglen || !memcmp(sig, inh->sig, inr->siglen))) { |
1162 | 0 | return inh; |
1163 | 0 | } |
1164 | 0 | } |
1165 | | |
1166 | | /* Either unknown netFn or signature does not match */ |
1167 | 0 | return NULL; |
1168 | 0 | } |
1169 | | |
1170 | | const ipmi_cmd_t * |
1171 | | ipmi_getcmd(ipmi_netfn_t *nf, uint32_t cmd) |
1172 | 0 | { |
1173 | 0 | static const ipmi_cmd_t ipmi_cmd_unknown = { |
1174 | 0 | 0x00, /* Code */ |
1175 | 0 | ipmi_notimpl, /* request */ |
1176 | 0 | ipmi_notimpl, /* response */ |
1177 | 0 | NULL, /* command codes */ |
1178 | 0 | NULL, /* subfunctions */ |
1179 | 0 | "Unknown command", |
1180 | 0 | 0 /* flag */ |
1181 | 0 | }; |
1182 | 0 | const ipmi_cmd_t *ic; |
1183 | 0 | size_t i, len; |
1184 | |
|
1185 | 0 | if (nf) { |
1186 | 0 | len = nf->cmdtablen; |
1187 | 0 | for (ic = nf->cmdtab, i = 0; i < len; i++, ic++) { |
1188 | 0 | if (ic->cmd == cmd) { |
1189 | 0 | return ic; |
1190 | 0 | } |
1191 | 0 | } |
1192 | 0 | } |
1193 | | |
1194 | 0 | return &ipmi_cmd_unknown; |
1195 | 0 | } |
1196 | | |
1197 | | /* ---------------------------------------------------------------- |
1198 | | Various utility functions. |
1199 | | ---------------------------------------------------------------- */ |
1200 | | |
1201 | | void |
1202 | | ipmi_notimpl(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree) |
1203 | 0 | { |
1204 | 0 | proto_tree_add_expert_remaining(tree, pinfo, &ei_impi_parser_not_implemented, tvb, 0); |
1205 | 0 | } |
1206 | | |
1207 | | void |
1208 | | ipmi_fmt_10ms_1based(char *s, uint32_t v) |
1209 | 0 | { |
1210 | 0 | snprintf(s, ITEM_LABEL_LENGTH, "%d.%03d seconds", v / 100, (v % 100) * 10); |
1211 | 0 | } |
1212 | | |
1213 | | void |
1214 | | ipmi_fmt_500ms_0based(char *s, uint32_t v) |
1215 | 0 | { |
1216 | 0 | ipmi_fmt_500ms_1based(s, ++v); |
1217 | 0 | } |
1218 | | |
1219 | | void |
1220 | | ipmi_fmt_500ms_1based(char *s, uint32_t v) |
1221 | 0 | { |
1222 | 0 | snprintf(s, ITEM_LABEL_LENGTH, "%d.%03d seconds", v / 2, (v % 2) * 500); |
1223 | 0 | } |
1224 | | |
1225 | | void |
1226 | | ipmi_fmt_1s_0based(char *s, uint32_t v) |
1227 | 0 | { |
1228 | 0 | ipmi_fmt_1s_1based(s, ++v); |
1229 | 0 | } |
1230 | | |
1231 | | void |
1232 | | ipmi_fmt_1s_1based(char *s, uint32_t v) |
1233 | 0 | { |
1234 | 0 | snprintf(s, ITEM_LABEL_LENGTH, "%d seconds", v); |
1235 | 0 | } |
1236 | | |
1237 | | void |
1238 | | ipmi_fmt_2s_0based(char *s, uint32_t v) |
1239 | 0 | { |
1240 | 0 | snprintf(s, ITEM_LABEL_LENGTH, "%d seconds", (v + 1) * 2); |
1241 | 0 | } |
1242 | | |
1243 | | void |
1244 | | ipmi_fmt_5s_1based(char *s, uint32_t v) |
1245 | 0 | { |
1246 | 0 | snprintf(s, ITEM_LABEL_LENGTH, "%d seconds", v * 5); |
1247 | 0 | } |
1248 | | |
1249 | | void |
1250 | | ipmi_fmt_version(char *s, uint32_t v) |
1251 | 0 | { |
1252 | 0 | snprintf(s, ITEM_LABEL_LENGTH, "%d.%d", v & 0x0f, (v >> 4) & 0x0f); |
1253 | 0 | } |
1254 | | |
1255 | | void |
1256 | | ipmi_fmt_channel(char *s, uint32_t v) |
1257 | 0 | { |
1258 | 0 | static const value_string chan_vals[] = { |
1259 | 0 | { 0x00, "Primary IPMB (IPMB-0)" }, |
1260 | 0 | { 0x07, "IPMB-L" }, |
1261 | 0 | { 0x0e, "Current channel" }, |
1262 | 0 | { 0x0f, "System Interface" }, |
1263 | 0 | { 0, NULL } |
1264 | 0 | }; |
1265 | 0 | char* tmp_str; |
1266 | |
|
1267 | 0 | tmp_str = val_to_str(NULL, v, chan_vals, "Channel #%d"); |
1268 | 0 | snprintf(s, ITEM_LABEL_LENGTH, "%s (0x%02x)", tmp_str, v); |
1269 | 0 | wmem_free(NULL, tmp_str); |
1270 | 0 | } |
1271 | | |
1272 | | void |
1273 | | ipmi_fmt_udpport(char *s, uint32_t v) |
1274 | 0 | { |
1275 | 0 | char* port_str = udp_port_to_display(NULL, v); |
1276 | 0 | snprintf(s, ITEM_LABEL_LENGTH, "%s (%d)", port_str, v); |
1277 | 0 | wmem_free(NULL, port_str); |
1278 | 0 | } |
1279 | | |
1280 | | void |
1281 | | ipmi_fmt_percent(char *s, uint32_t v) |
1282 | 0 | { |
1283 | 0 | snprintf(s, ITEM_LABEL_LENGTH, "%d%%", v); |
1284 | 0 | } |
1285 | | |
1286 | | const char * |
1287 | | ipmi_get_completion_code(uint8_t completion, const ipmi_cmd_t *cmd) |
1288 | 0 | { |
1289 | 0 | static const value_string std_completion_codes[] = { |
1290 | 0 | { 0x00, "Command Completed Normally" }, |
1291 | 0 | { 0xc0, "Node Busy" }, |
1292 | 0 | { 0xc1, "Invalid Command" }, |
1293 | 0 | { 0xc2, "Command invalid for given LUN" }, |
1294 | 0 | { 0xc3, "Timeout while processing command, response unavailable" }, |
1295 | 0 | { 0xc4, "Out of space" }, |
1296 | 0 | { 0xc5, "Reservation Canceled or Invalid Reservation ID" }, |
1297 | 0 | { 0xc6, "Request data truncated" }, |
1298 | 0 | { 0xc7, "Request data length invalid" }, |
1299 | 0 | { 0xc8, "Request data field length limit exceeded" }, |
1300 | 0 | { 0xc9, "Parameter out of range" }, |
1301 | 0 | { 0xca, "Cannot return number of requested data bytes" }, |
1302 | 0 | { 0xcb, "Requested Sensor, data, or record not present" }, |
1303 | 0 | { 0xcc, "Invalid data field in Request" }, |
1304 | 0 | { 0xcd, "Command illegal for specified sensor or record type" }, |
1305 | 0 | { 0xce, "Command response could not be provided" }, |
1306 | 0 | { 0xcf, "Cannot execute duplicated request" }, |
1307 | 0 | { 0xd0, "Command response could not be provided: SDR Repository in update mode" }, |
1308 | 0 | { 0xd1, "Command response could not be provided: device in firmware update mode" }, |
1309 | 0 | { 0xd2, "Command response could not be provided: BMC initialization or initialization agent in progress" }, |
1310 | 0 | { 0xd3, "Destination unavailable" }, |
1311 | 0 | { 0xd4, "Cannot execute command: insufficient privilege level or other security-based restriction" }, |
1312 | 0 | { 0xd5, "Cannot execute command: command, or request parameter(s), not supported in present state" }, |
1313 | 0 | { 0xd6, "Cannot execute command: parameter is illegal because subfunction is disabled or unavailable" }, |
1314 | 0 | { 0xff, "Unspecified error" }, |
1315 | |
|
1316 | 0 | { 0, NULL } |
1317 | 0 | }; |
1318 | 0 | const char *res; |
1319 | |
|
1320 | 0 | if (completion >= 0x01 && completion <= 0x7e) { |
1321 | 0 | return "Device specific (OEM) completion code"; |
1322 | 0 | } |
1323 | | |
1324 | 0 | if (completion >= 0x80 && completion <= 0xbe) { |
1325 | 0 | if (cmd && cmd->cs_cc && (res = try_val_to_str(completion, cmd->cs_cc)) != NULL) { |
1326 | 0 | return res; |
1327 | 0 | } |
1328 | 0 | return "Standard command-specific code"; |
1329 | 0 | } |
1330 | | |
1331 | 0 | return val_to_str_const(completion, std_completion_codes, "Unknown"); |
1332 | 0 | } |
1333 | | |
1334 | | static int |
1335 | | dissect_tmode(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
1336 | 0 | { |
1337 | 0 | ipmi_dissect_arg_t * arg = (ipmi_dissect_arg_t *) data; |
1338 | 0 | ipmi_context_t ctx; |
1339 | 0 | unsigned tvb_len = tvb_captured_length(tvb); |
1340 | 0 | uint8_t tmp; |
1341 | | |
1342 | | /* TMode message is at least 3 bytes length */ |
1343 | 0 | if (tvb_len < 3) { |
1344 | 0 | return 0; |
1345 | 0 | } |
1346 | | |
1347 | 0 | memset(&ctx, 0, sizeof(ctx)); |
1348 | | |
1349 | | /* get Net Fn/RS LUN field */ |
1350 | 0 | tmp = tvb_get_uint8(tvb, 0); |
1351 | | |
1352 | | /* set Net Fn */ |
1353 | 0 | ctx.hdr.netfn = tmp >> 2; |
1354 | | |
1355 | | /* |
1356 | | * NOTE: request/response matching code swaps RQ LUN with RS LUN |
1357 | | * fields in IPMB-like manner in order to find corresponding request |
1358 | | * so, we set both RS LUN and RQ LUN here for correct |
1359 | | * request/response matching |
1360 | | */ |
1361 | 0 | ctx.hdr.rq_lun = tmp & 3; |
1362 | 0 | ctx.hdr.rs_lun = tmp & 3; |
1363 | | |
1364 | | /* get RQ Seq field */ |
1365 | 0 | ctx.hdr.rq_seq = tvb_get_uint8(tvb, 1) >> 2; |
1366 | | |
1367 | | /* |
1368 | | * NOTE: bridge field is ignored in request/response matching |
1369 | | */ |
1370 | | |
1371 | | /* get command code */ |
1372 | 0 | ctx.hdr.cmd = tvb_get_uint8(tvb, 2); |
1373 | | |
1374 | | /* set dissect flags */ |
1375 | 0 | ctx.flags = IPMI_D_TMODE|IPMI_D_NO_CKS|IPMI_D_NO_RQ_SA; |
1376 | | |
1377 | | /* set header length */ |
1378 | 0 | ctx.hdr_len = 3; |
1379 | | |
1380 | | /* copy channel number and direction */ |
1381 | 0 | ctx.hdr.context = arg ? arg->context : IPMI_E_NONE; |
1382 | 0 | ctx.hdr.channel = arg ? arg->channel : 0; |
1383 | 0 | ctx.hdr.dir = arg ? arg->flags >> 7 : ctx.hdr.netfn & 1; |
1384 | |
|
1385 | 0 | if (ctx.hdr.context == IPMI_E_NONE) { |
1386 | | /* set source column */ |
1387 | 0 | col_set_str(pinfo->cinfo, COL_DEF_SRC, |
1388 | 0 | ctx.hdr.dir ? "Console" : "BMC"); |
1389 | | |
1390 | | /* set destination column */ |
1391 | 0 | col_set_str(pinfo->cinfo, COL_DEF_DST, |
1392 | 0 | ctx.hdr.dir ? "BMC" : "Console"); |
1393 | 0 | } |
1394 | | |
1395 | | /* dissect IPMI command */ |
1396 | 0 | return dissect_ipmi_cmd(tvb, pinfo, tree, proto_tmode, ett_ipmi, &ctx); |
1397 | 0 | } |
1398 | | |
1399 | | static int |
1400 | | dissect_kcs(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
1401 | 0 | { |
1402 | 0 | ipmi_dissect_arg_t * arg = (ipmi_dissect_arg_t *) data; |
1403 | 0 | ipmi_context_t ctx; |
1404 | 0 | unsigned tvb_len = tvb_captured_length(tvb); |
1405 | 0 | uint8_t tmp; |
1406 | | |
1407 | | /* KCS message is at least 2 bytes length */ |
1408 | 0 | if (tvb_len < 2) { |
1409 | 0 | return 0; |
1410 | 0 | } |
1411 | | |
1412 | 0 | memset(&ctx, 0, sizeof(ctx)); |
1413 | | |
1414 | | /* get Net Fn/RS LUN field */ |
1415 | 0 | tmp = tvb_get_uint8(tvb, 0); |
1416 | | |
1417 | | /* set Net Fn */ |
1418 | 0 | ctx.hdr.netfn = tmp >> 2; |
1419 | | |
1420 | | /* |
1421 | | * NOTE: request/response matching code swaps RQ LUN with RS LUN |
1422 | | * fields in IPMB-like manner in order to find corresponding request |
1423 | | * so, we set both RS LUN and RQ LUN here for correct |
1424 | | * request/response matching |
1425 | | */ |
1426 | 0 | ctx.hdr.rq_lun = tmp & 3; |
1427 | 0 | ctx.hdr.rs_lun = tmp & 3; |
1428 | | |
1429 | | /* get command code */ |
1430 | 0 | ctx.hdr.cmd = tvb_get_uint8(tvb, 1); |
1431 | | |
1432 | | /* set dissect flags */ |
1433 | 0 | ctx.flags = IPMI_D_NO_CKS|IPMI_D_NO_RQ_SA|IPMI_D_NO_SEQ; |
1434 | | |
1435 | | /* set header length */ |
1436 | 0 | ctx.hdr_len = 2; |
1437 | | |
1438 | | /* copy channel number and direction */ |
1439 | 0 | ctx.hdr.context = arg ? arg->context : 0; |
1440 | 0 | ctx.hdr.channel = arg ? arg->channel : 0; |
1441 | 0 | ctx.hdr.dir = arg ? arg->flags >> 7 : ctx.hdr.netfn & 1; |
1442 | |
|
1443 | 0 | if (ctx.hdr.context == IPMI_E_NONE) { |
1444 | | /* set source column */ |
1445 | 0 | col_set_str(pinfo->cinfo, COL_DEF_SRC, ctx.hdr.dir ? "HOST" : "BMC"); |
1446 | | |
1447 | | /* set destination column */ |
1448 | 0 | col_set_str(pinfo->cinfo, COL_DEF_DST, ctx.hdr.dir ? "BMC" : "HOST"); |
1449 | 0 | } |
1450 | | |
1451 | | /* dissect IPMI command */ |
1452 | 0 | return dissect_ipmi_cmd(tvb, pinfo, tree, proto_kcs, ett_ipmi, &ctx); |
1453 | 0 | } |
1454 | | |
1455 | | static uint8_t calc_cks(uint8_t start, tvbuff_t * tvb, unsigned off, unsigned len) |
1456 | 15 | { |
1457 | 631 | while (len--) { |
1458 | 616 | start += tvb_get_uint8(tvb, off++); |
1459 | 616 | } |
1460 | | |
1461 | 15 | return start; |
1462 | 15 | } |
1463 | | |
1464 | | static bool guess_imb_format(tvbuff_t *tvb, uint8_t env, |
1465 | | uint8_t channel, unsigned * imb_flags, uint8_t * cks1, uint8_t * cks2) |
1466 | 8 | { |
1467 | 8 | bool check_bc = false; |
1468 | 8 | bool check_sh = false; |
1469 | 8 | bool check_sa = false; |
1470 | 8 | unsigned tvb_len; |
1471 | 8 | unsigned sh_len; |
1472 | 8 | unsigned sa_len; |
1473 | 8 | unsigned rs_sa; |
1474 | | |
1475 | 8 | if (message_format == MSGFMT_NONE) { |
1476 | 0 | return false; |
1477 | 8 | } else if (message_format == MSGFMT_IPMB) { |
1478 | 0 | *imb_flags = IPMI_D_TRG_SA; |
1479 | 8 | } else if (message_format == MSGFMT_LAN) { |
1480 | 0 | *imb_flags = IPMI_D_TRG_SA|IPMI_D_SESSION_HANDLE; |
1481 | | /* channel 0 is primary IPMB */ |
1482 | 8 | } else if (!channel) { |
1483 | | /* check for broadcast if not in send message command */ |
1484 | 8 | if (env == IPMI_E_NONE) { |
1485 | | /* check broadcast */ |
1486 | 8 | check_bc = 1; |
1487 | | |
1488 | | /* slave address must be present */ |
1489 | 8 | *imb_flags = IPMI_D_TRG_SA; |
1490 | | /* check if in send message command */ |
1491 | 8 | } else if (env != IPMI_E_GETMSG) { |
1492 | | /* slave address must be present */ |
1493 | 0 | *imb_flags = IPMI_D_TRG_SA; |
1494 | 0 | } else /* IPMI_E_GETMSG */ { |
1495 | 0 | *imb_flags = 0; |
1496 | 0 | } |
1497 | | /* channel 15 is System Interface */ |
1498 | 8 | } else if (channel == 15) { |
1499 | | /* slave address must be present */ |
1500 | 0 | *imb_flags = IPMI_D_TRG_SA; |
1501 | | |
1502 | | /* check if in get message command */ |
1503 | 0 | if (env == IPMI_E_GETMSG) { |
1504 | | /* session handle must be present */ |
1505 | 0 | *imb_flags |= IPMI_D_SESSION_HANDLE; |
1506 | 0 | } |
1507 | | /* for other channels */ |
1508 | 0 | } else { |
1509 | 0 | if (env == IPMI_E_NONE) { |
1510 | | /* check broadcast */ |
1511 | 0 | check_bc = 1; |
1512 | | |
1513 | | /* slave address must be present */ |
1514 | 0 | *imb_flags = IPMI_D_TRG_SA; |
1515 | 0 | } else if (env == IPMI_E_SENDMSG_RQ) { |
1516 | | /* check session handle */ |
1517 | 0 | check_sh = 1; |
1518 | | |
1519 | | /* slave address must be present */ |
1520 | 0 | *imb_flags = IPMI_D_TRG_SA; |
1521 | 0 | } else if (env == IPMI_E_SENDMSG_RS) { |
1522 | | /* slave address must be present */ |
1523 | 0 | *imb_flags = IPMI_D_TRG_SA; |
1524 | 0 | } else /* IPMI_E_GETMSG */ { |
1525 | | /* check session handle */ |
1526 | 0 | check_sh = 1; |
1527 | | |
1528 | | /* check slave address presence */ |
1529 | 0 | check_sa = 1; |
1530 | | |
1531 | | /* no pre-requisites */ |
1532 | 0 | *imb_flags = 0; |
1533 | 0 | } |
1534 | 0 | } |
1535 | | |
1536 | | /* get message length */ |
1537 | 8 | tvb_len = tvb_captured_length(tvb); |
1538 | | |
1539 | | /* |
1540 | | * broadcast message starts with null, |
1541 | | * does not contain session handle |
1542 | | * but contains responder address |
1543 | | */ |
1544 | 8 | if (check_bc |
1545 | 8 | && tvb_len >= 8 |
1546 | 5 | && !tvb_get_uint8(tvb, 0) |
1547 | 3 | && !calc_cks(0, tvb, 1, 3) |
1548 | 2 | && !calc_cks(0, tvb, 4, tvb_len - 4)) { |
1549 | 0 | *imb_flags = IPMI_D_BROADCAST|IPMI_D_TRG_SA; |
1550 | 0 | *cks1 = 0; |
1551 | 0 | *cks2 = 0; |
1552 | 0 | return true; |
1553 | 0 | } |
1554 | | |
1555 | | /* |
1556 | | * message with the starts with session handle |
1557 | | * and contain responder address |
1558 | | */ |
1559 | 8 | if (check_sh |
1560 | 0 | && tvb_len >= 8 |
1561 | 0 | && !calc_cks(0, tvb, 1, 3) |
1562 | 0 | && !calc_cks(0, tvb, 4, tvb_len - 4)) { |
1563 | 0 | *imb_flags = IPMI_D_SESSION_HANDLE|IPMI_D_TRG_SA; |
1564 | 0 | *cks1 = 0; |
1565 | 0 | *cks2 = 0; |
1566 | 0 | return true; |
1567 | 0 | } |
1568 | | |
1569 | | /* |
1570 | | * message with responder address |
1571 | | */ |
1572 | 8 | if (check_sa |
1573 | 0 | && tvb_len >= 7 |
1574 | 0 | && !calc_cks(0, tvb, 0, 3) |
1575 | 0 | && !calc_cks(0, tvb, 3, tvb_len - 3)) { |
1576 | 0 | *imb_flags = IPMI_D_TRG_SA; |
1577 | 0 | *cks1 = 0; |
1578 | 0 | *cks2 = 0; |
1579 | 0 | return true; |
1580 | 0 | } |
1581 | | |
1582 | | |
1583 | 8 | if (*imb_flags & IPMI_D_SESSION_HANDLE) { |
1584 | 0 | sh_len = 1; |
1585 | 0 | sa_len = 1; |
1586 | 0 | rs_sa = 0; |
1587 | 8 | } else if (*imb_flags & IPMI_D_TRG_SA) { |
1588 | 8 | sh_len = 0; |
1589 | 8 | sa_len = 1; |
1590 | 8 | rs_sa = 0; |
1591 | 8 | } else { |
1592 | 0 | sh_len = 0; |
1593 | 0 | sa_len = 0; |
1594 | 0 | rs_sa = 0x20; |
1595 | 0 | } |
1596 | | |
1597 | | /* check message length */ |
1598 | 8 | if (tvb_len < 6 + sh_len + sa_len) { |
1599 | 3 | return false; |
1600 | 3 | } |
1601 | | |
1602 | | /* calculate checksum deltas */ |
1603 | 5 | *cks1 = calc_cks(rs_sa, tvb, sh_len, sa_len + 2); |
1604 | 5 | *cks2 = calc_cks(0, tvb, sh_len + sa_len + 2, |
1605 | 5 | tvb_len - sh_len - sa_len - 2); |
1606 | | |
1607 | 5 | return true; |
1608 | 8 | } |
1609 | | |
1610 | | int |
1611 | | do_dissect_ipmb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, |
1612 | | int hf_parent_item, int ett_tree, ipmi_dissect_arg_t * arg) |
1613 | 8 | { |
1614 | 8 | ipmi_context_t ctx; |
1615 | 8 | unsigned offset = 0; |
1616 | 8 | uint8_t tmp; |
1617 | | |
1618 | 8 | col_set_str(pinfo->cinfo, COL_PROTOCOL, "IPMB"); |
1619 | | |
1620 | 8 | memset(&ctx, 0, sizeof(ctx)); |
1621 | | |
1622 | | /* copy message context and channel */ |
1623 | 8 | ctx.hdr.context = arg ? arg->context : 0; |
1624 | 8 | ctx.hdr.channel = arg ? arg->channel : 0; |
1625 | | |
1626 | | /* guess IPMB message format */ |
1627 | 8 | if (!guess_imb_format(tvb, ctx.hdr.context, ctx.hdr.channel, |
1628 | 8 | &ctx.flags, &ctx.cks1, &ctx.cks2)) { |
1629 | 3 | return 0; |
1630 | 3 | } |
1631 | | |
1632 | | /* check if message is broadcast */ |
1633 | 5 | if (ctx.flags & IPMI_D_BROADCAST) { |
1634 | | /* skip first byte */ |
1635 | 0 | offset++; |
1636 | 0 | } |
1637 | | |
1638 | | /* check is session handle is specified */ |
1639 | 5 | if (ctx.flags & IPMI_D_SESSION_HANDLE) { |
1640 | 0 | ctx.hdr.session = tvb_get_uint8(tvb, offset++); |
1641 | 0 | } |
1642 | | |
1643 | | /* check is response address is specified */ |
1644 | 5 | if (ctx.flags & IPMI_D_TRG_SA) { |
1645 | 5 | ctx.hdr.rs_sa = tvb_get_uint8(tvb, offset++); |
1646 | 5 | } else { |
1647 | 0 | ctx.hdr.rs_sa = 0x20; |
1648 | 0 | } |
1649 | | |
1650 | | /* get Net Fn/RS LUN field */ |
1651 | 5 | tmp = tvb_get_uint8(tvb, offset++); |
1652 | | |
1653 | | /* set Net Fn and RS LUN */ |
1654 | 5 | ctx.hdr.netfn = tmp >> 2; |
1655 | 5 | ctx.hdr.rs_lun = tmp & 3; |
1656 | | |
1657 | | /* skip cks1 */ |
1658 | 5 | offset++; |
1659 | | |
1660 | | /* get RQ SA */ |
1661 | 5 | ctx.hdr.rq_sa = tvb_get_uint8(tvb, offset++); |
1662 | | |
1663 | | /* get RQ Seq/RQ LUN field */ |
1664 | 5 | tmp = tvb_get_uint8(tvb, offset++); |
1665 | | |
1666 | | /* set RQ Seq and RQ LUN */ |
1667 | 5 | ctx.hdr.rq_seq = tmp >> 2; |
1668 | 5 | ctx.hdr.rq_lun = tmp & 3; |
1669 | | |
1670 | | /* get command code */ |
1671 | 5 | ctx.hdr.cmd = tvb_get_uint8(tvb, offset++); |
1672 | | |
1673 | | /* set header length */ |
1674 | 5 | ctx.hdr_len = offset; |
1675 | | |
1676 | | /* copy direction */ |
1677 | 5 | ctx.hdr.dir = arg ? arg->flags >> 7 : ctx.hdr.netfn & 1; |
1678 | | |
1679 | 5 | if (ctx.hdr.context == IPMI_E_NONE) { |
1680 | 5 | unsigned red = arg ? (arg->flags & 0x40) : 0; |
1681 | | |
1682 | 5 | if (!ctx.hdr.channel) { |
1683 | 5 | col_add_fstr(pinfo->cinfo, COL_DEF_SRC, |
1684 | 5 | "0x%02x(%s)", ctx.hdr.rq_sa, red ? "IPMB-B" : "IPMB-A"); |
1685 | 5 | } else { |
1686 | 0 | col_add_fstr(pinfo->cinfo, COL_DEF_SRC, |
1687 | 0 | "0x%02x", ctx.hdr.rq_sa); |
1688 | 0 | } |
1689 | | |
1690 | 5 | col_add_fstr(pinfo->cinfo, COL_DEF_DST, "0x%02x", ctx.hdr.rs_sa); |
1691 | 5 | } |
1692 | | |
1693 | | /* dissect IPMI command */ |
1694 | 5 | return dissect_ipmi_cmd(tvb, pinfo, tree, hf_parent_item, ett_tree, &ctx); |
1695 | 8 | } |
1696 | | |
1697 | | static int |
1698 | | dissect_ipmi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
1699 | 8 | { |
1700 | 8 | return do_dissect_ipmb(tvb, pinfo, tree, proto_ipmb, ett_ipmi, |
1701 | 8 | (ipmi_dissect_arg_t *) data); |
1702 | 8 | } |
1703 | | |
1704 | | static int |
1705 | | dissect_i2c_ipmi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) |
1706 | 0 | { |
1707 | 0 | if (pinfo->pseudo_header->i2c.flags & 0x00000001) { |
1708 | | /* Master-receive transactions are not possible on IPMB */ |
1709 | 0 | return 0; |
1710 | 0 | } |
1711 | | |
1712 | 0 | return do_dissect_ipmb(tvb, pinfo, tree, proto_ipmb, ett_ipmi, |
1713 | 0 | (ipmi_dissect_arg_t *) data); |
1714 | 0 | } |
1715 | | |
1716 | | |
1717 | | /* Register IPMB protocol. |
1718 | | */ |
1719 | | void |
1720 | | proto_register_ipmi(void) |
1721 | 14 | { |
1722 | 14 | static hf_register_info hf[] = { |
1723 | 14 | { &hf_ipmi_command_data, { "Bus command data", "ipmi.bus_command_data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }}, |
1724 | 14 | { &hf_ipmi_session_handle, { "Session handle", "ipmi.session_handle", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }}, |
1725 | 14 | { &hf_ipmi_header_trg, { "Target Address", "ipmi.header.target", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, |
1726 | 14 | { &hf_ipmi_header_trg_lun, { "Target LUN", "ipmi.header.trg_lun", FT_UINT8, BASE_HEX, NULL, 0x03, NULL, HFILL }}, |
1727 | 14 | { &hf_ipmi_header_netfn, { "NetFN", "ipmi.header.netfn", FT_UINT8, BASE_HEX, NULL, 0xfc, NULL, HFILL }}, |
1728 | 14 | { &hf_ipmi_header_crc, { "Header Checksum", "ipmi.header.crc", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }}, |
1729 | 14 | { &hf_ipmi_header_src, { "Source Address", "ipmi.header.source", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }}, |
1730 | 14 | { &hf_ipmi_header_src_lun, { "Source LUN", "ipmi.header.src_lun", FT_UINT8, BASE_HEX, NULL, 0x03, NULL, HFILL }}, |
1731 | 14 | { &hf_ipmi_header_bridged, { "Bridged", "ipmi.header.bridged", FT_UINT8, BASE_HEX, NULL, 0x03, NULL, HFILL }}, |
1732 | 14 | { &hf_ipmi_header_sequence, { "Sequence Number", "ipmi.header.sequence", FT_UINT8, BASE_HEX, NULL, 0xfc, NULL, HFILL }}, |
1733 | 14 | { &hf_ipmi_header_command, { "Command", "ipmi.header.command", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }}, |
1734 | 14 | { &hf_ipmi_header_completion, { "Completion Code", "ipmi.header.completion", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }}, |
1735 | 14 | { &hf_ipmi_header_sig, { "Signature", "ipmi.header.signature", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }}, |
1736 | 14 | { &hf_ipmi_data_crc, { "Data checksum", "ipmi.data.crc", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }}, |
1737 | 14 | { &hf_ipmi_response_to, { "Response to", "ipmi.response_to", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0, NULL, HFILL }}, |
1738 | 14 | { &hf_ipmi_response_in, { "Response in", "ipmi.response_in", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0, NULL, HFILL }}, |
1739 | 14 | { &hf_ipmi_response_time, { "Responded in", "ipmi.response_time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0, NULL, HFILL }} |
1740 | 14 | }; |
1741 | 14 | static int *ett[] = { |
1742 | 14 | &ett_ipmi, |
1743 | 14 | &ett_header, |
1744 | 14 | &ett_header_byte_1, |
1745 | 14 | &ett_header_byte_4, |
1746 | 14 | &ett_data, |
1747 | 14 | &ett_typelen |
1748 | 14 | }; |
1749 | 14 | static const enum_val_t msgfmt_vals[] = { |
1750 | 14 | { "none", "None", MSGFMT_NONE }, |
1751 | 14 | { "ipmb", "IPMB", MSGFMT_IPMB }, |
1752 | 14 | { "lan", "Session-based (LAN, ...)", MSGFMT_LAN }, |
1753 | 14 | { "guess", "Use heuristics", MSGFMT_GUESS }, |
1754 | 14 | { NULL, NULL, 0 } |
1755 | 14 | }; |
1756 | 14 | static const enum_val_t oemsel_vals[] = { |
1757 | 14 | { "none", "None", IPMI_OEM_NONE }, |
1758 | 14 | { "pps", "Pigeon Point Systems", IPMI_OEM_PPS }, |
1759 | 14 | { NULL, NULL, 0 } |
1760 | 14 | }; |
1761 | | |
1762 | 14 | static ei_register_info ei[] = { |
1763 | 14 | { &ei_impi_parser_not_implemented, { "ipmi.parser_not_implemented", PI_UNDECODED, PI_WARN, "[PARSER NOT IMPLEMENTED]", EXPFILL }}, |
1764 | 14 | }; |
1765 | | |
1766 | 14 | module_t *module; |
1767 | 14 | expert_module_t* expert_ipmi; |
1768 | 14 | uint32_t i; |
1769 | | |
1770 | 14 | proto_ipmi = proto_register_protocol("Intelligent Platform Management Interface", |
1771 | 14 | "IPMI", |
1772 | 14 | "ipmi"); |
1773 | | |
1774 | 14 | proto_ipmb = proto_register_protocol("Intelligent Platform Management Bus", |
1775 | 14 | "IPMB", |
1776 | 14 | "ipmb"); |
1777 | 14 | proto_kcs = proto_register_protocol("Keyboard Controller Style Interface", |
1778 | 14 | "KCS", |
1779 | 14 | "kcs"); |
1780 | 14 | proto_tmode = proto_register_protocol("Serial Terminal Mode Interface", |
1781 | 14 | "TMode", |
1782 | 14 | "tmode"); |
1783 | | |
1784 | 14 | proto_register_field_array(proto_ipmi, hf, array_length(hf)); |
1785 | 14 | proto_register_subtree_array(ett, array_length(ett)); |
1786 | | |
1787 | 14 | expert_ipmi = expert_register_protocol(proto_ipmi); |
1788 | 14 | expert_register_field_array(expert_ipmi, ei, array_length(ei)); |
1789 | | |
1790 | 14 | ipmi_netfn_setdesc(IPMI_CHASSIS_REQ, "Chassis", 0); |
1791 | 14 | ipmi_netfn_setdesc(IPMI_BRIDGE_REQ, "Bridge", 0); |
1792 | 14 | ipmi_netfn_setdesc(IPMI_SE_REQ, "Sensor/Event", 0); |
1793 | 14 | ipmi_netfn_setdesc(IPMI_APP_REQ, "Application", 0); |
1794 | 14 | ipmi_netfn_setdesc(IPMI_UPDATE_REQ, "Firmware Update", 0); |
1795 | 14 | ipmi_netfn_setdesc(IPMI_STORAGE_REQ, "Storage", 0); |
1796 | 14 | ipmi_netfn_setdesc(IPMI_TRANSPORT_REQ, "Transport", 0); |
1797 | 14 | ipmi_netfn_setdesc(IPMI_GROUP_REQ, "Group", 1); |
1798 | 14 | ipmi_netfn_setdesc(IPMI_OEM_REQ, "OEM/Group", 3); |
1799 | 126 | for (i = 0x30; i < 0x40; i += 2) { |
1800 | 112 | ipmi_netfn_setdesc(i, "OEM", 0); |
1801 | 112 | } |
1802 | | |
1803 | 14 | register_dissector("ipmi", dissect_ipmi, proto_ipmi); |
1804 | 14 | ipmi_i2c_handle = register_dissector("ipmi.i2c", dissect_i2c_ipmi, proto_ipmi ); |
1805 | 14 | register_dissector("ipmb", dissect_ipmi, proto_ipmb); |
1806 | 14 | register_dissector("kcs", dissect_kcs, proto_kcs); |
1807 | 14 | register_dissector("tmode", dissect_tmode, proto_tmode); |
1808 | | |
1809 | 14 | module = prefs_register_protocol(proto_ipmi, NULL); |
1810 | 14 | prefs_register_bool_preference(module, "dissect_bus_commands", "Dissect bus commands", |
1811 | 14 | "Dissect IPMB commands", |
1812 | 14 | &dissect_bus_commands); |
1813 | 14 | prefs_register_bool_preference(module, "fru_langcode_is_english", "FRU Language Code is English", |
1814 | 14 | "FRU Language Code is English; strings are ASCII+LATIN1 (vs. Unicode)", |
1815 | 14 | &fru_langcode_is_english); |
1816 | 14 | prefs_register_uint_preference(module, "response_after_req", "Maximum delay of response message", |
1817 | 14 | "Do not search for responses coming after this timeout (milliseconds)", |
1818 | 14 | 10, &response_after_req); |
1819 | 14 | prefs_register_uint_preference(module, "response_before_req", "Response ahead of request", |
1820 | 14 | "Allow for responses before requests (milliseconds)", |
1821 | 14 | 10, &response_before_req); |
1822 | 14 | prefs_register_enum_preference(module, "msgfmt", "Format of embedded messages", |
1823 | 14 | "Format of messages embedded into Send/Get/Forward Message", |
1824 | 14 | &message_format, msgfmt_vals, false); |
1825 | 14 | prefs_register_enum_preference(module, "selected_oem", "OEM commands parsed as", |
1826 | 14 | "Selects which OEM format is used for commands that IPMI does not define", |
1827 | 14 | &selected_oem, oemsel_vals, false); |
1828 | 14 | } |
1829 | | |
1830 | | void proto_reg_handoff_ipmi(void) |
1831 | 14 | { |
1832 | 14 | dissector_add_for_decode_as("i2c.message", ipmi_i2c_handle ); |
1833 | 14 | } |
1834 | | |
1835 | | /* |
1836 | | * Editor modelines - https://www.wireshark.org/tools/modelines.html |
1837 | | * |
1838 | | * Local variables: |
1839 | | * c-basic-offset: 8 |
1840 | | * tab-width: 8 |
1841 | | * indent-tabs-mode: t |
1842 | | * End: |
1843 | | * |
1844 | | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
1845 | | * :indentSize=8:tabSize=8:noTabs=false: |
1846 | | */ |