2 * Routines for IPMI dissection
3 * Copyright 2002-2008, Alexey Neyman, Pigeon Point Systems <avn@pigeonpoint.com>
7 * Wireshark - Network traffic analyzer
8 * By Gerald Combs <gerald@wireshark.org>
9 * Copyright 1998 Gerald Combs
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
30 #ifdef HAVE_SYS_TYPES_H
31 #include <sys/types.h>
37 #include <epan/conversation.h>
38 #include <epan/emem.h>
39 #include <epan/packet.h>
40 #include <epan/to_str.h>
41 #include <epan/prefs.h>
42 #include <epan/addr_resolv.h>
44 #include "packet-ipmi.h"
47 * See the IPMI specifications at
49 * http://www.intel.com/design/servers/ipmi/
52 /* Define IPMI_DEBUG to enable printing the process of request-response pairing */
53 /* #define IPMI_DEBUG */
55 /* Top-level search structure: list of registered handlers for a given netFn */
56 struct ipmi_netfn_root {
64 /* We need more than a conversation. Over the same RMCP session
65 (or IPMB), there may be several addresses/SWIDs. Thus, in a single
66 Wireshark-maintained conversation we might need to find our own... */
67 struct ipmi_saved_data {
69 guint32 saved_data[NSAVED_DATA];
88 struct ipmi_reqresp *next;
89 struct ipmi_saved_data *data;
90 int (*whichresponse)(struct ipmi_header *hdr, struct ipmi_reqresp *rr);
94 } frames[MAX_RQRS_FRAMES];
100 struct ipmi_reqresp *rr;
103 struct ipmi_keytree {
107 struct ipmi_parse_typelen {
108 void (*get_len)(guint *, guint *, tvbuff_t *, guint, guint, gboolean);
109 void (*parse)(char *, tvbuff_t *, guint, guint);
113 struct ipmi_header *ipmi_current_hdr;
115 static gint proto_ipmi = -1;
117 static gboolean fru_langcode_is_english = TRUE;
118 static guint response_after_req = 5000;
119 static guint response_before_req = 0;
120 static guint message_format = MSGFMT_GUESS;
121 static guint selected_oem = IPMI_OEM_NONE;
123 static gint hf_ipmi_message = -1;
124 static gint hf_ipmi_session_handle = -1;
125 static gint hf_ipmi_header_broadcast = -1;
126 static gint hf_ipmi_header_trg = -1;
127 static gint hf_ipmi_header_trg_lun = -1;
128 static gint hf_ipmi_header_netfn = -1;
129 static gint hf_ipmi_header_crc = -1;
130 static gint hf_ipmi_header_src = -1;
131 static gint hf_ipmi_header_src_lun = -1;
132 static gint hf_ipmi_header_sequence = -1;
133 static gint hf_ipmi_header_command = -1;
134 static gint hf_ipmi_header_completion = -1;
135 static gint hf_ipmi_header_sig = -1;
136 static gint hf_ipmi_data_crc = -1;
137 static gint hf_ipmi_response_to = -1;
138 static gint hf_ipmi_response_in = -1;
139 static gint hf_ipmi_response_time = -1;
140 static gint hf_ipmi_bad_checksum = -1;
142 static gint ett_ipmi = -1;
143 static gint ett_header = -1;
144 static gint ett_header_byte_1 = -1;
145 static gint ett_header_byte_4 = -1;
146 static gint ett_data = -1;
147 static gint ett_typelen = -1;
149 static guint nest_level;
150 static packet_info *current_pinfo;
151 static struct ipmi_saved_data *current_saved_data;
152 static struct ipmi_netfn_root ipmi_cmd_tab[IPMI_NETFN_MAX];
156 debug_printf(const gchar *fmt _U_, ...)
158 #if defined(IPMI_DEBUG)
162 vfprintf(stderr, fmt, ap);
167 /* ----------------------------------------------------------------
168 Support for request-response caching.
169 ---------------------------------------------------------------- */
171 /* Key generation; returns the same key for requests and responses */
173 makekey(struct ipmi_header *hdr)
175 guint32 trg, src, res;
177 trg = (hdr->trg_sa << 2) | hdr->trg_lun;
178 src = (hdr->src_sa << 2) | hdr->src_lun;
179 res = trg < src ? (trg << 10) | src : (src << 10) | trg;
180 return (hdr->seq << 20) | res;
183 static struct ipmi_reqresp *
184 key_lookup_reqresp(struct ipmi_keyhead *kh, struct ipmi_header *hdr, frame_data *fd)
186 struct ipmi_reqresp *rr, *best_rr = NULL;
188 double d, best_d = (double)(2 * response_after_req);
189 guint8 netfn = hdr->netfn & 0x3e; /* disregard 'response' bit */
190 guint8 is_resp = hdr->netfn & 0x01;
193 /* Source/target SA/LUN and sequence number are assumed to match; se_tree*
194 ensure that. While checking for "being here", we can't rely on flags.visited,
195 as we may have more than one IPMI message in a single frame. */
196 for (rr = kh->rr; rr; rr = rr->next) {
197 if (rr->netfn != netfn || rr->cmd != hdr->cmd) {
201 for (i = 0; i < MAX_RQRS_FRAMES; i++) {
202 /* RQ=0 - 0th element is request frame number; RS/RS2 -
203 responses are non zero */
204 if (((!i) ^ is_resp) && rr->frames[i].num == fd->num) {
205 /* Already been here */
210 /* Reject responses before requests or more than 5 seconds ahead */
212 nstime_delta(&delta, &fd->abs_ts, &rr->frames[RQ].time);
214 /* Use RS here, not RS2 - frames[RS] is always filled if we had
215 at least one response */ /* TBD */
216 nstime_delta(&delta, &rr->frames[RS].time, &fd->abs_ts);
218 d = nstime_to_msec(&delta);
219 if (d < -(double)response_before_req || d > (double)response_after_req) {
223 if (fabs(d) < best_d) {
233 key_insert_reqresp(struct ipmi_keyhead *kh, struct ipmi_reqresp *rr)
235 /* Insert to head, so that the search would find most recent response */
240 static inline gboolean
241 set_framenums(struct ipmi_header *hdr, struct ipmi_reqresp *rr, frame_data *fd)
243 int which = hdr->netfn & 0x01 ? rr->whichresponse ? rr->whichresponse(hdr, rr) : RS : RQ;
245 if (rr->frames[which].num && rr->frames[which].num != fd->num) {
248 rr->frames[which].num = fd->num;
249 rr->frames[which].time = fd->abs_ts;
253 #define IS_SENDMSG(hdr) (((hdr)->netfn & 0x3e) == IPMI_APP_REQ && (hdr)->cmd == 0x34)
256 ipmi_sendmsg_whichresponse(struct ipmi_header *hdr, struct ipmi_reqresp *rr)
258 if (!IS_SENDMSG(hdr)) {
259 /* Not a Send Message: just a simple response */
263 if (hdr->data_len > 0) {
264 /* Trivial case: response with non-null data can only be a
265 response in AMC.0 style */
268 /* Otherwise, we need to somehow determine 1st and 2nd responses. Note
269 that both them may lack the data - in case that the embedded response
270 returned with error. Thus, employ the following algo:
271 - First, assign to [RS] frame (this also won't conflict with full response
272 received - it could only happen if send message succeeded)
273 - In case we see another data-less response, see that we assign the one
274 with success completion code to [RS] and with non-success code to [RS2].
276 We assume that we can't receive 2 responses with non-successful completion
277 (if the outmost Send Message failed, how was the embedded one sent?)
279 if (!rr->frames[RS].num) {
283 /* In case we received "success", move the other response to [RS2] */
285 if (!rr->frames[RS2].num) {
286 rr->frames[RS2] = rr->frames[RS];
291 /* [RS] occupied, non-successful */
296 ipmi_sendmsg_otheridx(struct ipmi_header *hdr)
298 return IS_SENDMSG(hdr) ? nest_level : RS;
302 ipmi_sendmsg_getheaders(struct ipmi_header *base, void *arg, guint i)
304 static struct ipmi_header hdr;
305 struct ipmi_header *wrapper = arg;
307 /* The problem stems from the fact that the original IPMI
308 specification (before errata came) did not specify the response
309 to Send Message (and even the fact that there are 2 responses -
310 to Send Message and to embedded command). Even then, there is
311 one vagueness remaining - whether the response should use
312 the sequence number from the wrapper or from the embedded message.
314 Thus, there are 3 types of responses to Send Message
316 * AMC.0-style: the response is embedded in a normal Send Message
317 response. Easiest case: such responses will be correctly detected
318 with the default code in ipmi_do_dissect.
320 * IPMI-style, with both variants of sequence numbers. Note that
321 most tools dealing with Send Message (e.g. ipmitool) circumvent
322 this vagueness by using the same sequence number in both wrapper
323 and embedded messages. If we detect such "smart" messages, we
324 provide only one extra header. For correctness, we have to provide
325 for both variants, however.
328 if (i >= 2 || (i == 1 && wrapper->seq == base->seq)) {
332 /* Construct hybrid header */
333 hdr.trg_sa = wrapper->trg_sa;
334 hdr.trg_lun = wrapper->trg_lun;
335 hdr.src_sa = wrapper->src_sa;
336 hdr.src_lun = wrapper->src_lun;
337 hdr.netfn = base->netfn;
339 hdr.seq = i ? base->seq : wrapper->seq;
340 hdr.ccode = base->ccode;
341 hdr.data_len = base->data_len;
346 maybe_insert_reqresp(ipmi_dissect_format_t *dfmt, struct ipmi_header *hdr)
349 struct ipmi_keytree *kt;
350 struct ipmi_keyhead *kh;
351 struct ipmi_reqresp *rr;
354 cnv = find_conversation(current_pinfo->fd->num, ¤t_pinfo->src,
355 ¤t_pinfo->dst, current_pinfo->ptype,
356 current_pinfo->srcport, current_pinfo->destport, 0);
358 cnv = conversation_new(current_pinfo->fd->num, ¤t_pinfo->src,
359 ¤t_pinfo->dst, current_pinfo->ptype,
360 current_pinfo->srcport, current_pinfo->destport, 0);
363 kt = conversation_get_proto_data(cnv, proto_ipmi);
365 kt = se_alloc(sizeof(struct ipmi_keytree));
366 kt->heads = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK,
368 conversation_add_proto_data(cnv, proto_ipmi, kt);
371 debug_printf("--> maybe_insert_reqresp( %d )\n", current_pinfo->fd->num);
374 debug_printf("Checking [ (%02x,%1x <-> %02x,%1x : %02x) %02x %02x ]\n",
375 hdr->trg_sa, hdr->trg_lun, hdr->src_sa, hdr->src_lun, hdr->seq,
376 hdr->netfn, hdr->cmd);
378 kh = se_tree_lookup32(kt->heads, key);
380 kh = se_alloc0(sizeof(struct ipmi_keyhead));
381 se_tree_insert32(kt->heads, key, kh);
383 if ((rr = key_lookup_reqresp(kh, hdr, current_pinfo->fd)) != NULL) {
384 /* Already recorded - set frame number and be done. Look no
385 further - even if there are several responses, we have
386 found the right one. */
387 debug_printf("Found existing [ <%d,%d,%d> (%02x,%1x <-> %02x,%1x : %02x) %02x %02x ]\n",
388 rr->frames[0].num, rr->frames[1].num, rr->frames[2].num,
389 hdr->trg_sa, hdr->trg_lun, hdr->src_sa, hdr->src_lun, hdr->seq,
391 if (!rr->whichresponse) {
392 rr->whichresponse = dfmt->whichresponse;
394 if (set_framenums(hdr, rr, current_pinfo->fd)) {
395 debug_printf("Set frames [ <%d,%d,%d> (%02x,%1x <-> %02x,%1x : %02x) %02x %02x ]\n",
396 rr->frames[0].num, rr->frames[1].num, rr->frames[2].num,
397 hdr->trg_sa, hdr->trg_lun, hdr->src_sa, hdr->src_lun, hdr->seq,
399 current_saved_data = rr->data;
403 /* Found, but already occupied. Fall through to allocating the structures */
404 current_saved_data = NULL;
406 /* Not found; allocate new structures */
407 if (!current_saved_data) {
408 /* One 'ipmi_saved_data' for all 'ipmi_req_resp' allocated */
409 current_saved_data = se_alloc0(sizeof(struct ipmi_saved_data));
411 rr = se_alloc0(sizeof(struct ipmi_reqresp));
412 rr->whichresponse = dfmt->whichresponse;
413 rr->netfn = hdr->netfn & 0x3e;
415 rr->data = current_saved_data;
416 set_framenums(hdr, rr, current_pinfo->fd);
417 key_insert_reqresp(kh, rr);
418 debug_printf("Inserted [ <%d,%d,%d> (%02x,%1x <-> %02x,%1x : %02x) %02x %02x ]\n",
419 rr->frames[0].num, rr->frames[1].num, rr->frames[2].num,
420 hdr->trg_sa, hdr->trg_lun, hdr->src_sa, hdr->src_lun, hdr->seq,
423 /* Do we have other headers to insert? */
424 hdr = dfmt->getmoreheaders ? dfmt->getmoreheaders(hdr, dfmt->arg, i++) : NULL;
429 add_reqresp_info(ipmi_dissect_format_t *dfmt, struct ipmi_header *hdr, proto_tree *tree, tvbuff_t *tvb)
432 struct ipmi_keytree *kt;
433 struct ipmi_keyhead *kh;
434 struct ipmi_reqresp *rr = NULL;
435 guint32 key, i, other_idx;
439 debug_printf("--> add_reqresp_info( %d )\n", current_pinfo->fd->num);
441 /* [0] is request; [1..MAX_RS_LEVEL] are responses */
442 other_idx = (hdr->netfn & 0x01) ? RQ : dfmt->otheridx ? dfmt->otheridx(hdr) : RS;
444 if (other_idx >= MAX_RQRS_FRAMES) {
445 /* No chance; we don't look that deep into nested Send Message.
446 Note that we'll use the other_idx value to distinguish
447 request from response. */
451 /* Here, we don't try to create any object - everything is assumed
452 to be created in maybe_insert_reqresp() */
453 if ((cnv = find_conversation(current_pinfo->fd->num, ¤t_pinfo->src,
454 ¤t_pinfo->dst, current_pinfo->ptype,
455 current_pinfo->srcport, current_pinfo->destport, 0)) == NULL) {
458 if ((kt = conversation_get_proto_data(cnv, proto_ipmi)) == NULL) {
464 debug_printf("Looking for [ (%02x,%1x <-> %02x,%1x : %02x) %02x %02x ]\n",
465 hdr->trg_sa, hdr->trg_lun, hdr->src_sa, hdr->src_lun, hdr->seq,
466 hdr->netfn, hdr->cmd);
468 if ((kh = se_tree_lookup32(kt->heads, key)) != NULL &&
469 (rr = key_lookup_reqresp(kh, hdr, current_pinfo->fd)) != NULL) {
470 debug_printf("Found [ <%d,%d,%d> (%02x,%1x <-> %02x,%1x : %02x) %02x %02x ]\n",
471 rr->frames[0].num, rr->frames[1].num, rr->frames[2].num,
472 hdr->trg_sa, hdr->trg_lun, hdr->src_sa, hdr->src_lun, hdr->seq,
474 if (rr->frames[other_idx].num) {
479 /* Do we have other headers to check? */
480 hdr = dfmt->getmoreheaders ? dfmt->getmoreheaders(hdr, dfmt->arg, i++) : NULL;
486 if (hdr->netfn & 0x01) {
488 ti = proto_tree_add_uint(tree, hf_ipmi_response_to,
489 tvb, 0, 0, rr->frames[RQ].num);
490 PROTO_ITEM_SET_GENERATED(ti);
491 nstime_delta(&ns, ¤t_pinfo->fd->abs_ts, &rr->frames[RQ].time);
492 ti = proto_tree_add_time(tree, hf_ipmi_response_time,
494 PROTO_ITEM_SET_GENERATED(ti);
497 ti = proto_tree_add_uint(tree, hf_ipmi_response_in,
498 tvb, 0, 0, rr->frames[other_idx].num);
499 PROTO_ITEM_SET_GENERATED(ti);
504 ti = proto_tree_add_text(tree, tvb, 0, 0, "No corresponding %s",
505 other_idx ? "response" : "request");
506 PROTO_ITEM_SET_GENERATED(ti);
509 /* Save data in request, retrieve in response */
511 ipmi_setsaveddata(guint idx, guint32 val)
513 DISSECTOR_ASSERT(idx < NSAVED_DATA);
514 current_saved_data->saved_data[idx] = val;
515 current_saved_data->set_data |= (1 << idx);
519 ipmi_getsaveddata(guint idx, guint32 *pval)
521 DISSECTOR_ASSERT(idx < NSAVED_DATA);
522 if (current_saved_data->set_data & (1 << idx)) {
523 *pval = current_saved_data->saved_data[idx];
529 /* ----------------------------------------------------------------
530 Support for Type/Length fields parsing.
531 ---------------------------------------------------------------- */
534 get_len_binary(guint *clen, guint *blen, tvbuff_t *tvb _U_, guint offs _U_,
535 guint len, gboolean len_is_bytes _U_)
542 parse_binary(char *p, tvbuff_t *tvb, guint offs, guint len)
544 static const char hex[] = "0123456789ABCDEF";
548 for (i = 0; i < len / 3; i++) {
549 v = tvb_get_guint8(tvb, offs + i);
560 static struct ipmi_parse_typelen ptl_binary = {
561 get_len_binary, parse_binary, "Binary"
565 get_len_bcdplus(guint *clen, guint *blen, tvbuff_t *tvb _U_, guint offs _U_,
566 guint len, gboolean len_is_bytes)
572 *blen = (len + 1) / 2;
578 parse_bcdplus(char *p, tvbuff_t *tvb, guint offs, guint len)
580 static const char bcd[] = "0123456789 -.:,_";
581 guint i, msk = 0xf0, shft = 4;
584 for (i = 0; i < len; i++) {
585 v = (tvb_get_guint8(tvb, offs + i / 2) & msk) >> shft;
592 static struct ipmi_parse_typelen ptl_bcdplus = {
593 get_len_bcdplus, parse_bcdplus, "BCD+"
597 get_len_6bit_ascii(guint *clen, guint *blen, tvbuff_t *tvb _U_, guint offs _U_,
598 guint len, gboolean len_is_bytes)
604 *blen = (len * 3 + 3) / 4;
610 parse_6bit_ascii(char *p, tvbuff_t *tvb, guint offs, guint len)
615 /* First, handle "full" triplets of bytes, 4 characters each */
616 for (i = 0; i < len / 4; i++) {
617 v = tvb_get_letoh24(tvb, offs + i * 3);
618 p[0] = ' ' + (v & 0x3f);
619 p[1] = ' ' + ((v >> 6) & 0x3f);
620 p[2] = ' ' + ((v >> 12) & 0x3f);
621 p[3] = ' ' + ((v >> 18) & 0x3f);
625 /* Do we have any characters left? */
630 v = (tvb_get_guint8(tvb, offs + 2) << 4) | (tvb_get_guint8(tvb, offs + 1) >> 4);
631 p[2] = ' ' + (v & 0x3f);
634 v = (tvb_get_guint8(tvb, offs + 1) << 2) | (tvb_get_guint8(tvb, offs) >> 6);
635 p[1] = ' ' + (v & 0x3f);
638 v = tvb_get_guint8(tvb, offs) & 0x3f;
639 p[0] = ' ' + (v & 0x3f);
643 static struct ipmi_parse_typelen ptl_6bit_ascii = {
644 get_len_6bit_ascii, parse_6bit_ascii, "6-bit ASCII"
648 get_len_8bit_ascii(guint *clen, guint *blen, tvbuff_t *tvb, guint offs,
649 guint len, gboolean len_is_bytes _U_)
654 *blen = len; /* One byte is one character */
656 for (i = 0; i < len; i++) {
657 ch = tvb_get_guint8(tvb, offs + i);
658 *clen += (ch >= 0x20 && ch <= 0x7f) ? 1 : 4;
663 parse_8bit_ascii(char *p, tvbuff_t *tvb, guint offs, guint len)
670 ch = tvb_get_guint8(tvb, offs++);
671 if (ch >= 0x20 && ch <= 0x7f) {
674 g_snprintf(p, 5, "\\x%02x", ch);
680 static struct ipmi_parse_typelen ptl_8bit_ascii = {
681 get_len_8bit_ascii, parse_8bit_ascii, "ASCII+Latin1"
685 get_len_unicode(guint *clen, guint *blen, tvbuff_t *tvb _U_, guint offs _U_,
686 guint len _U_, gboolean len_is_bytes)
689 *clen = len * 3; /* Each 2 bytes result in 6 chars printed: \Uxxxx */
698 parse_unicode(char *p, tvbuff_t *tvb, guint offs, guint len)
700 char *pmax = p + len;
704 ch0 = tvb_get_guint8(tvb, offs++);
705 ch1 = tvb_get_guint8(tvb, offs++);
706 g_snprintf(p, 7, "\\U%02x%02x", ch0, ch1);
711 static struct ipmi_parse_typelen ptl_unicode = {
712 get_len_unicode, parse_unicode, "Unicode"
716 ipmi_add_typelen(proto_tree *tree, const char *desc, tvbuff_t *tvb,
717 guint offs, gboolean is_fru)
719 static struct ipmi_parse_typelen *fru_eng[4] = {
720 &ptl_binary, &ptl_bcdplus, &ptl_6bit_ascii, &ptl_8bit_ascii
722 static struct ipmi_parse_typelen *fru_noneng[4] = {
723 &ptl_binary, &ptl_bcdplus, &ptl_6bit_ascii, &ptl_unicode
725 static struct ipmi_parse_typelen *ipmi[4] = {
726 &ptl_unicode, &ptl_bcdplus, &ptl_6bit_ascii, &ptl_8bit_ascii
728 struct ipmi_parse_typelen *ptr;
731 guint type, msk, clen, blen, len;
736 typelen = tvb_get_guint8(tvb, offs);
740 ptr = (fru_langcode_is_english ? fru_eng : fru_noneng)[type];
749 ptr->get_len(&clen, &blen, tvb, offs + 1, len, is_fru);
751 str = ep_alloc(clen + 1);
752 ptr->parse(str, tvb, offs + 1, clen);
755 ti = proto_tree_add_text(tree, tvb, offs, 1, "%s Type/Length byte: %s, %d %s",
756 desc, ptr->desc, len, unit);
757 s_tree = proto_item_add_subtree(ti, ett_typelen);
758 proto_tree_add_text(ti, tvb, offs, 1, "%sType: %s (0x%02x)",
759 ipmi_dcd8(typelen, 0xc0), ptr->desc, type);
760 proto_tree_add_text(ti, tvb, offs, 1, "%sLength: %d %s",
761 ipmi_dcd8(typelen, msk), len, unit);
763 proto_tree_add_text(tree, tvb, offs + 1, blen, "%s: [%s] '%s'",
764 desc, ptr->desc, str);
767 /* ----------------------------------------------------------------
768 Timestamp, IPMI-style.
769 ---------------------------------------------------------------- */
771 ipmi_add_timestamp(proto_tree *tree, gint hf, tvbuff_t *tvb, guint offset)
773 guint32 ts = tvb_get_letohl(tvb, offset);
777 if (ts == 0xffffffff) {
778 proto_tree_add_uint_format_value(tree, hf, tvb, offset, 4,
779 ts, "Unspecified/Invalid");
780 } else if (ts <= 0x20000000) {
787 proto_tree_add_uint_format_value(tree, hf, tvb, offset, 4,
788 ts, "%d days, %02d:%02d:%02d since SEL device's initialization",
792 strftime(buf, sizeof(buf), "%F %T", gmtime(&t));
793 proto_tree_add_uint_format_value(tree, hf, tvb, offset, 4,
798 /* ----------------------------------------------------------------
800 ---------------------------------------------------------------- */
803 ipmi_add_guid(proto_tree *tree, gint hf, tvbuff_t *tvb, guint offset)
808 guid.data1 = tvb_get_letohl(tvb, offset + 12);
809 guid.data2 = tvb_get_letohs(tvb, offset + 10);
810 guid.data3 = tvb_get_letohs(tvb, offset + 8);
811 for (i = 0; i < 8; i++) {
812 guid.data4[i] = tvb_get_guint8(tvb, offset + 7 - i);
814 proto_tree_add_guid(tree, hf, tvb, offset, 16, &guid);
817 /* ----------------------------------------------------------------
818 Routines for registering/looking up command parsers.
819 ---------------------------------------------------------------- */
822 ipmi_netfn_setdesc(guint32 netfn, const char *desc, guint32 siglen)
824 struct ipmi_netfn_root *inr;
826 inr = &ipmi_cmd_tab[netfn >> 1];
828 inr->siglen = siglen;
832 ipmi_register_netfn_cmdtab(guint32 netfn, guint oem_selector,
833 const guint8 *sig, guint32 siglen, const char *desc,
834 ipmi_cmd_t *cmdtab, guint32 cmdtablen)
836 struct ipmi_netfn_root *inr;
839 netfn >>= 1; /* Requests and responses grouped together */
840 if (netfn >= IPMI_NETFN_MAX) {
841 g_warning("NetFn too large: %x", netfn * 2);
845 inr = &ipmi_cmd_tab[netfn];
846 if (inr->siglen != siglen) {
847 /* All handlers per netFn should have the same signature length */
848 g_warning("NetFn %d: different signature lengths: %d vs %d",
849 netfn * 2, inr->siglen, siglen);
853 inh = g_malloc(sizeof(struct ipmi_netfn_handler));
855 inh->oem_selector = oem_selector;
857 inh->cmdtab = cmdtab;
858 inh->cmdtablen = cmdtablen;
860 inh->next = inr->list;
865 ipmi_getsiglen(guint32 netfn)
867 return ipmi_cmd_tab[netfn >> 1].siglen;
871 ipmi_getnetfnname(guint32 netfn, ipmi_netfn_t *nf)
875 dn = ipmi_cmd_tab[netfn >> 1].desc ?
876 ipmi_cmd_tab[netfn >> 1].desc : "Reserved";
877 db = nf ? nf->desc : NULL;
879 return ep_strdup_printf("%s (%s)", db, dn);
886 ipmi_getnetfn(guint32 netfn, const guint8 *sig)
888 struct ipmi_netfn_root *inr;
891 inr = &ipmi_cmd_tab[netfn >> 1];
892 for (inh = inr->list; inh; inh = inh->next) {
893 if ((inh->oem_selector == selected_oem || inh->oem_selector == IPMI_OEM_NONE)
894 && (!inr->siglen || !memcmp(sig, inh->sig, inr->siglen))) {
899 /* Either unknown netFn or signature does not match */
904 ipmi_getcmd(ipmi_netfn_t *nf, guint32 cmd)
906 static ipmi_cmd_t ipmi_cmd_unknown = {
908 ipmi_notimpl, /* request */
909 ipmi_notimpl, /* response */
910 NULL, /* command codes */
911 NULL, /* subfunctions */
920 for (ic = nf->cmdtab, i = 0; i < len; i++, ic++) {
921 if (ic->cmd == cmd) {
927 return &ipmi_cmd_unknown;
930 /* ----------------------------------------------------------------
931 Various utility functions.
932 ---------------------------------------------------------------- */
935 ipmi_notimpl(tvbuff_t *tvb, proto_tree *tree)
938 proto_tree_add_text(tree, tvb, 0, -1, "[PARSER NOT IMPLEMENTED]");
943 ipmi_dcd8(guint32 val, guint32 mask)
947 decode_bitfield_value(buf, val, mask, 8);
952 ipmi_fmt_10ms_1based(gchar *s, guint32 v)
954 g_snprintf(s, ITEM_LABEL_LENGTH, "%d.%03d seconds", v / 100, (v % 100) * 10);
958 ipmi_fmt_500ms_0based(gchar *s, guint32 v)
960 ipmi_fmt_500ms_1based(s, ++v);
964 ipmi_fmt_500ms_1based(gchar *s, guint32 v)
966 g_snprintf(s, ITEM_LABEL_LENGTH, "%d.%03d seconds", v / 2, (v % 2) * 500);
970 ipmi_fmt_1s_0based(gchar *s, guint32 v)
972 ipmi_fmt_1s_1based(s, ++v);
976 ipmi_fmt_1s_1based(gchar *s, guint32 v)
978 g_snprintf(s, ITEM_LABEL_LENGTH, "%d seconds", v);
982 ipmi_fmt_2s_0based(gchar *s, guint32 v)
984 g_snprintf(s, ITEM_LABEL_LENGTH, "%d seconds", (v + 1) * 2);
988 ipmi_fmt_5s_1based(gchar *s, guint32 v)
990 g_snprintf(s, ITEM_LABEL_LENGTH, "%d seconds", v * 5);
994 ipmi_fmt_version(gchar *s, guint32 v)
996 g_snprintf(s, ITEM_LABEL_LENGTH, "%d.%d", v & 0x0f, (v >> 4) & 0x0f);
1000 ipmi_fmt_channel(gchar *s, guint32 v)
1002 static const value_string chan_vals[] = {
1003 { 0x00, "Primary IPMB (IPMB-0)" },
1005 { 0x0e, "Current channel" },
1006 { 0x0f, "System Interface" },
1010 g_snprintf(s, ITEM_LABEL_LENGTH, "%s (0x%02x)",
1011 val_to_str(v, chan_vals, "Channel #%d"), v);
1015 ipmi_fmt_udpport(gchar *s, guint32 v)
1017 g_snprintf(s, ITEM_LABEL_LENGTH, "%s (%d)", get_udp_port(v), v);
1021 ipmi_fmt_percent(gchar *s, guint32 v)
1023 g_snprintf(s, ITEM_LABEL_LENGTH, "%d%%", v);
1027 ipmi_get_completion_code(guint8 completion, ipmi_cmd_t *cmd)
1029 static const value_string std_completion_codes[] = {
1030 { 0x00, "Command Completed Normally" },
1031 { 0xc0, "Node Busy" },
1032 { 0xc1, "Invalid Command" },
1033 { 0xc2, "Command invalid for given LUN" },
1034 { 0xc3, "Timeout while processing command, response unavailable" },
1035 { 0xc4, "Out of space" },
1036 { 0xc5, "Reservation Canceled or Invalid Reservation ID" },
1037 { 0xc6, "Request data truncated" },
1038 { 0xc7, "Request data length invalid" },
1039 { 0xc8, "Request data field length limit exceeded" },
1040 { 0xc9, "Parameter out of range" },
1041 { 0xca, "Cannot return number of requested data bytes" },
1042 { 0xcb, "Requested Sensor, data, or record not present" },
1043 { 0xcc, "Invalid data field in Request" },
1044 { 0xcd, "Command illegal for specified sensor or record type" },
1045 { 0xce, "Command response could not be provided" },
1046 { 0xcf, "Cannot execute duplicated request" },
1047 { 0xd0, "Command response could not be provided: SDR Repository in update mode" },
1048 { 0xd1, "Command response could not be provided: device in firmware update mode" },
1049 { 0xd2, "Command response could not be provided: BMC initialization or initialization agent in progress" },
1050 { 0xd3, "Destination unavailable" },
1051 { 0xd4, "Cannot execute command: insufficient privilege level or other security-based restriction" },
1052 { 0xd5, "Cannot execute command: command, or request parameter(s), not supported in present state" },
1053 { 0xd6, "Cannot execute command: parameter is illegal because subfunction is disabled or unavailable" },
1054 { 0xff, "Unspecified error" },
1060 if (completion >= 0x01 && completion <= 0x7e) {
1061 return "Device specific (OEM) completion code";
1064 if (completion >= 0x80 && completion <= 0xbe) {
1065 if (cmd && cmd->cs_cc && (res = match_strval(completion, cmd->cs_cc)) != NULL) {
1068 return "Standard command-specific code";
1071 return val_to_str(completion, std_completion_codes, "Unknown");
1074 /* Guess the parsing flags for a message
1077 ipmi_guess_dissect_flags(tvbuff_t *tvb)
1082 switch (message_format) {
1086 return IPMI_D_TRG_SA;
1088 return IPMI_D_TRG_SA|IPMI_D_SESSION_HANDLE;
1091 /* Try to guess the format */
1092 DISSECTOR_ASSERT(message_format == MSGFMT_GUESS);
1094 /* 6 is shortest message - Get Message with empty data */
1095 if (tvb_length(tvb) < 6) {
1099 /* Fetch the beginning */
1100 for (i = 0; i < 6; i++) {
1101 buf[i] = tvb_get_guint8(tvb, i);
1104 if ((buf[0] + buf[1] + buf[2]) % 0x100 == 0) {
1105 /* Looks like IPMB: first 3 bytes are zero module 256 */
1106 return IPMI_D_TRG_SA;
1109 if ((buf[1] + buf[2] + buf[3]) % 0x100 == 0) {
1110 /* Looks like LAN: like IPMB, prepended with extra byte (session handle) */
1111 return IPMI_D_TRG_SA|IPMI_D_SESSION_HANDLE;
1118 /* Print out IPMB packet.
1121 ipmi_do_dissect(tvbuff_t *tvb, proto_tree *ipmi_tree, ipmi_dissect_format_t *dfmt)
1123 proto_tree *hdr_tree, *data_tree, *s_tree;
1126 ipmi_netfn_t *in = NULL;
1127 ipmi_cmd_t *ic = NULL;
1128 ipmi_cmd_handler_t hnd = NULL;
1129 struct ipmi_saved_data *saved_saved_data;
1130 struct ipmi_header hdr, *saved_hdr;
1131 guint8 hdr_crc, hdr_exp_crc, data_crc, data_exp_crc;
1132 guint8 is_resp, is_broadcast = 0, i, tmp;
1133 guint len, siglen, hdrlen, offs, data_chk_offs;
1134 const char *bcast, *ndesc, *cdesc, *ccdesc;
1136 if (dfmt->flags & IPMI_D_NONE) {
1137 /* No parsing requested */
1138 g_snprintf(dfmt->info, ITEM_LABEL_LENGTH, "Unknown message (not parsed)");
1139 proto_tree_add_item(ipmi_tree, hf_ipmi_message, tvb, 0, tvb_length(tvb), TRUE);
1145 memset(&hdr, 0, sizeof(hdr));
1146 debug_printf("--> do_dissect(%d, nl %u, tree %s null)\n",
1147 current_pinfo->fd->num, nest_level, ipmi_tree ? "IS NOT" : "IS");
1149 /* Optional byte: in Send Message targeted to session-based channels */
1150 if (dfmt->flags & IPMI_D_SESSION_HANDLE) {
1154 /* Optional byte: 00 indicates General Call address - broadcast message */
1155 if ((dfmt->flags & IPMI_D_BROADCAST) && tvb_get_guint8(tvb, offs) == 0x00) {
1160 /* Byte 1: target slave address, may be absent (in Get Message) */
1161 hdr.trg_sa = (dfmt->flags & IPMI_D_TRG_SA) ? tvb_get_guint8(tvb, offs++) : 0;
1163 /* Byte 2: network function + target LUN */
1164 tmp = tvb_get_guint8(tvb, offs++);
1165 hdr.trg_lun = tmp & 0x03;
1166 hdr.netfn = (tmp >> 2) & 0x3f;
1167 hdr_exp_crc = (0 - hdr.trg_sa - tmp) & 0xff;
1169 /* Byte 3: header checksum */
1170 hdr_crc = tvb_get_guint8(tvb, offs++);
1172 /* Byte 4: source slave address */
1173 hdr.src_sa = tvb_get_guint8(tvb, offs++);
1175 /* Byte 5: sequence number + source LUN */
1176 tmp = tvb_get_guint8(tvb, offs++);
1177 hdr.src_lun = tmp & 0x03;
1178 hdr.seq = (tmp >> 2) & 0x3f;
1180 /* Byte 6: command code */
1181 hdr.cmd = tvb_get_guint8(tvb, offs++);
1183 /* Byte 7: completion code (in response) */
1184 is_resp = (hdr.netfn & 0x1) ? 1 : 0;
1185 hdr.ccode = is_resp ? tvb_get_guint8(tvb, offs++) : 0;
1187 /* 0-3 bytes: signature of the defining body */
1188 siglen = ipmi_getsiglen(hdr.netfn);
1189 in = ipmi_getnetfn(hdr.netfn, tvb_get_ptr(tvb, offs, siglen));
1192 /* Save header length */
1194 hdr.data_len = tvb_length(tvb) - hdrlen - 1;
1196 /* Get some text descriptions */
1197 ic = ipmi_getcmd(in, hdr.cmd);
1198 ndesc = ipmi_getnetfnname(hdr.netfn, in);
1200 ccdesc = ipmi_get_completion_code(hdr.ccode, ic);
1201 if (!is_broadcast) {
1203 } else if (ic->flags & CMD_MAYBROADCAST) {
1204 bcast = " (BROADCAST: command may not be broadcast)";
1206 bcast = " (BROADCAST)";
1210 /* Save globals - we may be called recursively */
1211 saved_hdr = ipmi_current_hdr;
1212 ipmi_current_hdr = &hdr;
1213 saved_saved_data = current_saved_data;
1214 current_saved_data = NULL;
1216 /* Select sub-handler */
1217 hnd = is_resp ? ic->parse_resp : ic->parse_req;
1219 /* Start new conversation if needed */
1220 if (!is_resp && (ic->flags & CMD_NEWCONV)) {
1221 conversation_new(current_pinfo->fd->num, ¤t_pinfo->src,
1222 ¤t_pinfo->dst, current_pinfo->ptype,
1223 current_pinfo->srcport, current_pinfo->destport, 0);
1226 /* Check if we need to insert request-response pair */
1227 maybe_insert_reqresp(dfmt, &hdr);
1229 /* Create data subset: all but header and last byte (checksum) */
1230 data_tvb = tvb_new_subset(tvb, hdrlen, hdr.data_len, hdr.data_len);
1232 /* Brief description of a packet */
1233 g_snprintf(dfmt->info, ITEM_LABEL_LENGTH, "%s, %s, seq 0x%02x%s%s%s",
1234 is_resp ? "Rsp" : "Req", cdesc, hdr.seq, bcast,
1235 hdr.ccode ? ", " : "", hdr.ccode ? ccdesc : "");
1237 if (!is_resp && (ic->flags & CMD_CALLRQ)) {
1238 hnd(data_tvb, NULL);
1242 add_reqresp_info(dfmt, &hdr, ipmi_tree, tvb);
1244 ti = proto_tree_add_text(ipmi_tree, tvb, 0, hdrlen,
1245 "Header: %s (%s) from 0x%02x to 0x%02x%s", cdesc,
1246 is_resp ? "Response" : "Request", hdr.src_sa, hdr.trg_sa, bcast);
1247 hdr_tree = proto_item_add_subtree(ti, ett_header);
1251 if (dfmt->flags & IPMI_D_SESSION_HANDLE) {
1252 proto_tree_add_item(hdr_tree, hf_ipmi_session_handle,
1253 tvb, offs++, 1, TRUE);
1256 /* Broadcast byte (optional) */
1258 proto_tree_add_uint_format(hdr_tree, hf_ipmi_header_broadcast,
1259 tvb, offs++, 1, 0x00, "Broadcast message");
1262 /* Target SA, if present */
1263 if (dfmt->flags & IPMI_D_TRG_SA) {
1264 proto_tree_add_item(hdr_tree, hf_ipmi_header_trg, tvb, offs++, 1, TRUE);
1267 /* Network function + target LUN */
1268 ti = proto_tree_add_text(hdr_tree, tvb, offs, 1,
1269 "Target LUN: 0x%02x, NetFN: %s %s (0x%02x)", hdr.trg_lun,
1270 ndesc, is_resp ? "Response" : "Request", hdr.netfn);
1271 s_tree = proto_item_add_subtree(ti, ett_header_byte_1);
1273 proto_tree_add_item(s_tree, hf_ipmi_header_trg_lun, tvb, offs, 1, TRUE);
1274 proto_tree_add_uint_format(s_tree, hf_ipmi_header_netfn, tvb, offs, 1,
1275 hdr.netfn << 2, "%sNetFn: %s %s (0x%02x)",
1276 ipmi_dcd8(hdr.netfn << 2, 0xfc),
1277 ndesc, is_resp ? "Response" : "Request", hdr.netfn);
1280 /* Header checksum */
1281 if (hdr_crc == hdr_exp_crc) {
1282 proto_tree_add_uint_format(hdr_tree, hf_ipmi_header_crc, tvb, offs++, 1,
1283 hdr_crc, "Header checksum: 0x%02x (correct)", hdr_crc);
1286 ti = proto_tree_add_boolean(hdr_tree, hf_ipmi_bad_checksum, tvb, 0, 0, TRUE);
1287 PROTO_ITEM_SET_HIDDEN(ti);
1288 proto_tree_add_uint_format(hdr_tree, hf_ipmi_header_crc, tvb, offs++, 1,
1289 hdr_crc, "Header checksum: 0x%02x (incorrect, expected 0x%02x)",
1290 hdr_crc, hdr_exp_crc);
1293 /* Remember where chk2 bytes start */
1294 data_chk_offs = offs;
1297 proto_tree_add_item(hdr_tree, hf_ipmi_header_src, tvb, offs++, 1, TRUE);
1299 /* Sequence number + source LUN */
1300 ti = proto_tree_add_text(hdr_tree, tvb, offs, 1,
1301 "Source LUN: 0x%02x, SeqNo: 0x%02x",
1302 hdr.src_lun, hdr.seq);
1303 s_tree = proto_item_add_subtree(ti, ett_header_byte_4);
1305 proto_tree_add_item(s_tree, hf_ipmi_header_src_lun, tvb, offs, 1, TRUE);
1306 proto_tree_add_item(s_tree, hf_ipmi_header_sequence, tvb, offs, 1, TRUE);
1310 proto_tree_add_uint_format(hdr_tree, hf_ipmi_header_command, tvb, offs++, 1,
1311 hdr.cmd, "Command: %s (0x%02x)", cdesc, hdr.cmd);
1313 /* Response code (if present) */
1315 proto_tree_add_uint_format(hdr_tree, hf_ipmi_header_completion, tvb, offs++, 1,
1316 hdr.ccode, "Completion code: %s (0x%02x)", ccdesc, hdr.ccode);
1319 /* Defining body signature (if present) */
1321 ti = proto_tree_add_item(hdr_tree, hf_ipmi_header_sig, tvb, offs, siglen, TRUE);
1322 proto_item_append_text(ti, " (%s)", ndesc);
1326 /* Call data parser */
1327 if (tvb_length(data_tvb) && hnd) {
1328 ti = proto_tree_add_text(ipmi_tree, data_tvb, 0, -1, "Data");
1329 data_tree = proto_item_add_subtree(ti, ett_data);
1330 hnd(data_tvb, data_tree);
1333 /* Checksum all but the last byte */
1334 len = tvb_length(tvb) - 1;
1335 data_crc = tvb_get_guint8(tvb, len);
1337 for (i = data_chk_offs; i < len; i++) {
1338 data_exp_crc += tvb_get_guint8(tvb, i);
1340 data_exp_crc = (0 - data_exp_crc) & 0xff;
1342 if (data_crc == data_exp_crc) {
1343 proto_tree_add_uint_format(ipmi_tree, hf_ipmi_data_crc, tvb, len, 1,
1344 data_crc, "Data checksum: 0x%02x (correct)", data_crc);
1347 ti = proto_tree_add_boolean(hdr_tree, hf_ipmi_bad_checksum, tvb, 0, 0, TRUE);
1348 PROTO_ITEM_SET_HIDDEN(ti);
1349 proto_tree_add_uint_format(ipmi_tree, hf_ipmi_data_crc, tvb, len, 1,
1350 data_crc, "Data checksum: 0x%02x (incorrect, expected 0x%02x)",
1351 data_crc, data_exp_crc);
1355 /* Restore globals, in case we've been called recursively */
1356 ipmi_current_hdr = saved_hdr;
1357 current_saved_data = saved_saved_data;
1362 dissect_ipmi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1364 proto_tree *ipmi_tree = NULL;
1366 ipmi_dissect_format_t dfmt;
1368 if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
1369 col_set_str(pinfo->cinfo, COL_PROTOCOL, "IPMI/ATCA");
1372 current_pinfo = pinfo;
1375 ti = proto_tree_add_item(tree, proto_ipmi, tvb, 0, -1, FALSE);
1376 ipmi_tree = proto_item_add_subtree(ti, ett_ipmi);
1379 memset(&dfmt, 0, sizeof(dfmt));
1380 dfmt.flags = IPMI_D_BROADCAST | IPMI_D_TRG_SA;
1381 ipmi_do_dissect(tvb, ipmi_tree, &dfmt);
1383 if (check_col(pinfo->cinfo, COL_INFO)) {
1384 col_clear(pinfo->cinfo, COL_INFO);
1385 col_add_str(pinfo->cinfo, COL_INFO, dfmt.info);
1390 /* Register IPMB protocol.
1393 proto_reg_handoff_ipmi(void)
1398 proto_register_ipmi(void)
1400 static hf_register_info hf[] = {
1401 { &hf_ipmi_message, { "Message", "ipmi.message", FT_BYTES, BASE_HEX, NULL, 0, "", HFILL }},
1402 { &hf_ipmi_session_handle, { "Session handle", "ipmi.session_handle", FT_UINT8, BASE_HEX, NULL, 0, "", HFILL }},
1403 { &hf_ipmi_header_broadcast, { "Broadcast message", "ipmi.header.broadcast", FT_UINT8, BASE_HEX, NULL, 0, "", HFILL }},
1404 { &hf_ipmi_header_trg, { "Target Address", "ipmi.header.target", FT_UINT8, BASE_HEX, NULL, 0x0, "", HFILL }},
1405 { &hf_ipmi_header_trg_lun, { "Target LUN", "ipmi.header.trg_lun", FT_UINT8, BASE_HEX, NULL, 0x03, "", HFILL }},
1406 { &hf_ipmi_header_netfn, { "NetFN", "ipmi.header.netfn", FT_UINT8, BASE_HEX, NULL, 0xfc, "", HFILL }},
1407 { &hf_ipmi_header_crc, { "Header Checksum", "ipmi.header.crc", FT_UINT8, BASE_HEX, NULL, 0, "", HFILL }},
1408 { &hf_ipmi_header_src, { "Source Address", "ipmi.header.source", FT_UINT8, BASE_HEX, NULL, 0, "", HFILL }},
1409 { &hf_ipmi_header_src_lun, { "Source LUN", "ipmi.header.src_lun", FT_UINT8, BASE_HEX, NULL, 0x03, "", HFILL }},
1410 { &hf_ipmi_header_sequence, { "Sequence Number", "ipmi.header.sequence", FT_UINT8, BASE_HEX, NULL, 0xfc, "", HFILL }},
1411 { &hf_ipmi_header_command, { "Command", "ipmi.header.command", FT_UINT8, BASE_HEX, NULL, 0, "", HFILL }},
1412 { &hf_ipmi_header_completion, { "Completion Code", "ipmi.header.completion", FT_UINT8, BASE_HEX, NULL, 0, "", HFILL }},
1413 { &hf_ipmi_header_sig, { "Signature", "ipmi.header.signature", FT_BYTES, BASE_HEX, NULL, 0, "", HFILL }},
1414 { &hf_ipmi_data_crc, { "Data checksum", "ipmi.data.crc", FT_UINT8, BASE_HEX, NULL, 0, "", HFILL }},
1415 { &hf_ipmi_response_to, { "Response to", "ipmi.response_to", FT_FRAMENUM, BASE_DEC, NULL, 0, "", HFILL }},
1416 { &hf_ipmi_response_in, { "Response in", "ipmi.response_in", FT_FRAMENUM, BASE_DEC, NULL, 0, "", HFILL }},
1417 { &hf_ipmi_response_time, { "Responded in", "ipmi.response_time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0, "", HFILL }},
1418 { &hf_ipmi_bad_checksum, { "Bad checksum", "ipmi.bad_checksum", FT_BOOLEAN, 8, NULL, 0, "", HFILL }}
1420 static gint *ett[] = {
1428 static const enum_val_t msgfmt_vals[] = {
1429 { "none", "None", MSGFMT_NONE },
1430 { "ipmb", "IPMB", MSGFMT_IPMB },
1431 { "lan", "Session-based (LAN, ...)", MSGFMT_LAN },
1432 { "guess", "Use heuristics", MSGFMT_GUESS },
1435 static const enum_val_t oemsel_vals[] = {
1436 { "none", "None", IPMI_OEM_NONE },
1437 { "pps", "Pigeon Point Systems", IPMI_OEM_PPS },
1443 proto_ipmi = proto_register_protocol("Intelligent Platform Management Interface",
1447 proto_register_field_array(proto_ipmi, hf, array_length(hf));
1448 proto_register_subtree_array(ett, array_length(ett));
1450 ipmi_netfn_setdesc(IPMI_CHASSIS_REQ, "Chassis", 0);
1451 ipmi_netfn_setdesc(IPMI_BRIDGE_REQ, "Bridge", 0);
1452 ipmi_netfn_setdesc(IPMI_SE_REQ, "Sensor/Event", 0);
1453 ipmi_netfn_setdesc(IPMI_APP_REQ, "Application", 0);
1454 ipmi_netfn_setdesc(IPMI_UPDATE_REQ, "Firmware Update", 0);
1455 ipmi_netfn_setdesc(IPMI_STORAGE_REQ, "Storage", 0);
1456 ipmi_netfn_setdesc(IPMI_TRANSPORT_REQ, "Transport", 0);
1457 ipmi_netfn_setdesc(IPMI_GROUP_REQ, "Group", 1);
1458 ipmi_netfn_setdesc(IPMI_OEM_REQ, "OEM/Group", 3);
1459 for (i = 0x30; i < 0x40; i += 2) {
1460 ipmi_netfn_setdesc(i, "OEM", 0);
1463 ipmi_register_chassis(proto_ipmi);
1464 ipmi_register_bridge(proto_ipmi);
1465 ipmi_register_se(proto_ipmi);
1466 ipmi_register_app(proto_ipmi);
1467 ipmi_register_update(proto_ipmi);
1468 ipmi_register_storage(proto_ipmi);
1469 ipmi_register_transport(proto_ipmi);
1470 ipmi_register_picmg(proto_ipmi);
1471 ipmi_register_pps(proto_ipmi);
1473 register_dissector("ipmi", dissect_ipmi, proto_ipmi);
1475 m = prefs_register_protocol(proto_ipmi, NULL);
1476 prefs_register_bool_preference(m, "fru_langcode_is_english", "FRU Language Code is English",
1477 "FRU Language Code is English; strings are ASCII+LATIN1 (vs. Unicode)",
1478 &fru_langcode_is_english);
1479 prefs_register_uint_preference(m, "response_after_req", "Maximum delay of response message",
1480 "Do not search for responses coming after this timeout (milliseconds)",
1481 10, &response_after_req);
1482 prefs_register_uint_preference(m, "response_before_req", "Response ahead of request",
1483 "Allow for responses before requests (milliseconds)",
1484 10, &response_before_req);
1485 prefs_register_enum_preference(m, "msgfmt", "Format of embedded messages",
1486 "Format of messages embedded into Send/Get/Forward Message",
1487 &message_format, msgfmt_vals, FALSE);
1488 prefs_register_enum_preference(m, "selected_oem", "OEM commands parsed as",
1489 "Selects which OEM format is used for commands that IPMI does not define",
1490 &selected_oem, oemsel_vals, FALSE);