TODO drsuapi compressed
[wireshark-sm.git] / epan / dissectors / packet-ipmi.c
blob12f47c36d00702e658b8721dd8f7b64973408802
1 /* packet-ipmi.c
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
12 #include "config.h"
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 {
39 ipmi_netfn_t *list;
40 const char *desc;
41 uint32_t siglen;
44 enum {
45 MSGFMT_NONE = 0,
46 MSGFMT_IPMB,
47 MSGFMT_LAN,
48 MSGFMT_GUESS
51 struct ipmi_parse_typelen {
52 void (*get_len)(unsigned *, unsigned *, tvbuff_t *, unsigned, unsigned, bool);
53 void (*parse)(char *, tvbuff_t *, unsigned, unsigned);
54 const char *desc;
57 /* IPMI parsing context */
58 typedef struct {
59 ipmi_header_t hdr;
60 unsigned hdr_len;
61 unsigned flags;
62 uint8_t cks1;
63 uint8_t cks2;
64 } ipmi_context_t;
66 /* Temporary request-response matching data. */
67 typedef struct {
68 /* Request header */
69 ipmi_header_t hdr;
70 /* Frame number where the request resides */
71 uint32_t frame_num;
72 /* Nest level of the request in the frame */
73 uint8_t nest_level;
74 } ipmi_request_t;
76 /* List of request-response matching data */
77 typedef wmem_list_t ipmi_request_list_t;
79 #define NSAVED_DATA 2
81 /* Per-command data */
82 typedef struct {
83 uint32_t matched_frame_num;
84 uint32_t saved_data[NSAVED_DATA];
85 } ipmi_cmd_data_t;
87 /* Per-frame data */
88 typedef struct {
89 ipmi_cmd_data_t * cmd_data[3];
90 nstime_t ts;
91 } ipmi_frame_data_t;
93 /* RB tree of frame data */
94 typedef wmem_tree_t ipmi_frame_tree_t;
96 /* cached dissector data */
97 typedef struct {
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 */
107 uint8_t curr_level;
108 /* subsequent nesting level */
109 uint8_t next_level;
110 /* top level message channel */
111 uint8_t curr_channel;
112 /* top level message direction */
113 uint8_t curr_dir;
114 /* pointer to current command */
115 const ipmi_header_t * curr_hdr;
116 /* current completion code */
117 uint8_t curr_ccode;
118 } ipmi_packet_data_t;
120 /* Maximum nest level where it worth caching data */
121 #define MAX_NEST_LEVEL 3
123 int proto_ipmi;
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;
155 static int ett_ipmi;
156 static int ett_header;
157 static int ett_header_byte_1;
158 static int ett_header_byte_4;
159 static int ett_data;
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);
178 if (!data) {
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;
196 return data;
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);
205 if (frame == NULL) {
206 frame = wmem_new0(wmem_file_scope(), ipmi_frame_data_t);
208 wmem_tree_insert32(data->frame_tree, frame_num, frame);
210 return frame;
213 static ipmi_request_t *
214 get_matched_request(ipmi_packet_data_t * data, const ipmi_header_t * rs_hdr,
215 unsigned flags)
217 wmem_list_frame_t * iter = wmem_list_head(data->request_list);
218 ipmi_header_t rq_hdr;
220 /* reset message context */
221 rq_hdr.context = 0;
223 /* copy channel */
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;
242 /* copy sequence */
243 rq_hdr.rq_seq = rs_hdr->rq_seq;
245 /* copy command */
246 rq_hdr.cmd = rs_hdr->cmd;
248 /* TODO: copy prefix bytes */
250 #ifdef DEBUG
251 fprintf(stderr, "%d, %d: rq_hdr : {\n"
252 "\tchannel=%d\n"
253 "\tdir=%d\n"
254 "\trs_sa=%x\n"
255 "\trs_lun=%d\n"
256 "\tnetfn=%x\n"
257 "\trq_sa=%x\n"
258 "\trq_lun=%d\n"
259 "\trq_seq=%x\n"
260 "\tcmd=%x\n}\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,
264 rq_hdr.cmd);
265 #endif
267 while (iter) {
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)) {
272 /* diregard rsSA */
273 rq_hdr.rq_sa = rq->hdr.rq_sa;
276 /* compare command headers */
277 if (!memcmp(&rq_hdr, &rq->hdr, sizeof(rq_hdr))) {
278 return rq;
281 /* proceed to next request */
282 iter = wmem_list_frame_next(iter);
285 return NULL;
288 static void
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);
293 while (iter) {
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);
296 nstime_t delta;
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);
312 } else {
313 break;
318 static void
319 match_request_response(ipmi_packet_data_t * data, const ipmi_header_t * hdr,
320 unsigned flags)
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 */
329 if (!rs_data) {
330 ipmi_request_t * rq;
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 */
339 if (rq) {
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;
367 static void
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 */
377 if (!rq_data) {
378 ipmi_request_t * rq;
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 */
393 rq->hdr.context = 0;
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);
406 #ifdef DEBUG
407 fprintf(stderr, "%d, %d: hdr : {\n"
408 "\tchannel=%d\n"
409 "\tdir=%d\n"
410 "\trs_sa=%x\n"
411 "\trs_lun=%d\n"
412 "\tnetfn=%x\n"
413 "\trq_sa=%x\n"
414 "\trq_lun=%d\n"
415 "\trq_seq=%x\n"
416 "\tcmd=%x\n}\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,
420 rq->hdr.cmd);
421 #endif
425 static void
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)
429 if (resp) {
430 col_add_fstr(pinfo->cinfo, COL_INFO, "Rsp, %s, %s (%02xh)",
431 cmd->desc, cc_str, cc_val);
432 } else {
433 col_add_fstr(pinfo->cinfo, COL_INFO, "Req, %s%s",
434 broadcast ? "Broadcast " : "", cmd->desc);
438 static int
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;
445 proto_item * ti;
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);
455 return 0;
458 /* get packet data */
459 data = get_packet_data(pinfo);
460 if (!data) {
461 return 0;
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 */
484 data->next_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);
507 } else {
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 */
521 if (is_resp) {
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);
527 } else {
528 cc_val = 0;
529 cc_str = NULL;
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);
539 if (tree) {
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);
544 } else {
545 char str[ITEM_LABEL_LENGTH];
547 if (is_resp) {
548 snprintf(str, ITEM_LABEL_LENGTH, "Rsp, %s, %s",
549 cmd->desc, cc_str);
550 } else {
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);
557 else
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) {
569 nstime_t ns;
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,
585 tvb, 0, 0, &ns);
587 /* mark field as a generated one */
588 proto_item_set_generated(ti);
590 } else {
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 */
607 offset = 0;
609 /* check if message is broadcast */
610 if (ctx->flags & IPMI_D_BROADCAST) {
611 /* skip first byte */
612 offset++;
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);
638 /* add Net Fn */
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 */
652 if (ctx->cks1) {
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);
658 } else {
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);
683 } else {
684 proto_tree_add_item(tmp_tree, hf_ipmi_header_src_lun,
685 tvb, offset, 1, ENC_LITTLE_ENDIAN);
688 /* print seq no */
689 proto_tree_add_item(tmp_tree, hf_ipmi_header_sequence, tvb,
690 offset++, 1, ENC_LITTLE_ENDIAN);
693 /* command code */
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);
698 if (is_resp) {
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);
705 if (siglen) {
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)
716 - ctx->hdr_len
717 - siglen
718 - (is_resp ? 1 : 0)
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)) {
745 uint8_t cks;
747 /* get cks2 offset */
748 offset = tvb_captured_length(tvb) - 1;
750 /* get cks2 */
751 cks = tvb_get_uint8(tvb, offset);
753 /* Header checksum */
754 if (ctx->cks2) {
755 uint8_t correct = cks - ctx->cks2;
757 proto_tree_add_uint_format_value(cmd_tree, hf_ipmi_data_crc,
758 tvb, offset, 1, cks,
759 "0x%02x (incorrect, expected 0x%02x)", cks, correct);
760 } else {
761 proto_tree_add_uint_format_value(cmd_tree, hf_ipmi_data_crc,
762 tvb, offset, 1, cks,
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);
795 /* check bounds */
796 if (data->curr_level >= MAX_NEST_LEVEL || idx >= NSAVED_DATA || !data->curr_frame ) {
797 return;
800 /* save data */
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);
809 /* check bounds */
810 if (data->curr_level >= MAX_NEST_LEVEL || idx >= NSAVED_DATA || !data->curr_frame ) {
811 return false;
814 /* get data */
815 *value = data->curr_frame->cmd_data[data->curr_level]->saved_data[idx];
816 return true;
819 /* ----------------------------------------------------------------
820 Support for Type/Length fields parsing.
821 ---------------------------------------------------------------- */
823 static void
824 get_len_binary(unsigned *clen, unsigned *blen, tvbuff_t *tvb _U_, unsigned offs _U_,
825 unsigned len, bool len_is_bytes _U_)
827 *clen = len * 3;
828 *blen = len;
831 static void
832 parse_binary(char *p, tvbuff_t *tvb, unsigned offs, unsigned len)
834 static const char hex[] = "0123456789ABCDEF";
835 uint8_t v;
836 unsigned i;
838 for (i = 0; i < len / 3; i++) {
839 v = tvb_get_uint8(tvb, offs + i);
840 *p++ = hex[v >> 4];
841 *p++ = hex[v & 0xf];
842 *p++ = ' ';
845 if (i) {
846 *--p = '\0';
850 static struct ipmi_parse_typelen ptl_binary = {
851 get_len_binary, parse_binary, "Binary"
854 static void
855 get_len_bcdplus(unsigned *clen, unsigned *blen, tvbuff_t *tvb _U_, unsigned offs _U_,
856 unsigned len, bool len_is_bytes)
858 if (len_is_bytes) {
859 *clen = len * 2;
860 *blen = len;
861 } else {
862 *blen = (len + 1) / 2;
863 *clen = len;
867 static void
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;
872 uint8_t v;
874 for (i = 0; i < len; i++) {
875 v = (tvb_get_uint8(tvb, offs + i / 2) & msk) >> shft;
876 *p++ = bcd[v];
877 msk ^= 0xff;
878 shft = 4 - shft;
882 static struct ipmi_parse_typelen ptl_bcdplus = {
883 get_len_bcdplus, parse_bcdplus, "BCD+"
886 static void
887 get_len_6bit_ascii(unsigned *clen, unsigned *blen, tvbuff_t *tvb _U_, unsigned offs _U_,
888 unsigned len, bool len_is_bytes)
890 if (len_is_bytes) {
891 *clen = len * 4 / 3;
892 *blen = len;
893 } else {
894 *blen = (len * 3 + 3) / 4;
895 *clen = len;
899 static void
900 parse_6bit_ascii(char *p, tvbuff_t *tvb, unsigned offs, unsigned len)
902 uint32_t v;
903 unsigned i;
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);
912 p += 4;
915 /* Do we have any characters left? */
916 offs += len / 4;
917 len &= 0x3;
918 switch (len) {
919 case 3:
920 v = (tvb_get_uint8(tvb, offs + 2) << 4) | (tvb_get_uint8(tvb, offs + 1) >> 4);
921 p[2] = ' ' + (v & 0x3f);
922 /* Fall thru */
923 case 2:
924 v = (tvb_get_uint8(tvb, offs + 1) << 2) | (tvb_get_uint8(tvb, offs) >> 6);
925 p[1] = ' ' + (v & 0x3f);
926 /* Fall thru */
927 case 1:
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"
937 static void
938 get_len_8bit_ascii(unsigned *clen, unsigned *blen, tvbuff_t *tvb, unsigned offs,
939 unsigned len, bool len_is_bytes _U_)
941 unsigned i;
942 uint8_t ch;
944 *blen = len; /* One byte is one character */
945 *clen = 0;
946 for (i = 0; i < len; i++) {
947 ch = tvb_get_uint8(tvb, offs + i);
948 *clen += (ch >= 0x20 && ch <= 0x7f) ? 1 : 4;
952 static void
953 parse_8bit_ascii(char *p, tvbuff_t *tvb, unsigned offs, unsigned len)
955 uint8_t ch;
956 char *pmax;
958 pmax = p + len;
959 while (p < pmax) {
960 ch = tvb_get_uint8(tvb, offs++);
961 if (ch >= 0x20 && ch <= 0x7f) {
962 *p++ = ch;
963 } else {
964 snprintf(p, 5, "\\x%02x", ch);
965 p += 4;
970 static struct ipmi_parse_typelen ptl_8bit_ascii = {
971 get_len_8bit_ascii, parse_8bit_ascii, "ASCII+Latin1"
974 static void
975 get_len_unicode(unsigned *clen, unsigned *blen, tvbuff_t *tvb _U_, unsigned offs _U_,
976 unsigned len _U_, bool len_is_bytes)
978 if (len_is_bytes) {
979 *clen = len * 3; /* Each 2 bytes result in 6 chars printed: \Uxxxx */
980 *blen = len;
981 } else {
982 *clen = len * 6;
983 *blen = len * 2;
987 static void
988 parse_unicode(char *p, tvbuff_t *tvb, unsigned offs, unsigned len)
990 char *pmax = p + len;
991 uint8_t ch0, ch1;
993 while (p < pmax) {
994 ch0 = tvb_get_uint8(tvb, offs++);
995 ch1 = tvb_get_uint8(tvb, offs++);
996 snprintf(p, 7, "\\U%02x%02x", ch0, ch1);
997 p += 6;
1001 static struct ipmi_parse_typelen ptl_unicode = {
1002 get_len_unicode, parse_unicode, "Unicode"
1005 void
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;
1019 proto_tree *s_tree;
1020 unsigned type, msk, clen, blen, len;
1021 const char *unit;
1022 char *str;
1023 uint8_t typelen;
1025 typelen = tvb_get_uint8(tvb, offs);
1026 type = typelen >> 6;
1027 if (is_fru) {
1028 msk = 0x3f;
1029 ptr = (fru_langcode_is_english ? fru_eng : fru_noneng)[type];
1030 unit = "bytes";
1031 } else {
1032 msk = 0x1f;
1033 ptr = ipmi[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);
1042 str[clen] = '\0';
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)",
1047 ptr->desc, type);
1048 proto_tree_add_uint_format_value(s_tree, hf_length, tvb, offs, 1, len, "%d %s",
1049 len, unit);
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 ---------------------------------------------------------------- */
1058 void
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));
1070 } else {
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 /* ----------------------------------------------------------------
1077 GUID, IPMI-style.
1078 ---------------------------------------------------------------- */
1080 void
1081 ipmi_add_guid(proto_tree *tree, int hf, tvbuff_t *tvb, unsigned offset)
1083 e_guid_t guid;
1084 int i;
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 ---------------------------------------------------------------- */
1099 static void
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];
1105 inr->desc = desc;
1106 inr->siglen = siglen;
1109 void
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;
1115 ipmi_netfn_t *inh;
1117 netfn >>= 1; /* Requests and responses grouped together */
1118 if (netfn >= IPMI_NETFN_MAX) {
1119 return;
1122 inr = &ipmi_cmd_tab[netfn];
1123 if (inr->siglen != siglen) {
1124 return;
1127 inh = wmem_new(wmem_epan_scope(), struct ipmi_netfn_handler);
1128 inh->desc = desc;
1129 inh->oem_selector = oem_selector;
1130 inh->sig = sig;
1131 inh->cmdtab = cmdtab;
1132 inh->cmdtablen = cmdtablen;
1134 inh->next = inr->list;
1135 inr->list = inh;
1138 uint32_t
1139 ipmi_getsiglen(uint32_t netfn)
1141 return ipmi_cmd_tab[netfn >> 1].siglen;
1144 const char *
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;
1152 if (db) {
1153 return wmem_strdup_printf(pool, "%s (%s)", db, dn);
1154 } else {
1155 return dn;
1159 ipmi_netfn_t *
1160 ipmi_getnetfn(uint32_t netfn, const uint8_t *sig)
1162 struct ipmi_netfn_root *inr;
1163 ipmi_netfn_t *inh;
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))) {
1169 return inh;
1173 /* Either unknown netFn or signature does not match */
1174 return NULL;
1177 const ipmi_cmd_t *
1178 ipmi_getcmd(ipmi_netfn_t *nf, uint32_t cmd)
1180 static const ipmi_cmd_t ipmi_cmd_unknown = {
1181 0x00, /* Code */
1182 ipmi_notimpl, /* request */
1183 ipmi_notimpl, /* response */
1184 NULL, /* command codes */
1185 NULL, /* subfunctions */
1186 "Unknown command",
1187 0 /* flag */
1189 const ipmi_cmd_t *ic;
1190 size_t i, len;
1192 if (nf) {
1193 len = nf->cmdtablen;
1194 for (ic = nf->cmdtab, i = 0; i < len; i++, ic++) {
1195 if (ic->cmd == cmd) {
1196 return ic;
1201 return &ipmi_cmd_unknown;
1204 /* ----------------------------------------------------------------
1205 Various utility functions.
1206 ---------------------------------------------------------------- */
1208 void
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);
1214 void
1215 ipmi_fmt_10ms_1based(char *s, uint32_t v)
1217 snprintf(s, ITEM_LABEL_LENGTH, "%d.%03d seconds", v / 100, (v % 100) * 10);
1220 void
1221 ipmi_fmt_500ms_0based(char *s, uint32_t v)
1223 ipmi_fmt_500ms_1based(s, ++v);
1226 void
1227 ipmi_fmt_500ms_1based(char *s, uint32_t v)
1229 snprintf(s, ITEM_LABEL_LENGTH, "%d.%03d seconds", v / 2, (v % 2) * 500);
1232 void
1233 ipmi_fmt_1s_0based(char *s, uint32_t v)
1235 ipmi_fmt_1s_1based(s, ++v);
1238 void
1239 ipmi_fmt_1s_1based(char *s, uint32_t v)
1241 snprintf(s, ITEM_LABEL_LENGTH, "%d seconds", v);
1244 void
1245 ipmi_fmt_2s_0based(char *s, uint32_t v)
1247 snprintf(s, ITEM_LABEL_LENGTH, "%d seconds", (v + 1) * 2);
1250 void
1251 ipmi_fmt_5s_1based(char *s, uint32_t v)
1253 snprintf(s, ITEM_LABEL_LENGTH, "%d seconds", v * 5);
1256 void
1257 ipmi_fmt_version(char *s, uint32_t v)
1259 snprintf(s, ITEM_LABEL_LENGTH, "%d.%d", v & 0x0f, (v >> 4) & 0x0f);
1262 void
1263 ipmi_fmt_channel(char *s, uint32_t v)
1265 static const value_string chan_vals[] = {
1266 { 0x00, "Primary IPMB (IPMB-0)" },
1267 { 0x07, "IPMB-L" },
1268 { 0x0e, "Current channel" },
1269 { 0x0f, "System Interface" },
1270 { 0, NULL }
1272 char* tmp_str;
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);
1279 void
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);
1287 void
1288 ipmi_fmt_percent(char *s, uint32_t v)
1290 snprintf(s, ITEM_LABEL_LENGTH, "%d%%", v);
1293 const char *
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" },
1323 { 0, NULL }
1325 const char *res;
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) {
1333 return res;
1335 return "Standard command-specific code";
1338 return val_to_str_const(completion, std_completion_codes, "Unknown");
1341 static int
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;
1345 ipmi_context_t ctx;
1346 unsigned tvb_len = tvb_captured_length(tvb);
1347 uint8_t tmp;
1349 /* TMode message is at least 3 bytes length */
1350 if (tvb_len < 3) {
1351 return 0;
1354 memset(&ctx, 0, sizeof(ctx));
1356 /* get Net Fn/RS LUN field */
1357 tmp = tvb_get_uint8(tvb, 0);
1359 /* set Net Fn */
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 */
1385 ctx.hdr_len = 3;
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);
1406 static int
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;
1410 ipmi_context_t ctx;
1411 unsigned tvb_len = tvb_captured_length(tvb);
1412 uint8_t tmp;
1414 /* KCS message is at least 2 bytes length */
1415 if (tvb_len < 2) {
1416 return 0;
1419 memset(&ctx, 0, sizeof(ctx));
1421 /* get Net Fn/RS LUN field */
1422 tmp = tvb_get_uint8(tvb, 0);
1424 /* set Net Fn */
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 */
1443 ctx.hdr_len = 2;
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)
1464 while (len--) {
1465 start += tvb_get_uint8(tvb, off++);
1468 return start;
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;
1477 unsigned tvb_len;
1478 unsigned sh_len;
1479 unsigned sa_len;
1480 unsigned rs_sa;
1482 if (message_format == MSGFMT_NONE) {
1483 return false;
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 */
1493 check_bc = 1;
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 */ {
1502 *imb_flags = 0;
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 */
1515 } else {
1516 if (env == IPMI_E_NONE) {
1517 /* check broadcast */
1518 check_bc = 1;
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 */
1524 check_sh = 1;
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 */
1533 check_sh = 1;
1535 /* check slave address presence */
1536 check_sa = 1;
1538 /* no pre-requisites */
1539 *imb_flags = 0;
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
1551 if (check_bc
1552 && tvb_len >= 8
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;
1557 *cks1 = 0;
1558 *cks2 = 0;
1559 return true;
1563 * message with the starts with session handle
1564 * and contain responder address
1566 if (check_sh
1567 && tvb_len >= 8
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;
1571 *cks1 = 0;
1572 *cks2 = 0;
1573 return true;
1577 * message with responder address
1579 if (check_sa
1580 && tvb_len >= 7
1581 && !calc_cks(0, tvb, 0, 3)
1582 && !calc_cks(0, tvb, 3, tvb_len - 3)) {
1583 *imb_flags = IPMI_D_TRG_SA;
1584 *cks1 = 0;
1585 *cks2 = 0;
1586 return true;
1590 if (*imb_flags & IPMI_D_SESSION_HANDLE) {
1591 sh_len = 1;
1592 sa_len = 1;
1593 rs_sa = 0;
1594 } else if (*imb_flags & IPMI_D_TRG_SA) {
1595 sh_len = 0;
1596 sa_len = 1;
1597 rs_sa = 0;
1598 } else {
1599 sh_len = 0;
1600 sa_len = 0;
1601 rs_sa = 0x20;
1604 /* check message length */
1605 if (tvb_len < 6 + sh_len + sa_len) {
1606 return false;
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);
1614 return true;
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)
1621 ipmi_context_t ctx;
1622 unsigned offset = 0;
1623 uint8_t tmp;
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)) {
1636 return 0;
1639 /* check if message is broadcast */
1640 if (ctx.flags & IPMI_D_BROADCAST) {
1641 /* skip first byte */
1642 offset++;
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++);
1653 } else {
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;
1664 /* skip cks1 */
1665 offset++;
1667 /* get RQ SA */
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");
1692 } else {
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);
1704 static int
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);
1711 static int
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 */
1716 return 0;
1719 return do_dissect_ipmb(tvb, pinfo, tree, proto_ipmb, ett_ipmi,
1720 (ipmi_dissect_arg_t *) data);
1724 /* Register IPMB protocol.
1726 void
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[] = {
1749 &ett_ipmi,
1750 &ett_header,
1751 &ett_header_byte_1,
1752 &ett_header_byte_4,
1753 &ett_data,
1754 &ett_typelen
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 },
1761 { NULL, NULL, 0 }
1763 static const enum_val_t oemsel_vals[] = {
1764 { "none", "None", IPMI_OEM_NONE },
1765 { "pps", "Pigeon Point Systems", IPMI_OEM_PPS },
1766 { NULL, NULL, 0 }
1769 static ei_register_info ei[] = {
1770 { &ei_impi_parser_not_implemented, { "ipmi.parser_not_implemented", PI_UNDECODED, PI_WARN, "[PARSER NOT IMPLEMENTED]", EXPFILL }},
1773 module_t *module;
1774 expert_module_t* expert_ipmi;
1775 uint32_t i;
1777 proto_ipmi = proto_register_protocol("Intelligent Platform Management Interface",
1778 "IPMI",
1779 "ipmi");
1781 proto_ipmb = proto_register_protocol("Intelligent Platform Management Bus",
1782 "IPMB",
1783 "ipmb");
1784 proto_kcs = proto_register_protocol("Keyboard Controller Style Interface",
1785 "KCS",
1786 "kcs");
1787 proto_tmode = proto_register_protocol("Serial Terminal Mode Interface",
1788 "TMode",
1789 "tmode");
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
1845 * Local variables:
1846 * c-basic-offset: 8
1847 * tab-width: 8
1848 * indent-tabs-mode: t
1849 * End:
1851 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1852 * :indentSize=8:tabSize=8:noTabs=false: