44a0ba014bcb0b9161b50fa78e660da40e6290c4
[obnox/wireshark/wip.git] / epan / dissectors / packet-ipmi.c
1 /* packet-ipmi.c
2  * Routines for IPMI dissection
3  * Copyright 2002-2008, Alexey Neyman, Pigeon Point Systems <avn@pigeonpoint.com>
4  *
5  * $Id$
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 1998 Gerald Combs
10  *
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.
15  *
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.
20  *
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.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #ifdef HAVE_SYS_TYPES_H
31 #include <sys/types.h>
32 #endif
33 #include <string.h>
34 #include <time.h>
35 #include <math.h>
36
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>
43
44 #include "packet-ipmi.h"
45
46 /*
47  * See the IPMI specifications at
48  *
49  *      http://www.intel.com/design/servers/ipmi/
50  */
51
52 /* Define IPMI_DEBUG to enable printing the process of request-response pairing */
53 /* #define IPMI_DEBUG */
54
55 /* Top-level search structure: list of registered handlers for a given netFn */
56 struct ipmi_netfn_root {
57         ipmi_netfn_t *list;
58         const char *desc;
59         guint32 siglen;
60 };
61
62 #define NSAVED_DATA 2
63
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 {
68         guint32 set_data;
69         guint32 saved_data[NSAVED_DATA];
70 };
71
72 enum {
73         RQ = 0,
74         RS,
75         RS2,
76
77         MAX_RQRS_FRAMES
78 };
79
80 enum {
81         MSGFMT_NONE = 0,
82         MSGFMT_IPMB,
83         MSGFMT_LAN,
84         MSGFMT_GUESS
85 };
86
87 struct ipmi_reqresp {
88         struct ipmi_reqresp *next;
89         struct ipmi_saved_data *data;
90         int (*whichresponse)(struct ipmi_header *hdr, struct ipmi_reqresp *rr);
91         struct {
92                 guint32 num;
93                 nstime_t time;
94         } frames[MAX_RQRS_FRAMES];
95         guint8 netfn;
96         guint8 cmd;
97 };
98
99 struct ipmi_keyhead {
100         struct ipmi_reqresp *rr;
101 };
102
103 struct ipmi_keytree {
104         emem_tree_t *heads;
105 };
106
107 struct ipmi_parse_typelen {
108         void (*get_len)(guint *, guint *, tvbuff_t *, guint, guint, gboolean);
109         void (*parse)(char *, tvbuff_t *, guint, guint);
110         const char *desc;
111 };
112
113 struct ipmi_header *ipmi_current_hdr;
114
115 static gint proto_ipmi = -1;
116
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;
122
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;
141
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;
148
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];
153
154 /* Debug support */
155 static void
156 debug_printf(const gchar *fmt _U_, ...)
157 {
158 #if defined(IPMI_DEBUG)
159         va_list ap;
160
161         va_start(ap, fmt);
162         vfprintf(stderr, fmt, ap);
163         va_end(ap);
164 #endif
165 }
166
167 /* ----------------------------------------------------------------
168    Support for request-response caching.
169 ---------------------------------------------------------------- */
170
171 /* Key generation; returns the same key for requests and responses */
172 static guint32
173 makekey(struct ipmi_header *hdr)
174 {
175         guint32 trg, src, res;
176
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;
181 }
182
183 static struct ipmi_reqresp *
184 key_lookup_reqresp(struct ipmi_keyhead *kh, struct ipmi_header *hdr, frame_data *fd)
185 {
186         struct ipmi_reqresp *rr, *best_rr = NULL;
187         nstime_t delta;
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;
191         int i;
192
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) {
198                         continue;
199                 }
200
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 */
206                                 return rr;
207                         }
208                 }
209
210                 /* Reject responses before requests or more than 5 seconds ahead */
211                 if (is_resp) {
212                         nstime_delta(&delta, &fd->abs_ts, &rr->frames[RQ].time);
213                 } else {
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);
217                 }
218                 d = nstime_to_msec(&delta);
219                 if (d < -(double)response_before_req || d > (double)response_after_req) {
220                         continue;
221                 }
222
223                 if (fabs(d) < best_d) {
224                         best_rr = rr;
225                         best_d = fabs(d);
226                 }
227         }
228
229         return best_rr;
230 }
231
232 static void
233 key_insert_reqresp(struct ipmi_keyhead *kh, struct ipmi_reqresp *rr)
234 {
235         /* Insert to head, so that the search would find most recent response */
236         rr->next = kh->rr;
237         kh->rr = rr;
238 }
239
240 static inline gboolean
241 set_framenums(struct ipmi_header *hdr, struct ipmi_reqresp *rr, frame_data *fd)
242 {
243         int which = hdr->netfn & 0x01 ? rr->whichresponse ? rr->whichresponse(hdr, rr) : RS : RQ;
244
245         if (rr->frames[which].num && rr->frames[which].num != fd->num) {
246                 return FALSE;
247         }
248         rr->frames[which].num = fd->num;
249         rr->frames[which].time = fd->abs_ts;
250         return TRUE;
251 }
252
253 #define IS_SENDMSG(hdr) (((hdr)->netfn & 0x3e) == IPMI_APP_REQ && (hdr)->cmd == 0x34)
254
255 int
256 ipmi_sendmsg_whichresponse(struct ipmi_header *hdr, struct ipmi_reqresp *rr)
257 {
258         if (!IS_SENDMSG(hdr)) {
259                 /* Not a Send Message: just a simple response */
260                 return RS;
261         }
262
263         if (hdr->data_len > 0) {
264                 /* Trivial case: response with non-null data can only be a
265                    response in AMC.0 style */
266                 return RS2;
267         }
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].
275
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?)
278         */
279         if (!rr->frames[RS].num) {
280                 return RS;
281         }
282
283         /* In case we received "success", move the other response to [RS2] */
284         if (!hdr->ccode) {
285                 if (!rr->frames[RS2].num) {
286                         rr->frames[RS2] = rr->frames[RS];
287                 }
288                 return RS;
289         }
290
291         /* [RS] occupied, non-successful */
292         return RS2;
293 }
294
295 int
296 ipmi_sendmsg_otheridx(struct ipmi_header *hdr)
297 {
298         return IS_SENDMSG(hdr) ? nest_level : RS;
299 }
300
301 struct ipmi_header *
302 ipmi_sendmsg_getheaders(struct ipmi_header *base, void *arg, guint i)
303 {
304         static struct ipmi_header hdr;
305         struct ipmi_header *wrapper = arg;
306
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.
313
314            Thus, there are 3 types of responses to Send Message
315
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.
319
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.
326         */
327
328         if (i >= 2 || (i == 1 && wrapper->seq == base->seq)) {
329                 return NULL;
330         }
331
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;
338         hdr.cmd = base->cmd;
339         hdr.seq = i ? base->seq : wrapper->seq;
340         hdr.ccode = base->ccode;
341         hdr.data_len = base->data_len;
342         return &hdr;
343 }
344
345 static void
346 maybe_insert_reqresp(ipmi_dissect_format_t *dfmt, struct ipmi_header *hdr)
347 {
348         conversation_t *cnv;
349         struct ipmi_keytree *kt;
350         struct ipmi_keyhead *kh;
351         struct ipmi_reqresp *rr;
352         guint32 key, i;
353
354         cnv = find_conversation(current_pinfo->fd->num, &current_pinfo->src,
355                         &current_pinfo->dst, current_pinfo->ptype,
356                         current_pinfo->srcport, current_pinfo->destport, 0);
357         if (!cnv) {
358                 cnv = conversation_new(current_pinfo->fd->num, &current_pinfo->src,
359                                 &current_pinfo->dst, current_pinfo->ptype,
360                                 current_pinfo->srcport, current_pinfo->destport, 0);
361         }
362
363         kt = conversation_get_proto_data(cnv, proto_ipmi);
364         if (!kt) {
365                 kt = se_alloc(sizeof(struct ipmi_keytree));
366                 kt->heads = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK,
367                                 "ipmi_key_heads");
368                 conversation_add_proto_data(cnv, proto_ipmi, kt);
369         }
370
371         debug_printf("--> maybe_insert_reqresp( %d )\n", current_pinfo->fd->num);
372         i = 0;
373         do {
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);
377                 key = makekey(hdr);
378                 kh = se_tree_lookup32(kt->heads, key);
379                 if (!kh) {
380                         kh = se_alloc0(sizeof(struct ipmi_keyhead));
381                         se_tree_insert32(kt->heads, key, kh);
382                 }
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,
390                                         rr->netfn, rr->cmd);
391                         if (!rr->whichresponse) {
392                                 rr->whichresponse = dfmt->whichresponse;
393                         }
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,
398                                                 rr->netfn, rr->cmd);
399                                 current_saved_data = rr->data;
400                                 return;
401                         }
402
403                         /* Found, but already occupied. Fall through to allocating the structures */
404                         current_saved_data = NULL;
405                 }
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));
410                 }
411                 rr = se_alloc0(sizeof(struct ipmi_reqresp));
412                 rr->whichresponse = dfmt->whichresponse;
413                 rr->netfn = hdr->netfn & 0x3e;
414                 rr->cmd = hdr->cmd;
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,
421                                 rr->netfn, rr->cmd);
422
423                 /* Do we have other headers to insert? */
424                 hdr = dfmt->getmoreheaders ? dfmt->getmoreheaders(hdr, dfmt->arg, i++) : NULL;
425         } while (hdr);
426 }
427
428 static void
429 add_reqresp_info(ipmi_dissect_format_t *dfmt, struct ipmi_header *hdr, proto_tree *tree, tvbuff_t *tvb)
430 {
431         conversation_t *cnv;
432         struct ipmi_keytree *kt;
433         struct ipmi_keyhead *kh;
434         struct ipmi_reqresp *rr = NULL;
435         guint32 key, i, other_idx;
436         proto_item *ti;
437         nstime_t ns;
438
439         debug_printf("--> add_reqresp_info( %d )\n", current_pinfo->fd->num);
440
441         /* [0] is request; [1..MAX_RS_LEVEL] are responses */
442         other_idx = (hdr->netfn & 0x01) ? RQ : dfmt->otheridx ? dfmt->otheridx(hdr) : RS;
443
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. */
448                 goto fallback;
449         }
450
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, &current_pinfo->src,
454                         &current_pinfo->dst, current_pinfo->ptype,
455                         current_pinfo->srcport, current_pinfo->destport, 0)) == NULL) {
456                 goto fallback;
457         }
458         if ((kt = conversation_get_proto_data(cnv, proto_ipmi)) == NULL) {
459                 goto fallback;
460         }
461
462         i = 0;
463         while (1) {
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);
467                 key = makekey(hdr);
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,
473                                         rr->netfn, rr->cmd);
474                         if (rr->frames[other_idx].num) {
475                                 break;
476                         }
477                 }
478
479                 /* Do we have other headers to check? */
480                 hdr = dfmt->getmoreheaders ? dfmt->getmoreheaders(hdr, dfmt->arg, i++) : NULL;
481                 if (!hdr) {
482                         goto fallback;
483                 }
484         }
485
486         if (hdr->netfn & 0x01) {
487                 /* Response */
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, &current_pinfo->fd->abs_ts, &rr->frames[RQ].time);
492                 ti = proto_tree_add_time(tree, hf_ipmi_response_time,
493                                 tvb, 0, 0, &ns);
494                 PROTO_ITEM_SET_GENERATED(ti);
495         } else {
496                 /* Request */
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);
500         }
501         return;
502
503 fallback:
504         ti = proto_tree_add_text(tree, tvb, 0, 0, "No corresponding %s",
505                         other_idx ? "response" : "request");
506         PROTO_ITEM_SET_GENERATED(ti);
507 }
508
509 /* Save data in request, retrieve in response */
510 void
511 ipmi_setsaveddata(guint idx, guint32 val)
512 {
513         DISSECTOR_ASSERT(idx < NSAVED_DATA);
514         current_saved_data->saved_data[idx] = val;
515         current_saved_data->set_data |= (1 << idx);
516 }
517
518 gboolean
519 ipmi_getsaveddata(guint idx, guint32 *pval)
520 {
521         DISSECTOR_ASSERT(idx < NSAVED_DATA);
522         if (current_saved_data->set_data & (1 << idx)) {
523                 *pval = current_saved_data->saved_data[idx];
524                 return TRUE;
525         }
526         return FALSE;
527 }
528
529 /* ----------------------------------------------------------------
530    Support for Type/Length fields parsing.
531 ---------------------------------------------------------------- */
532
533 static void
534 get_len_binary(guint *clen, guint *blen, tvbuff_t *tvb _U_, guint offs _U_,
535                 guint len, gboolean len_is_bytes _U_)
536 {
537         *clen = len * 3;
538         *blen = len;
539 }
540
541 static void
542 parse_binary(char *p, tvbuff_t *tvb, guint offs, guint len)
543 {
544         static const char hex[] = "0123456789ABCDEF";
545         guint8 v;
546         guint i;
547
548         for (i = 0; i < len / 3; i++) {
549                 v = tvb_get_guint8(tvb, offs + i);
550                 *p++ = hex[v >> 4];
551                 *p++ = hex[v & 0xf];
552                 *p++ = ' ';
553         }
554
555         if (i) {
556                 *--p = '\0';
557         }
558 }
559
560 static struct ipmi_parse_typelen ptl_binary = {
561         get_len_binary, parse_binary, "Binary"
562 };
563
564 static void
565 get_len_bcdplus(guint *clen, guint *blen, tvbuff_t *tvb _U_, guint offs _U_,
566                 guint len, gboolean len_is_bytes)
567 {
568         if (len_is_bytes) {
569                 *clen = len * 2;
570                 *blen = len;
571         } else {
572                 *blen = (len + 1) / 2;
573                 *clen = len;
574         }
575 }
576
577 static void
578 parse_bcdplus(char *p, tvbuff_t *tvb, guint offs, guint len)
579 {
580         static const char bcd[] = "0123456789 -.:,_";
581         guint i, msk = 0xf0, shft = 4;
582         guint8 v;
583
584         for (i = 0; i < len; i++) {
585                 v = (tvb_get_guint8(tvb, offs + i / 2) & msk) >> shft;
586                 *p++ = bcd[v];
587                 msk ^= 0xff;
588                 shft = 4 - shft;
589         }
590 }
591
592 static struct ipmi_parse_typelen ptl_bcdplus = {
593         get_len_bcdplus, parse_bcdplus, "BCD+"
594 };
595
596 static void
597 get_len_6bit_ascii(guint *clen, guint *blen, tvbuff_t *tvb _U_, guint offs _U_,
598                 guint len, gboolean len_is_bytes)
599 {
600         if (len_is_bytes) {
601                 *clen = len * 4 / 3;
602                 *blen = len;
603         } else {
604                 *blen = (len * 3 + 3) / 4;
605                 *clen = len;
606         }
607 }
608
609 static void
610 parse_6bit_ascii(char *p, tvbuff_t *tvb, guint offs, guint len)
611 {
612         guint32 v;
613         guint i;
614
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);
622                 p += 4;
623         }
624
625         /* Do we have any characters left? */
626         offs += len / 4;
627         len &= 0x3;
628         switch (len) {
629         case 3:
630                 v = (tvb_get_guint8(tvb, offs + 2) << 4) | (tvb_get_guint8(tvb, offs + 1) >> 4);
631                 p[2] = ' ' + (v & 0x3f);
632                 /* Fall thru */
633         case 2:
634                 v = (tvb_get_guint8(tvb, offs + 1) << 2) | (tvb_get_guint8(tvb, offs) >> 6);
635                 p[1] = ' ' + (v & 0x3f);
636                 /* Fall thru */
637         case 1:
638                 v = tvb_get_guint8(tvb, offs) & 0x3f;
639                 p[0] = ' ' + (v & 0x3f);
640         }
641 }
642
643 static struct ipmi_parse_typelen ptl_6bit_ascii = {
644         get_len_6bit_ascii, parse_6bit_ascii, "6-bit ASCII"
645 };
646
647 static void
648 get_len_8bit_ascii(guint *clen, guint *blen, tvbuff_t *tvb, guint offs,
649                 guint len, gboolean len_is_bytes _U_)
650 {
651         guint i;
652         guint8 ch;
653
654         *blen = len;    /* One byte is one character */
655         *clen = 0;
656         for (i = 0; i < len; i++) {
657                 ch = tvb_get_guint8(tvb, offs + i);
658                 *clen += (ch >= 0x20 && ch <= 0x7f) ? 1 : 4;
659         }
660 }
661
662 static void
663 parse_8bit_ascii(char *p, tvbuff_t *tvb, guint offs, guint len)
664 {
665         guint8 ch;
666         char *pmax;
667
668         pmax = p + len;
669         while (p < pmax) {
670                 ch = tvb_get_guint8(tvb, offs++);
671                 if (ch >= 0x20 && ch <= 0x7f) {
672                         *p++ = ch;
673                 } else {
674                         g_snprintf(p, 5, "\\x%02x", ch);
675                         p += 4;
676                 }
677         }
678 }
679
680 static struct ipmi_parse_typelen ptl_8bit_ascii = {
681         get_len_8bit_ascii, parse_8bit_ascii, "ASCII+Latin1"
682 };
683
684 static void
685 get_len_unicode(guint *clen, guint *blen, tvbuff_t *tvb _U_, guint offs _U_,
686                 guint len _U_, gboolean len_is_bytes)
687 {
688         if (len_is_bytes) {
689                 *clen = len * 3; /* Each 2 bytes result in 6 chars printed: \Uxxxx */
690                 *blen = len;
691         } else {
692                 *clen = len * 6;
693                 *blen = len * 2;
694         }
695 }
696
697 static void
698 parse_unicode(char *p, tvbuff_t *tvb, guint offs, guint len)
699 {
700         char *pmax = p + len;
701         guint8 ch0, ch1;
702
703         while (p < pmax) {
704                 ch0 = tvb_get_guint8(tvb, offs++);
705                 ch1 = tvb_get_guint8(tvb, offs++);
706                 g_snprintf(p, 7, "\\U%02x%02x", ch0, ch1);
707                 p += 6;
708         }
709 }
710
711 static struct ipmi_parse_typelen ptl_unicode = {
712         get_len_unicode, parse_unicode, "Unicode"
713 };
714
715 void
716 ipmi_add_typelen(proto_tree *tree, const char *desc, tvbuff_t *tvb,
717                 guint offs, gboolean is_fru)
718 {
719         static struct ipmi_parse_typelen *fru_eng[4] = {
720                 &ptl_binary, &ptl_bcdplus, &ptl_6bit_ascii, &ptl_8bit_ascii
721         };
722         static struct ipmi_parse_typelen *fru_noneng[4] = {
723                 &ptl_binary, &ptl_bcdplus, &ptl_6bit_ascii, &ptl_unicode
724         };
725         static struct ipmi_parse_typelen *ipmi[4] = {
726                 &ptl_unicode, &ptl_bcdplus, &ptl_6bit_ascii, &ptl_8bit_ascii
727         };
728         struct ipmi_parse_typelen *ptr;
729         proto_tree *s_tree;
730         proto_item *ti;
731         guint type, msk, clen, blen, len;
732         const char *unit;
733         char *str;
734         guint8 typelen;
735
736         typelen = tvb_get_guint8(tvb, offs);
737         type = typelen >> 6;
738         if (is_fru) {
739                 msk = 0x3f;
740                 ptr = (fru_langcode_is_english ? fru_eng : fru_noneng)[type];
741                 unit = "bytes";
742         } else {
743                 msk = 0x1f;
744                 ptr = ipmi[type];
745                 unit = "characters";
746         }
747
748         len = typelen & msk;
749         ptr->get_len(&clen, &blen, tvb, offs + 1, len, is_fru);
750
751         str = ep_alloc(clen + 1);
752         ptr->parse(str, tvb, offs + 1, clen);
753         str[clen] = '\0';
754
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);
762
763         proto_tree_add_text(tree, tvb, offs + 1, blen, "%s: [%s] '%s'",
764                         desc, ptr->desc, str);
765 }
766
767 /* ----------------------------------------------------------------
768    Timestamp, IPMI-style.
769 ---------------------------------------------------------------- */
770 void
771 ipmi_add_timestamp(proto_tree *tree, gint hf, tvbuff_t *tvb, guint offset)
772 {
773         guint32 ts = tvb_get_letohl(tvb, offset);
774         guint32 d, h, m, s;
775         char buf[64];
776
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) {
781                 s = ts % 60;
782                 m = ts / 60;
783                 h = m / 60;
784                 m %= 60;
785                 d = h / 24;
786                 h %= 24;
787                 proto_tree_add_uint_format_value(tree, hf, tvb, offset, 4,
788                                 ts, "%d days, %02d:%02d:%02d since SEL device's initialization",
789                                 d, h, m, s);
790         } else {
791                 time_t t = ts;
792                 strftime(buf, sizeof(buf), "%F %T", gmtime(&t));
793                 proto_tree_add_uint_format_value(tree, hf, tvb, offset, 4,
794                                 ts, "%s", buf);
795         }
796 }
797
798 /* ----------------------------------------------------------------
799    GUID, IPMI-style.
800 ---------------------------------------------------------------- */
801
802 void
803 ipmi_add_guid(proto_tree *tree, gint hf, tvbuff_t *tvb, guint offset)
804 {
805         e_guid_t guid;
806         int i;
807
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);
813         }
814         proto_tree_add_guid(tree, hf, tvb, offset, 16, &guid);
815 }
816
817 /* ----------------------------------------------------------------
818    Routines for registering/looking up command parsers.
819 ---------------------------------------------------------------- */
820
821 static void
822 ipmi_netfn_setdesc(guint32 netfn, const char *desc, guint32 siglen)
823 {
824         struct ipmi_netfn_root *inr;
825
826         inr = &ipmi_cmd_tab[netfn >> 1];
827         inr->desc = desc;
828         inr->siglen = siglen;
829 }
830
831 void
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)
835 {
836         struct ipmi_netfn_root *inr;
837         ipmi_netfn_t *inh;
838
839         netfn >>= 1;    /* Requests and responses grouped together */
840         if (netfn >= IPMI_NETFN_MAX) {
841                 g_warning("NetFn too large: %x", netfn * 2);
842                 return;
843         }
844
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);
850                 return;
851         }
852
853         inh = g_malloc(sizeof(struct ipmi_netfn_handler));
854         inh->desc = desc;
855         inh->oem_selector = oem_selector;
856         inh->sig = sig;
857         inh->cmdtab = cmdtab;
858         inh->cmdtablen = cmdtablen;
859
860         inh->next = inr->list;
861         inr->list = inh;
862 }
863
864 guint32
865 ipmi_getsiglen(guint32 netfn)
866 {
867         return ipmi_cmd_tab[netfn >> 1].siglen;
868 }
869
870 const char *
871 ipmi_getnetfnname(guint32 netfn, ipmi_netfn_t *nf)
872 {
873         const char *dn, *db;
874
875         dn = ipmi_cmd_tab[netfn >> 1].desc ?
876                 ipmi_cmd_tab[netfn >> 1].desc : "Reserved";
877         db = nf ? nf->desc : NULL;
878         if (db) {
879                 return ep_strdup_printf("%s (%s)", db, dn);
880         } else {
881                 return dn;
882         }
883 }
884
885 ipmi_netfn_t *
886 ipmi_getnetfn(guint32 netfn, const guint8 *sig)
887 {
888         struct ipmi_netfn_root *inr;
889         ipmi_netfn_t *inh;
890
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))) {
895                         return inh;
896                 }
897         }
898
899         /* Either unknown netFn or signature does not match */
900         return NULL;
901 }
902
903 ipmi_cmd_t *
904 ipmi_getcmd(ipmi_netfn_t *nf, guint32 cmd)
905 {
906         static ipmi_cmd_t ipmi_cmd_unknown = {
907                 0x00,           /* Code */
908                 ipmi_notimpl,   /* request */
909                 ipmi_notimpl,   /* response */
910                 NULL,           /* command codes */
911                 NULL,           /* subfunctions */
912                 "Unknown command",
913                 0               /* flag */
914         };
915         ipmi_cmd_t *ic;
916         size_t i, len;
917
918         if (nf) {
919                 len = nf->cmdtablen;
920                 for (ic = nf->cmdtab, i = 0; i < len; i++, ic++) {
921                         if (ic->cmd == cmd) {
922                                 return ic;
923                         }
924                 }
925         }
926
927         return &ipmi_cmd_unknown;
928 }
929
930 /* ----------------------------------------------------------------
931    Various utility functions.
932 ---------------------------------------------------------------- */
933
934 void
935 ipmi_notimpl(tvbuff_t *tvb, proto_tree *tree)
936 {
937         if (tree) {
938                 proto_tree_add_text(tree, tvb, 0, -1, "[PARSER NOT IMPLEMENTED]");
939         }
940 }
941
942 char *
943 ipmi_dcd8(guint32 val, guint32 mask)
944 {
945         static char buf[64];
946
947         decode_bitfield_value(buf, val, mask, 8);
948         return buf;
949 }
950
951 void
952 ipmi_fmt_10ms_1based(gchar *s, guint32 v)
953 {
954         g_snprintf(s, ITEM_LABEL_LENGTH, "%d.%03d seconds", v / 100, (v % 100) * 10);
955 }
956
957 void
958 ipmi_fmt_500ms_0based(gchar *s, guint32 v)
959 {
960         ipmi_fmt_500ms_1based(s, ++v);
961 }
962
963 void
964 ipmi_fmt_500ms_1based(gchar *s, guint32 v)
965 {
966         g_snprintf(s, ITEM_LABEL_LENGTH, "%d.%03d seconds", v / 2, (v % 2) * 500);
967 }
968
969 void
970 ipmi_fmt_1s_0based(gchar *s, guint32 v)
971 {
972         ipmi_fmt_1s_1based(s, ++v);
973 }
974
975 void
976 ipmi_fmt_1s_1based(gchar *s, guint32 v)
977 {
978         g_snprintf(s, ITEM_LABEL_LENGTH, "%d seconds", v);
979 }
980
981 void
982 ipmi_fmt_2s_0based(gchar *s, guint32 v)
983 {
984         g_snprintf(s, ITEM_LABEL_LENGTH, "%d seconds", (v + 1) * 2);
985 }
986
987 void
988 ipmi_fmt_5s_1based(gchar *s, guint32 v)
989 {
990         g_snprintf(s, ITEM_LABEL_LENGTH, "%d seconds", v * 5);
991 }
992
993 void
994 ipmi_fmt_version(gchar *s, guint32 v)
995 {
996         g_snprintf(s, ITEM_LABEL_LENGTH, "%d.%d", v & 0x0f, (v >> 4) & 0x0f);
997 }
998
999 void
1000 ipmi_fmt_channel(gchar *s, guint32 v)
1001 {
1002         static const value_string chan_vals[] = {
1003                 { 0x00, "Primary IPMB (IPMB-0)" },
1004                 { 0x07, "IPMB-L" },
1005                 { 0x0e, "Current channel" },
1006                 { 0x0f, "System Interface" },
1007                 { 0, NULL }
1008         };
1009
1010         g_snprintf(s, ITEM_LABEL_LENGTH, "%s (0x%02x)",
1011                         val_to_str(v, chan_vals, "Channel #%d"), v);
1012 }
1013
1014 void
1015 ipmi_fmt_udpport(gchar *s, guint32 v)
1016 {
1017         g_snprintf(s, ITEM_LABEL_LENGTH, "%s (%d)", get_udp_port(v), v);
1018 }
1019
1020 void
1021 ipmi_fmt_percent(gchar *s, guint32 v)
1022 {
1023         g_snprintf(s, ITEM_LABEL_LENGTH, "%d%%", v);
1024 }
1025
1026 const char *
1027 ipmi_get_completion_code(guint8 completion, ipmi_cmd_t *cmd)
1028 {
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" },
1055
1056                 { 0, NULL }
1057         };
1058         const char *res;
1059
1060         if (completion >= 0x01 && completion <= 0x7e) {
1061                 return "Device specific (OEM) completion code";
1062         }
1063
1064         if (completion >= 0x80 && completion <= 0xbe) {
1065                 if (cmd && cmd->cs_cc && (res = match_strval(completion, cmd->cs_cc)) != NULL) {
1066                         return res;
1067                 }
1068                 return "Standard command-specific code";
1069         }
1070
1071         return val_to_str(completion, std_completion_codes, "Unknown");
1072 }
1073
1074 /* Guess the parsing flags for a message
1075  */
1076 int
1077 ipmi_guess_dissect_flags(tvbuff_t *tvb)
1078 {
1079         int i;
1080         guint8 buf[6];
1081
1082         switch (message_format) {
1083         case MSGFMT_NONE:
1084                 return IPMI_D_NONE;
1085         case MSGFMT_IPMB:
1086                 return IPMI_D_TRG_SA;
1087         case MSGFMT_LAN:
1088                 return IPMI_D_TRG_SA|IPMI_D_SESSION_HANDLE;
1089         }
1090
1091         /* Try to guess the format */
1092         DISSECTOR_ASSERT(message_format == MSGFMT_GUESS);
1093
1094         /* 6 is shortest message - Get Message with empty data */
1095         if (tvb_length(tvb) < 6) {
1096                 return IPMI_D_NONE;
1097         }
1098
1099         /* Fetch the beginning */
1100         for (i = 0; i < 6; i++) {
1101                 buf[i] = tvb_get_guint8(tvb, i);
1102         }
1103
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;
1107         }
1108
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;
1112         }
1113
1114         /* Can't guess */
1115         return IPMI_D_NONE;
1116 }
1117
1118 /* Print out IPMB packet.
1119  */
1120 void
1121 ipmi_do_dissect(tvbuff_t *tvb, proto_tree *ipmi_tree, ipmi_dissect_format_t *dfmt)
1122 {
1123         proto_tree *hdr_tree, *data_tree, *s_tree;
1124         proto_item *ti;
1125         tvbuff_t *data_tvb;
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;
1135
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);
1140                 return;
1141         }
1142
1143         nest_level++;
1144         offs = 0;
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");
1148
1149         /* Optional byte: in Send Message targeted to session-based channels */
1150         if (dfmt->flags & IPMI_D_SESSION_HANDLE) {
1151                 offs++;
1152         }
1153
1154         /* Optional byte: 00 indicates General Call address - broadcast message */
1155         if ((dfmt->flags & IPMI_D_BROADCAST) && tvb_get_guint8(tvb, offs) == 0x00) {
1156                 is_broadcast = 1;
1157                 offs++;
1158         }
1159
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;
1162
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;
1168
1169         /* Byte 3: header checksum */
1170         hdr_crc = tvb_get_guint8(tvb, offs++);
1171
1172         /* Byte 4: source slave address */
1173         hdr.src_sa = tvb_get_guint8(tvb, offs++);
1174
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;
1179
1180         /* Byte 6: command code */
1181         hdr.cmd = tvb_get_guint8(tvb, offs++);
1182
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;
1186
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));
1190         offs += siglen;
1191
1192         /* Save header length */
1193         hdrlen = offs;
1194         hdr.data_len = tvb_length(tvb) - hdrlen - 1;
1195
1196         /* Get some text descriptions */
1197         ic = ipmi_getcmd(in, hdr.cmd);
1198         ndesc = ipmi_getnetfnname(hdr.netfn, in);
1199         cdesc = ic->desc;
1200         ccdesc = ipmi_get_completion_code(hdr.ccode, ic);
1201         if (!is_broadcast) {
1202                 bcast = "";
1203         } else if (ic->flags & CMD_MAYBROADCAST) {
1204                 bcast = " (BROADCAST: command may not be broadcast)";
1205         } else {
1206                 bcast = " (BROADCAST)";
1207         }
1208
1209         
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;
1215
1216         /* Select sub-handler */
1217         hnd = is_resp ? ic->parse_resp : ic->parse_req;
1218
1219         /* Start new conversation if needed */
1220         if (!is_resp && (ic->flags & CMD_NEWCONV)) {
1221                 conversation_new(current_pinfo->fd->num, &current_pinfo->src,
1222                                 &current_pinfo->dst, current_pinfo->ptype,
1223                                 current_pinfo->srcport, current_pinfo->destport, 0);
1224         }
1225
1226         /* Check if we need to insert request-response pair */
1227         maybe_insert_reqresp(dfmt, &hdr);
1228
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);
1231
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 : "");
1236
1237         if (!is_resp && (ic->flags & CMD_CALLRQ)) {
1238                 hnd(data_tvb, NULL);
1239         }
1240
1241         if (ipmi_tree) {
1242                 add_reqresp_info(dfmt, &hdr, ipmi_tree, tvb);
1243
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);
1248
1249                 offs = 0;
1250
1251                 if (dfmt->flags & IPMI_D_SESSION_HANDLE) {
1252                         proto_tree_add_item(hdr_tree, hf_ipmi_session_handle,
1253                                         tvb, offs++, 1, TRUE);
1254                 }
1255
1256                 /* Broadcast byte (optional) */
1257                 if (is_broadcast) {
1258                         proto_tree_add_uint_format(hdr_tree, hf_ipmi_header_broadcast,
1259                                         tvb, offs++, 1, 0x00, "Broadcast message");
1260                 }
1261
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);
1265                 }
1266
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);
1272                                 
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);
1278                 offs++;
1279
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);
1284                 }
1285                 else {
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);
1291                 }
1292
1293                 /* Remember where chk2 bytes start */
1294                 data_chk_offs = offs;
1295
1296                 /* Source SA */
1297                 proto_tree_add_item(hdr_tree, hf_ipmi_header_src, tvb, offs++, 1, TRUE);
1298
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);
1304
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);
1307                 offs++;
1308
1309                 /* Command */
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);
1312
1313                 /* Response code (if present) */
1314                 if (is_resp) {
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);
1317                 }
1318
1319                 /* Defining body signature (if present) */
1320                 if (siglen) {
1321                         ti = proto_tree_add_item(hdr_tree, hf_ipmi_header_sig, tvb, offs, siglen, TRUE);
1322                         proto_item_append_text(ti, " (%s)", ndesc);
1323                         offs += siglen;
1324                 }
1325
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);
1331                 }
1332
1333                 /* Checksum all but the last byte */
1334                 len = tvb_length(tvb) - 1;
1335                 data_crc = tvb_get_guint8(tvb, len);
1336                 data_exp_crc = 0;
1337                 for (i = data_chk_offs; i < len; i++) {
1338                         data_exp_crc += tvb_get_guint8(tvb, i);
1339                 }
1340                 data_exp_crc = (0 - data_exp_crc) & 0xff;
1341                 
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);
1345                 }
1346                 else {
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);
1352                 }
1353         }
1354
1355         /* Restore globals, in case we've been called recursively */
1356         ipmi_current_hdr = saved_hdr;
1357         current_saved_data = saved_saved_data;
1358         nest_level--;
1359 }
1360
1361 static void
1362 dissect_ipmi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1363 {
1364         proto_tree *ipmi_tree = NULL;
1365         proto_item *ti;
1366         ipmi_dissect_format_t dfmt;
1367
1368         if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
1369                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "IPMI/ATCA");
1370         }
1371
1372         current_pinfo = pinfo;
1373
1374         if (tree) {
1375                 ti = proto_tree_add_item(tree, proto_ipmi, tvb, 0, -1, FALSE);
1376                 ipmi_tree = proto_item_add_subtree(ti, ett_ipmi);
1377         }
1378
1379         memset(&dfmt, 0, sizeof(dfmt));
1380         dfmt.flags = IPMI_D_BROADCAST | IPMI_D_TRG_SA;
1381         ipmi_do_dissect(tvb, ipmi_tree, &dfmt);
1382
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);
1386         }
1387
1388 }
1389
1390 /* Register IPMB protocol.
1391  */
1392 void
1393 proto_reg_handoff_ipmi(void)
1394 {
1395 }
1396
1397 void
1398 proto_register_ipmi(void)
1399 {
1400         static hf_register_info hf[] = {
1401                 { &hf_ipmi_message, { "Message", "ipmi.message", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
1402                 { &hf_ipmi_session_handle, { "Session handle", "ipmi.session_handle", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1403                 { &hf_ipmi_header_broadcast, { "Broadcast message", "ipmi.header.broadcast", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1404                 { &hf_ipmi_header_trg, { "Target Address", "ipmi.header.target", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
1405                 { &hf_ipmi_header_trg_lun, { "Target LUN", "ipmi.header.trg_lun", FT_UINT8, BASE_HEX, NULL, 0x03, NULL, HFILL }},
1406                 { &hf_ipmi_header_netfn, { "NetFN", "ipmi.header.netfn", FT_UINT8, BASE_HEX, NULL, 0xfc, NULL, HFILL }},
1407                 { &hf_ipmi_header_crc, { "Header Checksum", "ipmi.header.crc", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1408                 { &hf_ipmi_header_src, { "Source Address", "ipmi.header.source", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1409                 { &hf_ipmi_header_src_lun, { "Source LUN", "ipmi.header.src_lun", FT_UINT8, BASE_HEX, NULL, 0x03, NULL, HFILL }},
1410                 { &hf_ipmi_header_sequence, { "Sequence Number", "ipmi.header.sequence", FT_UINT8, BASE_HEX, NULL, 0xfc, NULL, HFILL }},
1411                 { &hf_ipmi_header_command, { "Command", "ipmi.header.command", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1412                 { &hf_ipmi_header_completion, { "Completion Code", "ipmi.header.completion", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1413                 { &hf_ipmi_header_sig, { "Signature", "ipmi.header.signature", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
1414                 { &hf_ipmi_data_crc, { "Data checksum", "ipmi.data.crc", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1415                 { &hf_ipmi_response_to, { "Response to", "ipmi.response_to", FT_FRAMENUM, BASE_NONE, NULL, 0, NULL, HFILL }},
1416                 { &hf_ipmi_response_in, { "Response in", "ipmi.response_in", FT_FRAMENUM, BASE_NONE, NULL, 0, NULL, HFILL }},
1417                 { &hf_ipmi_response_time, { "Responded in", "ipmi.response_time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0, NULL, HFILL }},
1418                 { &hf_ipmi_bad_checksum, { "Bad checksum", "ipmi.bad_checksum", FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, HFILL }}
1419         };
1420         static gint *ett[] = {
1421                 &ett_ipmi,
1422                 &ett_header,
1423                 &ett_header_byte_1,
1424                 &ett_header_byte_4,
1425                 &ett_data,
1426                 &ett_typelen
1427         };
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 },
1433                 { NULL, NULL, 0 }
1434         };
1435         static const enum_val_t oemsel_vals[] = {
1436                 { "none", "None", IPMI_OEM_NONE },
1437                 { "pps", "Pigeon Point Systems", IPMI_OEM_PPS },
1438                 { NULL, NULL, 0 }
1439         };
1440         module_t *m;
1441         guint32 i;
1442
1443         proto_ipmi = proto_register_protocol("Intelligent Platform Management Interface",
1444                                 "IPMI/ATCA",
1445                                 "ipmi");
1446
1447         proto_register_field_array(proto_ipmi, hf, array_length(hf));
1448         proto_register_subtree_array(ett, array_length(ett));
1449
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);
1461         }
1462
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);
1472
1473         register_dissector("ipmi", dissect_ipmi, proto_ipmi);
1474
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);
1491 }