diff options
Diffstat (limited to 'src/plugins/lanplus/lanplus.c')
| -rw-r--r-- | src/plugins/lanplus/lanplus.c | 3680 | 
1 files changed, 3680 insertions, 0 deletions
| diff --git a/src/plugins/lanplus/lanplus.c b/src/plugins/lanplus/lanplus.c new file mode 100644 index 0000000..27b9610 --- /dev/null +++ b/src/plugins/lanplus/lanplus.c @@ -0,0 +1,3680 @@ +/* + * Copyright (c) 2003 Sun Microsystems, Inc.  All Rights Reserved. + *  + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + *  + * Redistribution of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + *  + * Redistribution in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + *  + * Neither the name of Sun Microsystems, Inc. or the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + *  + * This software is provided "AS IS," without a warranty of any kind. + * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, + * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. + * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE + * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL + * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, + * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR + * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF + * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, + * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <inttypes.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <errno.h> +#include <unistd.h> +#include <netdb.h> +#include <time.h> +#include <fcntl.h> +#include <assert.h> + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif +#include <ipmitool/helper.h> +#include <ipmitool/log.h> +#include <ipmitool/ipmi.h> +#include <ipmitool/ipmi_lanp.h> +#include <ipmitool/ipmi_channel.h> +#include <ipmitool/ipmi_intf.h> +#include <ipmitool/ipmi_strings.h> +#include <ipmitool/hpm2.h> +#include <ipmitool/bswap.h> +#include <openssl/rand.h> + +#include "lanplus.h" +#include "lanplus_crypt.h" +#include "lanplus_crypt_impl.h" +#include "lanplus_dump.h" +#include "rmcp.h" +#include "asf.h" + +/* + * LAN interface is required to support 45 byte request transactions and + * 42 byte response transactions. + */ +#define IPMI_LAN_MAX_REQUEST_SIZE	38	/* 45 - 7 */ +#define IPMI_LAN_MAX_RESPONSE_SIZE	34	/* 42 - 8 */ + +extern const struct valstr ipmi_rakp_return_codes[]; +extern const struct valstr ipmi_priv_levels[]; +extern const struct valstr ipmi_auth_algorithms[]; +extern const struct valstr ipmi_integrity_algorithms[]; +extern const struct valstr ipmi_encryption_algorithms[]; + +static struct ipmi_rq_entry * ipmi_req_entries; +static struct ipmi_rq_entry * ipmi_req_entries_tail; + + +static int ipmi_lanplus_setup(struct ipmi_intf * intf); +static int ipmi_lanplus_keepalive(struct ipmi_intf * intf); +static int ipmi_lan_send_packet(struct ipmi_intf * intf, uint8_t * data, int data_len); +static struct ipmi_rs * ipmi_lan_recv_packet(struct ipmi_intf * intf); +static struct ipmi_rs * ipmi_lan_poll_recv(struct ipmi_intf * intf); +static struct ipmi_rs * ipmi_lanplus_send_ipmi_cmd(struct ipmi_intf * intf, struct ipmi_rq * req); +static struct ipmi_rs * ipmi_lanplus_send_payload(struct ipmi_intf * intf, +												  struct ipmi_v2_payload * payload); +static void getIpmiPayloadWireRep( +								  struct ipmi_intf       * intf, +								  struct ipmi_v2_payload * payload,  /* in  */ +								  uint8_t  * out, +								  struct ipmi_rq * req, +								  uint8_t    rq_seq, +								  uint8_t curr_seq); +static void getSolPayloadWireRep( +								  struct ipmi_intf       * intf, +								 uint8_t          * msg, +								 struct ipmi_v2_payload * payload); +static void read_open_session_response(struct ipmi_rs * rsp, int offset); +static void read_rakp2_message(struct ipmi_rs * rsp, int offset, uint8_t alg); +static void read_rakp4_message(struct ipmi_rs * rsp, int offset, uint8_t alg); +static void read_session_data(struct ipmi_rs * rsp, int * offset, struct ipmi_session *s); +static void read_session_data_v15(struct ipmi_rs * rsp, int * offset, struct ipmi_session *s); +static void read_session_data_v2x(struct ipmi_rs * rsp, int * offset, struct ipmi_session *s); +static void read_ipmi_response(struct ipmi_rs * rsp, int * offset); +static void read_sol_packet(struct ipmi_rs * rsp, int * offset); +static struct ipmi_rs * ipmi_lanplus_recv_sol(struct ipmi_intf * intf); +static struct ipmi_rs * ipmi_lanplus_send_sol( +											  struct ipmi_intf * intf, +											  struct ipmi_v2_payload * payload); +static int check_sol_packet_for_new_data( +									 struct ipmi_intf * intf, +									 struct ipmi_rs *rsp); +static void ack_sol_packet( +							struct ipmi_intf * intf, +							struct ipmi_rs * rsp); +static void ipmi_lanp_set_max_rq_data_size(struct ipmi_intf * intf, uint16_t size); +static void ipmi_lanp_set_max_rp_data_size(struct ipmi_intf * intf, uint16_t size); + +static uint8_t bridgePossible = 0; + +struct ipmi_intf ipmi_lanplus_intf = { +	name:		"lanplus", +	desc:		"IPMI v2.0 RMCP+ LAN Interface", +	setup:		ipmi_lanplus_setup, +	open:		ipmi_lanplus_open, +	close:		ipmi_lanplus_close, +	sendrecv:	ipmi_lanplus_send_ipmi_cmd, +	recv_sol:	ipmi_lanplus_recv_sol, +	send_sol:	ipmi_lanplus_send_sol, +	keepalive:	ipmi_lanplus_keepalive, +	set_max_request_data_size: ipmi_lanp_set_max_rq_data_size, +	set_max_response_data_size: ipmi_lanp_set_max_rp_data_size, +	target_addr:	IPMI_BMC_SLAVE_ADDR, +}; + + +extern int verbose; + + + +/* + * lanplus_get_requested_ciphers + * + * Set the authentication, integrity and encryption algorithms based + * on the cipher suite ID.  See table 22-19 in the IPMIv2 spec for the + * source of this information. + * + * param cipher_suite_id [in] + * param auth_alg        [out] + * param integrity_alg   [out] + * param crypt_alg       [out] + * + * returns 0 on success + *         1 on failure + */ +int lanplus_get_requested_ciphers(int       cipher_suite_id, +								  uint8_t * auth_alg, +								  uint8_t * integrity_alg, +								  uint8_t * crypt_alg) +{ +	if ((cipher_suite_id < 0) || (cipher_suite_id > 14)) +		return 1; + +		/* See table 22-19 for the source of the statement */ +	switch (cipher_suite_id) +	{ +	case 0: +		*auth_alg      = IPMI_AUTH_RAKP_NONE; +		*integrity_alg = IPMI_INTEGRITY_NONE; +		*crypt_alg     = IPMI_CRYPT_NONE; +		break; +	case 1: +		*auth_alg      = IPMI_AUTH_RAKP_HMAC_SHA1; +		*integrity_alg = IPMI_INTEGRITY_NONE; +		*crypt_alg     = IPMI_CRYPT_NONE; +		break; +	case 2: +		*auth_alg      = IPMI_AUTH_RAKP_HMAC_SHA1; +		*integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96; +		*crypt_alg     = IPMI_CRYPT_NONE; +		break; +	case 3: +		*auth_alg      = IPMI_AUTH_RAKP_HMAC_SHA1; +		*integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96; +		*crypt_alg     = IPMI_CRYPT_AES_CBC_128; +		break; +	case 4: +		*auth_alg      = IPMI_AUTH_RAKP_HMAC_SHA1; +		*integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96; +		*crypt_alg     = IPMI_CRYPT_XRC4_128; +		break; +	case 5: +		*auth_alg      = IPMI_AUTH_RAKP_HMAC_SHA1; +		*integrity_alg = IPMI_INTEGRITY_HMAC_SHA1_96; +		*crypt_alg     = IPMI_CRYPT_XRC4_40; +		break; +	case 6: +		*auth_alg      = IPMI_AUTH_RAKP_HMAC_MD5; +		*integrity_alg = IPMI_INTEGRITY_NONE; +		*crypt_alg     = IPMI_CRYPT_NONE; +		break; +	case 7: +		*auth_alg      = IPMI_AUTH_RAKP_HMAC_MD5; +		*integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128; +		*crypt_alg     = IPMI_CRYPT_NONE; +		break; +	case 8: +		*auth_alg      = IPMI_AUTH_RAKP_HMAC_MD5; +		*integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128; +		*crypt_alg     = IPMI_CRYPT_AES_CBC_128; +		break; +	case 9: +		*auth_alg      = IPMI_AUTH_RAKP_HMAC_MD5; +		*integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128; +		*crypt_alg     = IPMI_CRYPT_XRC4_128; +		break; +	case 10: +		*auth_alg      = IPMI_AUTH_RAKP_HMAC_MD5; +		*integrity_alg = IPMI_INTEGRITY_HMAC_MD5_128; +		*crypt_alg     = IPMI_CRYPT_XRC4_40; +		break; +	case 11: +		*auth_alg      = IPMI_AUTH_RAKP_HMAC_MD5; +		*integrity_alg = IPMI_INTEGRITY_MD5_128; +		*crypt_alg     = IPMI_CRYPT_NONE; +		break; +	case 12: +		*auth_alg      = IPMI_AUTH_RAKP_HMAC_MD5; +		*integrity_alg = IPMI_INTEGRITY_MD5_128; +		*crypt_alg     = IPMI_CRYPT_AES_CBC_128; +		break; +	case 13: +		*auth_alg      = IPMI_AUTH_RAKP_HMAC_MD5; +		*integrity_alg = IPMI_INTEGRITY_MD5_128; +		*crypt_alg     = IPMI_CRYPT_XRC4_128; +		break; +	case 14: +		*auth_alg      = IPMI_AUTH_RAKP_HMAC_MD5; +		*integrity_alg = IPMI_INTEGRITY_MD5_128; +		*crypt_alg     = IPMI_CRYPT_XRC4_40; +		break; +	} + +	return 0; +} + + + +/* + * Reverse the order of arbitrarily long strings of bytes + */ +void lanplus_swap( +				  uint8_t * buffer, +						int             length) +{ +	int i; +	uint8_t temp; + +	for (i =0; i < length/2; ++i) +	{ +		temp = buffer[i]; +		buffer[i] = buffer[length - 1 - i]; +		buffer[length - 1 - i] = temp; +	} +} + + + +static const struct valstr plus_payload_types_vals[] = { +	 { IPMI_PAYLOAD_TYPE_IPMI,              "IPMI (0)" },	// IPMI Message +	 { IPMI_PAYLOAD_TYPE_SOL,               "SOL  (1)" },	// SOL (Serial over LAN) +	 { IPMI_PAYLOAD_TYPE_OEM,               "OEM  (2)" },	// OEM Explicid + +	 { IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST, "OpenSession Req (0x10)" }, +	 { IPMI_PAYLOAD_TYPE_RMCP_OPEN_RESPONSE,"OpenSession Resp (0x11)" }, +	 { IPMI_PAYLOAD_TYPE_RAKP_1,            "RAKP1 (0x12)" }, +	 { IPMI_PAYLOAD_TYPE_RAKP_2,            "RAKP2 (0x13)" }, +	 { IPMI_PAYLOAD_TYPE_RAKP_3,            "RAKP3 (0x14)" }, +	 { IPMI_PAYLOAD_TYPE_RAKP_4,            "RAKP4 (0x15)" }, +	{ 0x00, NULL }, +}; + + +static struct ipmi_rq_entry * +ipmi_req_add_entry(struct ipmi_intf * intf, struct ipmi_rq * req, uint8_t req_seq) +{ +	struct ipmi_rq_entry * e; + +	e = malloc(sizeof(struct ipmi_rq_entry)); +	if (e == NULL) { +		lprintf(LOG_ERR, "ipmitool: malloc failure"); +		return NULL; +	} + +	memset(e, 0, sizeof(struct ipmi_rq_entry)); +	memcpy(&e->req, req, sizeof(struct ipmi_rq)); + +	e->intf = intf; +	e->rq_seq = req_seq; + +	if (ipmi_req_entries == NULL) +		ipmi_req_entries = e; +	else +		ipmi_req_entries_tail->next = e; + +	ipmi_req_entries_tail = e; +	lprintf(LOG_DEBUG+3, "added list entry seq=0x%02x cmd=0x%02x", +		e->rq_seq, e->req.msg.cmd);      +	return e; +} + + +static struct ipmi_rq_entry * +ipmi_req_lookup_entry(uint8_t seq, uint8_t cmd) +{ +	struct ipmi_rq_entry * e = ipmi_req_entries; + +	while (e && (e->rq_seq != seq || e->req.msg.cmd != cmd)) { +		if (e == e->next) +			return NULL; +		e = e->next; +	} +	return e; +} + +static void +ipmi_req_remove_entry(uint8_t seq, uint8_t cmd) +{ +	struct ipmi_rq_entry * p, * e, * saved_next_entry; + +	e = p = ipmi_req_entries; + +	while (e && (e->rq_seq != seq || e->req.msg.cmd != cmd)) { +		p = e; +		e = e->next; +	} +	if (e) { +		lprintf(LOG_DEBUG+3, "removed list entry seq=0x%02x cmd=0x%02x", +			seq, cmd); +		saved_next_entry = e->next; +		p->next = (p->next == e->next) ? NULL : e->next; +		/* If entry being removed is first in list, fix up list head */ +		if (ipmi_req_entries == e) { +			if (ipmi_req_entries != p) +				ipmi_req_entries = p; +			else +				ipmi_req_entries = saved_next_entry; +		} +		/* If entry being removed is last in list, fix up list tail */ +		if (ipmi_req_entries_tail == e) { +			if (ipmi_req_entries_tail != p) +				ipmi_req_entries_tail = p; +			else +				ipmi_req_entries_tail = NULL; +		} + +		if (e->msg_data) { +			free(e->msg_data); +			e->msg_data = NULL; +		} +		free(e); +		e = NULL; +	} +} + +static void +ipmi_req_clear_entries(void) +{ +	struct ipmi_rq_entry * p, * e; + +	e = ipmi_req_entries; +	while (e) { +		lprintf(LOG_DEBUG+3, "cleared list entry seq=0x%02x cmd=0x%02x", +			e->rq_seq, e->req.msg.cmd); +		p = e->next; +		free(e); +		e = p; +	} +} + + +int +ipmi_lan_send_packet( +					 struct ipmi_intf * intf, +					 uint8_t * data, int +					 data_len) +{ +	if (verbose >= 5) +		printbuf(data, data_len, ">> sending packet"); + +	return send(intf->fd, data, data_len, 0); +} + + + +struct ipmi_rs * +ipmi_lan_recv_packet(struct ipmi_intf * intf) +{ +	static struct ipmi_rs rsp; +	fd_set read_set, err_set; +	struct timeval tmout; +	int ret; + +	FD_ZERO(&read_set); +	FD_SET(intf->fd, &read_set); + +	FD_ZERO(&err_set); +	FD_SET(intf->fd, &err_set); + +	tmout.tv_sec = intf->session->timeout; +	tmout.tv_usec = 0; + +	ret = select(intf->fd + 1, &read_set, NULL, &err_set, &tmout); +	if (ret < 0 || FD_ISSET(intf->fd, &err_set) || !FD_ISSET(intf->fd, &read_set)) +		return NULL; + +	/* the first read may return ECONNREFUSED because the rmcp ping +	 * packet--sent to UDP port 623--will be processed by both the +	 * BMC and the OS. +	 * +	 * The problem with this is that the ECONNREFUSED takes +	 * priority over any other received datagram; that means that +	 * the Connection Refused shows up _before_ the response packet, +	 * regardless of the order they were sent out.  (unless the +	 * response is read before the connection refused is returned) +	 */ +	ret = recv(intf->fd, &rsp.data, IPMI_BUF_SIZE, 0); + +	if (ret < 0) { +		FD_ZERO(&read_set); +		FD_SET(intf->fd, &read_set); + +		FD_ZERO(&err_set); +		FD_SET(intf->fd, &err_set); + +		tmout.tv_sec = intf->session->timeout; +		tmout.tv_usec = 0; + +		ret = select(intf->fd + 1, &read_set, NULL, &err_set, &tmout); +		if (ret < 0 || FD_ISSET(intf->fd, &err_set) || !FD_ISSET(intf->fd, &read_set)) +			return NULL; + +		ret = recv(intf->fd, &rsp.data, IPMI_BUF_SIZE, 0); +		if (ret < 0) +			return NULL; +	} + +	if (ret == 0) +		return NULL; + +	rsp.data[ret] = '\0'; +	rsp.data_len = ret; + +	if (verbose >= 5) +		printbuf(rsp.data, rsp.data_len, "<< received packet"); + +	return &rsp; +} + + + +/* + * parse response RMCP "pong" packet + * + * return -1 if ping response not received + * returns 0 if IPMI is NOT supported + * returns 1 if IPMI is supported + * + * udp.source	= 0x026f	// RMCP_UDP_PORT + * udp.dest	= ?		// udp.source from rmcp-ping + * udp.len	= ? + * udp.check	= ? + * rmcp.ver	= 0x06		// RMCP Version 1.0 + * rmcp.__res	= 0x00		// RESERVED + * rmcp.seq	= 0xff		// no RMCP ACK + * rmcp.class	= 0x06		// RMCP_CLASS_ASF + * asf.iana	= 0x000011be	// ASF_RMCP_IANA + * asf.type	= 0x40		// ASF_TYPE_PONG + * asf.tag	= ?		// asf.tag from rmcp-ping + * asf.__res	= 0x00		// RESERVED + * asf.len	= 0x10		// 16 bytes + * asf.data[3:0]= 0x000011be	// IANA# = RMCP_ASF_IANA if no OEM + * asf.data[7:4]= 0x00000000	// OEM-defined (not for IPMI) + * asf.data[8]	= 0x81		// supported entities + * 				// [7]=IPMI [6:4]=RES [3:0]=ASF_1.0 + * asf.data[9]	= 0x00		// supported interactions (reserved) + * asf.data[f:a]= 0x000000000000 + */ +static int +ipmi_handle_pong(struct ipmi_intf * intf, struct ipmi_rs * rsp) +{ +	struct rmcp_pong { +		struct rmcp_hdr rmcp; +		struct asf_hdr asf; +		uint32_t iana; +		uint32_t oem; +		uint8_t sup_entities; +		uint8_t sup_interact; +		uint8_t reserved[6]; +	} * pong; + +	if (!rsp) +		return -1; + +	pong = (struct rmcp_pong *)rsp->data; + +	if (verbose) +		printf("Received IPMI/RMCP response packet: " +				"IPMI%s Supported\n", +				(pong->sup_entities & 0x80) ? "" : " NOT"); + +	if (verbose > 1) +		printf("  ASF Version %s\n" +				"  RMCP Version %s\n" +				"  RMCP Sequence %d\n" +				"  IANA Enterprise %lu\n\n", +				(pong->sup_entities & 0x01) ? "1.0" : "unknown", +				(pong->rmcp.ver == 6) ? "1.0" : "unknown", +				pong->rmcp.seq, +				(unsigned long)ntohl(pong->iana)); + +	return (pong->sup_entities & 0x80) ? 1 : 0; +} + + +/* build and send RMCP presence ping packet + * + * RMCP ping + * + * udp.source	= ? + * udp.dest	= 0x026f	// RMCP_UDP_PORT + * udp.len	= ? + * udp.check	= ? + * rmcp.ver	= 0x06		// RMCP Version 1.0 + * rmcp.__res	= 0x00		// RESERVED + * rmcp.seq	= 0xff		// no RMCP ACK + * rmcp.class	= 0x06		// RMCP_CLASS_ASF + * asf.iana	= 0x000011be	// ASF_RMCP_IANA + * asf.type	= 0x80		// ASF_TYPE_PING + * asf.tag	= ?		// ASF sequence number + * asf.__res	= 0x00		// RESERVED + * asf.len	= 0x00 + * + */ +int +ipmiv2_lan_ping(struct ipmi_intf * intf) +{ +	struct asf_hdr asf_ping = { +		.iana	= htonl(ASF_RMCP_IANA), +		.type	= ASF_TYPE_PING, +	}; +	struct rmcp_hdr rmcp_ping = { +		.ver	= RMCP_VERSION_1, +		.class	= RMCP_CLASS_ASF, +		.seq	= 0xff, +	}; +	uint8_t * data; +	int len = sizeof(rmcp_ping) + sizeof(asf_ping); +	int rv; + +	data = malloc(len); +	if (data == NULL) { +		lprintf(LOG_ERR, "ipmitool: malloc failure"); +		return -1; +	} +	memset(data, 0, len); +	memcpy(data, &rmcp_ping, sizeof(rmcp_ping)); +	memcpy(data+sizeof(rmcp_ping), &asf_ping, sizeof(asf_ping)); + +	lprintf(LOG_DEBUG, "Sending IPMI/RMCP presence ping packet"); + +	rv = ipmi_lan_send_packet(intf, data, len); + +	free(data); +	data = NULL; + +	if (rv < 0) { +		lprintf(LOG_ERR, "Unable to send IPMI presence ping packet"); +		return -1; +	} + +	if (ipmi_lan_poll_recv(intf) == 0) +		return 0; + +	return 1; +} + + +/** + * + * ipmi_lan_poll_recv + * + * Receive whatever comes back.  Ignore received packets that don't correspond + * to a request we've sent. + * + * Returns: the ipmi_rs packet describing the/a reponse we expect. + */ +static struct ipmi_rs * +ipmi_lan_poll_recv(struct ipmi_intf * intf) +{ +	struct rmcp_hdr rmcp_rsp; +	struct ipmi_rs * rsp; +	struct ipmi_session * session = intf->session; +	int offset, rv; +	uint16_t payload_size; +	uint8_t ourAddress = intf->my_addr; + +	if (ourAddress == 0) { +		ourAddress = IPMI_BMC_SLAVE_ADDR; +	} + +	rsp = ipmi_lan_recv_packet(intf); + +	/* +	 * Not positive why we're looping.  Do we sometimes get stuff we don't +	 * expect? +	 */ +	while (rsp != NULL) { + +		/* parse response headers */ +		memcpy(&rmcp_rsp, rsp->data, 4); + +		if (rmcp_rsp.class == RMCP_CLASS_ASF) { +			/* might be ping response packet */ +			rv = ipmi_handle_pong(intf, rsp); +			return (rv <= 0) ? NULL : rsp; +		} + +		if (rmcp_rsp.class != RMCP_CLASS_IPMI) { +			lprintf(LOG_DEBUG, "Invalid RMCP class: %x", +				rmcp_rsp.class); +			rsp = ipmi_lan_recv_packet(intf); +			continue; +		} + + +		/* +		 * The authtype / payload type determines what we are receiving +		 */ +		offset = 4; + + +		/*-------------------------------------------------------------------- +		 *  +		 * The current packet could be one of several things: +		 * +		 * 1) An IPMI 1.5 packet (the response to our GET CHANNEL +		 *    AUTHENTICATION CAPABILITIES request) +		 * 2) An RMCP+ message with an IPMI reponse payload +		 * 3) AN RMCP+ open session response +		 * 4) An RAKP-2 message (response to an RAKP 1 message) +		 * 5) An RAKP-4 message (response to an RAKP 3 message) +		 * 6) A Serial Over LAN packet +		 * 7) An Invalid packet (one that doesn't match a request) +		 * ------------------------------------------------------------------- +		 */ + +		read_session_data(rsp, &offset, intf->session); + +		if (lanplus_has_valid_auth_code(rsp, intf->session) == 0) +		{ +			lprintf(LOG_ERR, "ERROR: Received message with invalid authcode!"); +			rsp = ipmi_lan_recv_packet(intf); +			assert(0); +			//continue; +		} + +		if ((session->v2_data.session_state == LANPLUS_STATE_ACTIVE)    && +			 (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) && +			 (rsp->session.bEncrypted)) + +		{ +			lanplus_decrypt_payload(session->v2_data.crypt_alg, +						session->v2_data.k2, +						rsp->data + offset, +						rsp->session.msglen, +						rsp->data + offset, +						&payload_size); +		} +		else +			payload_size = rsp->session.msglen; + + +		/* +		 * Handle IPMI responses (case #1 and #2) -- all IPMI reponses +		 */ +		if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_IPMI) +		{ +			struct ipmi_rq_entry * entry; +			int payload_start = offset; +			int extra_data_length; +			read_ipmi_response(rsp, &offset); + +			lprintf(LOG_DEBUG+1, "<< IPMI Response Session Header"); +			lprintf(LOG_DEBUG+1, "<<   Authtype                : %s", +				val2str(rsp->session.authtype, ipmi_authtype_session_vals)); +			lprintf(LOG_DEBUG+1, "<<   Payload type            : %s", +				val2str(rsp->session.payloadtype, plus_payload_types_vals)); +			lprintf(LOG_DEBUG+1, "<<   Session ID              : 0x%08lx", +				(long)rsp->session.id); +			lprintf(LOG_DEBUG+1, "<<   Sequence                : 0x%08lx", +				(long)rsp->session.seq); +			lprintf(LOG_DEBUG+1, "<<   IPMI Msg/Payload Length : %d", +				rsp->session.msglen); +			lprintf(LOG_DEBUG+1, "<< IPMI Response Message Header"); +			lprintf(LOG_DEBUG+1, "<<   Rq Addr    : %02x", +				rsp->payload.ipmi_response.rq_addr); +			lprintf(LOG_DEBUG+1, "<<   NetFn      : %02x", +				rsp->payload.ipmi_response.netfn); +			lprintf(LOG_DEBUG+1, "<<   Rq LUN     : %01x", +				rsp->payload.ipmi_response.rq_lun); +			lprintf(LOG_DEBUG+1, "<<   Rs Addr    : %02x", +				rsp->payload.ipmi_response.rs_addr); +			lprintf(LOG_DEBUG+1, "<<   Rq Seq     : %02x", +				rsp->payload.ipmi_response.rq_seq); +			lprintf(LOG_DEBUG+1, "<<   Rs Lun     : %01x", +				rsp->payload.ipmi_response.rs_lun); +			lprintf(LOG_DEBUG+1, "<<   Command    : %02x", +				rsp->payload.ipmi_response.cmd); +			lprintf(LOG_DEBUG+1, "<<   Compl Code : 0x%02x", +				rsp->ccode); + +			/* Are we expecting this packet? */ +			entry = ipmi_req_lookup_entry(rsp->payload.ipmi_response.rq_seq, +								rsp->payload.ipmi_response.cmd); + +			if (entry != NULL) { +				lprintf(LOG_DEBUG+2, "IPMI Request Match found"); +				if ( intf->target_addr != intf->my_addr && +				     bridgePossible && +				     rsp->data_len && +				     rsp->payload.ipmi_response.cmd == 0x34 && +				     (rsp->payload.ipmi_response.netfn == 0x06 || +				     rsp->payload.ipmi_response.netfn == 0x07) && +				     rsp->payload.ipmi_response.rs_lun == 0 ) +				{ +					/* Check completion code */ +					if (rsp->data[offset-1] == 0) +					{ +						lprintf(LOG_DEBUG, "Bridged command answer," +						     " waiting for next answer... "); +						ipmi_req_remove_entry( +							rsp->payload.ipmi_response.rq_seq, +							rsp->payload.ipmi_response.cmd); +						return ipmi_lan_poll_recv(intf); +					} +					else +					{ +						lprintf(LOG_DEBUG, "WARNING: Bridged " +								   "cmd ccode = 0x%02x", +								   rsp->data[offset-1]); +					} + +					if (rsp->data_len && +					    rsp->payload.ipmi_response.cmd == 0x34) { +						memcpy(rsp->data, &rsp->data[offset], +							(rsp->data_len-offset)); +						if (verbose > 2) +							printbuf( &rsp->data[offset], +							(rsp->data_len-offset), +							"bridge command response"); +					} +				} + +				ipmi_req_remove_entry(rsp->payload.ipmi_response.rq_seq, +								rsp->payload.ipmi_response.cmd); +			} else { +				lprintf(LOG_INFO, "IPMI Request Match NOT FOUND"); +				rsp = ipmi_lan_recv_packet(intf); +				continue; +			} + +			/* +			 * Good packet.  Shift response data to start of array. +			 * rsp->data becomes the variable length IPMI response data +			 * rsp->data_len becomes the length of that data +			 */ +			extra_data_length = payload_size - (offset - payload_start) - 1; +			if (rsp != NULL && extra_data_length) +			{ +				rsp->data_len = extra_data_length; +				memmove(rsp->data, rsp->data + offset, extra_data_length); +			} +			else +				rsp->data_len = 0; + +			break; +		} + + +		/* +		 * Open Response +		 */ +		else if (rsp->session.payloadtype == +			 IPMI_PAYLOAD_TYPE_RMCP_OPEN_RESPONSE) +		{ +			if (session->v2_data.session_state != +				 LANPLUS_STATE_OPEN_SESSION_SENT) +			{ +				lprintf(LOG_ERR, "Error: Received an Unexpected Open Session " +					"Response"); +				rsp = ipmi_lan_recv_packet(intf); +				continue; +			} + +			read_open_session_response(rsp, offset); +			break; +		} + + +		/* +		 * RAKP 2 +		 */ +		else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_RAKP_2) +		{ +			if (session->v2_data.session_state != LANPLUS_STATE_RAKP_1_SENT) +			{ +				lprintf(LOG_ERR, "Error: Received an Unexpected RAKP 2 message"); +				rsp = ipmi_lan_recv_packet(intf); +				continue; +			} + +			read_rakp2_message(rsp, offset, session->v2_data.auth_alg); +			break; +		} + + +		/* +		 * RAKP 4 +		 */ +		else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_RAKP_4) +		{ +			if (session->v2_data.session_state != LANPLUS_STATE_RAKP_3_SENT) +			{ +				lprintf(LOG_ERR, "Error: Received an Unexpected RAKP 4 message"); +				rsp = ipmi_lan_recv_packet(intf); +				continue; +			} + +			read_rakp4_message(rsp, offset, session->v2_data.auth_alg); +			break; +		} + + +		/* +		 * SOL +		 */ +		else if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL) +		{ +			int payload_start = offset; +			int extra_data_length; + +			if (session->v2_data.session_state != LANPLUS_STATE_ACTIVE) +			{ +				lprintf(LOG_ERR, "Error: Received an Unexpected SOL packet"); +				rsp = ipmi_lan_recv_packet(intf); +				continue; +			} + +			read_sol_packet(rsp, &offset); +			extra_data_length = payload_size - (offset - payload_start); +			if (rsp && extra_data_length) +			{ +				rsp->data_len = extra_data_length; +				memmove(rsp->data, rsp->data + offset, extra_data_length); +			} +			else +				rsp->data_len = 0; + +			break; +		} + +		else +		{ +			lprintf(LOG_ERR, "Invalid RMCP+ payload type : 0x%x", +				rsp->session.payloadtype); +			assert(0); +		} +	} + +	return rsp; +} + + + +/* + * read_open_session_reponse + * + * Initialize the ipmi_rs from the IPMI 2.x open session response data. + * + * The offset should point to the first byte of the the Open Session Response + * payload when this function is called. + * + * param rsp    [in/out] reading from the data and writing to the open_session_response + *              section + * param offset [in] tells us where the Open Session Response payload starts + * + * returns 0 on success, 1 on error + */ +void +read_open_session_response(struct ipmi_rs * rsp, int offset) +{ +	memset(&rsp->payload.open_session_response, 0, +			 sizeof(rsp->payload.open_session_response)); + +	 /*  Message tag */ +	 rsp->payload.open_session_response.message_tag = rsp->data[offset]; + +	 /* RAKP reponse code */ +	 rsp->payload.open_session_response.rakp_return_code = rsp->data[offset + 1]; + +	 /* Maximum privilege level */ +	 rsp->payload.open_session_response.max_priv_level = rsp->data[offset + 2]; + +	 /*** offset + 3 is reserved ***/ + +	 /* Remote console session ID */ +	 memcpy(&(rsp->payload.open_session_response.console_id), +			rsp->data + offset + 4, +			4); +	 #if WORDS_BIGENDIAN +	 rsp->payload.open_session_response.console_id = +		 BSWAP_32(rsp->payload.open_session_response.console_id); +	 #endif + +	/* only tag, status, privlvl, and console id are returned if error */ +	 if (rsp->payload.open_session_response.rakp_return_code != +		  IPMI_RAKP_STATUS_NO_ERRORS) +		 return; + +	 /* BMC session ID */ +	 memcpy(&(rsp->payload.open_session_response.bmc_id), +			rsp->data + offset + 8, +			4); +	 #if WORDS_BIGENDIAN +	 rsp->payload.open_session_response.bmc_id = +		 BSWAP_32(rsp->payload.open_session_response.bmc_id); +	 #endif + +	 /* And of course, our negotiated algorithms */ +	 rsp->payload.open_session_response.auth_alg      = rsp->data[offset + 16]; +	 rsp->payload.open_session_response.integrity_alg = rsp->data[offset + 24]; +	 rsp->payload.open_session_response.crypt_alg     = rsp->data[offset + 32]; +} + + + +/* + * read_rakp2_message + * + * Initialize the ipmi_rs from the IPMI 2.x RAKP 2 message + * + * The offset should point the first byte of the the RAKP 2 payload when this + * function is called. + * + * param rsp [in/out] reading from the data variable and writing to the rakp 2 + *       section + * param offset [in] tells us where hte rakp2 payload starts + * param auth_alg [in] describes the authentication algorithm was agreed upon in + *       the open session request/response phase.  We need to know that here so + *       that we know how many bytes (if any) to read fromt the packet. + * + * returns 0 on success, 1 on error + */ +void +read_rakp2_message( +					struct ipmi_rs * rsp, +					int offset, +					uint8_t auth_alg) +{ +	 int i; + +	 /*  Message tag */ +	 rsp->payload.rakp2_message.message_tag = rsp->data[offset]; + +	 /* RAKP reponse code */ +	 rsp->payload.rakp2_message.rakp_return_code = rsp->data[offset + 1]; + +	 /* Console session ID */ +	 memcpy(&(rsp->payload.rakp2_message.console_id), +			rsp->data + offset + 4, +			4); +	 #if WORDS_BIGENDIAN +	 rsp->payload.rakp2_message.console_id = +		 BSWAP_32(rsp->payload.rakp2_message.console_id); +	 #endif + +	 /* BMC random number */ +	 memcpy(&(rsp->payload.rakp2_message.bmc_rand), +			rsp->data + offset + 8, +			16); +	 #if WORDS_BIGENDIAN +	 lanplus_swap(rsp->payload.rakp2_message.bmc_rand, 16); +	 #endif + +	 /* BMC GUID */ +	 memcpy(&(rsp->payload.rakp2_message.bmc_guid), +			rsp->data + offset + 24, +			16); +	 #if WORDS_BIGENDIAN +	 lanplus_swap(rsp->payload.rakp2_message.bmc_guid, 16); +	 #endif +	  +	 /* Key exchange authentication code */ +	 switch (auth_alg) +	 { +	 case  IPMI_AUTH_RAKP_NONE: +		 /* Nothing to do here */ +		 break; + +	 case IPMI_AUTH_RAKP_HMAC_SHA1: +		 /* We need to copy 20 bytes */ +		 for (i = 0; i < 20; ++i) +			 rsp->payload.rakp2_message.key_exchange_auth_code[i] = +				 rsp->data[offset + 40 + i]; +		 break; + +	 case IPMI_AUTH_RAKP_HMAC_MD5: +		lprintf(LOG_ERR, "read_rakp2_message: no support for " +				"IPMI_AUTH_RAKP_HMAC_MD5"); +		 assert(0); +		 break; +	 } +} + + + +/* + * read_rakp4_message + * + * Initialize the ipmi_rs from the IPMI 2.x RAKP 4 message + * + * The offset should point the first byte of the the RAKP 4 payload when this + * function is called. + * + * param rsp [in/out] reading from the data variable and writing to the rakp + *       4 section + * param offset [in] tells us where hte rakp4 payload starts + * param integrity_alg [in] describes the authentication algorithm was + *       agreed upon in the open session request/response phase.  We need + *       to know that here so that we know how many bytes (if any) to read + *       from the packet. + * + * returns 0 on success, 1 on error + */ +void +read_rakp4_message( +					struct ipmi_rs * rsp, +					int offset, +					uint8_t auth_alg) +{ +	 int i; + +	 /*  Message tag */ +	 rsp->payload.rakp4_message.message_tag = rsp->data[offset]; + +	 /* RAKP reponse code */ +	 rsp->payload.rakp4_message.rakp_return_code = rsp->data[offset + 1]; + +	 /* Console session ID */ +	 memcpy(&(rsp->payload.rakp4_message.console_id), +			rsp->data + offset + 4, +			4); +	 #if WORDS_BIGENDIAN +	 rsp->payload.rakp4_message.console_id = +		 BSWAP_32(rsp->payload.rakp4_message.console_id); +	 #endif + +	  +	 /* Integrity check value */ +	 switch (auth_alg) +	 { +	 case  IPMI_AUTH_RAKP_NONE: +		 /* Nothing to do here */ +		 break; + +	 case IPMI_AUTH_RAKP_HMAC_SHA1: +		 /* We need to copy 12 bytes */ +		 for (i = 0; i < 12; ++i) +			 rsp->payload.rakp4_message.integrity_check_value[i] = +				 rsp->data[offset + 8 + i]; +		 break; + +	 case IPMI_AUTH_RAKP_HMAC_MD5: +		 lprintf(LOG_ERR, "read_rakp4_message: no support " +			 "for authentication algorithm 0x%x", auth_alg); +		 assert(0); +		 break;	  +	 } +} + + + + +/* + * read_session_data + * + * Initialize the ipmi_rsp from the session data in the packet + * + * The offset should point the first byte of the the IPMI session when this + * function is called. + * + * param rsp     [in/out] we read from the data buffer and populate the session + *               specific fields. + * param offset  [in/out] should point to the beginning of the session when + *               this function is called.  The offset will be adjusted to + *               point to the end of the session when this function exits. + * param session holds our session state + */ +void +read_session_data( +				  struct ipmi_rs * rsp, +				  int * offset, +				  struct ipmi_session * s) +{ +	/* We expect to read different stuff depending on the authtype */ +	rsp->session.authtype = rsp->data[*offset]; + +	if (rsp->session.authtype == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) +		read_session_data_v2x(rsp, offset, s); +	else +		read_session_data_v15(rsp, offset, s); +} + + + +/* + * read_session_data_v2x + * + * Initialize the ipmi_rsp from the v2.x session header of the packet. + * + * The offset should point to the first byte of the the IPMI session when this + * function is called.  When this function exits, offset will point to the + * start of payload. + * + * Should decrypt and perform integrity checking here? + * + * param rsp    [in/out] we read from the data buffer and populate the session + *              specific fields. + * param offset [in/out] should point to the beginning of the session when this + *              function is called.  The offset will be adjusted to point to + *              the end of the session when this function exits. + *  param s      holds our session state + */ +void +read_session_data_v2x( +					  struct ipmi_rs      * rsp, +					  int                 * offset, +					  struct ipmi_session * s) +{ +	rsp->session.authtype = rsp->data[(*offset)++]; + +	rsp->session.bEncrypted     = (rsp->data[*offset] & 0x80 ? 1 : 0); +	rsp->session.bAuthenticated = (rsp->data[*offset] & 0x40 ? 1 : 0); + + +	/* Payload type */ +	rsp->session.payloadtype = rsp->data[(*offset)++] & 0x3F; + +	/* Session ID */ +	memcpy(&rsp->session.id, rsp->data + *offset, 4); +	*offset += 4; +	#if WORDS_BIGENDIAN +	rsp->session.id = BSWAP_32(rsp->session.id); +	#endif + + +	/* +	 * Verify that the session ID is what we think it should be +	 */ +	if ((s->v2_data.session_state == LANPLUS_STATE_ACTIVE) && +		(rsp->session.id != s->v2_data.console_id)) +	{ +		lprintf(LOG_ERR, "packet session id 0x%x does not " +			"match active session 0x%0x", +			rsp->session.id, s->v2_data.console_id); +		assert(0); +	} + + +	/* Ignored, so far */ +	memcpy(&rsp->session.seq, rsp->data + *offset, 4); +	*offset += 4; +	#if WORDS_BIGENDIAN +	rsp->session.seq = BSWAP_32(rsp->session.seq); +	#endif		 + +	memcpy(&rsp->session.msglen, rsp->data + *offset, 2); +	*offset += 2; +	#if WORDS_BIGENDIAN +	rsp->session.msglen = BSWAP_16(rsp->session.msglen); +	#endif +} + + + +/* + * read_session_data_v15 + * + * Initialize the ipmi_rsp from the session header of the packet.  + * + * The offset should point the first byte of the the IPMI session when this + * function is called.  When this function exits, the offset will point to + * the start of the IPMI message. + * + * param rsp    [in/out] we read from the data buffer and populate the session + *              specific fields. + * param offset [in/out] should point to the beginning of the session when this + *              function is called.  The offset will be adjusted to point to the + *              end of the session when this function exits. + * param s      holds our session state + */ +void read_session_data_v15( +							struct ipmi_rs * rsp, +							int * offset, +							struct ipmi_session * s) +{ +	/* All v15 messages are IPMI messages */ +	rsp->session.payloadtype = IPMI_PAYLOAD_TYPE_IPMI; + +	rsp->session.authtype = rsp->data[(*offset)++]; + +	/* All v15 messages that we will receive are unencrypted/unauthenticated */ +	rsp->session.bEncrypted     = 0; +	rsp->session.bAuthenticated = 0; + +	/* skip the session id and sequence number fields */ +	*offset += 8; + +	/* This is the size of the whole payload */ +	rsp->session.msglen = rsp->data[(*offset)++]; +} + + + +/* + * read_ipmi_response + * + * Initialize the ipmi_rs from with the IPMI response specific data + * + * The offset should point the first byte of the the IPMI payload when this + * function is called.  + * + * param rsp    [in/out] we read from the data buffer and populate the IPMI + *              specific fields. + * param offset [in/out] should point to the beginning of the IPMI payload when + *              this function is called. + */ +void read_ipmi_response(struct ipmi_rs * rsp, int * offset) +{ +	/* +	 * The data here should be decrypted by now. +	 */ +	rsp->payload.ipmi_response.rq_addr = rsp->data[(*offset)++]; +	rsp->payload.ipmi_response.netfn   = rsp->data[*offset] >> 2; +	rsp->payload.ipmi_response.rq_lun  = rsp->data[(*offset)++] & 0x3; +	(*offset)++;		/* checksum */ +	rsp->payload.ipmi_response.rs_addr = rsp->data[(*offset)++]; +	rsp->payload.ipmi_response.rq_seq  = rsp->data[*offset] >> 2; +	rsp->payload.ipmi_response.rs_lun  = rsp->data[(*offset)++] & 0x3; +	rsp->payload.ipmi_response.cmd     = rsp->data[(*offset)++];  +	rsp->ccode                         = rsp->data[(*offset)++]; + +} + + + +/* + * read_sol_packet + * + * Initialize the ipmi_rs with the SOL response data + * + * The offset should point the first byte of the the SOL payload when this + * function is called.  + * + * param rsp    [in/out] we read from the data buffer and populate the + *              SOL specific fields. + * param offset [in/out] should point to the beginning of the SOL payload + *              when this function is called. + */ +void read_sol_packet(struct ipmi_rs * rsp, int * offset) +{ + +	/* +	 * The data here should be decrypted by now. +	 */ +	rsp->payload.sol_packet.packet_sequence_number = +		rsp->data[(*offset)++] & 0x0F; + +	rsp->payload.sol_packet.acked_packet_number = +		rsp->data[(*offset)++] & 0x0F; + +	rsp->payload.sol_packet.accepted_character_count = +		rsp->data[(*offset)++]; + +	rsp->payload.sol_packet.is_nack = +		rsp->data[*offset] & 0x40; + +	rsp->payload.sol_packet.transfer_unavailable = +		rsp->data[*offset] & 0x20; + +	rsp->payload.sol_packet.sol_inactive = +		rsp->data[*offset] & 0x10; + +	rsp->payload.sol_packet.transmit_overrun = +		rsp->data[*offset] & 0x08; + +	rsp->payload.sol_packet.break_detected = +		rsp->data[(*offset)++] & 0x04; + +	lprintf(LOG_DEBUG, "<<<<<<<<<< RECV FROM BMC <<<<<<<<<<<"); +	lprintf(LOG_DEBUG, "< SOL sequence number     : 0x%02x", +		rsp->payload.sol_packet.packet_sequence_number); +	lprintf(LOG_DEBUG, "< SOL acked packet        : 0x%02x", +		rsp->payload.sol_packet.acked_packet_number); +	lprintf(LOG_DEBUG, "< SOL accepted char count : 0x%02x", +		rsp->payload.sol_packet.accepted_character_count); +	lprintf(LOG_DEBUG, "< SOL is nack             : %s", +		rsp->payload.sol_packet.is_nack? "true" : "false"); +	lprintf(LOG_DEBUG, "< SOL xfer unavailable    : %s", +		rsp->payload.sol_packet.transfer_unavailable? "true" : "false"); +	lprintf(LOG_DEBUG, "< SOL inactive            : %s", +		rsp->payload.sol_packet.sol_inactive? "true" : "false"); +	lprintf(LOG_DEBUG, "< SOL transmit overrun    : %s", +		rsp->payload.sol_packet.transmit_overrun? "true" : "false"); +	lprintf(LOG_DEBUG, "< SOL break detected      : %s", +		rsp->payload.sol_packet.break_detected? "true" : "false"); +	lprintf(LOG_DEBUG, "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"); + +	if (verbose >= 5) +		printbuf(rsp->data + *offset - 4, 4, "SOL MSG FROM BMC"); +} + + + +/* + * getIpmiPayloadWireRep + * + * param out [out] will contain our wire representation + * param req [in] is the IPMI request to be written + * param crypt_alg [in] specifies the encryption to use + * param rq_seq [in] is the IPMI command sequence number. + */ +void getIpmiPayloadWireRep( +							struct ipmi_intf       * intf,  /* in out */ +							struct ipmi_v2_payload * payload,  /* in  */ +							uint8_t  * msg, +							struct ipmi_rq * req, +							uint8_t    rq_seq, +							uint8_t curr_seq) +{ +	int cs, tmp, len; +	int cs2 = 0; +	int cs3 = 0; +	uint8_t ourAddress = intf->my_addr; +	uint8_t bridgedRequest = 0; + +	if (ourAddress == 0) +		ourAddress = IPMI_BMC_SLAVE_ADDR; + +	len = 0; + +	/* IPMI Message Header -- Figure 13-4 of the IPMI v2.0 spec */ +	if ((intf->target_addr == ourAddress) || (!bridgePossible)) { +		cs = len; +	} else { +		bridgedRequest = 1; + +		if(intf->transit_addr != ourAddress && intf->transit_addr != 0) +		{ +			bridgedRequest++; +		} +		/* bridged request: encapsulate w/in Send Message */ +		cs = len; +		msg[len++] = IPMI_BMC_SLAVE_ADDR; +		msg[len++] = IPMI_NETFN_APP << 2; +		tmp = len - cs; +		msg[len++] = ipmi_csum(msg+cs, tmp); +		cs2 = len; +		msg[len++] = IPMI_REMOTE_SWID; +		msg[len++] = curr_seq << 2; + +		 +		msg[len++] = 0x34;			/* Send Message rqst */ +		if(bridgedRequest == 2) +			msg[len++] = (0x40|intf->transit_channel); /* Track request*/ +		else +			msg[len++] = (0x40|intf->target_channel);    /* Track request*/ +			 +		payload->payload_length += 7; +		cs = len; +		 +		if(bridgedRequest == 2) +		{ +			/* bridged request: encapsulate w/in Send Message */ +			cs = len; +			msg[len++] = intf->transit_addr; +			msg[len++] = IPMI_NETFN_APP << 2; +			tmp = len - cs; +			msg[len++] = ipmi_csum(msg+cs, tmp); +			cs3 = len; +			msg[len++] = intf->my_addr; +			msg[len++] = curr_seq << 2; +			msg[len++] = 0x34;			/* Send Message rqst */ +	#if 0  /* From lan.c example */ +			entry->req.msg.target_cmd = entry->req.msg.cmd;	/* Save target command */ +			entry->req.msg.cmd = 0x34;		/* (fixup request entry) */ +	#endif +			msg[len++] = (0x40|intf->target_channel); /* Track request*/ + +			payload->payload_length += 7; + +			cs = len; +		} +	} + +        lprintf(LOG_DEBUG,"%s RqAddr %#x transit %#x:%#x target %#x:%#x " +		"bridgePossible %d", +		bridgedRequest ? "Bridging" : "Local", +		intf->my_addr, intf->transit_addr, intf->transit_channel, +		intf->target_addr, intf->target_channel, +		bridgePossible); + +	/* rsAddr */ +	msg[len++] = intf->target_addr; /* IPMI_BMC_SLAVE_ADDR; */ + +	/* net Fn */ +	msg[len++] = req->msg.netfn << 2 | (req->msg.lun & 3); +	tmp = len - cs; + +	/* checkSum */ +	msg[len++] = ipmi_csum(msg+cs, tmp); +	cs = len; + +	/* rqAddr */ +	if (!bridgedRequest) +		msg[len++] = IPMI_REMOTE_SWID; +	else  /* Bridged message */ +		msg[len++] = intf->my_addr; + +	/* rqSeq / rqLUN */ +	msg[len++] = rq_seq << 2; + +	/* cmd */ +	msg[len++] = req->msg.cmd; + +	/* message data */ +	if (req->msg.data_len) { +		memcpy(msg + len, req->msg.data, req->msg.data_len); +		len += req->msg.data_len; +	} + +	/* second checksum */ +	tmp = len - cs; +	msg[len++] = ipmi_csum(msg+cs, tmp); + +	/* Dual bridged request: 2nd checksum */ +	if (bridgedRequest == 2) { +		tmp = len - cs3; +		msg[len++] = ipmi_csum(msg+cs3, tmp); +		payload->payload_length += 1; +	} + +	/* bridged request: 2nd checksum */ +	if (bridgedRequest) { +		tmp = len - cs2; +		msg[len++] = ipmi_csum(msg+cs2, tmp); +		payload->payload_length += 1; +	} +} + + + +/* + * getSolPayloadWireRep + * + * param msg [out] will contain our wire representation + * param payload [in] holds the v2 payload with our SOL data + */ +void getSolPayloadWireRep( +						  struct ipmi_intf       * intf,  /* in out */ +						  uint8_t          * msg,     /* output */ +						  struct ipmi_v2_payload * payload) /* input */ +{ +	int i = 0; + +	lprintf(LOG_DEBUG, ">>>>>>>>>> SENDING TO BMC >>>>>>>>>>"); +	lprintf(LOG_DEBUG, "> SOL sequence number     : 0x%02x", +		payload->payload.sol_packet.packet_sequence_number); +	lprintf(LOG_DEBUG, "> SOL acked packet        : 0x%02x", +		payload->payload.sol_packet.acked_packet_number); +	lprintf(LOG_DEBUG, "> SOL accepted char count : 0x%02x", +		payload->payload.sol_packet.accepted_character_count); +	lprintf(LOG_DEBUG, "> SOL is nack             : %s", +		payload->payload.sol_packet.is_nack ? "true" : "false"); +	lprintf(LOG_DEBUG, "> SOL assert ring wor     : %s", +		payload->payload.sol_packet.assert_ring_wor ? "true" : "false"); +	lprintf(LOG_DEBUG, "> SOL generate break      : %s", +		payload->payload.sol_packet.generate_break ? "true" : "false"); +	lprintf(LOG_DEBUG, "> SOL deassert cts        : %s", +		payload->payload.sol_packet.deassert_cts ? "true" : "false"); +	lprintf(LOG_DEBUG, "> SOL deassert dcd dsr    : %s", +		payload->payload.sol_packet.deassert_dcd_dsr ? "true" : "false"); +	lprintf(LOG_DEBUG, "> SOL flush inbound       : %s", +		payload->payload.sol_packet.flush_inbound ? "true" : "false"); +	lprintf(LOG_DEBUG, "> SOL flush outbound      : %s", +		payload->payload.sol_packet.flush_outbound ? "true" : "false"); + +	msg[i++] = payload->payload.sol_packet.packet_sequence_number; +	msg[i++] = payload->payload.sol_packet.acked_packet_number; +	msg[i++] = payload->payload.sol_packet.accepted_character_count; + +	msg[i]    = payload->payload.sol_packet.is_nack           ? 0x40 : 0; +	msg[i]   |= payload->payload.sol_packet.assert_ring_wor   ? 0x20 : 0; +	msg[i]   |= payload->payload.sol_packet.generate_break    ? 0x10 : 0; +	msg[i]   |= payload->payload.sol_packet.deassert_cts      ? 0x08 : 0; +	msg[i]   |= payload->payload.sol_packet.deassert_dcd_dsr  ? 0x04 : 0; +	msg[i]   |= payload->payload.sol_packet.flush_inbound     ? 0x02 : 0; +	msg[i++] |= payload->payload.sol_packet.flush_outbound    ? 0x01 : 0; + +	/* We may have data to add */ +	memcpy(msg + i, +			payload->payload.sol_packet.data, +			payload->payload.sol_packet.character_count); + +	lprintf(LOG_DEBUG, "> SOL character count     : %d", +		payload->payload.sol_packet.character_count); +	lprintf(LOG_DEBUG, ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"); + +	if (verbose >= 5 && payload->payload.sol_packet.character_count) +		printbuf(payload->payload.sol_packet.data, payload->payload.sol_packet.character_count, "SOL SEND DATA"); + +	/* +	 * At this point, the payload length becomes the whole payload +	 * length, including the 4 bytes at the beginning of the SOL +	 * packet +	 */ +	payload->payload_length = payload->payload.sol_packet.character_count + 4; +} + + + +/* + * ipmi_lanplus_build_v2x_msg + * + * Encapsulates the payload data to create the IPMI v2.0 / RMCP+ packet. + *  + * + * IPMI v2.0 LAN Request Message Format + * +----------------------+ + * |  rmcp.ver            | 4 bytes + * |  rmcp.__reserved     | + * |  rmcp.seq            | + * |  rmcp.class          | + * +----------------------+ + * |  session.authtype    | 10 bytes + * |  session.payloadtype | + * |  session.id          | + * |  session.seq         | + * +----------------------+ + * |  message length      | 2 bytes + * +----------------------+ + * | Confidentiality Hdr  | var (possibly absent) + * +----------------------+ + * |  Paylod              | var Payload + * +----------------------+ + * | Confidentiality Trlr | var (possibly absent) + * +----------------------+ + * | Integrity pad        | var (possibly absent) + * +----------------------+ + * | Pad length           | 1 byte (WTF?) + * +----------------------+ + * | Next Header          | 1 byte (WTF?) + * +----------------------+ + * | Authcode             | var (possibly absent) + * +----------------------+ + */ +void +ipmi_lanplus_build_v2x_msg( +							struct ipmi_intf       * intf,     /* in  */ +							struct ipmi_v2_payload * payload,  /* in  */ +							int                    * msg_len,  /* out */ +							uint8_t         ** msg_data, /* out */ +							uint8_t curr_seq) +{ +	uint32_t session_trailer_length = 0; +	struct ipmi_session * session = intf->session; +	struct rmcp_hdr rmcp = { +		.ver		= RMCP_VERSION_1, +		.class		= RMCP_CLASS_IPMI, +		.seq		= 0xff, +	}; + +	/* msg will hold the entire message to be sent */ +	uint8_t * msg; +	int len = 0; + + +	len = +		sizeof(rmcp)                +  // RMCP Header (4) +		10                          +  // IPMI Session Header +		2                           +  // Message length +		payload->payload_length     +  // The actual payload +		IPMI_MAX_INTEGRITY_PAD_SIZE +  // Integrity Pad +		1                           +  // Pad Length +		1                           +  // Next Header +		IPMI_MAX_AUTH_CODE_SIZE;       // Authcode + + +	msg = malloc(len); +	if (msg == NULL) { +		lprintf(LOG_ERR, "ipmitool: malloc failure"); +		return; +	} +	memset(msg, 0, len); + +	/* +	 *------------------------------------------ +	 * RMCP HEADER +	 *------------------------------------------ +	 */ +	memcpy(msg, &rmcp, sizeof(rmcp)); +	len = sizeof(rmcp); + + +	/* +	 *------------------------------------------ +	 * IPMI SESSION HEADER +	 *------------------------------------------ +	 */ +	/* ipmi session Auth Type / Format is always 0x06 for IPMI v2 */ +	msg[IPMI_LANPLUS_OFFSET_AUTHTYPE] = 0x06; + +	/* Payload Type -- also specifies whether were authenticated/encyrpted */ +	msg[IPMI_LANPLUS_OFFSET_PAYLOAD_TYPE] = payload->payload_type; + +	if (session->v2_data.session_state == LANPLUS_STATE_ACTIVE) +	{ +		msg[IPMI_LANPLUS_OFFSET_PAYLOAD_TYPE] |= +			((session->v2_data.crypt_alg != IPMI_CRYPT_NONE	)? 0x80 : 0x00); +		msg[IPMI_LANPLUS_OFFSET_PAYLOAD_TYPE] |= +			((session->v2_data.integrity_alg  != IPMI_INTEGRITY_NONE)? 0x40 : 0x00); +	} + +	if (session->v2_data.session_state == LANPLUS_STATE_ACTIVE) +	{ +		/* Session ID  -- making it LSB */ +		msg[IPMI_LANPLUS_OFFSET_SESSION_ID    ] = session->v2_data.bmc_id         & 0xff; +		msg[IPMI_LANPLUS_OFFSET_SESSION_ID + 1] = (session->v2_data.bmc_id >> 8)  & 0xff; +		msg[IPMI_LANPLUS_OFFSET_SESSION_ID + 2] = (session->v2_data.bmc_id >> 16) & 0xff; +		msg[IPMI_LANPLUS_OFFSET_SESSION_ID + 3] = (session->v2_data.bmc_id >> 24) & 0xff; + +		/* Sequence Number -- making it LSB */ +		msg[IPMI_LANPLUS_OFFSET_SEQUENCE_NUM    ] = session->out_seq         & 0xff; +		msg[IPMI_LANPLUS_OFFSET_SEQUENCE_NUM + 1] = (session->out_seq >> 8)  & 0xff; +		msg[IPMI_LANPLUS_OFFSET_SEQUENCE_NUM + 2] = (session->out_seq >> 16) & 0xff; +		msg[IPMI_LANPLUS_OFFSET_SEQUENCE_NUM + 3] = (session->out_seq >> 24) & 0xff; +	} + +	/* +	 * Payload Length is set below (we don't know how big the payload is until after +	 * encryption). +	 */ + +	/* +	 * Payload +	 * +	 * At this point we are ready to slam the payload in. +	 * This includes: +	 * 1) The confidentiality header +	 * 2) The payload proper (possibly encrypted) +	 * 3) The confidentiality trailer +	 * +	 */ +	switch (payload->payload_type) +	{ +	case IPMI_PAYLOAD_TYPE_IPMI: +		getIpmiPayloadWireRep(intf, +							  payload,  /* in  */ +							  msg + IPMI_LANPLUS_OFFSET_PAYLOAD, +							  payload->payload.ipmi_request.request, +							  payload->payload.ipmi_request.rq_seq, +							  curr_seq); +		break; + +	case IPMI_PAYLOAD_TYPE_SOL:  +		getSolPayloadWireRep(intf, +							 msg + IPMI_LANPLUS_OFFSET_PAYLOAD, +							 payload); + +		if (verbose >= 5) +			printbuf(msg + IPMI_LANPLUS_OFFSET_PAYLOAD, 4, "SOL MSG TO BMC"); + +		len += payload->payload_length; + +		break; + +	case IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST: +		/* never encrypted, so our job is easy */ +		memcpy(msg + IPMI_LANPLUS_OFFSET_PAYLOAD, +				payload->payload.open_session_request.request, +				payload->payload_length); +		len += payload->payload_length; +		break; + +	case IPMI_PAYLOAD_TYPE_RAKP_1: +		/* never encrypted, so our job is easy */ +		memcpy(msg + IPMI_LANPLUS_OFFSET_PAYLOAD, +				payload->payload.rakp_1_message.message, +				payload->payload_length); +		len += payload->payload_length; +		break; + +	case IPMI_PAYLOAD_TYPE_RAKP_3: +		/* never encrypted, so our job is easy */ +		memcpy(msg + IPMI_LANPLUS_OFFSET_PAYLOAD, +				payload->payload.rakp_3_message.message, +				payload->payload_length); +		len += payload->payload_length; +		break; + +	default: +		lprintf(LOG_ERR, "unsupported payload type 0x%x", +			payload->payload_type); +		free(msg); +		msg = NULL; +		assert(0); +		break; +	} + + +	/* +	 *------------------------------------------ +	 * ENCRYPT THE PAYLOAD IF NECESSARY +	 *------------------------------------------ +	 */ +	if (session->v2_data.session_state == LANPLUS_STATE_ACTIVE) +	{ +		/* Payload len is adjusted as necessary by lanplus_encrypt_payload */ +		lanplus_encrypt_payload(session->v2_data.crypt_alg,        /* input  */ +								session->v2_data.k2,               /* input  */ +								msg + IPMI_LANPLUS_OFFSET_PAYLOAD, /* input  */ +								payload->payload_length,           /* input  */ +								msg + IPMI_LANPLUS_OFFSET_PAYLOAD, /* output */ +								&(payload->payload_length));       /* output */ + +	} + +	/* Now we know the payload length */ +	msg[IPMI_LANPLUS_OFFSET_PAYLOAD_SIZE    ] = +		payload->payload_length        & 0xff; +	msg[IPMI_LANPLUS_OFFSET_PAYLOAD_SIZE + 1] = +		(payload->payload_length >> 8) & 0xff; + + +	/* +	 *------------------------------------------ +	 * SESSION TRAILER +	 *------------------------------------------ +	 */ +	if ((session->v2_data.session_state == LANPLUS_STATE_ACTIVE) && +		(session->v2_data.integrity_alg != IPMI_INTEGRITY_NONE)) +	{ +		uint32_t i, hmac_length, integrity_pad_size = 0, hmac_input_size; +		uint8_t * hmac_output; +		uint32_t start_of_session_trailer = +			IPMI_LANPLUS_OFFSET_PAYLOAD + +			payload->payload_length; + + +		/* +		 * Determine the required integrity pad length.  We have to make the +		 * data range covered by the authcode a multiple of 4. +		 */ +		uint32_t length_before_authcode; + +		if (ipmi_oem_active(intf, "icts")) { +			length_before_authcode = +				12                          + /* the stuff before the payload */ +				payload->payload_length; +		} else { +			length_before_authcode = +				12                          + /* the stuff before the payload */ +				payload->payload_length     + +				1                           + /* pad length field  */ +				1;                            /* next header field */ +		} + +		if (length_before_authcode % 4) +			integrity_pad_size = 4 - (length_before_authcode % 4); + +		for (i = 0; i < integrity_pad_size; ++i) +			msg[start_of_session_trailer + i] = 0xFF; + +		/* Pad length */ +		msg[start_of_session_trailer + integrity_pad_size] = integrity_pad_size; + +		/* Next Header */ +		msg[start_of_session_trailer + integrity_pad_size + 1] = +			0x07; /* Hardcoded per the spec, table 13-8 */ + +		hmac_input_size = +			12                      + +			payload->payload_length + +			integrity_pad_size      + +			2; + +		hmac_output = +			msg                         + +			IPMI_LANPLUS_OFFSET_PAYLOAD + +			payload->payload_length     + +			integrity_pad_size          + +			2; + +		if (verbose > 2) +			printbuf(msg + IPMI_LANPLUS_OFFSET_AUTHTYPE, hmac_input_size, "authcode input"); + + +		/* Auth Code */ +		lanplus_HMAC(session->v2_data.integrity_alg, +					 session->v2_data.k1,                /* key        */ +					 20,                                 /* key length */ +					 msg + IPMI_LANPLUS_OFFSET_AUTHTYPE, /* hmac input */ +					 hmac_input_size, +					 hmac_output, +					 &hmac_length); + +		assert(hmac_length == 20); + +		if (verbose > 2) +			printbuf(hmac_output, 12, "authcode output"); + +		/* Set session_trailer_length appropriately */ +		session_trailer_length = +			integrity_pad_size + +			2                  + /* pad length + next header */ +			12;                  /* Size of the authcode (we only use the first 12 bytes) */ +	} + + +	++(session->out_seq); +	if (!session->out_seq) +		++(session->out_seq); + +	*msg_len = +		IPMI_LANPLUS_OFFSET_PAYLOAD + +		payload->payload_length     + +		session_trailer_length; +	*msg_data = msg; +} + + + +/* + * ipmi_lanplus_build_v2x_ipmi_cmd + * + * Wraps ipmi_lanplus_build_v2x_msg and returns a new entry object for the + * command + * + */ +static struct ipmi_rq_entry * +ipmi_lanplus_build_v2x_ipmi_cmd( +								struct ipmi_intf * intf, +								struct ipmi_rq * req, +								int isRetry) +{ +	struct ipmi_v2_payload v2_payload; +	struct ipmi_rq_entry * entry; + +	/* +	 * We have a problem.  we need to know the sequence number here, +	 * because we use it in our stored entry.  But we also need to +	 * know the sequence number when we generate our IPMI +	 * representation far below. +	 */ +	static uint8_t curr_seq = 0; + +	if( isRetry == 0 ) +		curr_seq += 1; + +	if (curr_seq >= 64) +		curr_seq = 0; + + +	/* IPMI Message Header -- Figure 13-4 of the IPMI v2.0 spec */ +	if ((intf->target_addr == intf->my_addr) || (!bridgePossible)) +   { +	entry = ipmi_req_add_entry(intf, req, curr_seq); +   } +   else /* it's a bridge command */ +   { +      unsigned char backup_cmd; + +      /* Add entry for cmd */ +   	entry = ipmi_req_add_entry(intf, req, curr_seq); + +      if(entry) +      { +         /* Add entry for bridge cmd */ +         backup_cmd = req->msg.cmd; +         req->msg.cmd = 0x34; +   	   entry = ipmi_req_add_entry(intf, req, curr_seq); +         req->msg.cmd = backup_cmd; +      } +   }    + +	if (entry == NULL) +		return NULL; + +	// Build our payload +	v2_payload.payload_type                 = IPMI_PAYLOAD_TYPE_IPMI; +	v2_payload.payload_length               = req->msg.data_len + 7; +	v2_payload.payload.ipmi_request.request = req; +	v2_payload.payload.ipmi_request.rq_seq  = curr_seq; + +	ipmi_lanplus_build_v2x_msg(intf,                // in +					&v2_payload,         // in +					&(entry->msg_len),   // out +					&(entry->msg_data),  // out +					curr_seq); 		// in + +	return entry; +} + + + + + +/* + * IPMI LAN Request Message Format + * +--------------------+ + * |  rmcp.ver          | 4 bytes + * |  rmcp.__reserved   | + * |  rmcp.seq          | + * |  rmcp.class        | + * +--------------------+ + * |  session.authtype  | 9 bytes + * |  session.seq       | + * |  session.id        | + * +--------------------+ + * | [session.authcode] | 16 bytes (AUTHTYPE != none) + * +--------------------+ + * |  message length    | 1 byte + * +--------------------+ + * |  message.rs_addr   | 6 bytes + * |  message.netfn_lun | + * |  message.checksum  | + * |  message.rq_addr   | + * |  message.rq_seq    | + * |  message.cmd       | + * +--------------------+ + * | [request data]     | data_len bytes + * +--------------------+ + * |  checksum          | 1 byte + * +--------------------+ + */ +static struct ipmi_rq_entry * +ipmi_lanplus_build_v15_ipmi_cmd( +								struct ipmi_intf * intf, +								struct ipmi_rq * req) +{ +	struct rmcp_hdr rmcp = { +		.ver		= RMCP_VERSION_1, +		.class		= RMCP_CLASS_IPMI, +		.seq		= 0xff, +	}; +	uint8_t * msg; +	int cs, mp, len = 0, tmp; +	struct ipmi_session  * session = intf->session; +	struct ipmi_rq_entry * entry; + +	entry = ipmi_req_add_entry(intf, req, 0); +	if (entry == NULL) +		return NULL; + +	len = req->msg.data_len + 21; + +	msg = malloc(len); +	if (msg == NULL) { +		lprintf(LOG_ERR, "ipmitool: malloc failure"); +		return NULL; +	} +	memset(msg, 0, len); + +	/* rmcp header */ +	memcpy(msg, &rmcp, sizeof(rmcp)); +	len = sizeof(rmcp); + +	/* +	 * ipmi session header +	 */ +	/* Authtype should always be none for 1.5 packets sent from this +	 * interface +	 */ +	msg[len++] = IPMI_SESSION_AUTHTYPE_NONE; + +	msg[len++] = session->out_seq & 0xff; +	msg[len++] = (session->out_seq >> 8) & 0xff; +	msg[len++] = (session->out_seq >> 16) & 0xff; +	msg[len++] = (session->out_seq >> 24) & 0xff; + +	/* +	 * The session ID should be all zeroes for pre-session commands.  We +	 * should only be using the 1.5 interface for the pre-session Get +	 * Channel Authentication Capabilities command +	 */ +	msg[len++] = 0; +	msg[len++] = 0; +	msg[len++] = 0; +	msg[len++] = 0; + +	/* message length */ +	msg[len++] = req->msg.data_len + 7; + +	/* ipmi message header */ +	cs = mp = len; +	msg[len++] = IPMI_BMC_SLAVE_ADDR; +	msg[len++] = req->msg.netfn << 2; +	tmp = len - cs; +	msg[len++] = ipmi_csum(msg+cs, tmp); +	cs = len; +	msg[len++] = IPMI_REMOTE_SWID; + +	entry->rq_seq = 0; + +	msg[len++] = entry->rq_seq << 2; +	msg[len++] = req->msg.cmd; + +	lprintf(LOG_DEBUG+1, ">> IPMI Request Session Header"); +	lprintf(LOG_DEBUG+1, ">>   Authtype   : %s", +		val2str(IPMI_SESSION_AUTHTYPE_NONE, ipmi_authtype_session_vals)); +	lprintf(LOG_DEBUG+1, ">>   Sequence   : 0x%08lx", +		(long)session->out_seq); +	lprintf(LOG_DEBUG+1, ">>   Session ID : 0x%08lx", +		(long)0); + +	lprintf(LOG_DEBUG+1, ">> IPMI Request Message Header"); +	lprintf(LOG_DEBUG+1, ">>   Rs Addr    : %02x", IPMI_BMC_SLAVE_ADDR); +	lprintf(LOG_DEBUG+1, ">>   NetFn      : %02x", req->msg.netfn); +	lprintf(LOG_DEBUG+1, ">>   Rs LUN     : %01x", 0); +	lprintf(LOG_DEBUG+1, ">>   Rq Addr    : %02x", IPMI_REMOTE_SWID); +	lprintf(LOG_DEBUG+1, ">>   Rq Seq     : %02x", entry->rq_seq); +	lprintf(LOG_DEBUG+1, ">>   Rq Lun     : %01x", 0); +	lprintf(LOG_DEBUG+1, ">>   Command    : %02x", req->msg.cmd); + +	/* message data */ +	if (req->msg.data_len) { +		memcpy(msg+len, req->msg.data, req->msg.data_len); +		len += req->msg.data_len; +	} + +	/* second checksum */ +	tmp = len - cs; +	msg[len++] = ipmi_csum(msg+cs, tmp); + +	entry->msg_len = len; +	entry->msg_data = msg; + +	return entry; +} + + + +/* + * is_sol_packet + */ +static int +is_sol_packet(struct ipmi_rs * rsp) +{ +	return (rsp                                                           && +			(rsp->session.authtype    == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) && +			(rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL)); +} + + + +/* + * sol_response_acks_packet + */ +static int +sol_response_acks_packet( +						 struct ipmi_rs         * rsp, +						 struct ipmi_v2_payload * payload) +{ +	return (is_sol_packet(rsp)                                            && +			payload                                                       && +			(payload->payload_type    == IPMI_PAYLOAD_TYPE_SOL)           &&  +			(rsp->payload.sol_packet.acked_packet_number == +			 payload->payload.sol_packet.packet_sequence_number)); +} + + + +/* + * ipmi_lanplus_send_payload + * + */ +struct ipmi_rs * +ipmi_lanplus_send_payload( +						  struct ipmi_intf * intf, +						  struct ipmi_v2_payload * payload) +{ +	struct ipmi_rs      * rsp = NULL; +	uint8_t             * msg_data; +	int                   msg_length; +	struct ipmi_session * session = intf->session; +	struct ipmi_rq_entry * entry = NULL; +	int                   try = 0; +	int                   xmit = 1; +	time_t                ltime; +	uint32_t	      saved_timeout; + +	if (!intf->opened && intf->open && intf->open(intf) < 0) +		return NULL; + +	/* +	 * The session timeout is initialized in the above interface open, +	 * so it will only be valid after the open completes. +	 */ +	saved_timeout = session->timeout; +	while (try < session->retry) { +		//ltime = time(NULL); + +		if (xmit) { +			ltime = time(NULL); + +			if (payload->payload_type == IPMI_PAYLOAD_TYPE_IPMI) +			{ +				/* +				 * Build an IPMI v1.5 or v2 command +				 */ +				struct ipmi_rq * ipmi_request = payload->payload.ipmi_request.request; + +				lprintf(LOG_DEBUG, ""); +				lprintf(LOG_DEBUG, ">> Sending IPMI command payload"); +				lprintf(LOG_DEBUG, ">>    netfn   : 0x%02x", ipmi_request->msg.netfn); +				lprintf(LOG_DEBUG, ">>    command : 0x%02x", ipmi_request->msg.cmd); + +				if (verbose > 1) +				{ +					uint16_t i; +					fprintf(stderr, ">>    data    : "); +					for (i = 0; i < ipmi_request->msg.data_len; ++i) +						fprintf(stderr, "0x%02x ", ipmi_request->msg.data[i]); +					fprintf(stderr, "\n\n"); +				} + + +				/* +				 * If we are presession, and the command is GET CHANNEL AUTHENTICATION +				 * CAPABILITIES, we will build the command in v1.5 format.  This is so +				 * that we can ask any server whether it supports IPMI v2 / RMCP+ +				 * before we attempt to open a v2.x session. +				 */ +				if ((ipmi_request->msg.netfn == IPMI_NETFN_APP) && +					 (ipmi_request->msg.cmd   == IPMI_GET_CHANNEL_AUTH_CAP) && +					 (session->v2_data.bmc_id  == 0)) // jme - check +				{ +					lprintf(LOG_DEBUG+1, "BUILDING A v1.5 COMMAND"); +					entry = ipmi_lanplus_build_v15_ipmi_cmd(intf, ipmi_request); +				} +				else +				{ +					int isRetry = ( try > 0 ? 1 : 0 ); + +					lprintf(LOG_DEBUG+1, "BUILDING A v2 COMMAND"); +					entry = ipmi_lanplus_build_v2x_ipmi_cmd(intf, ipmi_request, isRetry); +				} + +				if (entry == NULL) { +					lprintf(LOG_ERR, "Aborting send command, unable to build"); +					return NULL; +				} + +				msg_data   = entry->msg_data; +				msg_length = entry->msg_len; +			} + +			else if (payload->payload_type == IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST) +			{ +				lprintf(LOG_DEBUG, ">> SENDING AN OPEN SESSION REQUEST\n"); +				assert(session->v2_data.session_state == LANPLUS_STATE_PRESESSION +						|| session->v2_data.session_state == LANPLUS_STATE_OPEN_SESSION_SENT); + +				ipmi_lanplus_build_v2x_msg(intf,        /* in  */ +								payload,     /* in  */ +								&msg_length, /* out */ +								&msg_data,   /* out */ +								0);  /* irrelevant for this msg*/ + +			} + +			else if (payload->payload_type == IPMI_PAYLOAD_TYPE_RAKP_1) +			{ +				lprintf(LOG_DEBUG, ">> SENDING A RAKP 1 MESSAGE\n"); +				assert(session->v2_data.session_state == +						 LANPLUS_STATE_OPEN_SESSION_RECEIEVED); + +				ipmi_lanplus_build_v2x_msg(intf,        /* in  */ +								payload,     /* in  */ +								&msg_length, /* out */ +								&msg_data,   /* out */ +								0);  /* irrelevant for this msg*/ + +			} + +			else if (payload->payload_type == IPMI_PAYLOAD_TYPE_RAKP_3) +			{ +				lprintf(LOG_DEBUG, ">> SENDING A RAKP 3 MESSAGE\n"); +				assert(session->v2_data.session_state == +						 LANPLUS_STATE_RAKP_2_RECEIVED); + +				ipmi_lanplus_build_v2x_msg(intf,        /* in  */ +								payload,     /* in  */ +								&msg_length, /* out */ +								&msg_data,   /* out */ +								0);  /* irrelevant for this msg*/ + +			} + +			else if (payload->payload_type == IPMI_PAYLOAD_TYPE_SOL) +			{ +				lprintf(LOG_DEBUG, ">> SENDING A SOL MESSAGE\n"); +				assert(session->v2_data.session_state == LANPLUS_STATE_ACTIVE); + +				ipmi_lanplus_build_v2x_msg(intf,        /* in  */ +								payload,     /* in  */ +								&msg_length, /* out */ +								&msg_data,   /* out */ +								0);  /* irrelevant for this msg*/ +			} + +			else +			{ +				lprintf(LOG_ERR, "Payload type 0x%0x is unsupported!", +					payload->payload_type); +				assert(0); +			} + + +			if (ipmi_lan_send_packet(intf, msg_data, msg_length) < 0) { +				lprintf(LOG_ERR, "IPMI LAN send command failed"); +				return NULL; +			} +		} + +		/* if we are set to noanswer we do not expect response */ +		if (intf->noanswer) +			break; + +		usleep(100); 			/* Not sure what this is for */ + +		/* Remember our connection state */ +		switch (payload->payload_type) +		{ +		case IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST: +			session->v2_data.session_state = LANPLUS_STATE_OPEN_SESSION_SENT; +			/* not retryable for timeouts, force no retry */ +			try = session->retry; +			break; +		case IPMI_PAYLOAD_TYPE_RAKP_1: +			session->v2_data.session_state = LANPLUS_STATE_RAKP_1_SENT; +			/* not retryable for timeouts, force no retry */ +			try = session->retry; +			break; +		case IPMI_PAYLOAD_TYPE_RAKP_3: +			/* not retryable for timeouts, force no retry */ +			try = session->retry; +			session->v2_data.session_state = LANPLUS_STATE_RAKP_3_SENT; +			break; +		} + + +		/* +		 * Special case for SOL outbound packets. +		 */ +		if (payload->payload_type == IPMI_PAYLOAD_TYPE_SOL) +		{ +			if (! payload->payload.sol_packet.packet_sequence_number) +			{ +				/* We're just sending an ACK.  No need to retry. */ +				break; +			} + + +			rsp = ipmi_lanplus_recv_sol(intf); /* Grab the next packet */ + +			if (sol_response_acks_packet(rsp, payload)) +				break; + +			else if (is_sol_packet(rsp) && rsp->data_len) +			{ +				/* +				 * We're still waiting for our ACK, but we more data from +				 * the BMC +				 */ +				intf->session->sol_data.sol_input_handler(rsp); +				/* In order to avoid duplicate output, just set data_len to 0 */ +				rsp->data_len = 0; +			} +		} + + +		/* Non-SOL processing */ +		else +		{ +			rsp = ipmi_lan_poll_recv(intf); + +			/* Duplicate Request ccode most likely indicates a response to +			   a previous retry. Ignore and keep polling. */ +			while ((rsp != NULL) && (rsp->ccode == 0xcf)) +			{ +				rsp = NULL; +				rsp = ipmi_lan_poll_recv(intf); +			} + +			if (rsp) +				break; +			/* This payload type is retryable for timeouts. */ +			if ((payload->payload_type == IPMI_PAYLOAD_TYPE_IPMI) && entry) { +				ipmi_req_remove_entry( entry->rq_seq, entry->req.msg.cmd); +			} +		} + +		/* only timeout if time exceeds the timeout value */ +		xmit = ((time(NULL) - ltime) >= session->timeout); + +		usleep(5000); + +		if (xmit) { +			/* increment session timeout by 1 second each retry */ +			session->timeout++; +		} + +		try++; +	} +	session->timeout = saved_timeout; + +	/* IPMI messages are deleted under ipmi_lan_poll_recv() */ +	switch (payload->payload_type) { +	case IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST: +	case IPMI_PAYLOAD_TYPE_RAKP_1: +	case IPMI_PAYLOAD_TYPE_RAKP_3: +		free(msg_data); +		msg_data = NULL; +		break; +	} + +	return rsp; +} + + + +/* + * is_sol_partial_ack + * + * Determine if the response is a partial ACK/NACK that indicates + * we need to resend part of our packet. + * + * returns the number of characters we need to resend, or + *         0 if this isn't an ACK or we don't need to resend anything + */ +int is_sol_partial_ack( +						struct ipmi_intf       * intf, +						struct ipmi_v2_payload * v2_payload, +						struct ipmi_rs         * rs) +{ +	int chars_to_resend = 0; + +	if (v2_payload                               && +		rs                                       && +		is_sol_packet(rs)                        && +		sol_response_acks_packet(rs, v2_payload) && +		(rs->payload.sol_packet.accepted_character_count < +		 v2_payload->payload.sol_packet.character_count)) +	{ +		if (ipmi_oem_active(intf, "intelplus") && +			 rs->payload.sol_packet.accepted_character_count == 0) +			return 0; + +		chars_to_resend = +			v2_payload->payload.sol_packet.character_count - +			rs->payload.sol_packet.accepted_character_count; +	} + +	return chars_to_resend; +} + + + +/* + * set_sol_packet_sequence_number + */ +static void set_sol_packet_sequence_number( +											struct ipmi_intf * intf, +											struct ipmi_v2_payload * v2_payload) +{ +	/* Keep our sequence number sane */ +	if (intf->session->sol_data.sequence_number > 0x0F) +		intf->session->sol_data.sequence_number = 1; + +	v2_payload->payload.sol_packet.packet_sequence_number = +		intf->session->sol_data.sequence_number++; +} + + + +/* + * ipmi_lanplus_send_sol + * + * Sends a SOL packet..  We handle partial ACK/NACKs from the BMC here. + * + * Returns a pointer to the SOL ACK we received, or + *         0 on failure + *  + */ +struct ipmi_rs * +ipmi_lanplus_send_sol( +					  struct ipmi_intf * intf, +					  struct ipmi_v2_payload * v2_payload) +{ +	struct ipmi_rs * rs; + +	/* +	 * chars_to_resend indicates either that we got a NACK telling us +	 * that we need to resend some part of our data. +	 */ +	int chars_to_resend = 0; + +	v2_payload->payload_type   = IPMI_PAYLOAD_TYPE_SOL; + +	/* +	 * Payload length is just the length of the character +	 * data here. +	 */ +	v2_payload->payload_length = v2_payload->payload.sol_packet.character_count; + +	v2_payload->payload.sol_packet.acked_packet_number = 0; /* NA */ + +	set_sol_packet_sequence_number(intf, v2_payload); + +	v2_payload->payload.sol_packet.accepted_character_count = 0; /* NA */ + +	rs = ipmi_lanplus_send_payload(intf, v2_payload); + +	/* Determine if we need to resend some of our data */ +	chars_to_resend = is_sol_partial_ack(intf, v2_payload, rs); + +	while (rs && !rs->payload.sol_packet.transfer_unavailable && +			 !rs->payload.sol_packet.is_nack && +			 chars_to_resend) +	{ +		/* +		 * We first need to handle any new data we might have +		 * received in our NACK +		 */ +		if (rs->data_len) +			intf->session->sol_data.sol_input_handler(rs); + +		set_sol_packet_sequence_number(intf, v2_payload); + +		/* Just send the required data */ +		memmove(v2_payload->payload.sol_packet.data, +				v2_payload->payload.sol_packet.data + +				rs->payload.sol_packet.accepted_character_count, +				chars_to_resend); + +		v2_payload->payload.sol_packet.character_count = chars_to_resend; + +		v2_payload->payload_length = v2_payload->payload.sol_packet.character_count; + +		rs = ipmi_lanplus_send_payload(intf, v2_payload); + +		chars_to_resend = is_sol_partial_ack(intf, v2_payload, rs); +	} + +	return rs; +} + + + +/* + * check_sol_packet_for_new_data + * + * Determine whether the SOL packet has already been seen + * and whether the packet has new data for us. + * + * This function has the side effect of removing an previously + * seen data, and moving new data to the front. + * + * It also "Remembers" the data so we don't get repeats. + * + * returns the number of new bytes in the SOL packet + */ +static int +check_sol_packet_for_new_data( +							  struct ipmi_intf * intf, +							  struct ipmi_rs *rsp) +{ +	static uint8_t last_received_sequence_number = 0; +	static uint8_t last_received_byte_count      = 0; +	int new_data_size                                  = 0; + + +	if (rsp && +		(rsp->session.authtype    == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) && +		(rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL)) +	{ +		/* Store the data length before we mod it */ +		uint8_t unaltered_data_len = rsp->data_len; + +		if (rsp->payload.sol_packet.packet_sequence_number == +			last_received_sequence_number) +		{ + +			/* +			 * This is the same as the last packet, but may include +			 * extra data +			 */ +			new_data_size = rsp->data_len - last_received_byte_count; + +			if (new_data_size > 0) +			{ +				/* We have more data to process */ +				memmove(rsp->data, +						rsp->data + +						rsp->data_len - new_data_size, +						new_data_size); +			} + +			rsp->data_len = new_data_size; +		} + + +		/* +		 *Rember the data for next round +		 */ +		if (rsp->payload.sol_packet.packet_sequence_number) +		{ +			last_received_sequence_number = +				rsp->payload.sol_packet.packet_sequence_number; + +			last_received_byte_count = unaltered_data_len; +		} +	} + + +	return new_data_size; +} + + + +/* + * ack_sol_packet + * + * Provided the specified packet looks reasonable, ACK it. + */ +static void +ack_sol_packet( +				struct ipmi_intf * intf, +				struct ipmi_rs * rsp) +{ +	if (rsp                                                           && +		(rsp->session.authtype    == IPMI_SESSION_AUTHTYPE_RMCP_PLUS) && +		(rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL)           && +		(rsp->payload.sol_packet.packet_sequence_number)) +	{ +		struct ipmi_v2_payload ack; + +		bzero(&ack, sizeof(struct ipmi_v2_payload)); + +		ack.payload_type   = IPMI_PAYLOAD_TYPE_SOL; + +		/* +		 * Payload length is just the length of the character +		 * data here. +		 */ +		ack.payload_length = 0; + +		/* ACK packets have sequence numbers of 0 */ +		ack.payload.sol_packet.packet_sequence_number = 0; + +		ack.payload.sol_packet.acked_packet_number = +			rsp->payload.sol_packet.packet_sequence_number; + +		ack.payload.sol_packet.accepted_character_count = rsp->data_len; + +		ipmi_lanplus_send_payload(intf, &ack); +	} +} + + + +/* + * ipmi_lanplus_recv_sol + * + * Receive a SOL packet and send an ACK in response. + * + */ +struct ipmi_rs * +ipmi_lanplus_recv_sol(struct ipmi_intf * intf) +{ +	struct ipmi_rs * rsp = ipmi_lan_poll_recv(intf); + +	if (rsp && rsp->session.authtype != 0) +	{ +		ack_sol_packet(intf, rsp); + +		/* +		 * Remembers the data sent, and alters the data to just +		 * include the new stuff. +		 */ +		check_sol_packet_for_new_data(intf, rsp); +	} +	return rsp; +} + + + +/** + * ipmi_lanplus_send_ipmi_cmd + * + * Build a payload request and dispatch it. + */ +struct ipmi_rs * +ipmi_lanplus_send_ipmi_cmd( +							struct ipmi_intf * intf, +							struct ipmi_rq * req) +{ +	struct ipmi_v2_payload v2_payload; + +	v2_payload.payload_type = IPMI_PAYLOAD_TYPE_IPMI; +	v2_payload.payload.ipmi_request.request = req; + +	return ipmi_lanplus_send_payload(intf, &v2_payload); +} + + +/* + * ipmi_get_auth_capabilities_cmd + * + * This command may have to be sent twice.  We first ask for the + * authentication capabilities with the "request IPMI v2 data bit" + * set.  If this fails, we send the same command without that bit + * set. + * + * param intf is the initialized (but possibly) pre-session interface + *       on which we will send the command + * param auth_cap [out] will be initialized to hold the Get Channel + *       Authentication Capabilities return data on success.  Its + *       contents will be undefined on error. + *  + * returns 0 on success + *         non-zero if we were unable to contact the BMC, or we cannot + *         get a successful response + * + */ +static int +ipmi_get_auth_capabilities_cmd( +								struct ipmi_intf * intf, +								struct get_channel_auth_cap_rsp * auth_cap) +{ +	struct ipmi_rs * rsp; +	struct ipmi_rq req; +	uint8_t msg_data[2]; +	uint8_t backupBridgePossible; + +	backupBridgePossible = bridgePossible; + +	bridgePossible = 0; + +	msg_data[0] = IPMI_LAN_CHANNEL_E | 0x80; // Ask for IPMI v2 data as well +	msg_data[1] = intf->session->privlvl; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn    = IPMI_NETFN_APP;            // 0x06 +	req.msg.cmd      = IPMI_GET_CHANNEL_AUTH_CAP; // 0x38 +	req.msg.data     = msg_data; +	req.msg.data_len = 2; + +	rsp = intf->sendrecv(intf, &req); + +	if (rsp == NULL || rsp->ccode > 0) { +		/* +		 * It's very possible that this failed because we asked for IPMI +		 * v2 data. Ask again, without requesting IPMI v2 data. +		 */ +		msg_data[0] &= 0x7F; + +		rsp = intf->sendrecv(intf, &req); + +		if (rsp == NULL) { +			lprintf(LOG_INFO, "Get Auth Capabilities error"); +			return 1; +		} +		if (rsp->ccode > 0) { +			lprintf(LOG_INFO, "Get Auth Capabilities error: %s", +				val2str(rsp->ccode, completion_code_vals)); +			return 1; +		} +	} + + +	memcpy(auth_cap, +			rsp->data, +			sizeof(struct get_channel_auth_cap_rsp)); + +	bridgePossible = backupBridgePossible; + +	return 0; +} + + + +static int +ipmi_close_session_cmd(struct ipmi_intf * intf) +{ +	struct ipmi_rs * rsp; +	struct ipmi_rq req; +	uint8_t msg_data[4]; +	uint32_t bmc_session_lsbf; +	uint8_t backupBridgePossible; + +	if (intf->session->v2_data.session_state != LANPLUS_STATE_ACTIVE) +		return -1; + +	backupBridgePossible = bridgePossible; + +	intf->target_addr = IPMI_BMC_SLAVE_ADDR; +	bridgePossible = 0; + +	bmc_session_lsbf = intf->session->v2_data.bmc_id; +#if WORDS_BIGENDIAN +	bmc_session_lsbf = BSWAP_32(bmc_session_lsbf); +#endif + +	memcpy(&msg_data, &bmc_session_lsbf, 4); + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn		= IPMI_NETFN_APP; +	req.msg.cmd		    = 0x3c; +	req.msg.data		= msg_data; +	req.msg.data_len	= 4; + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		/* Looks like the session was closed */ +		lprintf(LOG_ERR, "Close Session command failed"); +		return -1; +	} +	if (verbose > 2) +		printbuf(rsp->data, rsp->data_len, "close_session"); + +	if (rsp->ccode == 0x87) { +		lprintf(LOG_ERR, "Failed to Close Session: invalid " +			"session ID %08lx", +			(long)intf->session->v2_data.bmc_id); +		return -1; +	} +	if (rsp->ccode > 0) { +		lprintf(LOG_ERR, "Close Session command failed: %s", +			val2str(rsp->ccode, completion_code_vals)); +		return -1; +	} + +	lprintf(LOG_DEBUG, "Closed Session %08lx\n", +		(long)intf->session->v2_data.bmc_id); + +	bridgePossible = backupBridgePossible; + +	return 0; +} + + + +/* + * ipmi_lanplus_open_session + * + * Build and send the open session command.  See section 13.17 of the IPMI + * v2 specification for details. + */ +static int +ipmi_lanplus_open_session(struct ipmi_intf * intf) +{ +	struct ipmi_v2_payload v2_payload; +	struct ipmi_session * session = intf->session; +	uint8_t * msg; +	struct ipmi_rs * rsp; +	/* 0 = success, 1 = error, 2 = timeout */ +	int rc = 0; + + +	/* +	 * Build an Open Session Request Payload +	 */ +	msg = (uint8_t*)malloc(IPMI_OPEN_SESSION_REQUEST_SIZE); +	if (msg == NULL) { +		lprintf(LOG_ERR, "ipmitool: malloc failure"); +		return 1; +	} + +	memset(msg, 0, IPMI_OPEN_SESSION_REQUEST_SIZE); + +	msg[0] = 0; /* Message tag */ +	if (ipmi_oem_active(intf, "intelplus") || session->privlvl != IPMI_SESSION_PRIV_ADMIN) +		msg[1] = session->privlvl; +	else +		msg[1] = 0; /* Give us highest privlg level based on supported algorithms */ +	msg[2] = 0; /* reserved */ +	msg[3] = 0; /* reserved */ + +	/* Choose our session ID for easy recognition in the packet dump */ +	session->v2_data.console_id = 0xA0A2A3A4; +	msg[4] = session->v2_data.console_id & 0xff; +	msg[5] = (session->v2_data.console_id >> 8)  & 0xff; +	msg[6] = (session->v2_data.console_id >> 16) & 0xff; +	msg[7] = (session->v2_data.console_id >> 24) & 0xff; + + +	if (lanplus_get_requested_ciphers(session->cipher_suite_id, +									  &(session->v2_data.requested_auth_alg), +									  &(session->v2_data.requested_integrity_alg), +									  &(session->v2_data.requested_crypt_alg))) +	{ +		lprintf(LOG_WARNING, "Unsupported cipher suite ID : %d\n", +				session->cipher_suite_id); +		free(msg); +		msg = NULL; +		return 1; +	} + + +	/* +	 * Authentication payload +	 */ +	msg[8]  = 0; /* specifies authentication payload */ +	msg[9]  = 0; /* reserved */ +	msg[10] = 0; /* reserved */ +	msg[11] = 8; /* payload length */ +	msg[12] = session->v2_data.requested_auth_alg; +	msg[13] = 0; /* reserved */	 +	msg[14] = 0; /* reserved */ +	msg[15] = 0; /* reserved */	 + +	/* +	 * Integrity payload +	 */ +	msg[16] = 1; /* specifies integrity payload */ +	msg[17] = 0; /* reserved */ +	msg[18] = 0; /* reserved */ +	msg[19] = 8; /* payload length */ +	msg[20] = session->v2_data.requested_integrity_alg; +	msg[21] = 0; /* reserved */	 +	msg[22] = 0; /* reserved */ +	msg[23] = 0; /* reserved */ + +	/* +	 * Confidentiality/Encryption payload +	 */ +	msg[24] = 2; /* specifies confidentiality payload */ +	msg[25] = 0; /* reserved */ +	msg[26] = 0; /* reserved */ +	msg[27] = 8; /* payload length */ +	msg[28] = session->v2_data.requested_crypt_alg; +	msg[29] = 0; /* reserved */	 +	msg[30] = 0; /* reserved */ +	msg[31] = 0; /* reserved */ + + +	v2_payload.payload_type   = IPMI_PAYLOAD_TYPE_RMCP_OPEN_REQUEST; +	v2_payload.payload_length = IPMI_OPEN_SESSION_REQUEST_SIZE; +	v2_payload.payload.open_session_request.request = msg; + +	rsp = ipmi_lanplus_send_payload(intf, &v2_payload); + +	free(msg); +	msg = NULL; +	if (rsp == NULL ) { +		lprintf(LOG_DEBUG, "Timeout in open session response message."); +		return 2; +	} +	if (verbose) +		lanplus_dump_open_session_response(rsp); + +	if (rsp->payload.open_session_response.rakp_return_code != +		IPMI_RAKP_STATUS_NO_ERRORS) +	{ +		lprintf(LOG_WARNING, "Error in open session response message : %s\n", +			val2str(rsp->payload.open_session_response.rakp_return_code, +				ipmi_rakp_return_codes)); +		return 1; +	} +	else +	{ +		if (rsp->payload.open_session_response.console_id != +			 session->v2_data.console_id) { +			lprintf(LOG_WARNING, "Warning: Console session ID is not " +				"what we requested"); +		} + +		session->v2_data.max_priv_level = +			rsp->payload.open_session_response.max_priv_level; +		session->v2_data.bmc_id         = +			rsp->payload.open_session_response.bmc_id; +		session->v2_data.auth_alg       = +			rsp->payload.open_session_response.auth_alg; +		session->v2_data.integrity_alg  = +			rsp->payload.open_session_response.integrity_alg; +		session->v2_data.crypt_alg      = +			rsp->payload.open_session_response.crypt_alg; +		session->v2_data.session_state  = +			LANPLUS_STATE_OPEN_SESSION_RECEIEVED; + + +		/* +		 * Verify that we have agreed on a cipher suite +		 */ +		if (rsp->payload.open_session_response.auth_alg != +			session->v2_data.requested_auth_alg) +		{ +			lprintf(LOG_WARNING, "Authentication algorithm 0x%02x is " +					"not what we requested 0x%02x\n", +					rsp->payload.open_session_response.auth_alg, +					session->v2_data.requested_auth_alg); +			rc = 1; +		} +		else if (rsp->payload.open_session_response.integrity_alg != +				 session->v2_data.requested_integrity_alg) +		{ +			lprintf(LOG_WARNING, "Integrity algorithm 0x%02x is " +					"not what we requested 0x%02x\n", +					rsp->payload.open_session_response.integrity_alg, +					session->v2_data.requested_integrity_alg); +			rc = 1; +		} +		else if (rsp->payload.open_session_response.crypt_alg != +				 session->v2_data.requested_crypt_alg) +		{ +			lprintf(LOG_WARNING, "Encryption algorithm 0x%02x is " +					"not what we requested 0x%02x\n", +					rsp->payload.open_session_response.crypt_alg, +					session->v2_data.requested_crypt_alg); +			rc = 1; +		} + +	} + +	return rc; +} + + + +/* + * ipmi_lanplus_rakp1 + * + * Build and send the RAKP 1 message as part of the IPMI v2 / RMCP+ session + * negotiation protocol.  We also read and validate the RAKP 2 message received + * from the BMC, here.  See section 13.20 of the IPMI v2 specification for + * details. + * + * returns 0 on success + *         1 on failure + * + * Note that failure is only indicated if we have an internal error of + * some kind. If we actually get a RAKP 2 message in response to our + * RAKP 1 message, any errors will be stored in + * session->v2_data.rakp2_return_code and sent to the BMC in the RAKP + * 3 message. + */ +static int +ipmi_lanplus_rakp1(struct ipmi_intf * intf) +{ +	struct ipmi_v2_payload v2_payload; +	struct ipmi_session * session = intf->session; +	uint8_t * msg; +	struct ipmi_rs * rsp; +	int rc = 0;    /* 0 = success, 1 = error, 2 = timeout */ + +	/* +	 * Build a RAKP 1 message +	 */ +	msg = (uint8_t*)malloc(IPMI_RAKP1_MESSAGE_SIZE); +	if (msg == NULL) { +		lprintf(LOG_ERR, "ipmitool: malloc failure"); +		return 1; +	} +	memset(msg, 0, IPMI_RAKP1_MESSAGE_SIZE); + + +	msg[0] = 0; /* Message tag */ + +	msg[1] = 0; /* reserved */ +	msg[2] = 0; /* reserved */ +	msg[3] = 0; /* reserved */ + +	/* BMC session ID */ +	msg[4] = session->v2_data.bmc_id & 0xff; +	msg[5] = (session->v2_data.bmc_id >> 8)  & 0xff; +	msg[6] = (session->v2_data.bmc_id >> 16) & 0xff; +	msg[7] = (session->v2_data.bmc_id >> 24) & 0xff; + + +	/* We need a 16 byte random number */ +	if (lanplus_rand(session->v2_data.console_rand, 16)) +	{ +		// ERROR; +		lprintf(LOG_ERR, "ERROR generating random number " +			"in ipmi_lanplus_rakp1"); +		free(msg); +		msg = NULL; +		return 1; +	} +	memcpy(msg + 8, session->v2_data.console_rand, 16); +	#if WORDS_BIGENDIAN +	lanplus_swap(msg + 8, 16); +	#endif + +	if (verbose > 1) +		printbuf(session->v2_data.console_rand, 16, +				 ">> Console generated random number"); + + +	/* +	 * Requested maximum privilege level. +	 */ +	msg[24] = session->privlvl | session->v2_data.lookupbit; +	session->v2_data.requested_role = msg[24]; +	msg[25] = 0; /* reserved */ +	msg[26] = 0; /* reserved */ + + +	/* Username specification */ +	msg[27] = strlen((const char *)session->username); +	if (msg[27] > IPMI_MAX_USER_NAME_LENGTH) +	{ +		lprintf(LOG_ERR, "ERROR: user name too long.  " +			"(Exceeds %d characters)", +			IPMI_MAX_USER_NAME_LENGTH); +		free(msg); +		msg = NULL; +		return 1; +	} +	memcpy(msg + 28, session->username, msg[27]); + +	v2_payload.payload_type                   = IPMI_PAYLOAD_TYPE_RAKP_1; +	v2_payload.payload_length                 = +		IPMI_RAKP1_MESSAGE_SIZE - (16 - msg[27]); +	v2_payload.payload.rakp_1_message.message = msg; + +	rsp = ipmi_lanplus_send_payload(intf, &v2_payload); + +	free(msg); +	msg = NULL; + +	if (rsp == NULL) +	{ +		lprintf(LOG_WARNING, "> Error: no response from RAKP 1 message"); +		return 2; +	} + +	session->v2_data.session_state = LANPLUS_STATE_RAKP_2_RECEIVED; + +	if (verbose) +		lanplus_dump_rakp2_message(rsp, session->v2_data.auth_alg); + + + +	if (rsp->payload.rakp2_message.rakp_return_code != IPMI_RAKP_STATUS_NO_ERRORS) +	{ +		lprintf(LOG_INFO, "RAKP 2 message indicates an error : %s", +			val2str(rsp->payload.rakp2_message.rakp_return_code, +				ipmi_rakp_return_codes)); +		rc = 1; +	} + +	else +	{ +		memcpy(session->v2_data.bmc_rand, rsp->payload.rakp2_message.bmc_rand, 16); +		memcpy(session->v2_data.bmc_guid, rsp->payload.rakp2_message.bmc_guid, 16); + +		if (verbose > 2) +			printbuf(session->v2_data.bmc_rand, 16, "bmc_rand"); + +		/* +		 * It is at this point that we have to decode the random number and determine +		 * whether the BMC has authenticated. +		 */ +		if (! lanplus_rakp2_hmac_matches(session, +										 rsp->payload.rakp2_message.key_exchange_auth_code,  +										 intf)) +		{ +			/* Error */ +			lprintf(LOG_INFO, "> RAKP 2 HMAC is invalid"); +			session->v2_data.rakp2_return_code = IPMI_RAKP_STATUS_INVALID_INTEGRITY_CHECK_VALUE; +								rc = 1; +		} +		else +		{ +			/* Success */ +			session->v2_data.rakp2_return_code = IPMI_RAKP_STATUS_NO_ERRORS; +		} +	} + +	return rc; +} + + + +/* + * ipmi_lanplus_rakp3 + * + * Build and send the RAKP 3 message as part of the IPMI v2 / RMCP+ session + * negotiation protocol.  We also read and validate the RAKP 4 message received + * from the BMC, here.  See section 13.20 of the IPMI v2 specification for + * details. + * + * If the RAKP 2 return code is not IPMI_RAKP_STATUS_NO_ERRORS, we will + * exit with an error code immediately after sendint the RAKP 3 message. + * + * param intf is the intf that holds all the state we are concerned with + * + * returns 0 on success + *         1 on failure + */ +static int +ipmi_lanplus_rakp3(struct ipmi_intf * intf) +{ +	struct ipmi_v2_payload v2_payload; +	struct ipmi_session * session = intf->session; +	uint8_t * msg; +	struct ipmi_rs * rsp; + +	assert(session->v2_data.session_state == LANPLUS_STATE_RAKP_2_RECEIVED); +	 +	/* +	 * Build a RAKP 3 message +	 */ +	msg = (uint8_t*)malloc(IPMI_RAKP3_MESSAGE_MAX_SIZE); +	if (msg == NULL) { +		lprintf(LOG_ERR, "ipmitool: malloc failure"); +		return 1; +	} +	memset(msg, 0, IPMI_RAKP3_MESSAGE_MAX_SIZE); + + +	msg[0] = 0; /* Message tag */ +	msg[1] = session->v2_data.rakp2_return_code; +	 +	msg[2] = 0; /* reserved */ +	msg[3] = 0; /* reserved */ + +	/* BMC session ID */ +	msg[4] = session->v2_data.bmc_id & 0xff; +	msg[5] = (session->v2_data.bmc_id >> 8)  & 0xff; +	msg[6] = (session->v2_data.bmc_id >> 16) & 0xff; +	msg[7] = (session->v2_data.bmc_id >> 24) & 0xff; + +	v2_payload.payload_type                   = IPMI_PAYLOAD_TYPE_RAKP_3; +	v2_payload.payload_length                 = 8; +	v2_payload.payload.rakp_3_message.message = msg; + +	/* +	 * If the rakp2 return code indicates and error, we don't have to +	 * generate an authcode or session integrity key.  In that case, we +	 * are simply sending a RAKP 3 message to indicate to the BMC that the +	 * RAKP 2 message caused an error. +	 */ +	if (session->v2_data.rakp2_return_code == IPMI_RAKP_STATUS_NO_ERRORS) +	{ +		uint32_t auth_length; +		 +		if (lanplus_generate_rakp3_authcode(msg + 8, session, &auth_length, intf)) +		{ +			/* Error */ +			lprintf(LOG_INFO, "> Error generating RAKP 3 authcode"); +			free(msg); +			msg = NULL; +			return 1; +		} +		else +		{ +			/* Success */ +			v2_payload.payload_length += auth_length; +		} + +		/* Generate our Session Integrity Key, K1, and K2 */ +		if (lanplus_generate_sik(session, intf)) +		{ +			/* Error */ +			lprintf(LOG_INFO, "> Error generating session integrity key"); +			free(msg); +			msg = NULL; +			return 1; +		} +		else if (lanplus_generate_k1(session)) +		{ +			/* Error */ +			lprintf(LOG_INFO, "> Error generating K1 key"); +			free(msg); +			msg = NULL; +			return 1; +		} +		else if (lanplus_generate_k2(session)) +		{ +			/* Error */ +			lprintf(LOG_INFO, "> Error generating K1 key"); +			free(msg); +			msg = NULL; +			return 1; +		} +	} +	 + +	rsp = ipmi_lanplus_send_payload(intf, &v2_payload); + +	free(msg); +	msg = NULL; + +	if (session->v2_data.rakp2_return_code != IPMI_RAKP_STATUS_NO_ERRORS) +	{ +		/* +		 * If the previous RAKP 2 message received was deemed erroneous, +		 * we have nothing else to do here.  We only sent the RAKP 3 message +		 * to indicate to the BMC that the RAKP 2 message failed. +		 */ +		return 1; +	} +	else if (rsp == NULL) +	{ +		lprintf(LOG_WARNING, "> Error: no response from RAKP 3 message"); +		return 2; +	} + + +	/* +	 * We have a RAKP 4 message to chew on. +	 */ +	if (verbose) +		lanplus_dump_rakp4_message(rsp, session->v2_data.auth_alg); +	 + +	if (rsp->payload.open_session_response.rakp_return_code != IPMI_RAKP_STATUS_NO_ERRORS) +	{ +		lprintf(LOG_INFO, "RAKP 4 message indicates an error : %s", +			val2str(rsp->payload.rakp4_message.rakp_return_code, +				ipmi_rakp_return_codes)); +		return 1; +	} + +	else +	{ +		/* Validate the authcode */ +		if (lanplus_rakp4_hmac_matches(session, +										rsp->payload.rakp4_message.integrity_check_value, +										intf)) +		{ +			/* Success */ +			session->v2_data.session_state = LANPLUS_STATE_ACTIVE; +		} +		else +		{ +			/* Error */ +			lprintf(LOG_INFO, "> RAKP 4 message has invalid integrity check value"); +			return 1; +		} +	} + +	intf->abort = 0; +	return 0; +} + + + +/** + * ipmi_lan_close + */ +void +ipmi_lanplus_close(struct ipmi_intf * intf) +{ +	if (!intf->abort) +		ipmi_close_session_cmd(intf); + +	if (intf->fd >= 0) +		close(intf->fd); + +	ipmi_req_clear_entries(); + +	if (intf->session) { +		free(intf->session); +		intf->session = NULL; +	} + +	intf->session = NULL; +	intf->opened = 0; +	intf->manufacturer_id = IPMI_OEM_UNKNOWN; +	intf = NULL; +} + + + +static int +ipmi_set_session_privlvl_cmd(struct ipmi_intf * intf) +{ +	struct ipmi_rs * rsp; +	struct ipmi_rq req; +	uint8_t backupBridgePossible; +	uint8_t privlvl = intf->session->privlvl; + +	if (privlvl <= IPMI_SESSION_PRIV_USER) +		return 0;	/* no need to set higher */ + +	backupBridgePossible = bridgePossible; + +	bridgePossible = 0; + +	memset(&req, 0, sizeof(req)); +	req.msg.netfn		= IPMI_NETFN_APP; +	req.msg.cmd		= 0x3b; +	req.msg.data		= &privlvl; +	req.msg.data_len	= 1; + +	rsp = intf->sendrecv(intf, &req); +	if (rsp == NULL) { +		lprintf(LOG_ERR, "Set Session Privilege Level to %s failed", +			val2str(privlvl, ipmi_privlvl_vals)); +		bridgePossible = backupBridgePossible; +		return -1; +	} +	if (verbose > 2) +		printbuf(rsp->data, rsp->data_len, "set_session_privlvl"); + +	if (rsp->ccode > 0) { +		lprintf(LOG_ERR, "Set Session Privilege Level to %s failed: %s", +			val2str(privlvl, ipmi_privlvl_vals), +			val2str(rsp->ccode, completion_code_vals)); +		bridgePossible = backupBridgePossible; +		return -1; +	} + +	lprintf(LOG_DEBUG, "Set Session Privilege Level to %s\n", +		val2str(rsp->data[0], ipmi_privlvl_vals)); + +	bridgePossible = backupBridgePossible; + +	return 0; +} + +/** + * ipmi_lanplus_open + */ +int +ipmi_lanplus_open(struct ipmi_intf * intf) +{ +	int rc; +	int retry; +	struct get_channel_auth_cap_rsp auth_cap; +	struct ipmi_session *session; + +	if (!intf || !intf->session) +		return -1; +	session = intf->session; + + +	if (!session->port) +		session->port = IPMI_LANPLUS_PORT; +	if (!session->privlvl) +		session->privlvl = IPMI_SESSION_PRIV_ADMIN; +	if (!session->timeout) +		session->timeout = IPMI_LAN_TIMEOUT; +	if (!session->retry) +		session->retry = IPMI_LAN_RETRY; + +	if (session->hostname == NULL || strlen((const char *)session->hostname) == 0) { +		lprintf(LOG_ERR, "No hostname specified!"); +		return -1; +	} + +	intf->abort = 1; + + +	/* Setup our lanplus session state */ +	session->v2_data.auth_alg         = IPMI_AUTH_RAKP_NONE; +	session->v2_data.crypt_alg        = IPMI_CRYPT_NONE; +	session->v2_data.console_id       = 0x00; +	session->v2_data.bmc_id           = 0x00; +	session->sol_data.sequence_number = 1; +	//session->sol_data.last_received_sequence_number = 0; +	//session->sol_data.last_received_byte_count      = 0; +	memset(session->v2_data.sik, 0, IPMI_SIK_BUFFER_SIZE); + +	/* Kg is set in ipmi_intf */ +	//memset(session->v2_data.kg,  0, IPMI_KG_BUFFER_SIZE); + +	if (ipmi_intf_socket_connect (intf) == -1) { +		lprintf(LOG_ERR, "Could not open socket!"); +		return -1; +	} + +	if (intf->fd < 0) { +		lperror(LOG_ERR, "Connect to %s failed", +			session->hostname); +		intf->close(intf); +		return -1; +	} + +	intf->opened = 1; + +	/* +	 * +	 * Make sure the BMC supports IPMI v2 / RMCP+ +	 */ +	if (!ipmi_oem_active(intf, "i82571spt") && +			ipmi_get_auth_capabilities_cmd(intf, &auth_cap)) { +		lprintf(LOG_INFO, "Error issuing Get Channel " +			"Authentication Capabilies request"); +		goto fail; +	} + +	if (!ipmi_oem_active(intf, "i82571spt") && ! auth_cap.v20_data_available) +	{ +		lprintf(LOG_INFO, "This BMC does not support IPMI v2 / RMCP+"); +		goto fail; +	} + +	/* +	 * If the open/rakp1/rakp3 sequence encounters a timeout, the whole sequence +	 * needs to restart. The individual messages are not individually retryable, +	 * as the session state is advancing. +	 */ +	for (retry = 0; retry < IPMI_LAN_RETRY; retry++) { +		session->v2_data.session_state = LANPLUS_STATE_PRESESSION; +		/* +		 * Open session +		 */ +		if ((rc = ipmi_lanplus_open_session(intf)) == 1) { +			intf->close(intf); +			goto fail; +		} +		if (rc == 2) { +			lprintf(LOG_DEBUG, "Retry lanplus open session, %d", retry); +			continue; +		} +		/* +		 * RAKP 1 +		 */ +		if ((rc = ipmi_lanplus_rakp1(intf)) == 1) { +			intf->close(intf); +			goto fail; +		} +		if (rc == 2) { +			lprintf(LOG_DEBUG, "Retry lanplus rakp1, %d", retry); +			continue; +		} +		/* +		 * RAKP 3 +		 */ +		if ((rc = ipmi_lanplus_rakp3(intf)) == 1) { +			intf->close(intf); +			goto fail; +		} +		if (rc == 0) break; +		lprintf(LOG_DEBUG,"Retry lanplus rakp3, %d", retry); +	} + +	lprintf(LOG_DEBUG, "IPMIv2 / RMCP+ SESSION OPENED SUCCESSFULLY\n"); + +	if (!ipmi_oem_active(intf, "i82571spt")) { +		rc = ipmi_set_session_privlvl_cmd(intf); +		if (rc < 0) { +			intf->close(intf); +			goto fail; +		} +	} +	intf->manufacturer_id = ipmi_get_oem(intf); +	bridgePossible = 1; + +	/* automatically detect interface request and response sizes */ +	hpm2_detect_max_payload_size(intf); + +	return intf->fd; + + fail: +	lprintf(LOG_ERR, "Error: Unable to establish IPMI v2 / RMCP+ session"); +	intf->opened = 0; +	return -1; +} + + + +void test_crypt1(void) +{ +	uint8_t key[]  = +		{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, +		 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14}; + +	uint16_t  bytes_encrypted; +	uint16_t  bytes_decrypted; +	uint8_t   decrypt_buffer[1000]; +	uint8_t   encrypt_buffer[1000]; + +	uint8_t data[] = +		{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, +		 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, +		 0x11, 0x12}; + +	printbuf(data, sizeof(data), "original data"); + +	if (lanplus_encrypt_payload(IPMI_CRYPT_AES_CBC_128, +								key, +								data, +								sizeof(data), +								encrypt_buffer, +								&bytes_encrypted)) +	{ +		lprintf(LOG_ERR, "Encrypt test failed"); +		assert(0); +	} +	printbuf(encrypt_buffer, bytes_encrypted, "encrypted payload"); +	 + +	if (lanplus_decrypt_payload(IPMI_CRYPT_AES_CBC_128, +								key, +								encrypt_buffer, +								bytes_encrypted, +								decrypt_buffer, +								&bytes_decrypted)) +	{ +		lprintf(LOG_ERR, "Decrypt test failed\n"); +		assert(0); +	}	 +	printbuf(decrypt_buffer, bytes_decrypted, "decrypted payload"); +	 +	lprintf(LOG_DEBUG, "\nDone testing the encrypt/decyrpt methods!\n"); +	exit(0); +}	 + + + +void test_crypt2(void) +{ +	uint8_t key[]  = +		{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, +		 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14}; +	uint8_t iv[]  = +		  {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, +			0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14}; +	uint8_t data[8] = "12345678"; + +	uint8_t encrypt_buffer[1000]; +	uint8_t decrypt_buffer[1000]; +	uint32_t bytes_encrypted; +	uint32_t bytes_decrypted; + +	printbuf((const uint8_t *)data, strlen((const char *)data), "input data"); + +	lanplus_encrypt_aes_cbc_128(iv, +								key, +								data, +								strlen((const char *)data), +								encrypt_buffer, +								&bytes_encrypted); +	printbuf((const uint8_t *)encrypt_buffer, bytes_encrypted, "encrypt_buffer"); + +	lanplus_decrypt_aes_cbc_128(iv, +								key, +								encrypt_buffer, +								bytes_encrypted, +								decrypt_buffer, +								&bytes_decrypted); +	printbuf((const uint8_t *)decrypt_buffer, bytes_decrypted, "decrypt_buffer"); + +	lprintf(LOG_INFO, "\nDone testing the encrypt/decyrpt methods!\n"); +	exit(0); +} + + +/** + * send a get device id command to keep session active + */ +static int +ipmi_lanplus_keepalive(struct ipmi_intf * intf) +{ +	struct ipmi_rs * rsp; +	struct ipmi_rq req = { msg: { +		netfn: IPMI_NETFN_APP, +		cmd: 1, +	}}; + +	if (!intf->opened) +		return 0; + +	rsp = intf->sendrecv(intf, &req); +	while (rsp != NULL && is_sol_packet(rsp)) { +					 /* rsp was SOL data instead of our answer */ +					 /* since it didn't go through the sol recv, do sol recv stuff here */ +					 ack_sol_packet(intf, rsp); +					 check_sol_packet_for_new_data(intf, rsp); +					 if (rsp->data_len) +								intf->session->sol_data.sol_input_handler(rsp); +		rsp = ipmi_lan_poll_recv(intf); +		if (rsp == NULL) /* the get device id answer never got back, but retry mechanism was bypassed by SOL data */ +			return 0; /* so get device id command never returned, the connection is still alive */ +		  } + +	if (rsp == NULL) +		return -1; +	if (rsp->ccode > 0) +		return -1; + +	return 0; +} + + +/** + * ipmi_lanplus_setup + */ +static int ipmi_lanplus_setup(struct ipmi_intf * intf) +{ +	//test_crypt1(); +	assert("ipmi_lanplus_setup"); + +	if (lanplus_seed_prng(16)) +		return -1; + +	intf->session = malloc(sizeof(struct ipmi_session)); +	if (intf->session == NULL) { +		lprintf(LOG_ERR, "ipmitool: malloc failure"); +		return -1; +	} +	memset(intf->session, 0, sizeof(struct ipmi_session)); + +    /* setup default LAN maximum request and response sizes */ +    intf->max_request_data_size = IPMI_LAN_MAX_REQUEST_SIZE; +    intf->max_response_data_size = IPMI_LAN_MAX_RESPONSE_SIZE; + +	return 0; +} + +static void ipmi_lanp_set_max_rq_data_size(struct ipmi_intf * intf, uint16_t size) +{ +	if (intf->session->cipher_suite_id == 3) { +		/* +		 * encrypted payload can only be multiple of 16 bytes +		 */ +		size &= ~15; + +		/* +		 * decrement payload size on confidentiality header size +		 * plus minimal confidentiality trailer size +		 */ +		size -= (16 + 1); +	} + +	intf->max_request_data_size = size; +} + +static void ipmi_lanp_set_max_rp_data_size(struct ipmi_intf * intf, uint16_t size) +{ +	if (intf->session->cipher_suite_id == 3) { +		/* +		 * encrypted payload can only be multiple of 16 bytes +		 */ +		size &= ~15; + +		/* +		 * decrement payload size on confidentiality header size +		 * plus minimal confidentiality trailer size +		 */ +		size -= (16 + 1); +	} + +	intf->max_response_data_size = size; +} | 
