HACK: pinfo->private_data points to smb_info again
[wireshark-wip.git] / epan / dissectors / packet-ipmi.c
blobca57ffff83c27a2abd708d437640a7b0d40636bc
1 /* packet-ipmi.c
2 * Routines for IPMI dissection
3 * Copyright 2002-2008, Alexey Neyman, Pigeon Point Systems <avn@pigeonpoint.com>
5 * $Id$
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.
26 #include "config.h"
28 #include <string.h>
29 #include <time.h>
30 #include <math.h>
32 #include <epan/packet.h>
33 #include <epan/conversation.h>
34 #include <epan/wmem/wmem.h>
35 #include <epan/to_str.h>
36 #include <epan/prefs.h>
37 #include <epan/addr_resolv.h>
39 #include "packet-ipmi.h"
42 * See the IPMI specifications at
44 * http://www.intel.com/design/servers/ipmi/
47 /* Define IPMI_DEBUG to enable printing the process of request-response pairing */
48 /* #define IPMI_DEBUG */
50 /* Top-level search structure: list of registered handlers for a given netFn */
51 struct ipmi_netfn_root {
52 ipmi_netfn_t *list;
53 const char *desc;
54 guint32 siglen;
57 #define NSAVED_DATA 2
59 /* We need more than a conversation. Over the same RMCP session
60 (or IPMB), there may be several addresses/SWIDs. Thus, in a single
61 Wireshark-maintained conversation we might need to find our own... */
62 struct ipmi_saved_data {
63 guint32 set_data;
64 guint32 saved_data[NSAVED_DATA];
67 enum {
68 RQ = 0,
69 RS,
70 RS2,
72 MAX_RQRS_FRAMES
75 enum {
76 MSGFMT_NONE = 0,
77 MSGFMT_IPMB,
78 MSGFMT_LAN,
79 MSGFMT_GUESS
82 struct ipmi_reqresp {
83 struct ipmi_reqresp *next;
84 struct ipmi_saved_data *data;
85 int (*whichresponse)(struct ipmi_header *hdr, struct ipmi_reqresp *rr);
86 struct {
87 guint32 num;
88 nstime_t time;
89 } frames[MAX_RQRS_FRAMES];
90 guint8 netfn;
91 guint8 cmd;
94 struct ipmi_keyhead {
95 struct ipmi_reqresp *rr;
98 struct ipmi_keytree {
99 wmem_tree_t *heads;
102 struct ipmi_parse_typelen {
103 void (*get_len)(guint *, guint *, tvbuff_t *, guint, guint, gboolean);
104 void (*parse)(char *, tvbuff_t *, guint, guint);
105 const char *desc;
108 struct ipmi_header *ipmi_current_hdr;
110 static gint proto_ipmi = -1;
112 static gboolean fru_langcode_is_english = TRUE;
113 static guint response_after_req = 5000;
114 static guint response_before_req = 0;
115 static guint message_format = MSGFMT_GUESS;
116 static guint selected_oem = IPMI_OEM_NONE;
118 static gint hf_ipmi_message = -1;
119 static gint hf_ipmi_session_handle = -1;
120 static gint hf_ipmi_header_broadcast = -1;
121 static gint hf_ipmi_header_trg = -1;
122 static gint hf_ipmi_header_trg_lun = -1;
123 static gint hf_ipmi_header_netfn = -1;
124 static gint hf_ipmi_header_crc = -1;
125 static gint hf_ipmi_header_src = -1;
126 static gint hf_ipmi_header_src_lun = -1;
127 static gint hf_ipmi_header_sequence = -1;
128 static gint hf_ipmi_header_command = -1;
129 static gint hf_ipmi_header_completion = -1;
130 static gint hf_ipmi_header_sig = -1;
131 static gint hf_ipmi_data_crc = -1;
132 static gint hf_ipmi_response_to = -1;
133 static gint hf_ipmi_response_in = -1;
134 static gint hf_ipmi_response_time = -1;
135 static gint hf_ipmi_bad_checksum = -1;
137 static gint ett_ipmi = -1;
138 static gint ett_header = -1;
139 static gint ett_header_byte_1 = -1;
140 static gint ett_header_byte_4 = -1;
141 static gint ett_data = -1;
142 static gint ett_typelen = -1;
144 static guint nest_level;
145 static struct ipmi_saved_data *current_saved_data;
146 static struct ipmi_netfn_root ipmi_cmd_tab[IPMI_NETFN_MAX];
148 /* Debug support */
149 static void
150 debug_printf(const gchar *fmt _U_, ...)
152 #if defined(IPMI_DEBUG)
153 va_list ap;
155 va_start(ap, fmt);
156 vfprintf(stderr, fmt, ap);
157 va_end(ap);
158 #endif
161 /* ----------------------------------------------------------------
162 Support for request-response caching.
163 ---------------------------------------------------------------- */
165 /* Key generation; returns the same key for requests and responses */
166 static guint32
167 makekey(struct ipmi_header *hdr)
169 guint32 trg, src, res;
171 trg = (hdr->trg_sa << 2) | hdr->trg_lun;
172 src = (hdr->src_sa << 2) | hdr->src_lun;
173 res = trg < src ? (trg << 10) | src : (src << 10) | trg;
174 return (hdr->seq << 20) | res;
177 static struct ipmi_reqresp *
178 key_lookup_reqresp(struct ipmi_keyhead *kh, struct ipmi_header *hdr, frame_data *fd)
180 struct ipmi_reqresp *rr, *best_rr = NULL;
181 nstime_t delta;
182 double d, best_d = (double)(2 * response_after_req);
183 guint8 netfn = hdr->netfn & 0x3e; /* disregard 'response' bit */
184 guint8 is_resp = hdr->netfn & 0x01;
185 int i;
187 /* Source/target SA/LUN and sequence number are assumed to match; wmem_tree*
188 ensure that. While checking for "being here", we can't rely on flags.visited,
189 as we may have more than one IPMI message in a single frame. */
190 for (rr = kh->rr; rr; rr = rr->next) {
191 if (rr->netfn != netfn || rr->cmd != hdr->cmd) {
192 continue;
195 for (i = 0; i < MAX_RQRS_FRAMES; i++) {
196 /* RQ=0 - 0th element is request frame number; RS/RS2 -
197 responses are non zero */
198 if (((!i) ^ is_resp) && rr->frames[i].num == fd->num) {
199 /* Already been here */
200 return rr;
204 /* Reject responses before requests or more than 5 seconds ahead */
205 if (is_resp) {
206 nstime_delta(&delta, &fd->abs_ts, &rr->frames[RQ].time);
207 } else {
208 /* Use RS here, not RS2 - frames[RS] is always filled if we had
209 at least one response */ /* TBD */
210 nstime_delta(&delta, &rr->frames[RS].time, &fd->abs_ts);
212 d = nstime_to_msec(&delta);
213 if (d < -(double)response_before_req || d > (double)response_after_req) {
214 continue;
217 if (fabs(d) < best_d) {
218 best_rr = rr;
219 best_d = fabs(d);
223 return best_rr;
226 static void
227 key_insert_reqresp(struct ipmi_keyhead *kh, struct ipmi_reqresp *rr)
229 /* Insert to head, so that the search would find most recent response */
230 rr->next = kh->rr;
231 kh->rr = rr;
234 static inline gboolean
235 set_framenums(struct ipmi_header *hdr, struct ipmi_reqresp *rr, frame_data *fd)
237 int which = hdr->netfn & 0x01 ? rr->whichresponse ? rr->whichresponse(hdr, rr) : RS : RQ;
239 if (rr->frames[which].num && rr->frames[which].num != fd->num) {
240 return FALSE;
242 rr->frames[which].num = fd->num;
243 rr->frames[which].time = fd->abs_ts;
244 return TRUE;
247 #define IS_SENDMSG(hdr) (((hdr)->netfn & 0x3e) == IPMI_APP_REQ && (hdr)->cmd == 0x34)
250 ipmi_sendmsg_whichresponse(struct ipmi_header *hdr, struct ipmi_reqresp *rr)
252 if (!IS_SENDMSG(hdr)) {
253 /* Not a Send Message: just a simple response */
254 return RS;
257 if (hdr->data_len > 0) {
258 /* Trivial case: response with non-null data can only be a
259 response in AMC.0 style */
260 return RS2;
262 /* Otherwise, we need to somehow determine 1st and 2nd responses. Note
263 that both them may lack the data - in case that the embedded response
264 returned with error. Thus, employ the following algo:
265 - First, assign to [RS] frame (this also won't conflict with full response
266 received - it could only happen if send message succeeded)
267 - In case we see another data-less response, see that we assign the one
268 with success completion code to [RS] and with non-success code to [RS2].
270 We assume that we can't receive 2 responses with non-successful completion
271 (if the outmost Send Message failed, how was the embedded one sent?)
273 if (!rr->frames[RS].num) {
274 return RS;
277 /* In case we received "success", move the other response to [RS2] */
278 if (!hdr->ccode) {
279 if (!rr->frames[RS2].num) {
280 rr->frames[RS2] = rr->frames[RS];
282 return RS;
285 /* [RS] occupied, non-successful */
286 return RS2;
290 ipmi_sendmsg_otheridx(struct ipmi_header *hdr)
292 return IS_SENDMSG(hdr) ? nest_level : RS;
295 struct ipmi_header *
296 ipmi_sendmsg_getheaders(struct ipmi_header *base, void *arg, guint i)
298 static struct ipmi_header hdr;
299 struct ipmi_header *wrapper = (struct ipmi_header *)arg;
301 /* The problem stems from the fact that the original IPMI
302 specification (before errata came) did not specify the response
303 to Send Message (and even the fact that there are 2 responses -
304 to Send Message and to embedded command). Even then, there is
305 one vagueness remaining - whether the response should use
306 the sequence number from the wrapper or from the embedded message.
308 Thus, there are 3 types of responses to Send Message
310 * AMC.0-style: the response is embedded in a normal Send Message
311 response. Easiest case: such responses will be correctly detected
312 with the default code in ipmi_do_dissect.
314 * IPMI-style, with both variants of sequence numbers. Note that
315 most tools dealing with Send Message (e.g. ipmitool) circumvent
316 this vagueness by using the same sequence number in both wrapper
317 and embedded messages. If we detect such "smart" messages, we
318 provide only one extra header. For correctness, we have to provide
319 for both variants, however.
322 if (i >= 2 || (i == 1 && wrapper->seq == base->seq)) {
323 return NULL;
326 /* Construct hybrid header */
327 hdr.trg_sa = wrapper->trg_sa;
328 hdr.trg_lun = wrapper->trg_lun;
329 hdr.src_sa = wrapper->src_sa;
330 hdr.src_lun = wrapper->src_lun;
331 hdr.netfn = base->netfn;
332 hdr.cmd = base->cmd;
333 hdr.seq = i ? base->seq : wrapper->seq;
334 hdr.ccode = base->ccode;
335 hdr.data_len = base->data_len;
336 return &hdr;
339 static void
340 maybe_insert_reqresp(packet_info *pinfo, ipmi_dissect_format_t *dfmt, struct ipmi_header *hdr)
342 conversation_t *cnv;
343 struct ipmi_keytree *kt;
344 struct ipmi_keyhead *kh;
345 struct ipmi_reqresp *rr;
346 guint32 key, i;
348 cnv = find_or_create_conversation(pinfo);
350 kt = (struct ipmi_keytree *)conversation_get_proto_data(cnv, proto_ipmi);
351 if (!kt) {
352 kt = wmem_new(wmem_file_scope(), struct ipmi_keytree);
353 kt->heads = wmem_tree_new(wmem_file_scope());
354 conversation_add_proto_data(cnv, proto_ipmi, kt);
357 debug_printf("--> maybe_insert_reqresp( %d )\n", pinfo->fd->num);
358 i = 0;
359 do {
360 debug_printf("Checking [ (%02x,%1x <-> %02x,%1x : %02x) %02x %02x ]\n",
361 hdr->trg_sa, hdr->trg_lun, hdr->src_sa, hdr->src_lun, hdr->seq,
362 hdr->netfn, hdr->cmd);
363 key = makekey(hdr);
364 kh = (struct ipmi_keyhead *)wmem_tree_lookup32(kt->heads, key);
365 if (!kh) {
366 kh = wmem_new0(wmem_file_scope(), struct ipmi_keyhead);
367 wmem_tree_insert32(kt->heads, key, kh);
369 if ((rr = key_lookup_reqresp(kh, hdr, pinfo->fd)) != NULL) {
370 /* Already recorded - set frame number and be done. Look no
371 further - even if there are several responses, we have
372 found the right one. */
373 debug_printf("Found existing [ <%d,%d,%d> (%02x,%1x <-> %02x,%1x : %02x) %02x %02x ]\n",
374 rr->frames[0].num, rr->frames[1].num, rr->frames[2].num,
375 hdr->trg_sa, hdr->trg_lun, hdr->src_sa, hdr->src_lun, hdr->seq,
376 rr->netfn, rr->cmd);
377 if (!rr->whichresponse) {
378 rr->whichresponse = dfmt->whichresponse;
380 if (set_framenums(hdr, rr, pinfo->fd)) {
381 debug_printf("Set frames [ <%d,%d,%d> (%02x,%1x <-> %02x,%1x : %02x) %02x %02x ]\n",
382 rr->frames[0].num, rr->frames[1].num, rr->frames[2].num,
383 hdr->trg_sa, hdr->trg_lun, hdr->src_sa, hdr->src_lun, hdr->seq,
384 rr->netfn, rr->cmd);
385 current_saved_data = rr->data;
386 return;
389 /* Found, but already occupied. Fall through to allocating the structures */
390 current_saved_data = NULL;
392 /* Not found; allocate new structures */
393 if (!current_saved_data) {
394 /* One 'ipmi_saved_data' for all 'ipmi_req_resp' allocated */
395 current_saved_data = wmem_new0(wmem_file_scope(), struct ipmi_saved_data);
397 rr = wmem_new0(wmem_file_scope(), struct ipmi_reqresp);
398 rr->whichresponse = dfmt->whichresponse;
399 rr->netfn = hdr->netfn & 0x3e;
400 rr->cmd = hdr->cmd;
401 rr->data = current_saved_data;
402 set_framenums(hdr, rr, pinfo->fd);
403 key_insert_reqresp(kh, rr);
404 debug_printf("Inserted [ <%d,%d,%d> (%02x,%1x <-> %02x,%1x : %02x) %02x %02x ]\n",
405 rr->frames[0].num, rr->frames[1].num, rr->frames[2].num,
406 hdr->trg_sa, hdr->trg_lun, hdr->src_sa, hdr->src_lun, hdr->seq,
407 rr->netfn, rr->cmd);
409 /* Do we have other headers to insert? */
410 hdr = dfmt->getmoreheaders ? dfmt->getmoreheaders(hdr, dfmt->arg, i++) : NULL;
411 } while (hdr);
414 static void
415 add_reqresp_info(ipmi_dissect_format_t *dfmt, struct ipmi_header *hdr, proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo)
417 conversation_t *cnv;
418 struct ipmi_keytree *kt;
419 struct ipmi_keyhead *kh;
420 struct ipmi_reqresp *rr = NULL;
421 guint32 key, i, other_idx;
422 proto_item *ti;
423 nstime_t ns;
425 debug_printf("--> add_reqresp_info( %d )\n", pinfo->fd->num);
427 /* [0] is request; [1..MAX_RS_LEVEL] are responses */
428 other_idx = (hdr->netfn & 0x01) ? RQ : dfmt->otheridx ? dfmt->otheridx(hdr) : RS;
430 if (other_idx >= MAX_RQRS_FRAMES) {
431 /* No chance; we don't look that deep into nested Send Message.
432 Note that we'll use the other_idx value to distinguish
433 request from response. */
434 goto fallback;
437 /* Here, we don't try to create any object - everything is assumed
438 to be created in maybe_insert_reqresp() */
439 if ((cnv = find_conversation(pinfo->fd->num, &pinfo->src,
440 &pinfo->dst, pinfo->ptype,
441 pinfo->srcport, pinfo->destport, 0)) == NULL) {
442 goto fallback;
444 if ((kt = (struct ipmi_keytree *)conversation_get_proto_data(cnv, proto_ipmi)) == NULL) {
445 goto fallback;
448 i = 0;
449 while (1) {
450 debug_printf("Looking for [ (%02x,%1x <-> %02x,%1x : %02x) %02x %02x ]\n",
451 hdr->trg_sa, hdr->trg_lun, hdr->src_sa, hdr->src_lun, hdr->seq,
452 hdr->netfn, hdr->cmd);
453 key = makekey(hdr);
454 if ((kh = (struct ipmi_keyhead *)wmem_tree_lookup32(kt->heads, key)) != NULL &&
455 (rr = key_lookup_reqresp(kh, hdr, pinfo->fd)) != NULL) {
456 debug_printf("Found [ <%d,%d,%d> (%02x,%1x <-> %02x,%1x : %02x) %02x %02x ]\n",
457 rr->frames[0].num, rr->frames[1].num, rr->frames[2].num,
458 hdr->trg_sa, hdr->trg_lun, hdr->src_sa, hdr->src_lun, hdr->seq,
459 rr->netfn, rr->cmd);
460 if (rr->frames[other_idx].num) {
461 break;
465 /* Do we have other headers to check? */
466 hdr = dfmt->getmoreheaders ? dfmt->getmoreheaders(hdr, dfmt->arg, i++) : NULL;
467 if (!hdr) {
468 goto fallback;
472 if (hdr->netfn & 0x01) {
473 /* Response */
474 ti = proto_tree_add_uint(tree, hf_ipmi_response_to,
475 tvb, 0, 0, rr->frames[RQ].num);
476 PROTO_ITEM_SET_GENERATED(ti);
477 nstime_delta(&ns, &pinfo->fd->abs_ts, &rr->frames[RQ].time);
478 ti = proto_tree_add_time(tree, hf_ipmi_response_time,
479 tvb, 0, 0, &ns);
480 PROTO_ITEM_SET_GENERATED(ti);
481 } else {
482 /* Request */
483 ti = proto_tree_add_uint(tree, hf_ipmi_response_in,
484 tvb, 0, 0, rr->frames[other_idx].num);
485 PROTO_ITEM_SET_GENERATED(ti);
487 return;
489 fallback:
490 ti = proto_tree_add_text(tree, tvb, 0, 0, "No corresponding %s",
491 other_idx ? "response" : "request");
492 PROTO_ITEM_SET_GENERATED(ti);
495 /* Save data in request, retrieve in response */
496 void
497 ipmi_setsaveddata(guint idx, guint32 val)
499 DISSECTOR_ASSERT(idx < NSAVED_DATA);
500 current_saved_data->saved_data[idx] = val;
501 current_saved_data->set_data |= (1 << idx);
504 gboolean
505 ipmi_getsaveddata(guint idx, guint32 *pval)
507 DISSECTOR_ASSERT(idx < NSAVED_DATA);
508 if (current_saved_data->set_data & (1 << idx)) {
509 *pval = current_saved_data->saved_data[idx];
510 return TRUE;
512 return FALSE;
515 /* ----------------------------------------------------------------
516 Support for Type/Length fields parsing.
517 ---------------------------------------------------------------- */
519 static void
520 get_len_binary(guint *clen, guint *blen, tvbuff_t *tvb _U_, guint offs _U_,
521 guint len, gboolean len_is_bytes _U_)
523 *clen = len * 3;
524 *blen = len;
527 static void
528 parse_binary(char *p, tvbuff_t *tvb, guint offs, guint len)
530 static const char hex[] = "0123456789ABCDEF";
531 guint8 v;
532 guint i;
534 for (i = 0; i < len / 3; i++) {
535 v = tvb_get_guint8(tvb, offs + i);
536 *p++ = hex[v >> 4];
537 *p++ = hex[v & 0xf];
538 *p++ = ' ';
541 if (i) {
542 *--p = '\0';
546 static struct ipmi_parse_typelen ptl_binary = {
547 get_len_binary, parse_binary, "Binary"
550 static void
551 get_len_bcdplus(guint *clen, guint *blen, tvbuff_t *tvb _U_, guint offs _U_,
552 guint len, gboolean len_is_bytes)
554 if (len_is_bytes) {
555 *clen = len * 2;
556 *blen = len;
557 } else {
558 *blen = (len + 1) / 2;
559 *clen = len;
563 static void
564 parse_bcdplus(char *p, tvbuff_t *tvb, guint offs, guint len)
566 static const char bcd[] = "0123456789 -.:,_";
567 guint i, msk = 0xf0, shft = 4;
568 guint8 v;
570 for (i = 0; i < len; i++) {
571 v = (tvb_get_guint8(tvb, offs + i / 2) & msk) >> shft;
572 *p++ = bcd[v];
573 msk ^= 0xff;
574 shft = 4 - shft;
578 static struct ipmi_parse_typelen ptl_bcdplus = {
579 get_len_bcdplus, parse_bcdplus, "BCD+"
582 static void
583 get_len_6bit_ascii(guint *clen, guint *blen, tvbuff_t *tvb _U_, guint offs _U_,
584 guint len, gboolean len_is_bytes)
586 if (len_is_bytes) {
587 *clen = len * 4 / 3;
588 *blen = len;
589 } else {
590 *blen = (len * 3 + 3) / 4;
591 *clen = len;
595 static void
596 parse_6bit_ascii(char *p, tvbuff_t *tvb, guint offs, guint len)
598 guint32 v;
599 guint i;
601 /* First, handle "full" triplets of bytes, 4 characters each */
602 for (i = 0; i < len / 4; i++) {
603 v = tvb_get_letoh24(tvb, offs + i * 3);
604 p[0] = ' ' + (v & 0x3f);
605 p[1] = ' ' + ((v >> 6) & 0x3f);
606 p[2] = ' ' + ((v >> 12) & 0x3f);
607 p[3] = ' ' + ((v >> 18) & 0x3f);
608 p += 4;
611 /* Do we have any characters left? */
612 offs += len / 4;
613 len &= 0x3;
614 switch (len) {
615 case 3:
616 v = (tvb_get_guint8(tvb, offs + 2) << 4) | (tvb_get_guint8(tvb, offs + 1) >> 4);
617 p[2] = ' ' + (v & 0x3f);
618 /* Fall thru */
619 case 2:
620 v = (tvb_get_guint8(tvb, offs + 1) << 2) | (tvb_get_guint8(tvb, offs) >> 6);
621 p[1] = ' ' + (v & 0x3f);
622 /* Fall thru */
623 case 1:
624 v = tvb_get_guint8(tvb, offs) & 0x3f;
625 p[0] = ' ' + (v & 0x3f);
629 static struct ipmi_parse_typelen ptl_6bit_ascii = {
630 get_len_6bit_ascii, parse_6bit_ascii, "6-bit ASCII"
633 static void
634 get_len_8bit_ascii(guint *clen, guint *blen, tvbuff_t *tvb, guint offs,
635 guint len, gboolean len_is_bytes _U_)
637 guint i;
638 guint8 ch;
640 *blen = len; /* One byte is one character */
641 *clen = 0;
642 for (i = 0; i < len; i++) {
643 ch = tvb_get_guint8(tvb, offs + i);
644 *clen += (ch >= 0x20 && ch <= 0x7f) ? 1 : 4;
648 static void
649 parse_8bit_ascii(char *p, tvbuff_t *tvb, guint offs, guint len)
651 guint8 ch;
652 char *pmax;
654 pmax = p + len;
655 while (p < pmax) {
656 ch = tvb_get_guint8(tvb, offs++);
657 if (ch >= 0x20 && ch <= 0x7f) {
658 *p++ = ch;
659 } else {
660 g_snprintf(p, 5, "\\x%02x", ch);
661 p += 4;
666 static struct ipmi_parse_typelen ptl_8bit_ascii = {
667 get_len_8bit_ascii, parse_8bit_ascii, "ASCII+Latin1"
670 static void
671 get_len_unicode(guint *clen, guint *blen, tvbuff_t *tvb _U_, guint offs _U_,
672 guint len _U_, gboolean len_is_bytes)
674 if (len_is_bytes) {
675 *clen = len * 3; /* Each 2 bytes result in 6 chars printed: \Uxxxx */
676 *blen = len;
677 } else {
678 *clen = len * 6;
679 *blen = len * 2;
683 static void
684 parse_unicode(char *p, tvbuff_t *tvb, guint offs, guint len)
686 char *pmax = p + len;
687 guint8 ch0, ch1;
689 while (p < pmax) {
690 ch0 = tvb_get_guint8(tvb, offs++);
691 ch1 = tvb_get_guint8(tvb, offs++);
692 g_snprintf(p, 7, "\\U%02x%02x", ch0, ch1);
693 p += 6;
697 static struct ipmi_parse_typelen ptl_unicode = {
698 get_len_unicode, parse_unicode, "Unicode"
701 void
702 ipmi_add_typelen(proto_tree *tree, const char *desc, tvbuff_t *tvb,
703 guint offs, gboolean is_fru)
705 static struct ipmi_parse_typelen *fru_eng[4] = {
706 &ptl_binary, &ptl_bcdplus, &ptl_6bit_ascii, &ptl_8bit_ascii
708 static struct ipmi_parse_typelen *fru_noneng[4] = {
709 &ptl_binary, &ptl_bcdplus, &ptl_6bit_ascii, &ptl_unicode
711 static struct ipmi_parse_typelen *ipmi[4] = {
712 &ptl_unicode, &ptl_bcdplus, &ptl_6bit_ascii, &ptl_8bit_ascii
714 struct ipmi_parse_typelen *ptr;
715 proto_tree *s_tree;
716 proto_item *ti;
717 guint type, msk, clen, blen, len;
718 const char *unit;
719 char *str;
720 guint8 typelen;
722 typelen = tvb_get_guint8(tvb, offs);
723 type = typelen >> 6;
724 if (is_fru) {
725 msk = 0x3f;
726 ptr = (fru_langcode_is_english ? fru_eng : fru_noneng)[type];
727 unit = "bytes";
728 } else {
729 msk = 0x1f;
730 ptr = ipmi[type];
731 unit = "characters";
734 len = typelen & msk;
735 ptr->get_len(&clen, &blen, tvb, offs + 1, len, is_fru);
737 str = (char *)wmem_alloc(wmem_packet_scope(), clen + 1);
738 ptr->parse(str, tvb, offs + 1, clen);
739 str[clen] = '\0';
741 ti = proto_tree_add_text(tree, tvb, offs, 1, "%s Type/Length byte: %s, %d %s",
742 desc, ptr->desc, len, unit);
743 s_tree = proto_item_add_subtree(ti, ett_typelen);
744 proto_tree_add_text(s_tree, tvb, offs, 1, "%sType: %s (0x%02x)",
745 ipmi_dcd8(typelen, 0xc0), ptr->desc, type);
746 proto_tree_add_text(s_tree, tvb, offs, 1, "%sLength: %d %s",
747 ipmi_dcd8(typelen, msk), len, unit);
749 proto_tree_add_text(tree, tvb, offs + 1, blen, "%s: [%s] '%s'",
750 desc, ptr->desc, str);
753 /* ----------------------------------------------------------------
754 Timestamp, IPMI-style.
755 ---------------------------------------------------------------- */
756 void
757 ipmi_add_timestamp(proto_tree *tree, gint hf, tvbuff_t *tvb, guint offset)
759 guint32 ts = tvb_get_letohl(tvb, offset);
761 if (ts == 0xffffffff) {
762 proto_tree_add_uint_format_value(tree, hf, tvb, offset, 4,
763 ts, "Unspecified/Invalid");
764 } else if (ts <= 0x20000000) {
765 proto_tree_add_uint_format_value(tree, hf, tvb, offset, 4,
766 ts, "%s since SEL device's initialization",
767 time_secs_to_str_unsigned(ts));
768 } else {
769 proto_tree_add_uint_format_value(tree, hf, tvb, offset, 4,
770 ts, "%s", abs_time_secs_to_str(ts, ABSOLUTE_TIME_UTC, TRUE));
774 /* ----------------------------------------------------------------
775 GUID, IPMI-style.
776 ---------------------------------------------------------------- */
778 void
779 ipmi_add_guid(proto_tree *tree, gint hf, tvbuff_t *tvb, guint offset)
781 e_guid_t guid;
782 int i;
784 guid.data1 = tvb_get_letohl(tvb, offset + 12);
785 guid.data2 = tvb_get_letohs(tvb, offset + 10);
786 guid.data3 = tvb_get_letohs(tvb, offset + 8);
787 for (i = 0; i < 8; i++) {
788 guid.data4[i] = tvb_get_guint8(tvb, offset + 7 - i);
790 proto_tree_add_guid(tree, hf, tvb, offset, 16, &guid);
793 /* ----------------------------------------------------------------
794 Routines for registering/looking up command parsers.
795 ---------------------------------------------------------------- */
797 static void
798 ipmi_netfn_setdesc(guint32 netfn, const char *desc, guint32 siglen)
800 struct ipmi_netfn_root *inr;
802 inr = &ipmi_cmd_tab[netfn >> 1];
803 inr->desc = desc;
804 inr->siglen = siglen;
807 void
808 ipmi_register_netfn_cmdtab(guint32 netfn, guint oem_selector,
809 const guint8 *sig, guint32 siglen, const char *desc,
810 ipmi_cmd_t *cmdtab, guint32 cmdtablen)
812 struct ipmi_netfn_root *inr;
813 ipmi_netfn_t *inh;
815 netfn >>= 1; /* Requests and responses grouped together */
816 if (netfn >= IPMI_NETFN_MAX) {
817 g_warning("NetFn too large: %x", netfn * 2);
818 return;
821 inr = &ipmi_cmd_tab[netfn];
822 if (inr->siglen != siglen) {
823 /* All handlers per netFn should have the same signature length */
824 g_warning("NetFn %d: different signature lengths: %d vs %d",
825 netfn * 2, inr->siglen, siglen);
826 return;
829 inh = (struct ipmi_netfn_handler *)g_malloc(sizeof(struct ipmi_netfn_handler));
830 inh->desc = desc;
831 inh->oem_selector = oem_selector;
832 inh->sig = sig;
833 inh->cmdtab = cmdtab;
834 inh->cmdtablen = cmdtablen;
836 inh->next = inr->list;
837 inr->list = inh;
840 guint32
841 ipmi_getsiglen(guint32 netfn)
843 return ipmi_cmd_tab[netfn >> 1].siglen;
846 const char *
847 ipmi_getnetfnname(guint32 netfn, ipmi_netfn_t *nf)
849 const char *dn, *db;
851 dn = ipmi_cmd_tab[netfn >> 1].desc ?
852 ipmi_cmd_tab[netfn >> 1].desc : "Reserved";
853 db = nf ? nf->desc : NULL;
854 if (db) {
855 return wmem_strdup_printf(wmem_packet_scope(), "%s (%s)", db, dn);
856 } else {
857 return dn;
861 ipmi_netfn_t *
862 ipmi_getnetfn(guint32 netfn, const guint8 *sig)
864 struct ipmi_netfn_root *inr;
865 ipmi_netfn_t *inh;
867 inr = &ipmi_cmd_tab[netfn >> 1];
868 for (inh = inr->list; inh; inh = inh->next) {
869 if ((inh->oem_selector == selected_oem || inh->oem_selector == IPMI_OEM_NONE)
870 && (!inr->siglen || !memcmp(sig, inh->sig, inr->siglen))) {
871 return inh;
875 /* Either unknown netFn or signature does not match */
876 return NULL;
879 ipmi_cmd_t *
880 ipmi_getcmd(ipmi_netfn_t *nf, guint32 cmd)
882 static ipmi_cmd_t ipmi_cmd_unknown = {
883 0x00, /* Code */
884 ipmi_notimpl, /* request */
885 ipmi_notimpl, /* response */
886 NULL, /* command codes */
887 NULL, /* subfunctions */
888 "Unknown command",
889 0 /* flag */
891 ipmi_cmd_t *ic;
892 size_t i, len;
894 if (nf) {
895 len = nf->cmdtablen;
896 for (ic = nf->cmdtab, i = 0; i < len; i++, ic++) {
897 if (ic->cmd == cmd) {
898 return ic;
903 return &ipmi_cmd_unknown;
906 /* ----------------------------------------------------------------
907 Various utility functions.
908 ---------------------------------------------------------------- */
910 void
911 ipmi_notimpl(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
913 if (tree) {
914 proto_tree_add_text(tree, tvb, 0, -1, "[PARSER NOT IMPLEMENTED]");
918 char *
919 ipmi_dcd8(guint32 val, guint32 mask)
921 static char buf[64];
923 decode_bitfield_value(buf, val, mask, 8);
924 return buf;
927 void
928 ipmi_fmt_10ms_1based(gchar *s, guint32 v)
930 g_snprintf(s, ITEM_LABEL_LENGTH, "%d.%03d seconds", v / 100, (v % 100) * 10);
933 void
934 ipmi_fmt_500ms_0based(gchar *s, guint32 v)
936 ipmi_fmt_500ms_1based(s, ++v);
939 void
940 ipmi_fmt_500ms_1based(gchar *s, guint32 v)
942 g_snprintf(s, ITEM_LABEL_LENGTH, "%d.%03d seconds", v / 2, (v % 2) * 500);
945 void
946 ipmi_fmt_1s_0based(gchar *s, guint32 v)
948 ipmi_fmt_1s_1based(s, ++v);
951 void
952 ipmi_fmt_1s_1based(gchar *s, guint32 v)
954 g_snprintf(s, ITEM_LABEL_LENGTH, "%d seconds", v);
957 void
958 ipmi_fmt_2s_0based(gchar *s, guint32 v)
960 g_snprintf(s, ITEM_LABEL_LENGTH, "%d seconds", (v + 1) * 2);
963 void
964 ipmi_fmt_5s_1based(gchar *s, guint32 v)
966 g_snprintf(s, ITEM_LABEL_LENGTH, "%d seconds", v * 5);
969 void
970 ipmi_fmt_version(gchar *s, guint32 v)
972 g_snprintf(s, ITEM_LABEL_LENGTH, "%d.%d", v & 0x0f, (v >> 4) & 0x0f);
975 void
976 ipmi_fmt_channel(gchar *s, guint32 v)
978 static const value_string chan_vals[] = {
979 { 0x00, "Primary IPMB (IPMB-0)" },
980 { 0x07, "IPMB-L" },
981 { 0x0e, "Current channel" },
982 { 0x0f, "System Interface" },
983 { 0, NULL }
986 g_snprintf(s, ITEM_LABEL_LENGTH, "%s (0x%02x)",
987 val_to_str(v, chan_vals, "Channel #%d"), v);
990 void
991 ipmi_fmt_udpport(gchar *s, guint32 v)
993 g_snprintf(s, ITEM_LABEL_LENGTH, "%s (%d)", get_udp_port(v), v);
996 void
997 ipmi_fmt_percent(gchar *s, guint32 v)
999 g_snprintf(s, ITEM_LABEL_LENGTH, "%d%%", v);
1002 const char *
1003 ipmi_get_completion_code(guint8 completion, ipmi_cmd_t *cmd)
1005 static const value_string std_completion_codes[] = {
1006 { 0x00, "Command Completed Normally" },
1007 { 0xc0, "Node Busy" },
1008 { 0xc1, "Invalid Command" },
1009 { 0xc2, "Command invalid for given LUN" },
1010 { 0xc3, "Timeout while processing command, response unavailable" },
1011 { 0xc4, "Out of space" },
1012 { 0xc5, "Reservation Canceled or Invalid Reservation ID" },
1013 { 0xc6, "Request data truncated" },
1014 { 0xc7, "Request data length invalid" },
1015 { 0xc8, "Request data field length limit exceeded" },
1016 { 0xc9, "Parameter out of range" },
1017 { 0xca, "Cannot return number of requested data bytes" },
1018 { 0xcb, "Requested Sensor, data, or record not present" },
1019 { 0xcc, "Invalid data field in Request" },
1020 { 0xcd, "Command illegal for specified sensor or record type" },
1021 { 0xce, "Command response could not be provided" },
1022 { 0xcf, "Cannot execute duplicated request" },
1023 { 0xd0, "Command response could not be provided: SDR Repository in update mode" },
1024 { 0xd1, "Command response could not be provided: device in firmware update mode" },
1025 { 0xd2, "Command response could not be provided: BMC initialization or initialization agent in progress" },
1026 { 0xd3, "Destination unavailable" },
1027 { 0xd4, "Cannot execute command: insufficient privilege level or other security-based restriction" },
1028 { 0xd5, "Cannot execute command: command, or request parameter(s), not supported in present state" },
1029 { 0xd6, "Cannot execute command: parameter is illegal because subfunction is disabled or unavailable" },
1030 { 0xff, "Unspecified error" },
1032 { 0, NULL }
1034 const char *res;
1036 if (completion >= 0x01 && completion <= 0x7e) {
1037 return "Device specific (OEM) completion code";
1040 if (completion >= 0x80 && completion <= 0xbe) {
1041 if (cmd && cmd->cs_cc && (res = try_val_to_str(completion, cmd->cs_cc)) != NULL) {
1042 return res;
1044 return "Standard command-specific code";
1047 return val_to_str_const(completion, std_completion_codes, "Unknown");
1050 /* Guess the parsing flags for a message
1053 ipmi_guess_dissect_flags(tvbuff_t *tvb)
1055 int i;
1056 guint8 buf[6];
1058 switch (message_format) {
1059 case MSGFMT_NONE:
1060 return IPMI_D_NONE;
1061 case MSGFMT_IPMB:
1062 return IPMI_D_TRG_SA;
1063 case MSGFMT_LAN:
1064 return IPMI_D_TRG_SA|IPMI_D_SESSION_HANDLE;
1067 /* Try to guess the format */
1068 DISSECTOR_ASSERT(message_format == MSGFMT_GUESS);
1070 /* 6 is shortest message - Get Message with empty data */
1071 if (tvb_length(tvb) < 6) {
1072 return IPMI_D_NONE;
1075 /* Fetch the beginning */
1076 for (i = 0; i < 6; i++) {
1077 buf[i] = tvb_get_guint8(tvb, i);
1080 if ((buf[0] + buf[1] + buf[2]) % 0x100 == 0) {
1081 /* Looks like IPMB: first 3 bytes are zero module 256 */
1082 return IPMI_D_TRG_SA;
1085 if ((buf[1] + buf[2] + buf[3]) % 0x100 == 0) {
1086 /* Looks like LAN: like IPMB, prepended with extra byte (session handle) */
1087 return IPMI_D_TRG_SA|IPMI_D_SESSION_HANDLE;
1090 /* Can't guess */
1091 return IPMI_D_NONE;
1094 /* Print out IPMB packet.
1096 void
1097 ipmi_do_dissect(tvbuff_t *tvb, packet_info *pinfo, proto_tree *ipmi_tree, ipmi_dissect_format_t *dfmt)
1099 proto_tree *hdr_tree, *data_tree, *s_tree;
1100 proto_item *ti;
1101 tvbuff_t *data_tvb;
1102 ipmi_netfn_t *in = NULL;
1103 ipmi_cmd_t *ic = NULL;
1104 ipmi_cmd_handler_t hnd = NULL;
1105 struct ipmi_saved_data *saved_saved_data;
1106 struct ipmi_header hdr, *saved_hdr;
1107 guint8 hdr_crc, hdr_exp_crc, data_crc, data_exp_crc;
1108 guint8 is_resp, is_broadcast = 0, tmp;
1109 guint i, len, siglen, hdrlen, offs, data_chk_offs;
1110 const char *bcast, *ndesc, *cdesc, *ccdesc;
1112 if (dfmt->flags & IPMI_D_NONE) {
1113 /* No parsing requested */
1114 g_snprintf(dfmt->info, ITEM_LABEL_LENGTH, "Unknown message (not parsed)");
1115 proto_tree_add_item(ipmi_tree, hf_ipmi_message, tvb, 0, tvb_length(tvb), ENC_NA);
1116 return;
1119 nest_level++;
1120 offs = 0;
1121 memset(&hdr, 0, sizeof(hdr));
1122 debug_printf("--> do_dissect(%d, nl %u, tree %s null)\n",
1123 pinfo->fd->num, nest_level, ipmi_tree ? "IS NOT" : "IS");
1125 /* Optional byte: in Send Message targeted to session-based channels */
1126 if (dfmt->flags & IPMI_D_SESSION_HANDLE) {
1127 offs++;
1130 /* Optional byte: 00 indicates General Call address - broadcast message */
1131 if ((dfmt->flags & IPMI_D_BROADCAST) && tvb_get_guint8(tvb, offs) == 0x00) {
1132 is_broadcast = 1;
1133 offs++;
1136 /* Byte 1: target slave address, may be absent (in Get Message) */
1137 hdr.trg_sa = (dfmt->flags & IPMI_D_TRG_SA) ? tvb_get_guint8(tvb, offs++) : 0;
1139 /* Byte 2: network function + target LUN */
1140 tmp = tvb_get_guint8(tvb, offs++);
1141 hdr.trg_lun = tmp & 0x03;
1142 hdr.netfn = (tmp >> 2) & 0x3f;
1143 hdr_exp_crc = (0 - hdr.trg_sa - tmp) & 0xff;
1145 /* Byte 3: header checksum */
1146 hdr_crc = tvb_get_guint8(tvb, offs++);
1148 /* Byte 4: source slave address */
1149 hdr.src_sa = tvb_get_guint8(tvb, offs++);
1151 /* Byte 5: sequence number + source LUN */
1152 tmp = tvb_get_guint8(tvb, offs++);
1153 hdr.src_lun = tmp & 0x03;
1154 hdr.seq = (tmp >> 2) & 0x3f;
1156 /* Byte 6: command code */
1157 hdr.cmd = tvb_get_guint8(tvb, offs++);
1159 /* Byte 7: completion code (in response) */
1160 is_resp = (hdr.netfn & 0x1) ? 1 : 0;
1161 hdr.ccode = is_resp ? tvb_get_guint8(tvb, offs++) : 0;
1163 /* 0-3 bytes: signature of the defining body */
1164 siglen = ipmi_getsiglen(hdr.netfn);
1165 in = ipmi_getnetfn(hdr.netfn, tvb_get_ptr(tvb, offs, siglen));
1166 offs += siglen;
1168 /* Save header length */
1169 hdrlen = offs;
1170 hdr.data_len = tvb_length(tvb) - hdrlen - 1;
1172 /* Get some text descriptions */
1173 ic = ipmi_getcmd(in, hdr.cmd);
1174 ndesc = ipmi_getnetfnname(hdr.netfn, in);
1175 cdesc = ic->desc;
1176 ccdesc = ipmi_get_completion_code(hdr.ccode, ic);
1177 if (!is_broadcast) {
1178 bcast = "";
1179 } else if (ic->flags & CMD_MAYBROADCAST) {
1180 bcast = " (BROADCAST: command may not be broadcast)";
1181 } else {
1182 bcast = " (BROADCAST)";
1186 /* Save globals - we may be called recursively */
1187 saved_hdr = ipmi_current_hdr;
1188 ipmi_current_hdr = &hdr;
1189 saved_saved_data = current_saved_data;
1190 current_saved_data = NULL;
1192 /* Select sub-handler */
1193 hnd = is_resp ? ic->parse_resp : ic->parse_req;
1195 /* Start new conversation if needed */
1196 if (!is_resp && (ic->flags & CMD_NEWCONV)) {
1197 conversation_new(pinfo->fd->num, &pinfo->src,
1198 &pinfo->dst, pinfo->ptype,
1199 pinfo->srcport, pinfo->destport, 0);
1202 /* Check if we need to insert request-response pair */
1203 maybe_insert_reqresp(pinfo, dfmt, &hdr);
1205 /* Create data subset: all but header and last byte (checksum) */
1206 data_tvb = tvb_new_subset(tvb, hdrlen, hdr.data_len, hdr.data_len);
1208 /* Brief description of a packet */
1209 g_snprintf(dfmt->info, ITEM_LABEL_LENGTH, "%s, %s, seq 0x%02x%s%s%s",
1210 is_resp ? "Rsp" : "Req", cdesc, hdr.seq, bcast,
1211 hdr.ccode ? ", " : "", hdr.ccode ? ccdesc : "");
1213 if (!is_resp && (ic->flags & CMD_CALLRQ)) {
1214 hnd(data_tvb, pinfo, NULL);
1217 if (ipmi_tree) {
1218 add_reqresp_info(dfmt, &hdr, ipmi_tree, tvb, pinfo);
1220 ti = proto_tree_add_text(ipmi_tree, tvb, 0, hdrlen,
1221 "Header: %s (%s) from 0x%02x to 0x%02x%s", cdesc,
1222 is_resp ? "Response" : "Request", hdr.src_sa, hdr.trg_sa, bcast);
1223 hdr_tree = proto_item_add_subtree(ti, ett_header);
1225 offs = 0;
1227 if (dfmt->flags & IPMI_D_SESSION_HANDLE) {
1228 proto_tree_add_item(hdr_tree, hf_ipmi_session_handle,
1229 tvb, offs++, 1, ENC_LITTLE_ENDIAN);
1232 /* Broadcast byte (optional) */
1233 if (is_broadcast) {
1234 proto_tree_add_uint_format(hdr_tree, hf_ipmi_header_broadcast,
1235 tvb, offs++, 1, 0x00, "Broadcast message");
1238 /* Target SA, if present */
1239 if (dfmt->flags & IPMI_D_TRG_SA) {
1240 proto_tree_add_item(hdr_tree, hf_ipmi_header_trg, tvb, offs++, 1, ENC_LITTLE_ENDIAN);
1243 /* Network function + target LUN */
1244 ti = proto_tree_add_text(hdr_tree, tvb, offs, 1,
1245 "Target LUN: 0x%02x, NetFN: %s %s (0x%02x)", hdr.trg_lun,
1246 ndesc, is_resp ? "Response" : "Request", hdr.netfn);
1247 s_tree = proto_item_add_subtree(ti, ett_header_byte_1);
1249 proto_tree_add_item(s_tree, hf_ipmi_header_trg_lun, tvb, offs, 1, ENC_LITTLE_ENDIAN);
1250 proto_tree_add_uint_format(s_tree, hf_ipmi_header_netfn, tvb, offs, 1,
1251 hdr.netfn << 2, "%sNetFn: %s %s (0x%02x)",
1252 ipmi_dcd8(hdr.netfn << 2, 0xfc),
1253 ndesc, is_resp ? "Response" : "Request", hdr.netfn);
1254 offs++;
1256 /* Header checksum */
1257 if (hdr_crc == hdr_exp_crc) {
1258 proto_tree_add_uint_format_value(hdr_tree, hf_ipmi_header_crc, tvb, offs++, 1,
1259 hdr_crc, "0x%02x (correct)", hdr_crc);
1261 else {
1262 ti = proto_tree_add_boolean(hdr_tree, hf_ipmi_bad_checksum, tvb, 0, 0, TRUE);
1263 PROTO_ITEM_SET_HIDDEN(ti);
1264 proto_tree_add_uint_format_value(hdr_tree, hf_ipmi_header_crc, tvb, offs++, 1,
1265 hdr_crc, "0x%02x (incorrect, expected 0x%02x)",
1266 hdr_crc, hdr_exp_crc);
1269 /* Remember where chk2 bytes start */
1270 data_chk_offs = offs;
1272 /* Source SA */
1273 proto_tree_add_item(hdr_tree, hf_ipmi_header_src, tvb, offs++, 1, ENC_LITTLE_ENDIAN);
1275 /* Sequence number + source LUN */
1276 ti = proto_tree_add_text(hdr_tree, tvb, offs, 1,
1277 "Source LUN: 0x%02x, SeqNo: 0x%02x",
1278 hdr.src_lun, hdr.seq);
1279 s_tree = proto_item_add_subtree(ti, ett_header_byte_4);
1281 proto_tree_add_item(s_tree, hf_ipmi_header_src_lun, tvb, offs, 1, ENC_LITTLE_ENDIAN);
1282 proto_tree_add_item(s_tree, hf_ipmi_header_sequence, tvb, offs, 1, ENC_LITTLE_ENDIAN);
1283 offs++;
1285 /* Command */
1286 proto_tree_add_uint_format_value(hdr_tree, hf_ipmi_header_command, tvb, offs++, 1,
1287 hdr.cmd, "%s (0x%02x)", cdesc, hdr.cmd);
1289 /* Response code (if present) */
1290 if (is_resp) {
1291 proto_tree_add_uint_format_value(hdr_tree, hf_ipmi_header_completion, tvb, offs++, 1,
1292 hdr.ccode, "%s (0x%02x)", ccdesc, hdr.ccode);
1295 /* Defining body signature (if present) */
1296 if (siglen) {
1297 ti = proto_tree_add_item(hdr_tree, hf_ipmi_header_sig, tvb, offs, siglen, ENC_NA);
1298 proto_item_append_text(ti, " (%s)", ndesc);
1299 /*offs += siglen;*/
1302 /* Call data parser */
1303 if (tvb_length(data_tvb) && hnd) {
1304 ti = proto_tree_add_text(ipmi_tree, data_tvb, 0, -1, "Data");
1305 data_tree = proto_item_add_subtree(ti, ett_data);
1306 hnd(data_tvb, pinfo, data_tree);
1309 /* Checksum all but the last byte */
1310 len = tvb_length(tvb) - 1;
1311 data_crc = tvb_get_guint8(tvb, len);
1312 data_exp_crc = 0;
1313 for (i = data_chk_offs; i < len; i++) {
1314 data_exp_crc += tvb_get_guint8(tvb, i);
1316 data_exp_crc = (0 - data_exp_crc) & 0xff;
1318 if (data_crc == data_exp_crc) {
1319 proto_tree_add_uint_format_value(ipmi_tree, hf_ipmi_data_crc, tvb, len, 1,
1320 data_crc, "0x%02x (correct)", data_crc);
1322 else {
1323 ti = proto_tree_add_boolean(hdr_tree, hf_ipmi_bad_checksum, tvb, 0, 0, TRUE);
1324 PROTO_ITEM_SET_HIDDEN(ti);
1325 proto_tree_add_uint_format_value(ipmi_tree, hf_ipmi_data_crc, tvb, len, 1,
1326 data_crc, "0x%02x (incorrect, expected 0x%02x)",
1327 data_crc, data_exp_crc);
1331 /* Restore globals, in case we've been called recursively */
1332 ipmi_current_hdr = saved_hdr;
1333 current_saved_data = saved_saved_data;
1334 nest_level--;
1337 static void
1338 dissect_ipmi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1340 proto_tree *ipmi_tree = NULL;
1341 proto_item *ti;
1342 ipmi_dissect_format_t dfmt;
1344 col_set_str(pinfo->cinfo, COL_PROTOCOL, "IPMI/ATCA");
1346 if (tree) {
1347 ti = proto_tree_add_item(tree, proto_ipmi, tvb, 0, -1, ENC_NA);
1348 ipmi_tree = proto_item_add_subtree(ti, ett_ipmi);
1351 memset(&dfmt, 0, sizeof(dfmt));
1352 dfmt.flags = IPMI_D_BROADCAST | IPMI_D_TRG_SA;
1353 ipmi_do_dissect(tvb, pinfo, ipmi_tree, &dfmt);
1355 col_add_str(pinfo->cinfo, COL_INFO, dfmt.info);
1359 /* Register IPMB protocol.
1361 void
1362 proto_reg_handoff_ipmi(void)
1366 void
1367 proto_register_ipmi(void)
1369 static hf_register_info hf[] = {
1370 { &hf_ipmi_message, { "Message", "ipmi.message", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
1371 { &hf_ipmi_session_handle, { "Session handle", "ipmi.session_handle", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1372 { &hf_ipmi_header_broadcast, { "Broadcast message", "ipmi.header.broadcast", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1373 { &hf_ipmi_header_trg, { "Target Address", "ipmi.header.target", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
1374 { &hf_ipmi_header_trg_lun, { "Target LUN", "ipmi.header.trg_lun", FT_UINT8, BASE_HEX, NULL, 0x03, NULL, HFILL }},
1375 { &hf_ipmi_header_netfn, { "NetFN", "ipmi.header.netfn", FT_UINT8, BASE_HEX, NULL, 0xfc, NULL, HFILL }},
1376 { &hf_ipmi_header_crc, { "Header Checksum", "ipmi.header.crc", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1377 { &hf_ipmi_header_src, { "Source Address", "ipmi.header.source", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1378 { &hf_ipmi_header_src_lun, { "Source LUN", "ipmi.header.src_lun", FT_UINT8, BASE_HEX, NULL, 0x03, NULL, HFILL }},
1379 { &hf_ipmi_header_sequence, { "Sequence Number", "ipmi.header.sequence", FT_UINT8, BASE_HEX, NULL, 0xfc, NULL, HFILL }},
1380 { &hf_ipmi_header_command, { "Command", "ipmi.header.command", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1381 { &hf_ipmi_header_completion, { "Completion Code", "ipmi.header.completion", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1382 { &hf_ipmi_header_sig, { "Signature", "ipmi.header.signature", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
1383 { &hf_ipmi_data_crc, { "Data checksum", "ipmi.data.crc", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1384 { &hf_ipmi_response_to, { "Response to", "ipmi.response_to", FT_FRAMENUM, BASE_NONE, NULL, 0, NULL, HFILL }},
1385 { &hf_ipmi_response_in, { "Response in", "ipmi.response_in", FT_FRAMENUM, BASE_NONE, NULL, 0, NULL, HFILL }},
1386 { &hf_ipmi_response_time, { "Responded in", "ipmi.response_time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0, NULL, HFILL }},
1387 { &hf_ipmi_bad_checksum, { "Bad checksum", "ipmi.bad_checksum", FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL }}
1389 static gint *ett[] = {
1390 &ett_ipmi,
1391 &ett_header,
1392 &ett_header_byte_1,
1393 &ett_header_byte_4,
1394 &ett_data,
1395 &ett_typelen
1397 static const enum_val_t msgfmt_vals[] = {
1398 { "none", "None", MSGFMT_NONE },
1399 { "ipmb", "IPMB", MSGFMT_IPMB },
1400 { "lan", "Session-based (LAN, ...)", MSGFMT_LAN },
1401 { "guess", "Use heuristics", MSGFMT_GUESS },
1402 { NULL, NULL, 0 }
1404 static const enum_val_t oemsel_vals[] = {
1405 { "none", "None", IPMI_OEM_NONE },
1406 { "pps", "Pigeon Point Systems", IPMI_OEM_PPS },
1407 { NULL, NULL, 0 }
1409 module_t *m;
1410 guint32 i;
1412 proto_ipmi = proto_register_protocol("Intelligent Platform Management Interface",
1413 "IPMI/ATCA",
1414 "ipmi");
1416 proto_register_field_array(proto_ipmi, hf, array_length(hf));
1417 proto_register_subtree_array(ett, array_length(ett));
1419 ipmi_netfn_setdesc(IPMI_CHASSIS_REQ, "Chassis", 0);
1420 ipmi_netfn_setdesc(IPMI_BRIDGE_REQ, "Bridge", 0);
1421 ipmi_netfn_setdesc(IPMI_SE_REQ, "Sensor/Event", 0);
1422 ipmi_netfn_setdesc(IPMI_APP_REQ, "Application", 0);
1423 ipmi_netfn_setdesc(IPMI_UPDATE_REQ, "Firmware Update", 0);
1424 ipmi_netfn_setdesc(IPMI_STORAGE_REQ, "Storage", 0);
1425 ipmi_netfn_setdesc(IPMI_TRANSPORT_REQ, "Transport", 0);
1426 ipmi_netfn_setdesc(IPMI_GROUP_REQ, "Group", 1);
1427 ipmi_netfn_setdesc(IPMI_OEM_REQ, "OEM/Group", 3);
1428 for (i = 0x30; i < 0x40; i += 2) {
1429 ipmi_netfn_setdesc(i, "OEM", 0);
1432 ipmi_register_chassis(proto_ipmi);
1433 ipmi_register_bridge(proto_ipmi);
1434 ipmi_register_se(proto_ipmi);
1435 ipmi_register_app(proto_ipmi);
1436 ipmi_register_update(proto_ipmi);
1437 ipmi_register_storage(proto_ipmi);
1438 ipmi_register_transport(proto_ipmi);
1439 ipmi_register_picmg(proto_ipmi);
1440 ipmi_register_pps(proto_ipmi);
1442 register_dissector("ipmi", dissect_ipmi, proto_ipmi);
1444 m = prefs_register_protocol(proto_ipmi, NULL);
1445 prefs_register_bool_preference(m, "fru_langcode_is_english", "FRU Language Code is English",
1446 "FRU Language Code is English; strings are ASCII+LATIN1 (vs. Unicode)",
1447 &fru_langcode_is_english);
1448 prefs_register_uint_preference(m, "response_after_req", "Maximum delay of response message",
1449 "Do not search for responses coming after this timeout (milliseconds)",
1450 10, &response_after_req);
1451 prefs_register_uint_preference(m, "response_before_req", "Response ahead of request",
1452 "Allow for responses before requests (milliseconds)",
1453 10, &response_before_req);
1454 prefs_register_enum_preference(m, "msgfmt", "Format of embedded messages",
1455 "Format of messages embedded into Send/Get/Forward Message",
1456 &message_format, msgfmt_vals, FALSE);
1457 prefs_register_enum_preference(m, "selected_oem", "OEM commands parsed as",
1458 "Selects which OEM format is used for commands that IPMI does not define",
1459 &selected_oem, oemsel_vals, FALSE);