2 * Routines for IPMI dissection
3 * Copyright 2002-2008, Alexey Neyman, Pigeon Point Systems <avn@pigeonpoint.com>
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
16 #include <epan/packet.h>
17 #include <epan/conversation.h>
18 #include <epan/prefs.h>
19 #include <epan/addr_resolv.h>
21 #include "packet-ipmi.h"
23 static dissector_handle_t ipmi_i2c_handle
;
25 void proto_register_ipmi(void);
26 void proto_reg_handoff_ipmi(void);
29 * See the IPMI specifications at
31 * http://www.intel.com/design/servers/ipmi/
34 /* Define IPMI_DEBUG to enable printing the process of request-response pairing */
35 /* #define IPMI_DEBUG */
37 /* Top-level search structure: list of registered handlers for a given netFn */
38 struct ipmi_netfn_root
{
51 struct ipmi_parse_typelen
{
52 void (*get_len
)(unsigned *, unsigned *, tvbuff_t
*, unsigned, unsigned, bool);
53 void (*parse
)(char *, tvbuff_t
*, unsigned, unsigned);
57 /* IPMI parsing context */
66 /* Temporary request-response matching data. */
70 /* Frame number where the request resides */
72 /* Nest level of the request in the frame */
76 /* List of request-response matching data */
77 typedef wmem_list_t ipmi_request_list_t
;
81 /* Per-command data */
83 uint32_t matched_frame_num
;
84 uint32_t saved_data
[NSAVED_DATA
];
89 ipmi_cmd_data_t
* cmd_data
[3];
93 /* RB tree of frame data */
94 typedef wmem_tree_t ipmi_frame_tree_t
;
96 /* cached dissector data */
98 /* tree of cached frame data */
99 ipmi_frame_tree_t
* frame_tree
;
100 /* list of cached requests */
101 ipmi_request_list_t
* request_list
;
102 /* currently dissected frame number */
103 uint32_t curr_frame_num
;
104 /* currently dissected frame */
105 ipmi_frame_data_t
* curr_frame
;
106 /* current nesting level */
108 /* subsequent nesting level */
110 /* top level message channel */
111 uint8_t curr_channel
;
112 /* top level message direction */
114 /* pointer to current command */
115 const ipmi_header_t
* curr_hdr
;
116 /* current completion code */
118 } ipmi_packet_data_t
;
120 /* Maximum nest level where it worth caching data */
121 #define MAX_NEST_LEVEL 3
124 static int proto_ipmb
;
125 static int proto_kcs
;
126 static int proto_tmode
;
128 /* WARNING: Setting this to true might result in the entire dissector being
129 disabled by default or removed completely. */
130 static bool dissect_bus_commands
;
131 static bool fru_langcode_is_english
= true;
132 static unsigned response_after_req
= 5000;
133 static unsigned response_before_req
;
134 static unsigned message_format
= MSGFMT_GUESS
;
135 static unsigned selected_oem
= IPMI_OEM_NONE
;
137 static int hf_ipmi_command_data
;
138 static int hf_ipmi_session_handle
;
139 static int hf_ipmi_header_trg
;
140 static int hf_ipmi_header_trg_lun
;
141 static int hf_ipmi_header_netfn
;
142 static int hf_ipmi_header_crc
;
143 static int hf_ipmi_header_src
;
144 static int hf_ipmi_header_src_lun
;
145 static int hf_ipmi_header_bridged
;
146 static int hf_ipmi_header_sequence
;
147 static int hf_ipmi_header_command
;
148 static int hf_ipmi_header_completion
;
149 static int hf_ipmi_header_sig
;
150 static int hf_ipmi_data_crc
;
151 static int hf_ipmi_response_to
;
152 static int hf_ipmi_response_in
;
153 static int hf_ipmi_response_time
;
156 static int ett_header
;
157 static int ett_header_byte_1
;
158 static int ett_header_byte_4
;
160 static int ett_typelen
;
162 static expert_field ei_impi_parser_not_implemented
;
164 static struct ipmi_netfn_root ipmi_cmd_tab
[IPMI_NETFN_MAX
];
166 static ipmi_packet_data_t
*
167 get_packet_data(packet_info
* pinfo
)
169 ipmi_packet_data_t
* data
;
171 /* get conversation data */
172 conversation_t
* conv
= find_or_create_conversation(pinfo
);
174 /* get protocol-specific data */
175 data
= (ipmi_packet_data_t
*)
176 conversation_get_proto_data(conv
, proto_ipmi
);
179 /* allocate per-packet data */
180 data
= wmem_new0(wmem_file_scope(), ipmi_packet_data_t
);
182 /* allocate request list and frame tree */
183 data
->frame_tree
= wmem_tree_new(wmem_file_scope());
184 data
->request_list
= wmem_list_new(wmem_file_scope());
186 /* add protocol data */
187 conversation_add_proto_data(conv
, proto_ipmi
, data
);
190 /* check if packet has changed */
191 if (pinfo
->num
!= data
->curr_frame_num
) {
192 data
->curr_level
= 0;
193 data
->next_level
= 0;
199 static ipmi_frame_data_t
*
200 get_frame_data(ipmi_packet_data_t
* data
, uint32_t frame_num
)
202 ipmi_frame_data_t
* frame
= (ipmi_frame_data_t
*)
203 wmem_tree_lookup32(data
->frame_tree
, frame_num
);
206 frame
= wmem_new0(wmem_file_scope(), ipmi_frame_data_t
);
208 wmem_tree_insert32(data
->frame_tree
, frame_num
, frame
);
213 static ipmi_request_t
*
214 get_matched_request(ipmi_packet_data_t
* data
, const ipmi_header_t
* rs_hdr
,
217 wmem_list_frame_t
* iter
= wmem_list_head(data
->request_list
);
218 ipmi_header_t rq_hdr
;
220 /* reset message context */
224 rq_hdr
.channel
= data
->curr_channel
;
226 /* toggle packet direction */
227 rq_hdr
.dir
= rs_hdr
->dir
^ 1;
229 rq_hdr
.session
= rs_hdr
->session
;
231 /* swap responder address/lun */
232 rq_hdr
.rs_sa
= rs_hdr
->rq_sa
;
233 rq_hdr
.rs_lun
= rs_hdr
->rq_lun
;
235 /* remove reply flag */
236 rq_hdr
.netfn
= rs_hdr
->netfn
& ~1;
238 /* swap requester address/lun */
239 rq_hdr
.rq_sa
= rs_hdr
->rs_sa
;
240 rq_hdr
.rq_lun
= rs_hdr
->rs_lun
;
243 rq_hdr
.rq_seq
= rs_hdr
->rq_seq
;
246 rq_hdr
.cmd
= rs_hdr
->cmd
;
248 /* TODO: copy prefix bytes */
251 fprintf(stderr
, "%d, %d: rq_hdr : {\n"
261 data
->curr_frame_num
, data
->curr_level
,
262 rq_hdr
.channel
, rq_hdr
.dir
, rq_hdr
.rs_sa
, rq_hdr
.rs_lun
,
263 rq_hdr
.netfn
, rq_hdr
.rq_sa
, rq_hdr
.rq_lun
, rq_hdr
.rq_seq
,
268 ipmi_request_t
* rq
= (ipmi_request_t
*) wmem_list_frame_data(iter
);
270 /* check if in Get Message context */
271 if (rs_hdr
->context
== IPMI_E_GETMSG
&& !(flags
& IPMI_D_TRG_SA
)) {
273 rq_hdr
.rq_sa
= rq
->hdr
.rq_sa
;
276 /* compare command headers */
277 if (!memcmp(&rq_hdr
, &rq
->hdr
, sizeof(rq_hdr
))) {
281 /* proceed to next request */
282 iter
= wmem_list_frame_next(iter
);
289 remove_old_requests(ipmi_packet_data_t
* data
, const nstime_t
* curr_time
)
291 wmem_list_frame_t
* iter
= wmem_list_head(data
->request_list
);
294 ipmi_request_t
* rq
= (ipmi_request_t
*) wmem_list_frame_data(iter
);
295 ipmi_frame_data_t
* frame
= get_frame_data(data
, rq
->frame_num
);
298 /* calculate time delta */
299 nstime_delta(&delta
, curr_time
, &frame
->ts
);
301 if (nstime_to_msec(&delta
) > response_after_req
) {
302 wmem_list_frame_t
* del
= iter
;
304 /* proceed to next request */
305 iter
= wmem_list_frame_next(iter
);
307 /* free request data */
308 wmem_free(wmem_file_scope(), rq
);
310 /* remove list item */
311 wmem_list_remove_frame(data
->request_list
, del
);
319 match_request_response(ipmi_packet_data_t
* data
, const ipmi_header_t
* hdr
,
322 /* get current frame */
323 ipmi_frame_data_t
* rs_frame
= data
->curr_frame
;
325 /* get current command data */
326 ipmi_cmd_data_t
* rs_data
= rs_frame
->cmd_data
[data
->curr_level
];
328 /* check if parse response for the first time */
332 /* allocate command data */
333 rs_data
= wmem_new0(wmem_file_scope(), ipmi_cmd_data_t
);
335 /* search for matching request */
336 rq
= get_matched_request(data
, hdr
, flags
);
338 /* check if matching request is found */
340 /* get request frame data */
341 ipmi_frame_data_t
* rq_frame
=
342 get_frame_data(data
, rq
->frame_num
);
344 /* get command data */
345 ipmi_cmd_data_t
* rq_data
= rq_frame
->cmd_data
[rq
->nest_level
];
347 /* save matched frame numbers */
348 rq_data
->matched_frame_num
= data
->curr_frame_num
;
349 rs_data
->matched_frame_num
= rq
->frame_num
;
351 /* copy saved command data information */
352 rs_data
->saved_data
[0] = rq_data
->saved_data
[0];
353 rs_data
->saved_data
[1] = rq_data
->saved_data
[1];
355 /* remove request from the list */
356 wmem_list_remove(data
->request_list
, rq
);
358 /* delete request data */
359 wmem_free(wmem_file_scope(), rq
);
362 /* save command data pointer in frame */
363 rs_frame
->cmd_data
[data
->curr_level
] = rs_data
;
368 add_request(ipmi_packet_data_t
* data
, const ipmi_header_t
* hdr
)
370 /* get current frame */
371 ipmi_frame_data_t
* rq_frame
= data
->curr_frame
;
373 /* get current command data */
374 ipmi_cmd_data_t
* rq_data
= rq_frame
->cmd_data
[data
->curr_level
];
376 /* check if parse response for the first time */
380 /* allocate command data */
381 rq_data
= wmem_new0(wmem_file_scope(), ipmi_cmd_data_t
);
383 /* set command data pointer */
384 rq_frame
->cmd_data
[data
->curr_level
] = rq_data
;
386 /* allocate request data */
387 rq
= wmem_new0(wmem_file_scope(), ipmi_request_t
);
389 /* copy request header */
390 memcpy(&rq
->hdr
, hdr
, sizeof(rq
->hdr
));
392 /* override context, channel and direction */
394 rq
->hdr
.channel
= data
->curr_channel
;
395 rq
->hdr
.dir
= data
->curr_dir
;
397 /* set request frame number */
398 rq
->frame_num
= data
->curr_frame_num
;
400 /* set command nest level */
401 rq
->nest_level
= data
->curr_level
;
403 /* append request to list */
404 wmem_list_append(data
->request_list
, rq
);
407 fprintf(stderr
, "%d, %d: hdr : {\n"
417 data
->curr_frame_num
, data
->curr_level
,
418 rq
->hdr
.channel
, rq
->hdr
.dir
, rq
->hdr
.rs_sa
, rq
->hdr
.rs_lun
,
419 rq
->hdr
.netfn
, rq
->hdr
.rq_sa
, rq
->hdr
.rq_lun
, rq
->hdr
.rq_seq
,
426 add_command_info(packet_info
*pinfo
, const ipmi_cmd_t
* cmd
,
427 bool resp
, uint8_t cc_val
, const char * cc_str
, bool broadcast
)
430 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Rsp, %s, %s (%02xh)",
431 cmd
->desc
, cc_str
, cc_val
);
433 col_add_fstr(pinfo
->cinfo
, COL_INFO
, "Req, %s%s",
434 broadcast
? "Broadcast " : "", cmd
->desc
);
439 dissect_ipmi_cmd(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
,
440 int hf_parent_item
, int ett_tree
, const ipmi_context_t
* ctx
)
442 ipmi_packet_data_t
* data
;
443 ipmi_netfn_t
* cmd_list
;
444 const ipmi_cmd_t
* cmd
;
446 proto_tree
* cmd_tree
= NULL
, * tmp_tree
;
447 uint8_t prev_level
, cc_val
;
448 unsigned offset
, siglen
, is_resp
;
449 const char * cc_str
, * netfn_str
;
451 if (!dissect_bus_commands
) {
452 ti
= proto_tree_add_item(tree
, hf_parent_item
, tvb
, 0, -1, ENC_NA
);
453 cmd_tree
= proto_item_add_subtree(ti
, ett_tree
);
454 proto_tree_add_item(cmd_tree
, hf_ipmi_command_data
, tvb
, 0, -1, ENC_NA
);
458 /* get packet data */
459 data
= get_packet_data(pinfo
);
464 /* get prefix length */
465 siglen
= ipmi_getsiglen(ctx
->hdr
.netfn
);
467 /* get response flag */
468 is_resp
= ctx
->hdr
.netfn
& 1;
470 /* check message length */
471 if (tvb_captured_length(tvb
) < ctx
->hdr_len
+ siglen
+ is_resp
472 + !(ctx
->flags
& IPMI_D_NO_CKS
)) {
473 /* don bother with anything */
474 return call_data_dissector(tvb
, pinfo
, tree
);
477 /* save nest level */
478 prev_level
= data
->curr_level
;
480 /* assign next nest level */
481 data
->curr_level
= data
->next_level
;
483 /* increment next nest level */
486 /* check for the first invocation */
487 if (!data
->curr_level
) {
488 /* get current frame data */
489 data
->curr_frame
= get_frame_data(data
, pinfo
->num
);
490 data
->curr_frame_num
= pinfo
->num
;
492 /* copy frame timestamp */
493 memcpy(&data
->curr_frame
->ts
, &pinfo
->abs_ts
, sizeof(nstime_t
));
495 /* cache channel and direction */
496 data
->curr_channel
= ctx
->hdr
.channel
;
497 data
->curr_dir
= ctx
->hdr
.dir
;
499 /* remove requests which are too old */
500 remove_old_requests(data
, &pinfo
->abs_ts
);
503 if (data
->curr_level
< MAX_NEST_LEVEL
) {
504 if (ctx
->hdr
.netfn
& 1) {
505 /* perform request/response matching */
506 match_request_response(data
, &ctx
->hdr
, ctx
->flags
);
508 /* add request to the list for later matching */
509 add_request(data
, &ctx
->hdr
);
513 /* get command list by network function code */
514 cmd_list
= ipmi_getnetfn(ctx
->hdr
.netfn
,
515 tvb_get_ptr(tvb
, ctx
->hdr_len
+ is_resp
, siglen
));
517 /* get command descriptor */
518 cmd
= ipmi_getcmd(cmd_list
, ctx
->hdr
.cmd
);
520 /* check if response */
522 /* get completion code */
523 cc_val
= tvb_get_uint8(tvb
, ctx
->hdr_len
);
525 /* get completion code desc */
526 cc_str
= ipmi_get_completion_code(cc_val
, cmd
);
532 /* check if not inside a message */
533 if (!data
->curr_level
) {
534 /* add packet info */
535 add_command_info(pinfo
, cmd
, is_resp
, cc_val
, cc_str
,
536 ctx
->flags
& IPMI_D_BROADCAST
? true : false);
540 /* add parent node */
541 if (!data
->curr_level
) {
542 ti
= proto_tree_add_item(tree
, hf_parent_item
, tvb
, 0, -1, ENC_NA
);
543 cmd_tree
= proto_item_add_subtree(ti
, ett_tree
);
545 char str
[ITEM_LABEL_LENGTH
];
548 snprintf(str
, ITEM_LABEL_LENGTH
, "Rsp, %s, %s",
551 snprintf(str
, ITEM_LABEL_LENGTH
, "Req, %s", cmd
->desc
);
553 if (proto_registrar_get_ftype(hf_parent_item
) == FT_STRING
) {
554 ti
= proto_tree_add_string(tree
, hf_parent_item
, tvb
, 0, -1, str
);
555 cmd_tree
= proto_item_add_subtree(ti
, ett_tree
);
558 cmd_tree
= proto_tree_add_subtree(tree
, tvb
, 0, -1, ett_tree
, NULL
, str
);
561 if (data
->curr_level
< MAX_NEST_LEVEL
) {
562 /* check if response */
563 if (ctx
->hdr
.netfn
& 1) {
564 /* get current command data */
565 ipmi_cmd_data_t
* rs_data
=
566 data
->curr_frame
->cmd_data
[data
->curr_level
];
568 if (rs_data
->matched_frame_num
) {
571 /* add "Request to:" field */
572 ti
= proto_tree_add_uint(cmd_tree
, hf_ipmi_response_to
,
573 tvb
, 0, 0, rs_data
->matched_frame_num
);
575 /* mark field as a generated one */
576 proto_item_set_generated(ti
);
578 /* calculate delta time */
579 nstime_delta(&ns
, &pinfo
->abs_ts
,
580 &get_frame_data(data
,
581 rs_data
->matched_frame_num
)->ts
);
583 /* add "Response time" field */
584 ti
= proto_tree_add_time(cmd_tree
, hf_ipmi_response_time
,
587 /* mark field as a generated one */
588 proto_item_set_generated(ti
);
591 /* get current command data */
592 ipmi_cmd_data_t
* rq_data
=
593 data
->curr_frame
->cmd_data
[data
->curr_level
];
595 if (rq_data
->matched_frame_num
) {
596 /* add "Response in:" field */
597 ti
= proto_tree_add_uint(cmd_tree
, hf_ipmi_response_in
,
598 tvb
, 0, 0, rq_data
->matched_frame_num
);
600 /* mark field as a generated one */
601 proto_item_set_generated(ti
);
606 /* set starting offset */
609 /* check if message is broadcast */
610 if (ctx
->flags
& IPMI_D_BROADCAST
) {
611 /* skip first byte */
615 /* check if session handle is specified */
616 if (ctx
->flags
& IPMI_D_SESSION_HANDLE
) {
617 /* add session handle field */
618 proto_tree_add_item(cmd_tree
, hf_ipmi_session_handle
,
619 tvb
, offset
++, 1, ENC_LITTLE_ENDIAN
);
622 /* check if responder address is specified */
623 if (ctx
->flags
& IPMI_D_TRG_SA
) {
624 /* add response address field */
625 proto_tree_add_item(cmd_tree
, hf_ipmi_header_trg
, tvb
,
626 offset
++, 1, ENC_LITTLE_ENDIAN
);
629 /* get NetFn string */
630 netfn_str
= ipmi_getnetfnname(pinfo
->pool
, ctx
->hdr
.netfn
, cmd_list
);
632 /* Network function + target LUN */
633 tmp_tree
= proto_tree_add_subtree_format(cmd_tree
, tvb
, offset
, 1,
634 ett_header_byte_1
, NULL
, "Target LUN: 0x%02x, NetFN: %s %s (0x%02x)",
635 ctx
->hdr
.rs_lun
, netfn_str
,
636 is_resp
? "Response" : "Request", ctx
->hdr
.netfn
);
639 proto_tree_add_uint_format(tmp_tree
, hf_ipmi_header_netfn
, tvb
,
640 offset
, 1, ctx
->hdr
.netfn
<< 2,
641 "NetFn: %s %s (0x%02x)", netfn_str
,
642 is_resp
? "Response" : "Request", ctx
->hdr
.netfn
);
644 proto_tree_add_item(tmp_tree
, hf_ipmi_header_trg_lun
, tvb
,
645 offset
++, 1, ENC_LITTLE_ENDIAN
);
647 /* check if cks1 is specified */
648 if (!(ctx
->flags
& IPMI_D_NO_CKS
)) {
649 uint8_t cks
= tvb_get_uint8(tvb
, offset
);
651 /* Header checksum */
653 uint8_t correct
= cks
- ctx
->cks1
;
655 proto_tree_add_uint_format_value(cmd_tree
, hf_ipmi_header_crc
,
656 tvb
, offset
++, 1, cks
,
657 "0x%02x (incorrect, expected 0x%02x)", cks
, correct
);
659 proto_tree_add_uint_format_value(cmd_tree
, hf_ipmi_header_crc
,
660 tvb
, offset
++, 1, cks
,
661 "0x%02x (correct)", cks
);
665 /* check if request address is specified */
666 if (!(ctx
->flags
& IPMI_D_NO_RQ_SA
)) {
667 /* add request address field */
668 proto_tree_add_item(cmd_tree
, hf_ipmi_header_src
, tvb
,
669 offset
++, 1, ENC_LITTLE_ENDIAN
);
672 /* check if request sequence is specified */
673 if (!(ctx
->flags
& IPMI_D_NO_SEQ
)) {
674 /* Sequence number + source LUN */
675 tmp_tree
= proto_tree_add_subtree_format(cmd_tree
, tvb
, offset
, 1,
676 ett_header_byte_4
, NULL
, "%s: 0x%02x, SeqNo: 0x%02x",
677 (ctx
->flags
& IPMI_D_TMODE
) ? "Bridged" : "Source LUN",
678 ctx
->hdr
.rq_lun
, ctx
->hdr
.rq_seq
);
680 if (ctx
->flags
& IPMI_D_TMODE
) {
681 proto_tree_add_item(tmp_tree
, hf_ipmi_header_bridged
,
682 tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
684 proto_tree_add_item(tmp_tree
, hf_ipmi_header_src_lun
,
685 tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
689 proto_tree_add_item(tmp_tree
, hf_ipmi_header_sequence
, tvb
,
690 offset
++, 1, ENC_LITTLE_ENDIAN
);
694 proto_tree_add_uint_format_value(cmd_tree
, hf_ipmi_header_command
,
695 tvb
, offset
++, 1, ctx
->hdr
.cmd
, "%s (0x%02x)",
696 cmd
->desc
, ctx
->hdr
.cmd
);
699 /* completion code */
700 proto_tree_add_uint_format_value(cmd_tree
,
701 hf_ipmi_header_completion
, tvb
, offset
++, 1,
702 cc_val
, "%s (0x%02x)", cc_str
, cc_val
);
706 /* command prefix (if present) */
707 ti
= proto_tree_add_item(cmd_tree
, hf_ipmi_header_sig
, tvb
,
708 offset
, siglen
, ENC_NA
);
709 proto_item_append_text(ti
, " (%s)", netfn_str
);
713 if (tree
|| (cmd
->flags
& CMD_CALLRQ
)) {
714 /* calculate message data length */
715 unsigned data_len
= tvb_captured_length(tvb
)
719 - !(ctx
->flags
& IPMI_D_NO_CKS
);
721 /* create data subset */
722 tvbuff_t
* data_tvb
= tvb_new_subset_length(tvb
,
723 ctx
->hdr_len
+ siglen
+ (is_resp
? 1 : 0), data_len
);
725 /* Select sub-handler */
726 ipmi_cmd_handler_t hnd
= is_resp
? cmd
->parse_resp
: cmd
->parse_req
;
728 if (hnd
&& tvb_captured_length(data_tvb
)) {
729 /* create data field */
730 tmp_tree
= proto_tree_add_subtree(cmd_tree
, data_tvb
, 0, -1, ett_data
, NULL
, "Data");
732 /* save current command */
733 data
->curr_hdr
= &ctx
->hdr
;
735 /* save current completion code */
736 data
->curr_ccode
= cc_val
;
738 /* call command parser */
739 hnd(data_tvb
, pinfo
, tmp_tree
);
743 /* check if cks2 is specified */
744 if (tree
&& !(ctx
->flags
& IPMI_D_NO_CKS
)) {
747 /* get cks2 offset */
748 offset
= tvb_captured_length(tvb
) - 1;
751 cks
= tvb_get_uint8(tvb
, offset
);
753 /* Header checksum */
755 uint8_t correct
= cks
- ctx
->cks2
;
757 proto_tree_add_uint_format_value(cmd_tree
, hf_ipmi_data_crc
,
759 "0x%02x (incorrect, expected 0x%02x)", cks
, correct
);
761 proto_tree_add_uint_format_value(cmd_tree
, hf_ipmi_data_crc
,
763 "0x%02x (correct)", cks
);
767 /* decrement next nest level */
768 data
->next_level
= data
->curr_level
;
770 /* restore previous nest level */
771 data
->curr_level
= prev_level
;
773 return tvb_captured_length(tvb
);
776 /* Get currently parsed message header */
777 const ipmi_header_t
* ipmi_get_hdr(packet_info
* pinfo
)
779 ipmi_packet_data_t
* data
= get_packet_data(pinfo
);
780 return data
->curr_hdr
;
783 /* Get completion code for currently parsed message */
784 uint8_t ipmi_get_ccode(packet_info
* pinfo
)
786 ipmi_packet_data_t
* data
= get_packet_data(pinfo
);
787 return data
->curr_ccode
;
790 /* Save request data for later use in response */
791 void ipmi_set_data(packet_info
*pinfo
, unsigned idx
, uint32_t value
)
793 ipmi_packet_data_t
* data
= get_packet_data(pinfo
);
796 if (data
->curr_level
>= MAX_NEST_LEVEL
|| idx
>= NSAVED_DATA
|| !data
->curr_frame
) {
801 data
->curr_frame
->cmd_data
[data
->curr_level
]->saved_data
[idx
] = value
;
804 /* Get saved request data */
805 bool ipmi_get_data(packet_info
*pinfo
, unsigned idx
, uint32_t * value
)
807 ipmi_packet_data_t
* data
= get_packet_data(pinfo
);
810 if (data
->curr_level
>= MAX_NEST_LEVEL
|| idx
>= NSAVED_DATA
|| !data
->curr_frame
) {
815 *value
= data
->curr_frame
->cmd_data
[data
->curr_level
]->saved_data
[idx
];
819 /* ----------------------------------------------------------------
820 Support for Type/Length fields parsing.
821 ---------------------------------------------------------------- */
824 get_len_binary(unsigned *clen
, unsigned *blen
, tvbuff_t
*tvb _U_
, unsigned offs _U_
,
825 unsigned len
, bool len_is_bytes _U_
)
832 parse_binary(char *p
, tvbuff_t
*tvb
, unsigned offs
, unsigned len
)
834 static const char hex
[] = "0123456789ABCDEF";
838 for (i
= 0; i
< len
/ 3; i
++) {
839 v
= tvb_get_uint8(tvb
, offs
+ i
);
850 static struct ipmi_parse_typelen ptl_binary
= {
851 get_len_binary
, parse_binary
, "Binary"
855 get_len_bcdplus(unsigned *clen
, unsigned *blen
, tvbuff_t
*tvb _U_
, unsigned offs _U_
,
856 unsigned len
, bool len_is_bytes
)
862 *blen
= (len
+ 1) / 2;
868 parse_bcdplus(char *p
, tvbuff_t
*tvb
, unsigned offs
, unsigned len
)
870 static const char bcd
[] = "0123456789 -.:,_";
871 unsigned i
, msk
= 0xf0, shft
= 4;
874 for (i
= 0; i
< len
; i
++) {
875 v
= (tvb_get_uint8(tvb
, offs
+ i
/ 2) & msk
) >> shft
;
882 static struct ipmi_parse_typelen ptl_bcdplus
= {
883 get_len_bcdplus
, parse_bcdplus
, "BCD+"
887 get_len_6bit_ascii(unsigned *clen
, unsigned *blen
, tvbuff_t
*tvb _U_
, unsigned offs _U_
,
888 unsigned len
, bool len_is_bytes
)
894 *blen
= (len
* 3 + 3) / 4;
900 parse_6bit_ascii(char *p
, tvbuff_t
*tvb
, unsigned offs
, unsigned len
)
905 /* First, handle "full" triplets of bytes, 4 characters each */
906 for (i
= 0; i
< len
/ 4; i
++) {
907 v
= tvb_get_letoh24(tvb
, offs
+ i
* 3);
908 p
[0] = ' ' + (v
& 0x3f);
909 p
[1] = ' ' + ((v
>> 6) & 0x3f);
910 p
[2] = ' ' + ((v
>> 12) & 0x3f);
911 p
[3] = ' ' + ((v
>> 18) & 0x3f);
915 /* Do we have any characters left? */
920 v
= (tvb_get_uint8(tvb
, offs
+ 2) << 4) | (tvb_get_uint8(tvb
, offs
+ 1) >> 4);
921 p
[2] = ' ' + (v
& 0x3f);
924 v
= (tvb_get_uint8(tvb
, offs
+ 1) << 2) | (tvb_get_uint8(tvb
, offs
) >> 6);
925 p
[1] = ' ' + (v
& 0x3f);
928 v
= tvb_get_uint8(tvb
, offs
) & 0x3f;
929 p
[0] = ' ' + (v
& 0x3f);
933 static struct ipmi_parse_typelen ptl_6bit_ascii
= {
934 get_len_6bit_ascii
, parse_6bit_ascii
, "6-bit ASCII"
938 get_len_8bit_ascii(unsigned *clen
, unsigned *blen
, tvbuff_t
*tvb
, unsigned offs
,
939 unsigned len
, bool len_is_bytes _U_
)
944 *blen
= len
; /* One byte is one character */
946 for (i
= 0; i
< len
; i
++) {
947 ch
= tvb_get_uint8(tvb
, offs
+ i
);
948 *clen
+= (ch
>= 0x20 && ch
<= 0x7f) ? 1 : 4;
953 parse_8bit_ascii(char *p
, tvbuff_t
*tvb
, unsigned offs
, unsigned len
)
960 ch
= tvb_get_uint8(tvb
, offs
++);
961 if (ch
>= 0x20 && ch
<= 0x7f) {
964 snprintf(p
, 5, "\\x%02x", ch
);
970 static struct ipmi_parse_typelen ptl_8bit_ascii
= {
971 get_len_8bit_ascii
, parse_8bit_ascii
, "ASCII+Latin1"
975 get_len_unicode(unsigned *clen
, unsigned *blen
, tvbuff_t
*tvb _U_
, unsigned offs _U_
,
976 unsigned len _U_
, bool len_is_bytes
)
979 *clen
= len
* 3; /* Each 2 bytes result in 6 chars printed: \Uxxxx */
988 parse_unicode(char *p
, tvbuff_t
*tvb
, unsigned offs
, unsigned len
)
990 char *pmax
= p
+ len
;
994 ch0
= tvb_get_uint8(tvb
, offs
++);
995 ch1
= tvb_get_uint8(tvb
, offs
++);
996 snprintf(p
, 7, "\\U%02x%02x", ch0
, ch1
);
1001 static struct ipmi_parse_typelen ptl_unicode
= {
1002 get_len_unicode
, parse_unicode
, "Unicode"
1006 ipmi_add_typelen(packet_info
*pinfo
, proto_tree
*tree
, int hf_string
, int hf_type
, int hf_length
, tvbuff_t
*tvb
,
1007 unsigned offs
, bool is_fru
)
1009 static struct ipmi_parse_typelen
*fru_eng
[4] = {
1010 &ptl_binary
, &ptl_bcdplus
, &ptl_6bit_ascii
, &ptl_8bit_ascii
1012 static struct ipmi_parse_typelen
*fru_noneng
[4] = {
1013 &ptl_binary
, &ptl_bcdplus
, &ptl_6bit_ascii
, &ptl_unicode
1015 static struct ipmi_parse_typelen
*ipmi
[4] = {
1016 &ptl_unicode
, &ptl_bcdplus
, &ptl_6bit_ascii
, &ptl_8bit_ascii
1018 struct ipmi_parse_typelen
*ptr
;
1020 unsigned type
, msk
, clen
, blen
, len
;
1025 typelen
= tvb_get_uint8(tvb
, offs
);
1026 type
= typelen
>> 6;
1029 ptr
= (fru_langcode_is_english
? fru_eng
: fru_noneng
)[type
];
1034 unit
= "characters";
1037 len
= typelen
& msk
;
1038 ptr
->get_len(&clen
, &blen
, tvb
, offs
+ 1, len
, is_fru
);
1040 str
= (char *)wmem_alloc(pinfo
->pool
, clen
+ 1);
1041 ptr
->parse(str
, tvb
, offs
+ 1, clen
);
1044 s_tree
= proto_tree_add_subtree_format(tree
, tvb
, offs
, 1, ett_typelen
, NULL
,
1045 "%s Type/Length byte: %s, %d %s", (proto_registrar_get_nth(hf_string
))->name
, ptr
->desc
, len
, unit
);
1046 proto_tree_add_uint_format_value(s_tree
, hf_type
, tvb
, offs
, 1, type
, "%s (0x%02x)",
1048 proto_tree_add_uint_format_value(s_tree
, hf_length
, tvb
, offs
, 1, len
, "%d %s",
1051 proto_tree_add_string_format_value(tree
, hf_string
, tvb
, offs
+ 1, blen
, str
,
1052 "[%s] '%s'", ptr
->desc
, str
);
1055 /* ----------------------------------------------------------------
1056 Timestamp, IPMI-style.
1057 ---------------------------------------------------------------- */
1059 ipmi_add_timestamp(packet_info
*pinfo
, proto_tree
*tree
, int hf
, tvbuff_t
*tvb
, unsigned offset
)
1061 uint32_t ts
= tvb_get_letohl(tvb
, offset
);
1063 if (ts
== 0xffffffff) {
1064 proto_tree_add_uint_format_value(tree
, hf
, tvb
, offset
, 4,
1065 ts
, "Unspecified/Invalid");
1066 } else if (ts
<= 0x20000000) {
1067 proto_tree_add_uint_format_value(tree
, hf
, tvb
, offset
, 4,
1068 ts
, "%s since SEL device's initialization",
1069 unsigned_time_secs_to_str(pinfo
->pool
, ts
));
1071 proto_tree_add_uint_format_value(tree
, hf
, tvb
, offset
, 4,
1072 ts
, "%s", abs_time_secs_to_str(pinfo
->pool
, ts
, ABSOLUTE_TIME_UTC
, true));
1076 /* ----------------------------------------------------------------
1078 ---------------------------------------------------------------- */
1081 ipmi_add_guid(proto_tree
*tree
, int hf
, tvbuff_t
*tvb
, unsigned offset
)
1086 guid
.data1
= tvb_get_letohl(tvb
, offset
+ 12);
1087 guid
.data2
= tvb_get_letohs(tvb
, offset
+ 10);
1088 guid
.data3
= tvb_get_letohs(tvb
, offset
+ 8);
1089 for (i
= 0; i
< 8; i
++) {
1090 guid
.data4
[i
] = tvb_get_uint8(tvb
, offset
+ 7 - i
);
1092 proto_tree_add_guid(tree
, hf
, tvb
, offset
, 16, &guid
);
1095 /* ----------------------------------------------------------------
1096 Routines for registering/looking up command parsers.
1097 ---------------------------------------------------------------- */
1100 ipmi_netfn_setdesc(uint32_t netfn
, const char *desc
, uint32_t siglen
)
1102 struct ipmi_netfn_root
*inr
;
1104 inr
= &ipmi_cmd_tab
[netfn
>> 1];
1106 inr
->siglen
= siglen
;
1110 ipmi_register_netfn_cmdtab(uint32_t netfn
, unsigned oem_selector
,
1111 const uint8_t *sig
, uint32_t siglen
, const char *desc
,
1112 const ipmi_cmd_t
*cmdtab
, uint32_t cmdtablen
)
1114 struct ipmi_netfn_root
*inr
;
1117 netfn
>>= 1; /* Requests and responses grouped together */
1118 if (netfn
>= IPMI_NETFN_MAX
) {
1122 inr
= &ipmi_cmd_tab
[netfn
];
1123 if (inr
->siglen
!= siglen
) {
1127 inh
= wmem_new(wmem_epan_scope(), struct ipmi_netfn_handler
);
1129 inh
->oem_selector
= oem_selector
;
1131 inh
->cmdtab
= cmdtab
;
1132 inh
->cmdtablen
= cmdtablen
;
1134 inh
->next
= inr
->list
;
1139 ipmi_getsiglen(uint32_t netfn
)
1141 return ipmi_cmd_tab
[netfn
>> 1].siglen
;
1145 ipmi_getnetfnname(wmem_allocator_t
*pool
, uint32_t netfn
, ipmi_netfn_t
*nf
)
1147 const char *dn
, *db
;
1149 dn
= ipmi_cmd_tab
[netfn
>> 1].desc
?
1150 ipmi_cmd_tab
[netfn
>> 1].desc
: "Reserved";
1151 db
= nf
? nf
->desc
: NULL
;
1153 return wmem_strdup_printf(pool
, "%s (%s)", db
, dn
);
1160 ipmi_getnetfn(uint32_t netfn
, const uint8_t *sig
)
1162 struct ipmi_netfn_root
*inr
;
1165 inr
= &ipmi_cmd_tab
[netfn
>> 1];
1166 for (inh
= inr
->list
; inh
; inh
= inh
->next
) {
1167 if ((inh
->oem_selector
== selected_oem
|| inh
->oem_selector
== IPMI_OEM_NONE
)
1168 && (!inr
->siglen
|| !memcmp(sig
, inh
->sig
, inr
->siglen
))) {
1173 /* Either unknown netFn or signature does not match */
1178 ipmi_getcmd(ipmi_netfn_t
*nf
, uint32_t cmd
)
1180 static const ipmi_cmd_t ipmi_cmd_unknown
= {
1182 ipmi_notimpl
, /* request */
1183 ipmi_notimpl
, /* response */
1184 NULL
, /* command codes */
1185 NULL
, /* subfunctions */
1189 const ipmi_cmd_t
*ic
;
1193 len
= nf
->cmdtablen
;
1194 for (ic
= nf
->cmdtab
, i
= 0; i
< len
; i
++, ic
++) {
1195 if (ic
->cmd
== cmd
) {
1201 return &ipmi_cmd_unknown
;
1204 /* ----------------------------------------------------------------
1205 Various utility functions.
1206 ---------------------------------------------------------------- */
1209 ipmi_notimpl(tvbuff_t
*tvb
, packet_info
*pinfo _U_
, proto_tree
*tree
)
1211 proto_tree_add_expert(tree
, pinfo
, &ei_impi_parser_not_implemented
, tvb
, 0, -1);
1215 ipmi_fmt_10ms_1based(char *s
, uint32_t v
)
1217 snprintf(s
, ITEM_LABEL_LENGTH
, "%d.%03d seconds", v
/ 100, (v
% 100) * 10);
1221 ipmi_fmt_500ms_0based(char *s
, uint32_t v
)
1223 ipmi_fmt_500ms_1based(s
, ++v
);
1227 ipmi_fmt_500ms_1based(char *s
, uint32_t v
)
1229 snprintf(s
, ITEM_LABEL_LENGTH
, "%d.%03d seconds", v
/ 2, (v
% 2) * 500);
1233 ipmi_fmt_1s_0based(char *s
, uint32_t v
)
1235 ipmi_fmt_1s_1based(s
, ++v
);
1239 ipmi_fmt_1s_1based(char *s
, uint32_t v
)
1241 snprintf(s
, ITEM_LABEL_LENGTH
, "%d seconds", v
);
1245 ipmi_fmt_2s_0based(char *s
, uint32_t v
)
1247 snprintf(s
, ITEM_LABEL_LENGTH
, "%d seconds", (v
+ 1) * 2);
1251 ipmi_fmt_5s_1based(char *s
, uint32_t v
)
1253 snprintf(s
, ITEM_LABEL_LENGTH
, "%d seconds", v
* 5);
1257 ipmi_fmt_version(char *s
, uint32_t v
)
1259 snprintf(s
, ITEM_LABEL_LENGTH
, "%d.%d", v
& 0x0f, (v
>> 4) & 0x0f);
1263 ipmi_fmt_channel(char *s
, uint32_t v
)
1265 static const value_string chan_vals
[] = {
1266 { 0x00, "Primary IPMB (IPMB-0)" },
1268 { 0x0e, "Current channel" },
1269 { 0x0f, "System Interface" },
1274 tmp_str
= val_to_str_wmem(NULL
, v
, chan_vals
, "Channel #%d");
1275 snprintf(s
, ITEM_LABEL_LENGTH
, "%s (0x%02x)", tmp_str
, v
);
1276 wmem_free(NULL
, tmp_str
);
1280 ipmi_fmt_udpport(char *s
, uint32_t v
)
1282 char* port_str
= udp_port_to_display(NULL
, v
);
1283 snprintf(s
, ITEM_LABEL_LENGTH
, "%s (%d)", port_str
, v
);
1284 wmem_free(NULL
, port_str
);
1288 ipmi_fmt_percent(char *s
, uint32_t v
)
1290 snprintf(s
, ITEM_LABEL_LENGTH
, "%d%%", v
);
1294 ipmi_get_completion_code(uint8_t completion
, const ipmi_cmd_t
*cmd
)
1296 static const value_string std_completion_codes
[] = {
1297 { 0x00, "Command Completed Normally" },
1298 { 0xc0, "Node Busy" },
1299 { 0xc1, "Invalid Command" },
1300 { 0xc2, "Command invalid for given LUN" },
1301 { 0xc3, "Timeout while processing command, response unavailable" },
1302 { 0xc4, "Out of space" },
1303 { 0xc5, "Reservation Canceled or Invalid Reservation ID" },
1304 { 0xc6, "Request data truncated" },
1305 { 0xc7, "Request data length invalid" },
1306 { 0xc8, "Request data field length limit exceeded" },
1307 { 0xc9, "Parameter out of range" },
1308 { 0xca, "Cannot return number of requested data bytes" },
1309 { 0xcb, "Requested Sensor, data, or record not present" },
1310 { 0xcc, "Invalid data field in Request" },
1311 { 0xcd, "Command illegal for specified sensor or record type" },
1312 { 0xce, "Command response could not be provided" },
1313 { 0xcf, "Cannot execute duplicated request" },
1314 { 0xd0, "Command response could not be provided: SDR Repository in update mode" },
1315 { 0xd1, "Command response could not be provided: device in firmware update mode" },
1316 { 0xd2, "Command response could not be provided: BMC initialization or initialization agent in progress" },
1317 { 0xd3, "Destination unavailable" },
1318 { 0xd4, "Cannot execute command: insufficient privilege level or other security-based restriction" },
1319 { 0xd5, "Cannot execute command: command, or request parameter(s), not supported in present state" },
1320 { 0xd6, "Cannot execute command: parameter is illegal because subfunction is disabled or unavailable" },
1321 { 0xff, "Unspecified error" },
1327 if (completion
>= 0x01 && completion
<= 0x7e) {
1328 return "Device specific (OEM) completion code";
1331 if (completion
>= 0x80 && completion
<= 0xbe) {
1332 if (cmd
&& cmd
->cs_cc
&& (res
= try_val_to_str(completion
, cmd
->cs_cc
)) != NULL
) {
1335 return "Standard command-specific code";
1338 return val_to_str_const(completion
, std_completion_codes
, "Unknown");
1342 dissect_tmode(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
1344 ipmi_dissect_arg_t
* arg
= (ipmi_dissect_arg_t
*) data
;
1346 unsigned tvb_len
= tvb_captured_length(tvb
);
1349 /* TMode message is at least 3 bytes length */
1354 memset(&ctx
, 0, sizeof(ctx
));
1356 /* get Net Fn/RS LUN field */
1357 tmp
= tvb_get_uint8(tvb
, 0);
1360 ctx
.hdr
.netfn
= tmp
>> 2;
1363 * NOTE: request/response matching code swaps RQ LUN with RS LUN
1364 * fields in IPMB-like manner in order to find corresponding request
1365 * so, we set both RS LUN and RQ LUN here for correct
1366 * request/response matching
1368 ctx
.hdr
.rq_lun
= tmp
& 3;
1369 ctx
.hdr
.rs_lun
= tmp
& 3;
1371 /* get RQ Seq field */
1372 ctx
.hdr
.rq_seq
= tvb_get_uint8(tvb
, 1) >> 2;
1375 * NOTE: bridge field is ignored in request/response matching
1378 /* get command code */
1379 ctx
.hdr
.cmd
= tvb_get_uint8(tvb
, 2);
1381 /* set dissect flags */
1382 ctx
.flags
= IPMI_D_TMODE
|IPMI_D_NO_CKS
|IPMI_D_NO_RQ_SA
;
1384 /* set header length */
1387 /* copy channel number and direction */
1388 ctx
.hdr
.context
= arg
? arg
->context
: IPMI_E_NONE
;
1389 ctx
.hdr
.channel
= arg
? arg
->channel
: 0;
1390 ctx
.hdr
.dir
= arg
? arg
->flags
>> 7 : ctx
.hdr
.netfn
& 1;
1392 if (ctx
.hdr
.context
== IPMI_E_NONE
) {
1393 /* set source column */
1394 col_set_str(pinfo
->cinfo
, COL_DEF_SRC
,
1395 ctx
.hdr
.dir
? "Console" : "BMC");
1397 /* set destination column */
1398 col_set_str(pinfo
->cinfo
, COL_DEF_DST
,
1399 ctx
.hdr
.dir
? "BMC" : "Console");
1402 /* dissect IPMI command */
1403 return dissect_ipmi_cmd(tvb
, pinfo
, tree
, proto_tmode
, ett_ipmi
, &ctx
);
1407 dissect_kcs(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
1409 ipmi_dissect_arg_t
* arg
= (ipmi_dissect_arg_t
*) data
;
1411 unsigned tvb_len
= tvb_captured_length(tvb
);
1414 /* KCS message is at least 2 bytes length */
1419 memset(&ctx
, 0, sizeof(ctx
));
1421 /* get Net Fn/RS LUN field */
1422 tmp
= tvb_get_uint8(tvb
, 0);
1425 ctx
.hdr
.netfn
= tmp
>> 2;
1428 * NOTE: request/response matching code swaps RQ LUN with RS LUN
1429 * fields in IPMB-like manner in order to find corresponding request
1430 * so, we set both RS LUN and RQ LUN here for correct
1431 * request/response matching
1433 ctx
.hdr
.rq_lun
= tmp
& 3;
1434 ctx
.hdr
.rs_lun
= tmp
& 3;
1436 /* get command code */
1437 ctx
.hdr
.cmd
= tvb_get_uint8(tvb
, 1);
1439 /* set dissect flags */
1440 ctx
.flags
= IPMI_D_NO_CKS
|IPMI_D_NO_RQ_SA
|IPMI_D_NO_SEQ
;
1442 /* set header length */
1445 /* copy channel number and direction */
1446 ctx
.hdr
.context
= arg
? arg
->context
: 0;
1447 ctx
.hdr
.channel
= arg
? arg
->channel
: 0;
1448 ctx
.hdr
.dir
= arg
? arg
->flags
>> 7 : ctx
.hdr
.netfn
& 1;
1450 if (ctx
.hdr
.context
== IPMI_E_NONE
) {
1451 /* set source column */
1452 col_set_str(pinfo
->cinfo
, COL_DEF_SRC
, ctx
.hdr
.dir
? "HOST" : "BMC");
1454 /* set destination column */
1455 col_set_str(pinfo
->cinfo
, COL_DEF_DST
, ctx
.hdr
.dir
? "BMC" : "HOST");
1458 /* dissect IPMI command */
1459 return dissect_ipmi_cmd(tvb
, pinfo
, tree
, proto_kcs
, ett_ipmi
, &ctx
);
1462 static uint8_t calc_cks(uint8_t start
, tvbuff_t
* tvb
, unsigned off
, unsigned len
)
1465 start
+= tvb_get_uint8(tvb
, off
++);
1471 static bool guess_imb_format(tvbuff_t
*tvb
, uint8_t env
,
1472 uint8_t channel
, unsigned * imb_flags
, uint8_t * cks1
, uint8_t * cks2
)
1474 bool check_bc
= false;
1475 bool check_sh
= false;
1476 bool check_sa
= false;
1482 if (message_format
== MSGFMT_NONE
) {
1484 } else if (message_format
== MSGFMT_IPMB
) {
1485 *imb_flags
= IPMI_D_TRG_SA
;
1486 } else if (message_format
== MSGFMT_LAN
) {
1487 *imb_flags
= IPMI_D_TRG_SA
|IPMI_D_SESSION_HANDLE
;
1488 /* channel 0 is primary IPMB */
1489 } else if (!channel
) {
1490 /* check for broadcast if not in send message command */
1491 if (env
== IPMI_E_NONE
) {
1492 /* check broadcast */
1495 /* slave address must be present */
1496 *imb_flags
= IPMI_D_TRG_SA
;
1497 /* check if in send message command */
1498 } else if (env
!= IPMI_E_GETMSG
) {
1499 /* slave address must be present */
1500 *imb_flags
= IPMI_D_TRG_SA
;
1501 } else /* IPMI_E_GETMSG */ {
1504 /* channel 15 is System Interface */
1505 } else if (channel
== 15) {
1506 /* slave address must be present */
1507 *imb_flags
= IPMI_D_TRG_SA
;
1509 /* check if in get message command */
1510 if (env
== IPMI_E_GETMSG
) {
1511 /* session handle must be present */
1512 *imb_flags
|= IPMI_D_SESSION_HANDLE
;
1514 /* for other channels */
1516 if (env
== IPMI_E_NONE
) {
1517 /* check broadcast */
1520 /* slave address must be present */
1521 *imb_flags
= IPMI_D_TRG_SA
;
1522 } else if (env
== IPMI_E_SENDMSG_RQ
) {
1523 /* check session handle */
1526 /* slave address must be present */
1527 *imb_flags
= IPMI_D_TRG_SA
;
1528 } else if (env
== IPMI_E_SENDMSG_RS
) {
1529 /* slave address must be present */
1530 *imb_flags
= IPMI_D_TRG_SA
;
1531 } else /* IPMI_E_GETMSG */ {
1532 /* check session handle */
1535 /* check slave address presence */
1538 /* no pre-requisites */
1543 /* get message length */
1544 tvb_len
= tvb_captured_length(tvb
);
1547 * broadcast message starts with null,
1548 * does not contain session handle
1549 * but contains responder address
1553 && !tvb_get_uint8(tvb
, 0)
1554 && !calc_cks(0, tvb
, 1, 3)
1555 && !calc_cks(0, tvb
, 4, tvb_len
- 4)) {
1556 *imb_flags
= IPMI_D_BROADCAST
|IPMI_D_TRG_SA
;
1563 * message with the starts with session handle
1564 * and contain responder address
1568 && !calc_cks(0, tvb
, 1, 3)
1569 && !calc_cks(0, tvb
, 4, tvb_len
- 4)) {
1570 *imb_flags
= IPMI_D_SESSION_HANDLE
|IPMI_D_TRG_SA
;
1577 * message with responder address
1581 && !calc_cks(0, tvb
, 0, 3)
1582 && !calc_cks(0, tvb
, 3, tvb_len
- 3)) {
1583 *imb_flags
= IPMI_D_TRG_SA
;
1590 if (*imb_flags
& IPMI_D_SESSION_HANDLE
) {
1594 } else if (*imb_flags
& IPMI_D_TRG_SA
) {
1604 /* check message length */
1605 if (tvb_len
< 6 + sh_len
+ sa_len
) {
1609 /* calculate checksum deltas */
1610 *cks1
= calc_cks(rs_sa
, tvb
, sh_len
, sa_len
+ 2);
1611 *cks2
= calc_cks(0, tvb
, sh_len
+ sa_len
+ 2,
1612 tvb_len
- sh_len
- sa_len
- 2);
1618 do_dissect_ipmb(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
,
1619 int hf_parent_item
, int ett_tree
, ipmi_dissect_arg_t
* arg
)
1622 unsigned offset
= 0;
1625 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "IPMB");
1627 memset(&ctx
, 0, sizeof(ctx
));
1629 /* copy message context and channel */
1630 ctx
.hdr
.context
= arg
? arg
->context
: 0;
1631 ctx
.hdr
.channel
= arg
? arg
->channel
: 0;
1633 /* guess IPMB message format */
1634 if (!guess_imb_format(tvb
, ctx
.hdr
.context
, ctx
.hdr
.channel
,
1635 &ctx
.flags
, &ctx
.cks1
, &ctx
.cks2
)) {
1639 /* check if message is broadcast */
1640 if (ctx
.flags
& IPMI_D_BROADCAST
) {
1641 /* skip first byte */
1645 /* check is session handle is specified */
1646 if (ctx
.flags
& IPMI_D_SESSION_HANDLE
) {
1647 ctx
.hdr
.session
= tvb_get_uint8(tvb
, offset
++);
1650 /* check is response address is specified */
1651 if (ctx
.flags
& IPMI_D_TRG_SA
) {
1652 ctx
.hdr
.rs_sa
= tvb_get_uint8(tvb
, offset
++);
1654 ctx
.hdr
.rs_sa
= 0x20;
1657 /* get Net Fn/RS LUN field */
1658 tmp
= tvb_get_uint8(tvb
, offset
++);
1660 /* set Net Fn and RS LUN */
1661 ctx
.hdr
.netfn
= tmp
>> 2;
1662 ctx
.hdr
.rs_lun
= tmp
& 3;
1668 ctx
.hdr
.rq_sa
= tvb_get_uint8(tvb
, offset
++);
1670 /* get RQ Seq/RQ LUN field */
1671 tmp
= tvb_get_uint8(tvb
, offset
++);
1673 /* set RQ Seq and RQ LUN */
1674 ctx
.hdr
.rq_seq
= tmp
>> 2;
1675 ctx
.hdr
.rq_lun
= tmp
& 3;
1677 /* get command code */
1678 ctx
.hdr
.cmd
= tvb_get_uint8(tvb
, offset
++);
1680 /* set header length */
1681 ctx
.hdr_len
= offset
;
1683 /* copy direction */
1684 ctx
.hdr
.dir
= arg
? arg
->flags
>> 7 : ctx
.hdr
.netfn
& 1;
1686 if (ctx
.hdr
.context
== IPMI_E_NONE
) {
1687 unsigned red
= arg
? (arg
->flags
& 0x40) : 0;
1689 if (!ctx
.hdr
.channel
) {
1690 col_add_fstr(pinfo
->cinfo
, COL_DEF_SRC
,
1691 "0x%02x(%s)", ctx
.hdr
.rq_sa
, red
? "IPMB-B" : "IPMB-A");
1693 col_add_fstr(pinfo
->cinfo
, COL_DEF_SRC
,
1694 "0x%02x", ctx
.hdr
.rq_sa
);
1697 col_add_fstr(pinfo
->cinfo
, COL_DEF_DST
, "0x%02x", ctx
.hdr
.rs_sa
);
1700 /* dissect IPMI command */
1701 return dissect_ipmi_cmd(tvb
, pinfo
, tree
, hf_parent_item
, ett_tree
, &ctx
);
1705 dissect_ipmi(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
1707 return do_dissect_ipmb(tvb
, pinfo
, tree
, proto_ipmb
, ett_ipmi
,
1708 (ipmi_dissect_arg_t
*) data
);
1712 dissect_i2c_ipmi(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
1714 if (pinfo
->pseudo_header
->i2c
.flags
& 0x00000001) {
1715 /* Master-receive transactions are not possible on IPMB */
1719 return do_dissect_ipmb(tvb
, pinfo
, tree
, proto_ipmb
, ett_ipmi
,
1720 (ipmi_dissect_arg_t
*) data
);
1724 /* Register IPMB protocol.
1727 proto_register_ipmi(void)
1729 static hf_register_info hf
[] = {
1730 { &hf_ipmi_command_data
, { "Bus command data", "ipmi.bus_command_data", FT_BYTES
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}},
1731 { &hf_ipmi_session_handle
, { "Session handle", "ipmi.session_handle", FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}},
1732 { &hf_ipmi_header_trg
, { "Target Address", "ipmi.header.target", FT_UINT8
, BASE_HEX
, NULL
, 0x0, NULL
, HFILL
}},
1733 { &hf_ipmi_header_trg_lun
, { "Target LUN", "ipmi.header.trg_lun", FT_UINT8
, BASE_HEX
, NULL
, 0x03, NULL
, HFILL
}},
1734 { &hf_ipmi_header_netfn
, { "NetFN", "ipmi.header.netfn", FT_UINT8
, BASE_HEX
, NULL
, 0xfc, NULL
, HFILL
}},
1735 { &hf_ipmi_header_crc
, { "Header Checksum", "ipmi.header.crc", FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}},
1736 { &hf_ipmi_header_src
, { "Source Address", "ipmi.header.source", FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}},
1737 { &hf_ipmi_header_src_lun
, { "Source LUN", "ipmi.header.src_lun", FT_UINT8
, BASE_HEX
, NULL
, 0x03, NULL
, HFILL
}},
1738 { &hf_ipmi_header_bridged
, { "Bridged", "ipmi.header.bridged", FT_UINT8
, BASE_HEX
, NULL
, 0x03, NULL
, HFILL
}},
1739 { &hf_ipmi_header_sequence
, { "Sequence Number", "ipmi.header.sequence", FT_UINT8
, BASE_HEX
, NULL
, 0xfc, NULL
, HFILL
}},
1740 { &hf_ipmi_header_command
, { "Command", "ipmi.header.command", FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}},
1741 { &hf_ipmi_header_completion
, { "Completion Code", "ipmi.header.completion", FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}},
1742 { &hf_ipmi_header_sig
, { "Signature", "ipmi.header.signature", FT_BYTES
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}},
1743 { &hf_ipmi_data_crc
, { "Data checksum", "ipmi.data.crc", FT_UINT8
, BASE_HEX
, NULL
, 0, NULL
, HFILL
}},
1744 { &hf_ipmi_response_to
, { "Response to", "ipmi.response_to", FT_FRAMENUM
, BASE_NONE
, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST
), 0, NULL
, HFILL
}},
1745 { &hf_ipmi_response_in
, { "Response in", "ipmi.response_in", FT_FRAMENUM
, BASE_NONE
, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE
), 0, NULL
, HFILL
}},
1746 { &hf_ipmi_response_time
, { "Responded in", "ipmi.response_time", FT_RELATIVE_TIME
, BASE_NONE
, NULL
, 0, NULL
, HFILL
}}
1748 static int *ett
[] = {
1756 static const enum_val_t msgfmt_vals
[] = {
1757 { "none", "None", MSGFMT_NONE
},
1758 { "ipmb", "IPMB", MSGFMT_IPMB
},
1759 { "lan", "Session-based (LAN, ...)", MSGFMT_LAN
},
1760 { "guess", "Use heuristics", MSGFMT_GUESS
},
1763 static const enum_val_t oemsel_vals
[] = {
1764 { "none", "None", IPMI_OEM_NONE
},
1765 { "pps", "Pigeon Point Systems", IPMI_OEM_PPS
},
1769 static ei_register_info ei
[] = {
1770 { &ei_impi_parser_not_implemented
, { "ipmi.parser_not_implemented", PI_UNDECODED
, PI_WARN
, "[PARSER NOT IMPLEMENTED]", EXPFILL
}},
1774 expert_module_t
* expert_ipmi
;
1777 proto_ipmi
= proto_register_protocol("Intelligent Platform Management Interface",
1781 proto_ipmb
= proto_register_protocol("Intelligent Platform Management Bus",
1784 proto_kcs
= proto_register_protocol("Keyboard Controller Style Interface",
1787 proto_tmode
= proto_register_protocol("Serial Terminal Mode Interface",
1791 proto_register_field_array(proto_ipmi
, hf
, array_length(hf
));
1792 proto_register_subtree_array(ett
, array_length(ett
));
1794 expert_ipmi
= expert_register_protocol(proto_ipmi
);
1795 expert_register_field_array(expert_ipmi
, ei
, array_length(ei
));
1797 ipmi_netfn_setdesc(IPMI_CHASSIS_REQ
, "Chassis", 0);
1798 ipmi_netfn_setdesc(IPMI_BRIDGE_REQ
, "Bridge", 0);
1799 ipmi_netfn_setdesc(IPMI_SE_REQ
, "Sensor/Event", 0);
1800 ipmi_netfn_setdesc(IPMI_APP_REQ
, "Application", 0);
1801 ipmi_netfn_setdesc(IPMI_UPDATE_REQ
, "Firmware Update", 0);
1802 ipmi_netfn_setdesc(IPMI_STORAGE_REQ
, "Storage", 0);
1803 ipmi_netfn_setdesc(IPMI_TRANSPORT_REQ
, "Transport", 0);
1804 ipmi_netfn_setdesc(IPMI_GROUP_REQ
, "Group", 1);
1805 ipmi_netfn_setdesc(IPMI_OEM_REQ
, "OEM/Group", 3);
1806 for (i
= 0x30; i
< 0x40; i
+= 2) {
1807 ipmi_netfn_setdesc(i
, "OEM", 0);
1810 register_dissector("ipmi", dissect_ipmi
, proto_ipmi
);
1811 ipmi_i2c_handle
= register_dissector("ipmi.i2c", dissect_i2c_ipmi
, proto_ipmi
);
1812 register_dissector("ipmb", dissect_ipmi
, proto_ipmb
);
1813 register_dissector("kcs", dissect_kcs
, proto_kcs
);
1814 register_dissector("tmode", dissect_tmode
, proto_tmode
);
1816 module
= prefs_register_protocol(proto_ipmi
, NULL
);
1817 prefs_register_bool_preference(module
, "dissect_bus_commands", "Dissect bus commands",
1818 "Dissect IPMB commands",
1819 &dissect_bus_commands
);
1820 prefs_register_bool_preference(module
, "fru_langcode_is_english", "FRU Language Code is English",
1821 "FRU Language Code is English; strings are ASCII+LATIN1 (vs. Unicode)",
1822 &fru_langcode_is_english
);
1823 prefs_register_uint_preference(module
, "response_after_req", "Maximum delay of response message",
1824 "Do not search for responses coming after this timeout (milliseconds)",
1825 10, &response_after_req
);
1826 prefs_register_uint_preference(module
, "response_before_req", "Response ahead of request",
1827 "Allow for responses before requests (milliseconds)",
1828 10, &response_before_req
);
1829 prefs_register_enum_preference(module
, "msgfmt", "Format of embedded messages",
1830 "Format of messages embedded into Send/Get/Forward Message",
1831 &message_format
, msgfmt_vals
, false);
1832 prefs_register_enum_preference(module
, "selected_oem", "OEM commands parsed as",
1833 "Selects which OEM format is used for commands that IPMI does not define",
1834 &selected_oem
, oemsel_vals
, false);
1837 void proto_reg_handoff_ipmi(void)
1839 dissector_add_for_decode_as("i2c.message", ipmi_i2c_handle
);
1843 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1848 * indent-tabs-mode: t
1851 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1852 * :indentSize=8:tabSize=8:noTabs=false: