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