proto_tree_add_subtree[_format]
[metze/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  * Wireshark - Network traffic analyzer
6  * By Gerald Combs <gerald@wireshark.org>
7  * Copyright 1998 Gerald Combs
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
22  */
23
24 #include "config.h"
25
26 #include <string.h>
27
28 #include <stdio.h>
29
30 #include <epan/packet.h>
31 #include <epan/conversation.h>
32 #include <epan/wmem/wmem.h>
33 #include <epan/to_str.h>
34 #include <epan/prefs.h>
35 #include <epan/addr_resolv.h>
36
37 #include "packet-ipmi.h"
38
39 void proto_register_ipmi(void);
40
41 /*
42  * See the IPMI specifications at
43  *
44  *      http://www.intel.com/design/servers/ipmi/
45  */
46
47 /* Define IPMI_DEBUG to enable printing the process of request-response pairing */
48 /* #define IPMI_DEBUG */
49
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;
55 };
56
57 enum {
58         MSGFMT_NONE = 0,
59         MSGFMT_IPMB,
60         MSGFMT_LAN,
61         MSGFMT_GUESS
62 };
63
64 struct ipmi_parse_typelen {
65         void (*get_len)(guint *, guint *, tvbuff_t *, guint, guint, gboolean);
66         void (*parse)(char *, tvbuff_t *, guint, guint);
67         const char *desc;
68 };
69
70 /* IPMI parsing context */
71 typedef struct {
72         ipmi_header_t   hdr;
73         guint                   hdr_len;
74         guint                   flags;
75         guint8                  cks1;
76         guint8                  cks2;
77 } ipmi_context_t;
78
79 /* Temporary request-response matching data. */
80 typedef struct {
81         /* Request header */
82         ipmi_header_t   hdr;
83         /* Frame number where the request resides */
84         guint32                 frame_num;
85         /* Nest level of the request in the frame */
86         guint8                  nest_level;
87 } ipmi_request_t;
88
89 /* List of request-response matching data */
90 typedef wmem_list_t ipmi_request_list_t;
91
92 #define NSAVED_DATA 2
93
94 /* Per-command data */
95 typedef struct {
96         guint32         matched_frame_num;
97         guint32         saved_data[NSAVED_DATA];
98 } ipmi_cmd_data_t;
99
100 /* Per-frame data */
101 typedef struct {
102         ipmi_cmd_data_t *       cmd_data[3];
103         nstime_t                        ts;
104 } ipmi_frame_data_t;
105
106 /* RB tree of frame data */
107 typedef wmem_tree_t ipmi_frame_tree_t;
108
109 /* cached dissector data */
110 typedef struct {
111         /* tree of cached frame data */
112         ipmi_frame_tree_t *             frame_tree;
113         /* list of cached requests */
114         ipmi_request_list_t *   request_list;
115         /* currently dissected frame number */
116         guint32                                 curr_frame_num;
117         /* currently dissected frame */
118         ipmi_frame_data_t *             curr_frame;
119         /* current nesting level */
120         guint8                                  curr_level;
121         /* subsequent nesting level */
122         guint8                                  next_level;
123         /* top level message channel */
124         guint8                                  curr_channel;
125         /* top level message direction */
126         guint8                                  curr_dir;
127         /* pointer to current command */
128         const ipmi_header_t *   curr_hdr;
129         /* current completion code */
130         guint8                                  curr_ccode;
131 } ipmi_packet_data_t;
132
133 /* Maximum nest level where it worth caching data */
134 #define MAX_NEST_LEVEL  3
135
136 static dissector_handle_t data_dissector;
137
138 gint proto_ipmi = -1;
139 static gint proto_ipmb = -1;
140 static gint proto_kcs = -1;
141 static gint proto_tmode = -1;
142
143 static gboolean fru_langcode_is_english = TRUE;
144 static guint response_after_req = 5000;
145 static guint response_before_req = 0;
146 static guint message_format = MSGFMT_GUESS;
147 static guint selected_oem = IPMI_OEM_NONE;
148
149 static gint hf_ipmi_session_handle = -1;
150 static gint hf_ipmi_header_trg = -1;
151 static gint hf_ipmi_header_trg_lun = -1;
152 static gint hf_ipmi_header_netfn = -1;
153 static gint hf_ipmi_header_crc = -1;
154 static gint hf_ipmi_header_src = -1;
155 static gint hf_ipmi_header_src_lun = -1;
156 static gint hf_ipmi_header_bridged = -1;
157 static gint hf_ipmi_header_sequence = -1;
158 static gint hf_ipmi_header_command = -1;
159 static gint hf_ipmi_header_completion = -1;
160 static gint hf_ipmi_header_sig = -1;
161 static gint hf_ipmi_data_crc = -1;
162 static gint hf_ipmi_response_to = -1;
163 static gint hf_ipmi_response_in = -1;
164 static gint hf_ipmi_response_time = -1;
165
166 static gint ett_ipmi = -1;
167 static gint ett_header = -1;
168 static gint ett_header_byte_1 = -1;
169 static gint ett_header_byte_4 = -1;
170 static gint ett_data = -1;
171 static gint ett_typelen = -1;
172
173 static expert_field ei_impi_parser_not_implemented = EI_INIT;
174
175 static struct ipmi_netfn_root ipmi_cmd_tab[IPMI_NETFN_MAX];
176
177 static ipmi_packet_data_t *
178 get_packet_data(packet_info * pinfo)
179 {
180         ipmi_packet_data_t * data;
181
182         /* get conversation data */
183         conversation_t * conv = find_or_create_conversation(pinfo);
184
185         /* get protocol-specific data */
186         data = (ipmi_packet_data_t *)
187                         conversation_get_proto_data(conv, proto_ipmi);
188
189         if (!data) {
190                 /* allocate per-packet data */
191                 data = wmem_new0(wmem_file_scope(), ipmi_packet_data_t);
192
193                 /* allocate request list and frame tree */
194                 data->frame_tree = wmem_tree_new(wmem_file_scope());
195                 data->request_list = wmem_list_new(wmem_file_scope());
196
197                 /* add protocol data */
198                 conversation_add_proto_data(conv, proto_ipmi, data);
199         }
200
201         /* check if packet has changed */
202         if (pinfo->fd->num != data->curr_frame_num) {
203                 data->curr_level = 0;
204                 data->next_level = 0;
205         }
206
207         return data;
208 }
209
210 static ipmi_frame_data_t *
211 get_frame_data(ipmi_packet_data_t * data, guint32 frame_num)
212 {
213         ipmi_frame_data_t * frame = (ipmi_frame_data_t *)
214                         wmem_tree_lookup32(data->frame_tree, frame_num);
215
216         if (frame == NULL) {
217                 frame = wmem_new0(wmem_file_scope(), ipmi_frame_data_t);
218
219                 wmem_tree_insert32(data->frame_tree, frame_num, frame);
220         }
221         return frame;
222 }
223
224 static ipmi_request_t *
225 get_matched_request(ipmi_packet_data_t * data, const ipmi_header_t * rs_hdr,
226                 guint flags)
227 {
228         wmem_list_frame_t * iter = wmem_list_head(data->request_list);
229         ipmi_header_t rq_hdr;
230
231         /* reset message context */
232         rq_hdr.context = 0;
233
234         /* copy channel */
235         rq_hdr.channel = data->curr_channel;
236
237         /* toggle packet direction */
238         rq_hdr.dir = rs_hdr->dir ^ 1;
239
240         rq_hdr.session = rs_hdr->session;
241
242         /* swap responder address/lun */
243         rq_hdr.rs_sa = rs_hdr->rq_sa;
244         rq_hdr.rs_lun = rs_hdr->rq_lun;
245
246         /* remove reply flag */
247         rq_hdr.netfn = rs_hdr->netfn & ~1;
248
249         /* swap requester address/lun */
250         rq_hdr.rq_sa = rs_hdr->rs_sa;
251         rq_hdr.rq_lun = rs_hdr->rs_lun;
252
253         /* copy sequence */
254         rq_hdr.rq_seq = rs_hdr->rq_seq;
255
256         /* copy command */
257         rq_hdr.cmd = rs_hdr->cmd;
258
259         /* TODO: copy prefix bytes */
260
261 #ifdef DEBUG
262         fprintf(stderr, "%d, %d: rq_hdr : {\n"
263                         "\tchannel=%d\n"
264                         "\tdir=%d\n"
265                         "\trs_sa=%x\n"
266                         "\trs_lun=%d\n"
267                         "\tnetfn=%x\n"
268                         "\trq_sa=%x\n"
269                         "\trq_lun=%d\n"
270                         "\trq_seq=%x\n"
271                         "\tcmd=%x\n}\n",
272                         data->curr_frame_num, data->curr_level,
273                         rq_hdr.channel, rq_hdr.dir, rq_hdr.rs_sa, rq_hdr.rs_lun,
274                         rq_hdr.netfn, rq_hdr.rq_sa, rq_hdr.rq_lun, rq_hdr.rq_seq,
275                         rq_hdr.cmd);
276 #endif
277
278         while (iter) {
279                 ipmi_request_t * rq = (ipmi_request_t *) wmem_list_frame_data(iter);
280
281                 /* check if in Get Message context */
282                 if (rs_hdr->context == IPMI_E_GETMSG && !(flags & IPMI_D_TRG_SA)) {
283                         /* diregard rsSA */
284                         rq_hdr.rq_sa = rq->hdr.rq_sa;
285                 }
286
287                 /* compare command headers */
288                 if (!memcmp(&rq_hdr, &rq->hdr, sizeof(rq_hdr))) {
289                         return rq;
290                 }
291
292                 /* proceed to next request */
293                 iter = wmem_list_frame_next(iter);
294         }
295
296         return NULL;
297 }
298
299 static void
300 remove_old_requests(ipmi_packet_data_t * data, const nstime_t * curr_time)
301 {
302         wmem_list_frame_t * iter = wmem_list_head(data->request_list);
303
304         while (iter) {
305                 ipmi_request_t * rq = (ipmi_request_t *) wmem_list_frame_data(iter);
306                 ipmi_frame_data_t * frame = get_frame_data(data, rq->frame_num);
307                 nstime_t delta;
308
309                 /* calculate time delta */
310                 nstime_delta(&delta, curr_time, &frame->ts);
311
312                 if (nstime_to_msec(&delta) > response_after_req) {
313                         wmem_list_frame_t * del = iter;
314
315                         /* proceed to next request */
316                         iter = wmem_list_frame_next(iter);
317
318                         /* free request data */
319                         wmem_free(wmem_file_scope(), rq);
320
321                         /* remove list item */
322                         wmem_list_remove_frame(data->request_list, del);
323                 } else {
324                         break;
325                 }
326         }
327 }
328
329 static void
330 match_request_response(ipmi_packet_data_t * data, const ipmi_header_t * hdr,
331                 guint flags)
332 {
333         /* get current frame */
334         ipmi_frame_data_t * rs_frame = data->curr_frame;
335
336         /* get current command data */
337         ipmi_cmd_data_t * rs_data = rs_frame->cmd_data[data->curr_level];
338
339         /* check if parse response for the first time */
340         if (!rs_data) {
341                 ipmi_request_t * rq;
342
343                 /* allocate command data */
344                 rs_data = wmem_new0(wmem_file_scope(), ipmi_cmd_data_t);
345
346                 /* search for matching request */
347                 rq = get_matched_request(data, hdr, flags);
348
349                 /* check if matching request is found */
350                 if (rq) {
351                         /* get request frame data */
352                         ipmi_frame_data_t * rq_frame =
353                                         get_frame_data(data, rq->frame_num);
354
355                         /* get command data */
356                         ipmi_cmd_data_t * rq_data = rq_frame->cmd_data[rq->nest_level];
357
358                         /* save matched frame numbers */
359                         rq_data->matched_frame_num = data->curr_frame_num;
360                         rs_data->matched_frame_num = rq->frame_num;
361
362                         /* copy saved command data information */
363                         rs_data->saved_data[0] = rq_data->saved_data[0];
364                         rs_data->saved_data[1] = rq_data->saved_data[1];
365
366                         /* remove request from the list */
367                         wmem_list_remove(data->request_list, rq);
368
369                         /* delete request data */
370                         wmem_free(wmem_file_scope(), rq);
371                 }
372
373                 /* save command data pointer in frame */
374                 rs_frame->cmd_data[data->curr_level] = rs_data;
375         }
376 }
377
378 static void
379 add_request(ipmi_packet_data_t * data, const ipmi_header_t * hdr)
380 {
381         /* get current frame */
382         ipmi_frame_data_t * rq_frame = data->curr_frame;
383
384         /* get current command data */
385         ipmi_cmd_data_t * rq_data = rq_frame->cmd_data[data->curr_level];
386
387         /* check if parse response for the first time */
388         if (!rq_data) {
389                 ipmi_request_t * rq;
390
391                 /* allocate command data */
392                 rq_data = wmem_new0(wmem_file_scope(), ipmi_cmd_data_t);
393
394                 /* set command data pointer */
395                 rq_frame->cmd_data[data->curr_level] = rq_data;
396
397                 /* allocate request data */
398                 rq = wmem_new0(wmem_file_scope(), ipmi_request_t);
399
400                 /* copy request header */
401                 memcpy(&rq->hdr, hdr, sizeof(rq->hdr));
402
403                 /* override context, channel and direction */
404                 rq->hdr.context = 0;
405                 rq->hdr.channel = data->curr_channel;
406                 rq->hdr.dir = data->curr_dir;
407
408                 /* set request frame number */
409                 rq->frame_num = data->curr_frame_num;
410
411                 /* set command nest level */
412                 rq->nest_level = data->curr_level;
413
414                 /* append request to list */
415                 wmem_list_append(data->request_list, rq);
416
417 #ifdef DEBUG
418         fprintf(stderr, "%d, %d: hdr : {\n"
419                         "\tchannel=%d\n"
420                         "\tdir=%d\n"
421                         "\trs_sa=%x\n"
422                         "\trs_lun=%d\n"
423                         "\tnetfn=%x\n"
424                         "\trq_sa=%x\n"
425                         "\trq_lun=%d\n"
426                         "\trq_seq=%x\n"
427                         "\tcmd=%x\n}\n",
428                         data->curr_frame_num, data->curr_level,
429                         rq->hdr.channel, rq->hdr.dir, rq->hdr.rs_sa, rq->hdr.rs_lun,
430                         rq->hdr.netfn, rq->hdr.rq_sa, rq->hdr.rq_lun, rq->hdr.rq_seq,
431                         rq->hdr.cmd);
432 #endif
433         }
434 }
435
436 static void
437 add_command_info(packet_info *pinfo, ipmi_cmd_t * cmd,
438                 gboolean resp, guint8 cc_val, const char * cc_str, gboolean broadcast)
439 {
440         if (resp) {
441                 col_add_fstr(pinfo->cinfo, COL_INFO, "Rsp, %s, %s (%02xh)",
442                                 cmd->desc, cc_str, cc_val);
443         } else {
444                 col_add_fstr(pinfo->cinfo, COL_INFO, "Req, %s%s",
445                                 broadcast ? "Broadcast " : "", cmd->desc);
446         }
447 }
448
449 static int
450 dissect_ipmi_cmd(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
451                 gint hf_parent_item, gint ett_tree, const ipmi_context_t * ctx)
452 {
453         ipmi_packet_data_t * data;
454         ipmi_netfn_t * cmd_list;
455         ipmi_cmd_t * cmd;
456         proto_item * ti;
457         proto_tree * cmd_tree = NULL, * tmp_tree;
458         guint8 prev_level, cc_val;
459         guint offset, siglen, is_resp;
460         const char * cc_str, * netfn_str;
461
462         /* get packet data */
463         data = get_packet_data(pinfo);
464         if (!data) {
465                 return 0;
466         }
467
468         /* get prefix length */
469         siglen = ipmi_getsiglen(ctx->hdr.netfn);
470
471         /* get response flag */
472         is_resp = ctx->hdr.netfn & 1;
473
474         /* check message length */
475         if (tvb_captured_length(tvb) < ctx->hdr_len + siglen + is_resp
476                         + !(ctx->flags & IPMI_D_NO_CKS)) {
477                 /* don bother with anything */
478                 return call_dissector(data_dissector, tvb, pinfo, tree);
479         }
480
481         /* save nest level */
482         prev_level = data->curr_level;
483
484         /* assign next nest level */
485         data->curr_level = data->next_level;
486
487         /* increment next nest level */
488         data->next_level++;
489
490         /* check for the first invocation */
491         if (!data->curr_level) {
492                 /* get current frame data */
493                 data->curr_frame = get_frame_data(data, pinfo->fd->num);
494                 data->curr_frame_num = pinfo->fd->num;
495
496                 /* copy frame timestamp */
497                 memcpy(&data->curr_frame->ts, &pinfo->fd->abs_ts, sizeof(nstime_t));
498
499                 /* cache channel and direction */
500                 data->curr_channel = ctx->hdr.channel;
501                 data->curr_dir = ctx->hdr.dir;
502
503                 /* remove requests which are too old */
504                 remove_old_requests(data, &pinfo->fd->abs_ts);
505         }
506
507         if (data->curr_level < MAX_NEST_LEVEL) {
508                 if (ctx->hdr.netfn & 1) {
509                         /* perform request/response matching */
510                         match_request_response(data, &ctx->hdr, ctx->flags);
511                 } else {
512                         /* add request to the list for later matching */
513                         add_request(data, &ctx->hdr);
514                 }
515         }
516
517         /* get command list by network function code */
518         cmd_list = ipmi_getnetfn(ctx->hdr.netfn,
519                         tvb_get_ptr(tvb, ctx->hdr_len + is_resp, siglen));
520
521         /* get command descriptor */
522         cmd = ipmi_getcmd(cmd_list, ctx->hdr.cmd);
523
524         /* check if response */
525         if (is_resp) {
526                 /* get completion code */
527                 cc_val = tvb_get_guint8(tvb, ctx->hdr_len);
528
529                 /* get completion code desc */
530                 cc_str = ipmi_get_completion_code(cc_val, cmd);
531         } else {
532                 cc_val = 0;
533                 cc_str = NULL;
534         }
535
536         /* check if not inside a message */
537         if (!data->curr_level) {
538                 /* add packet info */
539                 add_command_info(pinfo, cmd, is_resp, cc_val, cc_str,
540                                 ctx->flags & IPMI_D_BROADCAST ? TRUE : FALSE);
541         }
542
543         if (tree) {
544                 /* add parent node */
545                 if (!data->curr_level) {
546                         ti = proto_tree_add_item(tree, hf_parent_item, tvb, 0, -1, ENC_NA);
547                 } else {
548                         char str[ITEM_LABEL_LENGTH];
549
550                         if (is_resp) {
551                                 g_snprintf(str, ITEM_LABEL_LENGTH, "Rsp, %s, %s",
552                                                 cmd->desc, cc_str);
553                         } else {
554                                 g_snprintf(str, ITEM_LABEL_LENGTH, "Req, %s", cmd->desc);
555                         }
556                         if (proto_registrar_get_ftype(hf_parent_item) == FT_STRING)
557                                 ti = proto_tree_add_string(tree, hf_parent_item, tvb, 0, -1, str);
558                         else
559                                 ti = proto_tree_add_text(tree, tvb, 0, -1, "%s", str);
560                 }
561
562                 /* add message sub-tree */
563                 cmd_tree = proto_item_add_subtree(ti, ett_tree);
564
565                 if (data->curr_level < MAX_NEST_LEVEL) {
566                         /* check if response */
567                         if (ctx->hdr.netfn & 1) {
568                                 /* get current command data */
569                                 ipmi_cmd_data_t * rs_data =
570                                                 data->curr_frame->cmd_data[data->curr_level];
571
572                                 if (rs_data->matched_frame_num) {
573                                         nstime_t ns;
574
575                                         /* add "Request to:" field */
576                                         ti = proto_tree_add_uint(cmd_tree, hf_ipmi_response_to,
577                                                         tvb, 0, 0, rs_data->matched_frame_num);
578
579                                         /* mark field as a generated one */
580                                         PROTO_ITEM_SET_GENERATED(ti);
581
582                                         /* calculate delta time */
583                                         nstime_delta(&ns, &pinfo->fd->abs_ts,
584                                                         &get_frame_data(data,
585                                                                         rs_data->matched_frame_num)->ts);
586
587                                         /* add "Response time" field */
588                                         ti = proto_tree_add_time(cmd_tree, hf_ipmi_response_time,
589                                                         tvb, 0, 0, &ns);
590
591                                         /* mark field as a generated one */
592                                         PROTO_ITEM_SET_GENERATED(ti);
593                                         }
594                         } else {
595                                 /* get current command data */
596                                 ipmi_cmd_data_t * rq_data =
597                                                 data->curr_frame->cmd_data[data->curr_level];
598
599                                 if (rq_data->matched_frame_num) {
600                                         /* add "Response in:" field  */
601                                         ti = proto_tree_add_uint(cmd_tree, hf_ipmi_response_in,
602                                                         tvb, 0, 0, rq_data->matched_frame_num);
603
604                                         /* mark field as a generated one */
605                                         PROTO_ITEM_SET_GENERATED(ti);
606                                 }
607                         }
608                 }
609
610                 /* set starting offset */
611                 offset = 0;
612
613                 /* check if message is broadcast */
614                 if (ctx->flags & IPMI_D_BROADCAST) {
615                         /* skip first byte */
616                         offset++;
617                 }
618
619                 /* check if session handle is specified */
620                 if (ctx->flags & IPMI_D_SESSION_HANDLE) {
621                         /* add session handle field */
622                         proto_tree_add_item(cmd_tree, hf_ipmi_session_handle,
623                                         tvb, offset++, 1, ENC_LITTLE_ENDIAN);
624                 }
625
626                 /* check if responder address is specified */
627                 if (ctx->flags & IPMI_D_TRG_SA) {
628                         /* add response address field */
629                         proto_tree_add_item(cmd_tree, hf_ipmi_header_trg, tvb,
630                                         offset++, 1, ENC_LITTLE_ENDIAN);
631                 }
632
633                 /* get NetFn string */
634                 netfn_str = ipmi_getnetfnname(ctx->hdr.netfn, cmd_list);
635
636                 /* Network function + target LUN */
637                 tmp_tree = proto_tree_add_subtree_format(cmd_tree, tvb, offset, 1,
638                                 ett_header_byte_1, NULL, "Target LUN: 0x%02x, NetFN: %s %s (0x%02x)",
639                                 ctx->hdr.rs_lun, netfn_str,
640                                 is_resp ? "Response" : "Request", ctx->hdr.netfn);
641
642                 /* add Net Fn */
643                 proto_tree_add_uint_format(tmp_tree, hf_ipmi_header_netfn, tvb,
644                                 offset, 1, ctx->hdr.netfn << 2,
645                                 "NetFn: %s %s (0x%02x)", netfn_str,
646                                 is_resp ? "Response" : "Request", ctx->hdr.netfn);
647
648                 proto_tree_add_item(tmp_tree, hf_ipmi_header_trg_lun, tvb,
649                                 offset++, 1, ENC_LITTLE_ENDIAN);
650
651                 /* check if cks1 is specified */
652                 if (!(ctx->flags & IPMI_D_NO_CKS)) {
653                         guint8 cks = tvb_get_guint8(tvb, offset);
654
655                         /* Header checksum */
656                         if (ctx->cks1) {
657                                 guint8 correct = cks - ctx->cks1;
658
659                                 proto_tree_add_uint_format_value(cmd_tree, hf_ipmi_header_crc,
660                                                 tvb, offset++, 1, cks,
661                                                 "0x%02x (incorrect, expected 0x%02x)", cks, correct);
662                         } else {
663                                 proto_tree_add_uint_format_value(cmd_tree, hf_ipmi_header_crc,
664                                                 tvb, offset++, 1, cks,
665                                                 "0x%02x (correct)", cks);
666                         }
667                 }
668
669                 /* check if request address is specified */
670                 if (!(ctx->flags & IPMI_D_NO_RQ_SA)) {
671                         /* add request address field */
672                         proto_tree_add_item(cmd_tree, hf_ipmi_header_src, tvb,
673                                         offset++, 1, ENC_LITTLE_ENDIAN);
674                 }
675
676                 /* check if request sequence is specified */
677                 if (!(ctx->flags & IPMI_D_NO_SEQ)) {
678                         /* Sequence number + source LUN */
679                         tmp_tree = proto_tree_add_subtree_format(cmd_tree, tvb, offset, 1,
680                                         ett_header_byte_4, NULL, "%s: 0x%02x, SeqNo: 0x%02x",
681                                         (ctx->flags & IPMI_D_TMODE) ? "Bridged" : "Source LUN",
682                                                         ctx->hdr.rq_lun, ctx->hdr.rq_seq);
683
684                         if (ctx->flags & IPMI_D_TMODE) {
685                                 proto_tree_add_item(tmp_tree, hf_ipmi_header_bridged,
686                                                 tvb, offset, 1, ENC_LITTLE_ENDIAN);
687                         } else {
688                                 proto_tree_add_item(tmp_tree, hf_ipmi_header_src_lun,
689                                                 tvb, offset, 1, ENC_LITTLE_ENDIAN);
690                         }
691
692                         /* print seq no */
693                         proto_tree_add_item(tmp_tree, hf_ipmi_header_sequence, tvb,
694                                         offset++, 1, ENC_LITTLE_ENDIAN);
695                 }
696
697                 /* command code */
698                 proto_tree_add_uint_format_value(cmd_tree, hf_ipmi_header_command,
699                                 tvb, offset++, 1, ctx->hdr.cmd, "%s (0x%02x)",
700                                 cmd->desc, ctx->hdr.cmd);
701
702                 if (is_resp) {
703                         /* completion code */
704                         proto_tree_add_uint_format_value(cmd_tree,
705                                         hf_ipmi_header_completion, tvb, offset++, 1,
706                                         cc_val, "%s (0x%02x)", cc_str, cc_val);
707                 }
708
709                 if (siglen) {
710                         /* command prefix (if present) */
711                         ti = proto_tree_add_item(cmd_tree, hf_ipmi_header_sig, tvb,
712                                         offset, siglen, ENC_NA);
713                         proto_item_append_text(ti, " (%s)", netfn_str);
714                 }
715         }
716
717         if (tree || (cmd->flags & CMD_CALLRQ)) {
718                 /* calculate message data length */
719                 guint data_len = tvb_captured_length(tvb)
720                                 - ctx->hdr_len
721                                 - siglen
722                                 - (is_resp ? 1 : 0)
723                                 - !(ctx->flags & IPMI_D_NO_CKS);
724
725                 /* create data subset */
726                 tvbuff_t * data_tvb = tvb_new_subset_length(tvb,
727                                 ctx->hdr_len + siglen + (is_resp ? 1 : 0), data_len);
728
729                 /* Select sub-handler */
730                 ipmi_cmd_handler_t hnd = is_resp ? cmd->parse_resp : cmd->parse_req;
731
732                 if (hnd && tvb_captured_length(data_tvb)) {
733                         /* create data field */
734                         tmp_tree = proto_tree_add_subtree(cmd_tree, data_tvb, 0, -1, ett_data, NULL, "Data");
735
736                         /* save current command */
737                         data->curr_hdr = &ctx->hdr;
738
739                         /* save current completion code */
740                         data->curr_ccode = cc_val;
741
742                         /* call command parser */
743                         hnd(data_tvb, pinfo, tmp_tree);
744                 }
745         }
746
747         /* check if cks2 is specified */
748         if (tree && !(ctx->flags & IPMI_D_NO_CKS)) {
749                 guint8 cks;
750
751                 /* get cks2 offset */
752                 offset = tvb_captured_length(tvb) - 1;
753
754                 /* get cks2 */
755                 cks = tvb_get_guint8(tvb, offset);
756
757                 /* Header checksum */
758                 if (ctx->cks2) {
759                         guint8 correct = cks - ctx->cks2;
760
761                         proto_tree_add_uint_format_value(cmd_tree, hf_ipmi_data_crc,
762                                         tvb, offset, 1, cks,
763                                         "0x%02x (incorrect, expected 0x%02x)", cks, correct);
764                 } else {
765                         proto_tree_add_uint_format_value(cmd_tree, hf_ipmi_data_crc,
766                                         tvb, offset, 1, cks,
767                                         "0x%02x (correct)", cks);
768                 }
769         }
770
771         /* decrement next nest level */
772         data->next_level = data->curr_level;
773
774         /* restore previous nest level */
775         data->curr_level = prev_level;
776
777         return tvb_captured_length(tvb);
778 }
779
780 /* Get currently parsed message header */
781 const ipmi_header_t * ipmi_get_hdr(packet_info * pinfo)
782 {
783         ipmi_packet_data_t * data = get_packet_data(pinfo);
784         return data->curr_hdr;
785 }
786
787 /* Get completion code for currently parsed message */
788 guint8 ipmi_get_ccode(packet_info * pinfo)
789 {
790         ipmi_packet_data_t * data = get_packet_data(pinfo);
791         return data->curr_ccode;
792 }
793
794 /* Save request data for later use in response */
795 void ipmi_set_data(packet_info *pinfo, guint idx, guint32 value)
796 {
797         ipmi_packet_data_t * data = get_packet_data(pinfo);
798
799         /* check bounds */
800         if (data->curr_level >= MAX_NEST_LEVEL || idx >= NSAVED_DATA ) {
801                 return;
802         }
803
804         /* save data */
805         data->curr_frame->cmd_data[data->curr_level]->saved_data[idx] = value;
806 }
807
808 /* Get saved request data */
809 gboolean ipmi_get_data(packet_info *pinfo, guint idx, guint32 * value)
810 {
811         ipmi_packet_data_t * data = get_packet_data(pinfo);
812
813         /* check bounds */
814         if (data->curr_level >= MAX_NEST_LEVEL || idx >= NSAVED_DATA ) {
815                 return FALSE;
816         }
817
818         /* get data */
819         *value = data->curr_frame->cmd_data[data->curr_level]->saved_data[idx];
820         return TRUE;
821 }
822
823 /* ----------------------------------------------------------------
824    Support for Type/Length fields parsing.
825 ---------------------------------------------------------------- */
826
827 static void
828 get_len_binary(guint *clen, guint *blen, tvbuff_t *tvb _U_, guint offs _U_,
829                 guint len, gboolean len_is_bytes _U_)
830 {
831         *clen = len * 3;
832         *blen = len;
833 }
834
835 static void
836 parse_binary(char *p, tvbuff_t *tvb, guint offs, guint len)
837 {
838         static const char hex[] = "0123456789ABCDEF";
839         guint8 v;
840         guint i;
841
842         for (i = 0; i < len / 3; i++) {
843                 v = tvb_get_guint8(tvb, offs + i);
844                 *p++ = hex[v >> 4];
845                 *p++ = hex[v & 0xf];
846                 *p++ = ' ';
847         }
848
849         if (i) {
850                 *--p = '\0';
851         }
852 }
853
854 static struct ipmi_parse_typelen ptl_binary = {
855         get_len_binary, parse_binary, "Binary"
856 };
857
858 static void
859 get_len_bcdplus(guint *clen, guint *blen, tvbuff_t *tvb _U_, guint offs _U_,
860                 guint len, gboolean len_is_bytes)
861 {
862         if (len_is_bytes) {
863                 *clen = len * 2;
864                 *blen = len;
865         } else {
866                 *blen = (len + 1) / 2;
867                 *clen = len;
868         }
869 }
870
871 static void
872 parse_bcdplus(char *p, tvbuff_t *tvb, guint offs, guint len)
873 {
874         static const char bcd[] = "0123456789 -.:,_";
875         guint i, msk = 0xf0, shft = 4;
876         guint8 v;
877
878         for (i = 0; i < len; i++) {
879                 v = (tvb_get_guint8(tvb, offs + i / 2) & msk) >> shft;
880                 *p++ = bcd[v];
881                 msk ^= 0xff;
882                 shft = 4 - shft;
883         }
884 }
885
886 static struct ipmi_parse_typelen ptl_bcdplus = {
887         get_len_bcdplus, parse_bcdplus, "BCD+"
888 };
889
890 static void
891 get_len_6bit_ascii(guint *clen, guint *blen, tvbuff_t *tvb _U_, guint offs _U_,
892                 guint len, gboolean len_is_bytes)
893 {
894         if (len_is_bytes) {
895                 *clen = len * 4 / 3;
896                 *blen = len;
897         } else {
898                 *blen = (len * 3 + 3) / 4;
899                 *clen = len;
900         }
901 }
902
903 static void
904 parse_6bit_ascii(char *p, tvbuff_t *tvb, guint offs, guint len)
905 {
906         guint32 v;
907         guint i;
908
909         /* First, handle "full" triplets of bytes, 4 characters each */
910         for (i = 0; i < len / 4; i++) {
911                 v = tvb_get_letoh24(tvb, offs + i * 3);
912                 p[0] = ' ' + (v & 0x3f);
913                 p[1] = ' ' + ((v >> 6) & 0x3f);
914                 p[2] = ' ' + ((v >> 12) & 0x3f);
915                 p[3] = ' ' + ((v >> 18) & 0x3f);
916                 p += 4;
917         }
918
919         /* Do we have any characters left? */
920         offs += len / 4;
921         len &= 0x3;
922         switch (len) {
923         case 3:
924                 v = (tvb_get_guint8(tvb, offs + 2) << 4) | (tvb_get_guint8(tvb, offs + 1) >> 4);
925                 p[2] = ' ' + (v & 0x3f);
926                 /* Fall thru */
927         case 2:
928                 v = (tvb_get_guint8(tvb, offs + 1) << 2) | (tvb_get_guint8(tvb, offs) >> 6);
929                 p[1] = ' ' + (v & 0x3f);
930                 /* Fall thru */
931         case 1:
932                 v = tvb_get_guint8(tvb, offs) & 0x3f;
933                 p[0] = ' ' + (v & 0x3f);
934         }
935 }
936
937 static struct ipmi_parse_typelen ptl_6bit_ascii = {
938         get_len_6bit_ascii, parse_6bit_ascii, "6-bit ASCII"
939 };
940
941 static void
942 get_len_8bit_ascii(guint *clen, guint *blen, tvbuff_t *tvb, guint offs,
943                 guint len, gboolean len_is_bytes _U_)
944 {
945         guint i;
946         guint8 ch;
947
948         *blen = len;    /* One byte is one character */
949         *clen = 0;
950         for (i = 0; i < len; i++) {
951                 ch = tvb_get_guint8(tvb, offs + i);
952                 *clen += (ch >= 0x20 && ch <= 0x7f) ? 1 : 4;
953         }
954 }
955
956 static void
957 parse_8bit_ascii(char *p, tvbuff_t *tvb, guint offs, guint len)
958 {
959         guint8 ch;
960         char *pmax;
961
962         pmax = p + len;
963         while (p < pmax) {
964                 ch = tvb_get_guint8(tvb, offs++);
965                 if (ch >= 0x20 && ch <= 0x7f) {
966                         *p++ = ch;
967                 } else {
968                         g_snprintf(p, 5, "\\x%02x", ch);
969                         p += 4;
970                 }
971         }
972 }
973
974 static struct ipmi_parse_typelen ptl_8bit_ascii = {
975         get_len_8bit_ascii, parse_8bit_ascii, "ASCII+Latin1"
976 };
977
978 static void
979 get_len_unicode(guint *clen, guint *blen, tvbuff_t *tvb _U_, guint offs _U_,
980                 guint len _U_, gboolean len_is_bytes)
981 {
982         if (len_is_bytes) {
983                 *clen = len * 3; /* Each 2 bytes result in 6 chars printed: \Uxxxx */
984                 *blen = len;
985         } else {
986                 *clen = len * 6;
987                 *blen = len * 2;
988         }
989 }
990
991 static void
992 parse_unicode(char *p, tvbuff_t *tvb, guint offs, guint len)
993 {
994         char *pmax = p + len;
995         guint8 ch0, ch1;
996
997         while (p < pmax) {
998                 ch0 = tvb_get_guint8(tvb, offs++);
999                 ch1 = tvb_get_guint8(tvb, offs++);
1000                 g_snprintf(p, 7, "\\U%02x%02x", ch0, ch1);
1001                 p += 6;
1002         }
1003 }
1004
1005 static struct ipmi_parse_typelen ptl_unicode = {
1006         get_len_unicode, parse_unicode, "Unicode"
1007 };
1008
1009 void
1010 ipmi_add_typelen(proto_tree *tree, const char *desc, tvbuff_t *tvb,
1011                 guint offs, gboolean is_fru)
1012 {
1013         static struct ipmi_parse_typelen *fru_eng[4] = {
1014                 &ptl_binary, &ptl_bcdplus, &ptl_6bit_ascii, &ptl_8bit_ascii
1015         };
1016         static struct ipmi_parse_typelen *fru_noneng[4] = {
1017                 &ptl_binary, &ptl_bcdplus, &ptl_6bit_ascii, &ptl_unicode
1018         };
1019         static struct ipmi_parse_typelen *ipmi[4] = {
1020                 &ptl_unicode, &ptl_bcdplus, &ptl_6bit_ascii, &ptl_8bit_ascii
1021         };
1022         struct ipmi_parse_typelen *ptr;
1023         proto_tree *s_tree;
1024         guint type, msk, clen, blen, len;
1025         const char *unit;
1026         char *str;
1027         guint8 typelen;
1028
1029         typelen = tvb_get_guint8(tvb, offs);
1030         type = typelen >> 6;
1031         if (is_fru) {
1032                 msk = 0x3f;
1033                 ptr = (fru_langcode_is_english ? fru_eng : fru_noneng)[type];
1034                 unit = "bytes";
1035         } else {
1036                 msk = 0x1f;
1037                 ptr = ipmi[type];
1038                 unit = "characters";
1039         }
1040
1041         len = typelen & msk;
1042         ptr->get_len(&clen, &blen, tvb, offs + 1, len, is_fru);
1043
1044         str = (char *)wmem_alloc(wmem_packet_scope(), clen + 1);
1045         ptr->parse(str, tvb, offs + 1, clen);
1046         str[clen] = '\0';
1047
1048         s_tree = proto_tree_add_subtree_format(tree, tvb, offs, 1, ett_typelen, NULL,
1049                         "%s Type/Length byte: %s, %d %s", desc, ptr->desc, len, unit);
1050         proto_tree_add_text(s_tree, tvb, offs, 1, "%sType: %s (0x%02x)",
1051                         ipmi_dcd8(typelen, 0xc0), ptr->desc, type);
1052         proto_tree_add_text(s_tree, tvb, offs, 1, "%sLength: %d %s",
1053                         ipmi_dcd8(typelen, msk), len, unit);
1054
1055         proto_tree_add_text(tree, tvb, offs + 1, blen, "%s: [%s] '%s'",
1056                         desc, ptr->desc, str);
1057 }
1058
1059 /* ----------------------------------------------------------------
1060    Timestamp, IPMI-style.
1061 ---------------------------------------------------------------- */
1062 void
1063 ipmi_add_timestamp(proto_tree *tree, gint hf, tvbuff_t *tvb, guint offset)
1064 {
1065         guint32 ts = tvb_get_letohl(tvb, offset);
1066
1067         if (ts == 0xffffffff) {
1068                 proto_tree_add_uint_format_value(tree, hf, tvb, offset, 4,
1069                                 ts, "Unspecified/Invalid");
1070         } else if (ts <= 0x20000000) {
1071                 proto_tree_add_uint_format_value(tree, hf, tvb, offset, 4,
1072                                 ts, "%s since SEL device's initialization",
1073                                 time_secs_to_str_unsigned(wmem_packet_scope(), ts));
1074         } else {
1075                 proto_tree_add_uint_format_value(tree, hf, tvb, offset, 4,
1076                                 ts, "%s", abs_time_secs_to_str(wmem_packet_scope(), ts, ABSOLUTE_TIME_UTC, TRUE));
1077         }
1078 }
1079
1080 /* ----------------------------------------------------------------
1081    GUID, IPMI-style.
1082 ---------------------------------------------------------------- */
1083
1084 void
1085 ipmi_add_guid(proto_tree *tree, gint hf, tvbuff_t *tvb, guint offset)
1086 {
1087         e_guid_t guid;
1088         int i;
1089
1090         guid.data1 = tvb_get_letohl(tvb, offset + 12);
1091         guid.data2 = tvb_get_letohs(tvb, offset + 10);
1092         guid.data3 = tvb_get_letohs(tvb, offset + 8);
1093         for (i = 0; i < 8; i++) {
1094                 guid.data4[i] = tvb_get_guint8(tvb, offset + 7 - i);
1095         }
1096         proto_tree_add_guid(tree, hf, tvb, offset, 16, &guid);
1097 }
1098
1099 /* ----------------------------------------------------------------
1100    Routines for registering/looking up command parsers.
1101 ---------------------------------------------------------------- */
1102
1103 static void
1104 ipmi_netfn_setdesc(guint32 netfn, const char *desc, guint32 siglen)
1105 {
1106         struct ipmi_netfn_root *inr;
1107
1108         inr = &ipmi_cmd_tab[netfn >> 1];
1109         inr->desc = desc;
1110         inr->siglen = siglen;
1111 }
1112
1113 void
1114 ipmi_register_netfn_cmdtab(guint32 netfn, guint oem_selector,
1115                 const guint8 *sig, guint32 siglen, const char *desc,
1116                 ipmi_cmd_t *cmdtab, guint32 cmdtablen)
1117 {
1118         struct ipmi_netfn_root *inr;
1119         ipmi_netfn_t *inh;
1120
1121         netfn >>= 1;    /* Requests and responses grouped together */
1122         if (netfn >= IPMI_NETFN_MAX) {
1123                 return;
1124         }
1125
1126         inr = &ipmi_cmd_tab[netfn];
1127         if (inr->siglen != siglen) {
1128                 return;
1129         }
1130
1131         inh = (struct ipmi_netfn_handler *)g_malloc(sizeof(struct ipmi_netfn_handler));
1132         inh->desc = desc;
1133         inh->oem_selector = oem_selector;
1134         inh->sig = sig;
1135         inh->cmdtab = cmdtab;
1136         inh->cmdtablen = cmdtablen;
1137
1138         inh->next = inr->list;
1139         inr->list = inh;
1140 }
1141
1142 guint32
1143 ipmi_getsiglen(guint32 netfn)
1144 {
1145         return ipmi_cmd_tab[netfn >> 1].siglen;
1146 }
1147
1148 const char *
1149 ipmi_getnetfnname(guint32 netfn, ipmi_netfn_t *nf)
1150 {
1151         const char *dn, *db;
1152
1153         dn = ipmi_cmd_tab[netfn >> 1].desc ?
1154                 ipmi_cmd_tab[netfn >> 1].desc : "Reserved";
1155         db = nf ? nf->desc : NULL;
1156         if (db) {
1157                 return wmem_strdup_printf(wmem_packet_scope(), "%s (%s)", db, dn);
1158         } else {
1159                 return dn;
1160         }
1161 }
1162
1163 ipmi_netfn_t *
1164 ipmi_getnetfn(guint32 netfn, const guint8 *sig)
1165 {
1166         struct ipmi_netfn_root *inr;
1167         ipmi_netfn_t *inh;
1168
1169         inr = &ipmi_cmd_tab[netfn >> 1];
1170         for (inh = inr->list; inh; inh = inh->next) {
1171                 if ((inh->oem_selector == selected_oem || inh->oem_selector == IPMI_OEM_NONE)
1172                                 && (!inr->siglen || !memcmp(sig, inh->sig, inr->siglen))) {
1173                         return inh;
1174                 }
1175         }
1176
1177         /* Either unknown netFn or signature does not match */
1178         return NULL;
1179 }
1180
1181 ipmi_cmd_t *
1182 ipmi_getcmd(ipmi_netfn_t *nf, guint32 cmd)
1183 {
1184         static ipmi_cmd_t ipmi_cmd_unknown = {
1185                 0x00,           /* Code */
1186                 ipmi_notimpl,   /* request */
1187                 ipmi_notimpl,   /* response */
1188                 NULL,           /* command codes */
1189                 NULL,           /* subfunctions */
1190                 "Unknown command",
1191                 0               /* flag */
1192         };
1193         ipmi_cmd_t *ic;
1194         size_t i, len;
1195
1196         if (nf) {
1197                 len = nf->cmdtablen;
1198                 for (ic = nf->cmdtab, i = 0; i < len; i++, ic++) {
1199                         if (ic->cmd == cmd) {
1200                                 return ic;
1201                         }
1202                 }
1203         }
1204
1205         return &ipmi_cmd_unknown;
1206 }
1207
1208 /* ----------------------------------------------------------------
1209    Various utility functions.
1210 ---------------------------------------------------------------- */
1211
1212 void
1213 ipmi_notimpl(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
1214 {
1215         proto_tree_add_expert(tree, pinfo, &ei_impi_parser_not_implemented, tvb, 0, -1);
1216 }
1217
1218 char *
1219 ipmi_dcd8(guint32 val, guint32 mask)
1220 {
1221         static char buf[64];
1222
1223         decode_bitfield_value(buf, val, mask, 8);
1224         return buf;
1225 }
1226
1227 void
1228 ipmi_fmt_10ms_1based(gchar *s, guint32 v)
1229 {
1230         g_snprintf(s, ITEM_LABEL_LENGTH, "%d.%03d seconds", v / 100, (v % 100) * 10);
1231 }
1232
1233 void
1234 ipmi_fmt_500ms_0based(gchar *s, guint32 v)
1235 {
1236         ipmi_fmt_500ms_1based(s, ++v);
1237 }
1238
1239 void
1240 ipmi_fmt_500ms_1based(gchar *s, guint32 v)
1241 {
1242         g_snprintf(s, ITEM_LABEL_LENGTH, "%d.%03d seconds", v / 2, (v % 2) * 500);
1243 }
1244
1245 void
1246 ipmi_fmt_1s_0based(gchar *s, guint32 v)
1247 {
1248         ipmi_fmt_1s_1based(s, ++v);
1249 }
1250
1251 void
1252 ipmi_fmt_1s_1based(gchar *s, guint32 v)
1253 {
1254         g_snprintf(s, ITEM_LABEL_LENGTH, "%d seconds", v);
1255 }
1256
1257 void
1258 ipmi_fmt_2s_0based(gchar *s, guint32 v)
1259 {
1260         g_snprintf(s, ITEM_LABEL_LENGTH, "%d seconds", (v + 1) * 2);
1261 }
1262
1263 void
1264 ipmi_fmt_5s_1based(gchar *s, guint32 v)
1265 {
1266         g_snprintf(s, ITEM_LABEL_LENGTH, "%d seconds", v * 5);
1267 }
1268
1269 void
1270 ipmi_fmt_version(gchar *s, guint32 v)
1271 {
1272         g_snprintf(s, ITEM_LABEL_LENGTH, "%d.%d", v & 0x0f, (v >> 4) & 0x0f);
1273 }
1274
1275 void
1276 ipmi_fmt_channel(gchar *s, guint32 v)
1277 {
1278         static const value_string chan_vals[] = {
1279                 { 0x00, "Primary IPMB (IPMB-0)" },
1280                 { 0x07, "IPMB-L" },
1281                 { 0x0e, "Current channel" },
1282                 { 0x0f, "System Interface" },
1283                 { 0, NULL }
1284         };
1285
1286         g_snprintf(s, ITEM_LABEL_LENGTH, "%s (0x%02x)",
1287                         val_to_str(v, chan_vals, "Channel #%d"), v);
1288 }
1289
1290 void
1291 ipmi_fmt_udpport(gchar *s, guint32 v)
1292 {
1293         g_snprintf(s, ITEM_LABEL_LENGTH, "%s (%d)", ep_udp_port_to_display(v), v);
1294 }
1295
1296 void
1297 ipmi_fmt_percent(gchar *s, guint32 v)
1298 {
1299         g_snprintf(s, ITEM_LABEL_LENGTH, "%d%%", v);
1300 }
1301
1302 const char *
1303 ipmi_get_completion_code(guint8 completion, ipmi_cmd_t *cmd)
1304 {
1305         static const value_string std_completion_codes[] = {
1306                 { 0x00, "Command Completed Normally" },
1307                 { 0xc0, "Node Busy" },
1308                 { 0xc1, "Invalid Command" },
1309                 { 0xc2, "Command invalid for given LUN" },
1310                 { 0xc3, "Timeout while processing command, response unavailable" },
1311                 { 0xc4, "Out of space" },
1312                 { 0xc5, "Reservation Canceled or Invalid Reservation ID" },
1313                 { 0xc6, "Request data truncated" },
1314                 { 0xc7, "Request data length invalid" },
1315                 { 0xc8, "Request data field length limit exceeded" },
1316                 { 0xc9, "Parameter out of range" },
1317                 { 0xca, "Cannot return number of requested data bytes" },
1318                 { 0xcb, "Requested Sensor, data, or record not present" },
1319                 { 0xcc, "Invalid data field in Request" },
1320                 { 0xcd, "Command illegal for specified sensor or record type" },
1321                 { 0xce, "Command response could not be provided" },
1322                 { 0xcf, "Cannot execute duplicated request" },
1323                 { 0xd0, "Command response could not be provided: SDR Repository in update mode" },
1324                 { 0xd1, "Command response could not be provided: device in firmware update mode" },
1325                 { 0xd2, "Command response could not be provided: BMC initialization or initialization agent in progress" },
1326                 { 0xd3, "Destination unavailable" },
1327                 { 0xd4, "Cannot execute command: insufficient privilege level or other security-based restriction" },
1328                 { 0xd5, "Cannot execute command: command, or request parameter(s), not supported in present state" },
1329                 { 0xd6, "Cannot execute command: parameter is illegal because subfunction is disabled or unavailable" },
1330                 { 0xff, "Unspecified error" },
1331
1332                 { 0, NULL }
1333         };
1334         const char *res;
1335
1336         if (completion >= 0x01 && completion <= 0x7e) {
1337                 return "Device specific (OEM) completion code";
1338         }
1339
1340         if (completion >= 0x80 && completion <= 0xbe) {
1341                 if (cmd && cmd->cs_cc && (res = try_val_to_str(completion, cmd->cs_cc)) != NULL) {
1342                         return res;
1343                 }
1344                 return "Standard command-specific code";
1345         }
1346
1347         return val_to_str_const(completion, std_completion_codes, "Unknown");
1348 }
1349
1350 static int
1351 dissect_tmode(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
1352 {
1353         ipmi_dissect_arg_t * arg = (ipmi_dissect_arg_t *) data;
1354         ipmi_context_t ctx;
1355         guint tvb_len = tvb_captured_length(tvb);
1356         guint8 tmp;
1357
1358         /* TMode message is at least 3 bytes length */
1359         if (tvb_len < 3) {
1360                 return 0;
1361         }
1362
1363         memset(&ctx, 0, sizeof(ctx));
1364
1365         /* get Net Fn/RS LUN field */
1366         tmp = tvb_get_guint8(tvb, 0);
1367
1368         /* set Net Fn */
1369         ctx.hdr.netfn = tmp >> 2;
1370
1371         /*
1372          * NOTE: request/response matching code swaps RQ LUN with RS LUN
1373          * fields in IPMB-like manner in order to find corresponding request
1374          * so, we set both RS LUN and RQ LUN here for correct
1375          * request/response matching
1376          */
1377         ctx.hdr.rq_lun = tmp & 3;
1378         ctx.hdr.rs_lun = tmp & 3;
1379
1380         /* get RQ Seq field */
1381         ctx.hdr.rq_seq = tvb_get_guint8(tvb, 1) >> 2;
1382
1383         /*
1384          * NOTE: bridge field is ignored in request/response matching
1385          */
1386
1387         /* get command code */
1388         ctx.hdr.cmd = tvb_get_guint8(tvb, 2);
1389
1390         /* set dissect flags */
1391         ctx.flags = IPMI_D_TMODE|IPMI_D_NO_CKS|IPMI_D_NO_RQ_SA;
1392
1393         /* set header length */
1394         ctx.hdr_len = 3;
1395
1396         /* copy channel number and direction */
1397         ctx.hdr.context = arg ? arg->context : IPMI_E_NONE;
1398         ctx.hdr.channel = arg ? arg->channel : 0;
1399         ctx.hdr.dir = arg ? arg->flags >> 7 : ctx.hdr.netfn & 1;
1400
1401         if (ctx.hdr.context == IPMI_E_NONE) {
1402                 /* set source column */
1403                 col_set_str(pinfo->cinfo, COL_DEF_SRC,
1404                                 ctx.hdr.dir ? "Console" : "BMC");
1405
1406                 /* set destination column */
1407                 col_set_str(pinfo->cinfo, COL_DEF_DST,
1408                                 ctx.hdr.dir ? "BMC" : "Console");
1409         }
1410
1411         /* dissect IPMI command */
1412         return dissect_ipmi_cmd(tvb, pinfo, tree, proto_tmode, ett_ipmi, &ctx);
1413 }
1414
1415 static int
1416 dissect_kcs(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
1417 {
1418         ipmi_dissect_arg_t * arg = (ipmi_dissect_arg_t *) data;
1419         ipmi_context_t ctx;
1420         guint tvb_len = tvb_captured_length(tvb);
1421         guint8 tmp;
1422
1423         /* KCS message is at least 2 bytes length */
1424         if (tvb_len < 2) {
1425                 return 0;
1426         }
1427
1428         memset(&ctx, 0, sizeof(ctx));
1429
1430         /* get Net Fn/RS LUN field */
1431         tmp = tvb_get_guint8(tvb, 0);
1432
1433         /* set Net Fn */
1434         ctx.hdr.netfn = tmp >> 2;
1435
1436         /*
1437          * NOTE: request/response matching code swaps RQ LUN with RS LUN
1438          * fields in IPMB-like manner in order to find corresponding request
1439          * so, we set both RS LUN and RQ LUN here for correct
1440          * request/response matching
1441          */
1442         ctx.hdr.rq_lun = tmp & 3;
1443         ctx.hdr.rs_lun = tmp & 3;
1444
1445         /* get command code */
1446         ctx.hdr.cmd = tvb_get_guint8(tvb, 1);
1447
1448         /* set dissect flags */
1449         ctx.flags = IPMI_D_NO_CKS|IPMI_D_NO_RQ_SA|IPMI_D_NO_SEQ;
1450
1451         /* set header length */
1452         ctx.hdr_len = 2;
1453
1454         /* copy channel number and direction */
1455         ctx.hdr.context = arg ? arg->context : 0;
1456         ctx.hdr.channel = arg ? arg->channel : 0;
1457         ctx.hdr.dir = arg ? arg->flags >> 7 : ctx.hdr.netfn & 1;
1458
1459         if (ctx.hdr.context == IPMI_E_NONE) {
1460                 /* set source column */
1461                 col_set_str(pinfo->cinfo, COL_DEF_SRC, ctx.hdr.dir ? "HOST" : "BMC");
1462
1463                 /* set destination column */
1464                 col_set_str(pinfo->cinfo, COL_DEF_DST, ctx.hdr.dir ? "BMC" : "HOST");
1465         }
1466
1467         /* dissect IPMI command */
1468         return dissect_ipmi_cmd(tvb, pinfo, tree, proto_kcs, ett_ipmi, &ctx);
1469 }
1470
1471 static guint8 calc_cks(guint8 start, tvbuff_t * tvb, guint off, guint len)
1472 {
1473         while (len--) {
1474                 start += tvb_get_guint8(tvb, off++);
1475         }
1476
1477         return start;
1478 }
1479
1480 static gboolean guess_imb_format(tvbuff_t *tvb, guint8 env,
1481                 guint8 channel, guint * imb_flags, guint8 * cks1, guint8 * cks2)
1482 {
1483         gboolean check_bc = FALSE;
1484         gboolean check_sh = FALSE;
1485         gboolean check_sa = FALSE;
1486         guint tvb_len;
1487         guint sh_len;
1488         guint sa_len;
1489         guint rs_sa;
1490
1491         if (message_format == MSGFMT_NONE) {
1492                 return FALSE;
1493         } else if (message_format == MSGFMT_IPMB) {
1494                 *imb_flags = IPMI_D_TRG_SA;
1495         } else if (message_format == MSGFMT_LAN) {
1496                 *imb_flags = IPMI_D_TRG_SA|IPMI_D_SESSION_HANDLE;
1497         /* channel 0 is primary IPMB */
1498         } else if (!channel) {
1499                 /* check for broadcast if not in send message command */
1500                 if (env == IPMI_E_NONE) {
1501                         /* check broadcast */
1502                         check_bc = 1;
1503
1504                         /* slave address must be present */
1505                         *imb_flags = IPMI_D_TRG_SA;
1506                 /* check if in send message command */
1507                 } else if (env != IPMI_E_GETMSG) {
1508                         /* slave address must be present */
1509                         *imb_flags = IPMI_D_TRG_SA;
1510                 } else /* IPMI_E_GETMSG */ {
1511                         *imb_flags = 0;
1512                 }
1513         /* channel 15 is System Interface */
1514         } else if (channel == 15) {
1515                 /* slave address must be present */
1516                 *imb_flags = IPMI_D_TRG_SA;
1517
1518                 /* check if in get message command */
1519                 if (env == IPMI_E_GETMSG) {
1520                         /* session handle must be present */
1521                         *imb_flags |= IPMI_D_SESSION_HANDLE;
1522                 }
1523         /* for other channels */
1524         } else {
1525                 if (env == IPMI_E_NONE) {
1526                         /* check broadcast */
1527                         check_bc = 1;
1528
1529                         /* slave address must be present */
1530                         *imb_flags = IPMI_D_TRG_SA;
1531                 } else if (env == IPMI_E_SENDMSG_RQ) {
1532                         /* check session handle */
1533                         check_sh = 1;
1534
1535                         /* slave address must be present */
1536                         *imb_flags = IPMI_D_TRG_SA;
1537                 } else if (env == IPMI_E_SENDMSG_RS) {
1538                         /* slave address must be present */
1539                         *imb_flags = IPMI_D_TRG_SA;
1540                 } else /* IPMI_E_GETMSG */ {
1541                         /* check session handle */
1542                         check_sh = 1;
1543
1544                         /* check slave address presence */
1545                         check_sa = 1;
1546
1547                         /* no pre-requisites */
1548                         *imb_flags = 0;
1549                 }
1550         }
1551
1552         /* get message length */
1553         tvb_len = tvb_captured_length(tvb);
1554
1555         /*
1556          * broadcast message starts with null,
1557          * does not contain session handle
1558          * but contains responder address
1559          */
1560         if (check_bc
1561                         && tvb_len >= 8
1562                         && !tvb_get_guint8(tvb, 0)
1563                         && !calc_cks(0, tvb, 1, 3)
1564                         && !calc_cks(0, tvb, 4, tvb_len - 4)) {
1565                 *imb_flags = IPMI_D_BROADCAST|IPMI_D_TRG_SA;
1566                 *cks1 = 0;
1567                 *cks2 = 0;
1568                 return TRUE;
1569         }
1570
1571         /*
1572          * message with the starts with session handle
1573          * and contain responder address
1574          */
1575         if (check_sh
1576                         && tvb_len >= 8
1577                         && !calc_cks(0, tvb, 1, 3)
1578                         && !calc_cks(0, tvb, 4, tvb_len - 4)) {
1579                 *imb_flags = IPMI_D_SESSION_HANDLE|IPMI_D_TRG_SA;
1580                 *cks1 = 0;
1581                 *cks2 = 0;
1582                 return TRUE;
1583         }
1584
1585         /*
1586          * message with responder address
1587          */
1588         if (check_sa
1589                         && tvb_len >= 7
1590                         && !calc_cks(0, tvb, 0, 3)
1591                         && !calc_cks(0, tvb, 3, tvb_len - 3)) {
1592                 *imb_flags = IPMI_D_TRG_SA;
1593                 *cks1 = 0;
1594                 *cks2 = 0;
1595                 return TRUE;
1596         }
1597
1598
1599         if (*imb_flags & IPMI_D_SESSION_HANDLE) {
1600                 sh_len = 1;
1601                 sa_len = 1;
1602                 rs_sa = 0;
1603         } else if (*imb_flags & IPMI_D_TRG_SA) {
1604                 sh_len = 0;
1605                 sa_len = 1;
1606                 rs_sa = 0;
1607         } else {
1608                 sh_len = 0;
1609                 sa_len = 0;
1610                 rs_sa = 0x20;
1611         }
1612
1613         /* check message length */
1614         if (tvb_len < 6 + sh_len + sa_len) {
1615                 return FALSE;
1616         }
1617
1618         /* calculate checksum deltas */
1619         *cks1 = calc_cks(rs_sa, tvb, sh_len, sa_len + 2);
1620         *cks2 = calc_cks(0, tvb, sh_len + sa_len + 2,
1621                         tvb_len - sh_len - sa_len - 2);
1622
1623         return TRUE;
1624 }
1625
1626 int
1627 do_dissect_ipmb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
1628                 gint hf_parent_item, gint ett_tree, ipmi_dissect_arg_t * arg)
1629 {
1630         ipmi_context_t ctx;
1631         guint offset = 0;
1632         guint8 tmp;
1633
1634         memset(&ctx, 0, sizeof(ctx));
1635
1636         /* copy message context and channel */
1637         ctx.hdr.context = arg ? arg->context : 0;
1638         ctx.hdr.channel = arg ? arg->channel : 0;
1639
1640         /* guess IPMB message format */
1641         if (!guess_imb_format(tvb, ctx.hdr.context, ctx.hdr.channel,
1642                         &ctx.flags, &ctx.cks1, &ctx.cks2)) {
1643                 return 0;
1644         }
1645
1646         /* check if message is broadcast */
1647         if (ctx.flags & IPMI_D_BROADCAST) {
1648                 /* skip first byte */
1649                 offset++;
1650         }
1651
1652         /* check is session handle is specified */
1653         if (ctx.flags & IPMI_D_SESSION_HANDLE) {
1654                 ctx.hdr.session = tvb_get_guint8(tvb, offset++);
1655         }
1656
1657         /* check is response address is specified */
1658         if (ctx.flags & IPMI_D_TRG_SA) {
1659                 ctx.hdr.rs_sa = tvb_get_guint8(tvb, offset++);
1660         } else {
1661                 ctx.hdr.rs_sa = 0x20;
1662         }
1663
1664         /* get Net Fn/RS LUN field */
1665         tmp = tvb_get_guint8(tvb, offset++);
1666
1667         /* set Net Fn  and RS LUN */
1668         ctx.hdr.netfn = tmp >> 2;
1669         ctx.hdr.rs_lun = tmp & 3;
1670
1671         /* skip cks1 */
1672         offset++;
1673
1674         /* get RQ SA */
1675         ctx.hdr.rq_sa = tvb_get_guint8(tvb, offset++);
1676
1677         /* get RQ Seq/RQ LUN field */
1678         tmp = tvb_get_guint8(tvb, offset++);
1679
1680         /* set RQ Seq  and RQ LUN */
1681         ctx.hdr.rq_seq = tmp >> 2;
1682         ctx.hdr.rq_lun = tmp & 3;
1683
1684         /* get command code */
1685         ctx.hdr.cmd = tvb_get_guint8(tvb, offset++);
1686
1687         /* set header length */
1688         ctx.hdr_len = offset;
1689
1690         /* copy direction */
1691         ctx.hdr.dir = arg ? arg->flags >> 7 : ctx.hdr.netfn & 1;
1692
1693         if (ctx.hdr.context == IPMI_E_NONE) {
1694                 guint red = arg ? (arg->flags & 0x40) : 0;
1695
1696                 if (!ctx.hdr.channel) {
1697                         col_add_fstr(pinfo->cinfo, COL_DEF_SRC,
1698                                         "0x%02x(%s)", ctx.hdr.rq_sa, red ? "IPMB-B" : "IPMB-A");
1699                 } else {
1700                         col_add_fstr(pinfo->cinfo, COL_DEF_SRC,
1701                                         "0x%02x", ctx.hdr.rq_sa);
1702                 }
1703
1704                 col_add_fstr(pinfo->cinfo, COL_DEF_DST, "0x%02x", ctx.hdr.rs_sa);
1705         }
1706
1707         /* dissect IPMI command */
1708         return dissect_ipmi_cmd(tvb, pinfo, tree, hf_parent_item, ett_tree, &ctx);
1709 }
1710
1711 static int
1712 dissect_ipmi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
1713 {
1714         return do_dissect_ipmb(tvb, pinfo, tree, proto_ipmb, ett_ipmi,
1715                         (ipmi_dissect_arg_t *) data);
1716 }
1717
1718 /* Register IPMB protocol.
1719  */
1720 void
1721 proto_register_ipmi(void)
1722 {
1723         static hf_register_info hf[] = {
1724                 { &hf_ipmi_session_handle, { "Session handle", "ipmi.session_handle", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1725                 { &hf_ipmi_header_trg, { "Target Address", "ipmi.header.target", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }},
1726                 { &hf_ipmi_header_trg_lun, { "Target LUN", "ipmi.header.trg_lun", FT_UINT8, BASE_HEX, NULL, 0x03, NULL, HFILL }},
1727                 { &hf_ipmi_header_netfn, { "NetFN", "ipmi.header.netfn", FT_UINT8, BASE_HEX, NULL, 0xfc, NULL, HFILL }},
1728                 { &hf_ipmi_header_crc, { "Header Checksum", "ipmi.header.crc", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1729                 { &hf_ipmi_header_src, { "Source Address", "ipmi.header.source", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1730                 { &hf_ipmi_header_src_lun, { "Source LUN", "ipmi.header.src_lun", FT_UINT8, BASE_HEX, NULL, 0x03, NULL, HFILL }},
1731                 { &hf_ipmi_header_bridged, { "Bridged", "ipmi.header.bridged", FT_UINT8, BASE_HEX, NULL, 0x03, NULL, HFILL }},
1732                 { &hf_ipmi_header_sequence, { "Sequence Number", "ipmi.header.sequence", FT_UINT8, BASE_HEX, NULL, 0xfc, NULL, HFILL }},
1733                 { &hf_ipmi_header_command, { "Command", "ipmi.header.command", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1734                 { &hf_ipmi_header_completion, { "Completion Code", "ipmi.header.completion", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1735                 { &hf_ipmi_header_sig, { "Signature", "ipmi.header.signature", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
1736                 { &hf_ipmi_data_crc, { "Data checksum", "ipmi.data.crc", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
1737                 { &hf_ipmi_response_to, { "Response to", "ipmi.response_to", FT_FRAMENUM, BASE_NONE, NULL, 0, NULL, HFILL }},
1738                 { &hf_ipmi_response_in, { "Response in", "ipmi.response_in", FT_FRAMENUM, BASE_NONE, NULL, 0, NULL, HFILL }},
1739                 { &hf_ipmi_response_time, { "Responded in", "ipmi.response_time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0, NULL, HFILL }}
1740         };
1741         static gint *ett[] = {
1742                 &ett_ipmi,
1743                 &ett_header,
1744                 &ett_header_byte_1,
1745                 &ett_header_byte_4,
1746                 &ett_data,
1747                 &ett_typelen
1748         };
1749         static const enum_val_t msgfmt_vals[] = {
1750                 { "none", "None", MSGFMT_NONE },
1751                 { "ipmb", "IPMB", MSGFMT_IPMB },
1752                 { "lan", "Session-based (LAN, ...)", MSGFMT_LAN },
1753                 { "guess", "Use heuristics", MSGFMT_GUESS },
1754                 { NULL, NULL, 0 }
1755         };
1756         static const enum_val_t oemsel_vals[] = {
1757                 { "none", "None", IPMI_OEM_NONE },
1758                 { "pps", "Pigeon Point Systems", IPMI_OEM_PPS },
1759                 { NULL, NULL, 0 }
1760         };
1761
1762         static ei_register_info ei[] = {
1763                 { &ei_impi_parser_not_implemented, { "ipmi.parser_not_implemented", PI_UNDECODED, PI_WARN, "[PARSER NOT IMPLEMENTED]", EXPFILL }},
1764         };
1765
1766         module_t *m;
1767         expert_module_t* expert_ipmi;
1768         guint32 i;
1769
1770         proto_ipmi = proto_register_protocol("Intelligent Platform Management Interface",
1771                                 "IPMI",
1772                                 "ipmi");
1773
1774         proto_ipmb = proto_register_protocol("Intelligent Platform Management Bus",
1775                                 "IPMB",
1776                                 "ipmb");
1777         proto_kcs = proto_register_protocol("Keyboard Controller Style Interface",
1778                                 "KCS",
1779                                 "kcs");
1780         proto_tmode = proto_register_protocol("Serial Terminal Mode Interface",
1781                                 "TMode",
1782                                 "tmode");
1783
1784         proto_register_field_array(proto_ipmi, hf, array_length(hf));
1785         proto_register_subtree_array(ett, array_length(ett));
1786
1787         expert_ipmi = expert_register_protocol(proto_ipmi);
1788         expert_register_field_array(expert_ipmi, ei, array_length(ei));
1789
1790         ipmi_netfn_setdesc(IPMI_CHASSIS_REQ, "Chassis", 0);
1791         ipmi_netfn_setdesc(IPMI_BRIDGE_REQ, "Bridge", 0);
1792         ipmi_netfn_setdesc(IPMI_SE_REQ, "Sensor/Event", 0);
1793         ipmi_netfn_setdesc(IPMI_APP_REQ, "Application", 0);
1794         ipmi_netfn_setdesc(IPMI_UPDATE_REQ, "Firmware Update", 0);
1795         ipmi_netfn_setdesc(IPMI_STORAGE_REQ, "Storage", 0);
1796         ipmi_netfn_setdesc(IPMI_TRANSPORT_REQ, "Transport", 0);
1797         ipmi_netfn_setdesc(IPMI_GROUP_REQ, "Group", 1);
1798         ipmi_netfn_setdesc(IPMI_OEM_REQ, "OEM/Group", 3);
1799         for (i = 0x30; i < 0x40; i += 2) {
1800                 ipmi_netfn_setdesc(i, "OEM", 0);
1801         }
1802
1803         new_register_dissector("ipmi", dissect_ipmi, proto_ipmi);
1804         new_register_dissector("ipmb", dissect_ipmi, proto_ipmb);
1805         new_register_dissector("kcs", dissect_kcs, proto_kcs);
1806         new_register_dissector("tmode", dissect_tmode, proto_tmode);
1807
1808         data_dissector = find_dissector("data");
1809
1810         m = prefs_register_protocol(proto_ipmi, NULL);
1811         prefs_register_bool_preference(m, "fru_langcode_is_english", "FRU Language Code is English",
1812                         "FRU Language Code is English; strings are ASCII+LATIN1 (vs. Unicode)",
1813                         &fru_langcode_is_english);
1814         prefs_register_uint_preference(m, "response_after_req", "Maximum delay of response message",
1815                         "Do not search for responses coming after this timeout (milliseconds)",
1816                         10, &response_after_req);
1817         prefs_register_uint_preference(m, "response_before_req", "Response ahead of request",
1818                         "Allow for responses before requests (milliseconds)",
1819                         10, &response_before_req);
1820         prefs_register_enum_preference(m, "msgfmt", "Format of embedded messages",
1821                         "Format of messages embedded into Send/Get/Forward Message",
1822                         &message_format, msgfmt_vals, FALSE);
1823         prefs_register_enum_preference(m, "selected_oem", "OEM commands parsed as",
1824                         "Selects which OEM format is used for commands that IPMI does not define",
1825                         &selected_oem, oemsel_vals, FALSE);
1826 }
1827
1828 /*
1829  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
1830  *
1831  * Local variables:
1832  * c-basic-offset: 8
1833  * tab-width: 8
1834  * indent-tabs-mode: t
1835  * End:
1836  *
1837  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
1838  * :indentSize=8:tabSize=8:noTabs=false:
1839  */