#! /bin/sh /usr/share/dpatch/dpatch-run ## 20_ipmi_isol.dpatch by ## ## All lines beginning with `## DP:' are a description of the patch. ## DP: 20_ipmi_isol, closes #412816 @DPATCH@ diff -urN ipmitool-1.8.9.orig/include/ipmitool/ipmi_isol.h ipmitool-1.8.9/include/ipmitool/ipmi_isol.h --- ipmitool-1.8.9.orig/include/ipmitool/ipmi_isol.h 2007-02-22 08:38:55.000000000 +0000 +++ ipmitool-1.8.9/include/ipmitool/ipmi_isol.h 2007-12-13 10:16:57.063986495 +0000 @@ -41,15 +41,16 @@ #define ISOL_ENABLE_PARAM 0x01 #define ISOL_AUTHENTICATION_PARAM 0x02 -#define ISOL_ENABLE_FLAG 0x01 -#define ISOL_PRIVILEGE_LEVEL_USER 0x02 + #define ISOL_BAUD_RATE_PARAM 0x05 -#define ISOL_BAUD_RATE_9600 0x06 -#define ISOL_BAUD_RATE_19200 0x07 -#define ISOL_BAUD_RATE_38400 0x08 -#define ISOL_BAUD_RATE_57600 0x09 -#define ISOL_BAUD_RATE_115200 0x0A -#define ISOL_PREFERRED_BAUD_RATE 0x07 + +#define ISOL_PREFERRED_BAUD_RATE 0x07 + +struct isol_config_parameters { + uint8_t enabled; + uint8_t privilege_level; + uint8_t bit_rate; +}; int ipmi_isol_main(struct ipmi_intf *, int, char **); diff -urN ipmitool-1.8.9.orig/include/ipmitool/ipmi_isol.h.orig ipmitool-1.8.9/include/ipmitool/ipmi_isol.h.orig --- ipmitool-1.8.9.orig/include/ipmitool/ipmi_isol.h.orig 1970-01-01 01:00:00.000000000 +0100 +++ ipmitool-1.8.9/include/ipmitool/ipmi_isol.h.orig 2007-02-22 08:38:55.000000000 +0000 @@ -0,0 +1,56 @@ +/* + * 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. + */ + +#ifndef IPMI_ISOL_H +#define IPMI_ISOL_H + +#include + +#define ACTIVATE_ISOL 0x01 +#define SET_ISOL_CONFIG 0x03 +#define GET_ISOL_CONFIG 0x04 + +#define ISOL_ENABLE_PARAM 0x01 +#define ISOL_AUTHENTICATION_PARAM 0x02 +#define ISOL_ENABLE_FLAG 0x01 +#define ISOL_PRIVILEGE_LEVEL_USER 0x02 +#define ISOL_BAUD_RATE_PARAM 0x05 +#define ISOL_BAUD_RATE_9600 0x06 +#define ISOL_BAUD_RATE_19200 0x07 +#define ISOL_BAUD_RATE_38400 0x08 +#define ISOL_BAUD_RATE_57600 0x09 +#define ISOL_BAUD_RATE_115200 0x0A +#define ISOL_PREFERRED_BAUD_RATE 0x07 + +int ipmi_isol_main(struct ipmi_intf *, int, char **); + +#endif /* IPMI_SOL_H */ diff -urN ipmitool-1.8.9.orig/lib/ipmi_isol.c ipmitool-1.8.9/lib/ipmi_isol.c --- ipmitool-1.8.9.orig/lib/ipmi_isol.c 2007-02-22 08:38:56.000000000 +0000 +++ ipmitool-1.8.9/lib/ipmi_isol.c 2007-12-13 10:16:57.063986495 +0000 @@ -32,7 +32,17 @@ #include #include +#include #include +#include +#include +#include +#include +#include +#include + + +#include #include #include @@ -41,39 +51,40 @@ #include #include -const struct valstr ipmi_isol_baud_vals[] = { - { ISOL_BAUD_RATE_9600, "9600" }, - { ISOL_BAUD_RATE_19200, "19200" }, - { ISOL_BAUD_RATE_38400, "38400" }, - { ISOL_BAUD_RATE_57600, "57600" }, - { ISOL_BAUD_RATE_115200, "115200" }, - { 0x00, NULL } -}; +static struct termios _saved_tio; +static int _in_raw_mode = 0; extern int verbose; -static int ipmi_isol_setup(struct ipmi_intf * intf, char baudsetting) +#define ISOL_ESCAPE_CHARACTER '~' + +/* + * ipmi_get_isol_info + */ +static int ipmi_get_isol_info(struct ipmi_intf * intf, + struct isol_config_parameters * params) { struct ipmi_rs * rsp; struct ipmi_rq req; - unsigned char data[6]; + unsigned char data[6]; - /* TEST FOR AVAILABILITY */ + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_ISOL; + req.msg.cmd = GET_ISOL_CONFIG; + req.msg.data = data; + req.msg.data_len = 4; + /* GET ISOL ENABLED CONFIG */ + memset(data, 0, 6); data[0] = 0x00; data[1] = ISOL_ENABLE_PARAM; - data[2] = ISOL_ENABLE_FLAG; - - memset(&req, 0, sizeof(req)); - req.msg.netfn = IPMI_NETFN_ISOL; - req.msg.cmd = SET_ISOL_CONFIG; - req.msg.data = data; - req.msg.data_len = 3; + data[2] = 0x00; /* block */ + data[3] = 0x00; /* selector */ rsp = intf->sendrecv(intf, &req); if (rsp == NULL) { - lprintf(LOG_ERR, "Error in Set ISOL Config Command"); + lprintf(LOG_ERR, "Error in Get ISOL Config Command"); return -1; } if (rsp->ccode == 0xc1) { @@ -81,20 +92,19 @@ return -1; } if (rsp->ccode > 0) { - lprintf(LOG_ERR, "Error in Set ISOL Config Command: %s", + lprintf(LOG_ERR, "Error in Get ISOL Config Command: %s", val2str(rsp->ccode, completion_code_vals)); return -1; } + params->enabled = rsp->data[1]; - /* GET ISOL CONFIG */ - + /* GET ISOL AUTHENTICATON CONFIG */ + memset(data, 0, 6); data[0] = 0x00; data[1] = ISOL_AUTHENTICATION_PARAM; data[2] = 0x00; /* block */ data[3] = 0x00; /* selector */ - req.msg.cmd = GET_ISOL_CONFIG; - req.msg.data_len = 4; rsp = intf->sendrecv(intf, &req); if (rsp == NULL) { @@ -106,86 +116,713 @@ val2str(rsp->ccode, completion_code_vals)); return -1; } - - if (verbose > 1) - printbuf(rsp->data, rsp->data_len, "ISOL Config"); - - /* SET ISOL CONFIG - AUTHENTICATION */ - + params->privilege_level = rsp->data[1]; + + /* GET ISOL BAUD RATE CONFIG */ + memset(data, 0, 6); data[0] = 0x00; - data[1] = ISOL_AUTHENTICATION_PARAM; - data[2] = ISOL_PRIVILEGE_LEVEL_USER | (rsp->data[1] & 0x80); - req.msg.cmd = SET_ISOL_CONFIG; - req.msg.data_len = 3; + data[1] = ISOL_BAUD_RATE_PARAM; + data[2] = 0x00; /* block */ + data[3] = 0x00; /* selector */ rsp = intf->sendrecv(intf, &req); if (rsp == NULL) { - lprintf(LOG_ERR, "Error in Set ISOL Config (Authentication) Command"); + lprintf(LOG_ERR, "Error in Get ISOL Config Command"); return -1; } if (rsp->ccode > 0) { - lprintf(LOG_ERR, "Error in Set ISOL Config (Authentication) Command: %s", + lprintf(LOG_ERR, "Error in Get ISOL Config Command: %s", val2str(rsp->ccode, completion_code_vals)); return -1; } + params->bit_rate = rsp->data[1]; - /* SET ISOL CONFIG - BAUD RATE */ + return 0; +} - memset(data, 0, 6); - data[0] = 0x00; - data[1] = ISOL_BAUD_RATE_PARAM; - data[2] = baudsetting; +static int ipmi_print_isol_info(struct ipmi_intf * intf) +{ + struct isol_config_parameters params = {0}; + if (ipmi_get_isol_info(intf, ¶ms)) + return -1; + + if (csv_output) + { + printf("%s,", (params.enabled & 0x1)?"true": "false"); + printf("%s,", + val2str((params.privilege_level & 0xf), ipmi_privlvl_vals)); + printf("%s,", + val2str((params.bit_rate & 0xf), ipmi_bit_rate_vals)); + } + else + { + printf("Enabled : %s\n", + (params.enabled & 0x1)?"true": "false"); + printf("Privilege Level : %s\n", + val2str((params.privilege_level & 0xf), ipmi_privlvl_vals)); + printf("Bit Rate (kbps) : %s\n", + val2str((params.bit_rate & 0xf), ipmi_bit_rate_vals)); + } + + return 0; +} + +static int ipmi_isol_set_param(struct ipmi_intf * intf, + const char *param, + const char *value) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char data[6]; + struct isol_config_parameters params = {0}; + + /* We need other values to complete the request */ + if (ipmi_get_isol_info(intf, ¶ms)) + return -1; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_ISOL; req.msg.cmd = SET_ISOL_CONFIG; + req.msg.data = data; req.msg.data_len = 3; + memset(data, 0, 6); + + /* + * enabled + */ + if (strcmp(param, "enabled") == 0) + { + data[1] = ISOL_ENABLE_PARAM; + if (strcmp(value, "true") == 0) + data[2] = 0x01; + else if (strcmp(value, "false") == 0) + data[2] = 0x00; + else { + lprintf(LOG_ERR, "Invalid value %s for parameter %s", + value, param); + lprintf(LOG_ERR, "Valid values are true and false"); + return -1; + } + } + + /* + * privilege-level + */ + else if (strcmp(param, "privilege-level") == 0) + { + data[1] = ISOL_AUTHENTICATION_PARAM; + if (! strcmp(value, "user")) + data[2] = 0x02; + else if (! strcmp(value, "operator")) + data[2] = 0x03; + else if (! strcmp(value, "admin")) + data[2] = 0x04; + else if (! strcmp(value, "oem")) + data[2] = 0x05; + else + { + lprintf(LOG_ERR, "Invalid value %s for parameter %s", + value, param); + lprintf(LOG_ERR, "Valid values are user, operator, admin, and oem"); + return -1; + } + /* We need to mask bit7 from the fetched value */ + data[2] |= (params.privilege_level & 0x80) ? 0x80 : 0x00; + } + + /* + * bit-rate + */ + else if (strcmp(param, "bit-rate") == 0) + { + data[1] = ISOL_BAUD_RATE_PARAM; + if (strncmp(value, "9.6", 3) == 0) { + data[2] = 0x06; + } + else if (strncmp(value, "19.2", 4) == 0) { + data[2] = 0x07; + } + else if (strncmp(value, "38.4", 4) == 0) { + data[2] = 0x08; + } + else if (strncmp(value, "57.6", 4) == 0) { + data[2] = 0x09; + } + else if (strncmp(value, "115.2", 5) == 0) { + data[2] = 0x0A; + } + else { + lprintf(LOG_ERR, "ISOL - Unsupported baud rate: %s", value); + lprintf(LOG_ERR, "Valid values are 9.6, 19.2, 38.4, 57.6 and 115.2"); + return -1; + } + } + else + { + lprintf(LOG_ERR, "Error: invalid ISOL parameter %s", param); + return -1; + } + + + /* + * Execute the request + */ + rsp = intf->sendrecv(intf, &req); if (rsp == NULL) { - lprintf(LOG_ERR, "Error in Set ISOL Config (Baud Rate) Command"); + lprintf(LOG_ERR, "Error setting ISOL parameter '%s'", param); return -1; } if (rsp->ccode > 0) { - lprintf(LOG_ERR, "Error in Set ISOL Config (Baud Rate) Command: %s", - val2str(rsp->ccode, completion_code_vals)); + lprintf(LOG_ERR, "Error setting ISOL parameter '%s': %s", + param, val2str(rsp->ccode, completion_code_vals)); return -1; } - printf("Set ISOL Baud Rate to %s\n", - val2str(baudsetting, ipmi_isol_baud_vals)); + return 0; +} + +static void +leave_raw_mode(void) +{ + if (!_in_raw_mode) + return; + if (tcsetattr(fileno(stdin), TCSADRAIN, &_saved_tio) == -1) + perror("tcsetattr"); + else + _in_raw_mode = 0; +} + + + +static void +enter_raw_mode(void) +{ + struct termios tio; + if (tcgetattr(fileno(stdin), &tio) == -1) { + perror("tcgetattr"); + return; + } + _saved_tio = tio; + tio.c_iflag |= IGNPAR; + tio.c_iflag &= ~(ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXANY | IXOFF)\ + ; + tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL); + // #ifdef IEXTEN + tio.c_lflag &= ~IEXTEN; + // #endif + tio.c_oflag &= ~OPOST; + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + if (tcsetattr(fileno(stdin), TCSADRAIN, &tio) == -1) + perror("tcsetattr"); + else + _in_raw_mode = 1; +} + + +static void +sendBreak(struct ipmi_intf * intf) +{ + struct ipmi_v2_payload v2_payload; + + memset(&v2_payload, 0, sizeof(v2_payload)); + + v2_payload.payload.sol_packet.character_count = 0; + v2_payload.payload.sol_packet.generate_break = 1; + + intf->send_sol(intf, &v2_payload); +} + +/* + * suspendSelf + * + * Put ourself in the background + * + * param bRestoreTty specifies whether we will put our self back + * in raw mode when we resume + */ +static void +suspendSelf(int bRestoreTty) +{ + leave_raw_mode(); + kill(getpid(), SIGTSTP); + + if (bRestoreTty) + enter_raw_mode(); +} + + + +/* + * printiSolEscapeSequences + * + * Send some useful documentation to the user + */ +static void +printiSolEscapeSequences(void) +{ + printf( + "%c?\r\n\ + Supported escape sequences:\r\n\ + %c. - terminate connection\r\n\ + %c^Z - suspend ipmitool\r\n\ + %c^X - suspend ipmitool, but don't restore tty on restart\r\n\ + %cB - send break\r\n\ + %c? - this message\r\n\ + %c%c - send the escape character by typing it twice\r\n\ + (Note that escapes are only recognized immediately after newline.)\r\n", + ISOL_ESCAPE_CHARACTER, + ISOL_ESCAPE_CHARACTER, + ISOL_ESCAPE_CHARACTER, + ISOL_ESCAPE_CHARACTER, + ISOL_ESCAPE_CHARACTER, + ISOL_ESCAPE_CHARACTER, + ISOL_ESCAPE_CHARACTER, + ISOL_ESCAPE_CHARACTER); +} + + + +/* + * output + * + * Send the specified data to stdout + */ +static void +output(struct ipmi_rs * rsp) +{ + if (rsp) + { + int i; + for (i = 0; i < rsp->data_len; ++i) + putc(rsp->data[i], stdout); + + fflush(stdout); + } +} + +/* + * ipmi_isol_deactivate + */ +static int +ipmi_isol_deactivate(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t data[6]; + struct isol_config_parameters params; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_ISOL; + req.msg.cmd = ACTIVATE_ISOL; + req.msg.data = data; + req.msg.data_len = 5; + memset(data, 0, 6); + data[0] = 0x00; /* Deactivate */ + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + data[5] = 0x00; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error deactivating ISOL"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error deactivating ISOL: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + /* response contain 4 additional bytes : 80 00 32 ff + Don't know what to use them for yet... */ return 0; } -int ipmi_isol_main(struct ipmi_intf * intf, int argc, char ** argv) +/* + * processiSolUserInput + * + * Act on user input into the ISOL session. The only reason this + * is complicated is that we have to process escape sequences. + * + * return 0 on success + * 1 if we should exit + * < 0 on error (BMC probably closed the session) + */ +static int +processiSolUserInput(struct ipmi_intf * intf, + uint8_t * input, + uint16_t buffer_length) { - int ret = 0; + static int escape_pending = 0; + static int last_was_cr = 1; + struct ipmi_v2_payload v2_payload; + int length = 0; + int retval = 0; + char ch; + int i; + + memset(&v2_payload, 0, sizeof(v2_payload)); + + /* + * Our first order of business is to check the input for escape + * sequences to act on. + */ + for (i = 0; i < buffer_length; ++i) + { + ch = input[i]; + + if (escape_pending){ + escape_pending = 0; + + /* + * Process a possible escape sequence. + */ + switch (ch) { + case '.': + printf("%c. [terminated ipmitool]\r\n", ISOL_ESCAPE_CHARACTER); + retval = 1; + break; + case 'Z' - 64: + printf("%c^Z [suspend ipmitool]\r\n", ISOL_ESCAPE_CHARACTER); + suspendSelf(1); /* Restore tty back to raw */ + continue; + + case 'X' - 64: + printf("%c^X [suspend ipmitool]\r\n", ISOL_ESCAPE_CHARACTER); + suspendSelf(0); /* Don't restore to raw mode */ + continue; + + case 'B': + printf("%cb [send break]\r\n", ISOL_ESCAPE_CHARACTER); + sendBreak(intf); + continue; + + case '?': + printiSolEscapeSequences(); + continue; + default: + if (ch != ISOL_ESCAPE_CHARACTER) + v2_payload.payload.sol_packet.data[length++] = + ISOL_ESCAPE_CHARACTER; + v2_payload.payload.sol_packet.data[length++] = ch; + } + } - if (argc < 2 || strncmp(argv[0], "help", 4) == 0) { - lprintf(LOG_NOTICE, "ISOL Commands: setup "); - lprintf(LOG_NOTICE, "ISOL Baud Rates: 9600, 19200, 38400, 57600, 115200"); - return 0; - } - - if (strncmp(argv[0], "setup", 5) == 0) { - if (strncmp(argv[1], "9600", 4) == 0) { - ret = ipmi_isol_setup(intf, ISOL_BAUD_RATE_9600); - } - else if (strncmp(argv[1], "19200", 5) == 0) { - ret = ipmi_isol_setup(intf, ISOL_BAUD_RATE_19200); + else + { + if (last_was_cr && (ch == ISOL_ESCAPE_CHARACTER)) { + escape_pending = 1; + continue; + } + + v2_payload.payload.sol_packet.data[length++] = ch; } - else if (strncmp(argv[1], "38400", 5) == 0) { - ret = ipmi_isol_setup(intf, ISOL_BAUD_RATE_38400); + + + /* + * Normal character. Record whether it was a newline. + */ + last_was_cr = (ch == '\r' || ch == '\n'); + } + + /* + * If there is anything left to process we dispatch it to the BMC, + * send intf->session->sol_data.max_outbound_payload_size bytes + * at a time. + */ + if (length) + { + struct ipmi_rs * rsp; + + v2_payload.payload.sol_packet.flush_outbound = 1; /* Not sure if necessary ? */ + v2_payload.payload.sol_packet.character_count = length; + rsp = intf->send_sol(intf, &v2_payload); + + if (! rsp) { + lprintf(LOG_ERR, "Error sending SOL data"); + retval = -1; } - else if (strncmp(argv[1], "57600", 5) == 0) { - ret = ipmi_isol_setup(intf, ISOL_BAUD_RATE_57600); + + /* If the sequence number is set we know we have new data */ + else if ((rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_SOL) && + (rsp->payload.sol_packet.packet_sequence_number)) + output(rsp); + } + return retval; +} + +/* + * ipmi_isol_red_pill + */ +static int +ipmi_isol_red_pill(struct ipmi_intf * intf) +{ + char * buffer; + int numRead; + int bShouldExit = 0; + int bBmcClosedSession = 0; + fd_set read_fds; + struct timeval tv; + int retval; + int buffer_size = 255; + int timedout = 0; + + buffer = (char*)malloc(buffer_size); + if (buffer == NULL) { + lprintf(LOG_ERR, "ipmitool: malloc failure"); + return -1; + } + + enter_raw_mode(); + + while (! bShouldExit) + { + FD_ZERO(&read_fds); + FD_SET(0, &read_fds); + FD_SET(intf->fd, &read_fds); + + /* Wait up to half a second */ + tv.tv_sec = 0; + tv.tv_usec = 500000; + + retval = select(intf->fd + 1, &read_fds, NULL, NULL, &tv); + + if (retval) + { + if (retval == -1) + { + /* ERROR */ + perror("select"); + return -1; + } + + timedout = 0; + + /* + * Process input from the user + */ + if (FD_ISSET(0, &read_fds)) + { + bzero(buffer, sizeof(buffer)); + numRead = read(fileno(stdin), + buffer, + buffer_size); + + if (numRead > 0) + { + int rc = processiSolUserInput(intf, buffer, numRead); + + if (rc) + { + if (rc < 0) + bShouldExit = bBmcClosedSession = 1; + else + bShouldExit = 1; + } + } + else + { + bShouldExit = 1; + } + } + + + /* + * Process input from the BMC + */ + else if (FD_ISSET(intf->fd, &read_fds)) + { + struct ipmi_rs * rs = intf->recv_sol(intf); + if (! rs) + { + bShouldExit = bBmcClosedSession = 1; + } + else + output(rs); + } + + + /* + * ERROR in select + */ + else + { + lprintf(LOG_ERR, "Error: Select returned with nothing to read"); + bShouldExit = 1; + } } - else if (strncmp(argv[1], "115200", 6) == 0) { - ret = ipmi_isol_setup(intf, ISOL_BAUD_RATE_115200); + else + { + if ((++timedout) == 20) /* Every 10 seconds we send a keepalive */ + { + intf->keepalive(intf); + timedout = 0; + } } - else { - lprintf(LOG_ERR, "ISOL - Unsupported baud rate: %s", argv[1]); - ret = -1; + } + + leave_raw_mode(); + + if (bBmcClosedSession) + { + lprintf(LOG_ERR, "SOL session closed by BMC"); + } + else + ipmi_isol_deactivate(intf); + + return 0; +} + +/* + * ipmi_isol_activate + */ +static int +ipmi_isol_activate(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + uint8_t data[6]; + struct isol_config_parameters params; + + if (ipmi_get_isol_info(intf, ¶ms)) + return -1; + + if (!(params.enabled & 0x1)) { + lprintf(LOG_ERR, "ISOL is not enabled!"); + return -1; + } + + /* + * Setup a callback so that the lanplus processing knows what + * to do with packets that come unexpectedly (while waiting for + * an ACK, perhaps. + */ + intf->session->sol_data.sol_input_handler = output; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_ISOL; + req.msg.cmd = ACTIVATE_ISOL; + req.msg.data = data; + req.msg.data_len = 5; + + memset(data, 0, 6); + data[0] = 0x01; + data[1] = 0x00; + data[2] = 0x00; + data[3] = 0x00; + data[5] = 0x00; + + rsp = intf->sendrecv(intf, &req); + if (NULL != rsp) { + switch (rsp->ccode) { + case 0x00: + if (rsp->data_len == 4) { + break; + } else { + lprintf(LOG_ERR, "Error: Unexpected data length (%d) received " + "in ISOL activation response", + rsp->data_len); + return -1; + } + break; + case 0x80: + lprintf(LOG_ERR, "Info: ISOL already active on another session"); + return -1; + case 0x81: + lprintf(LOG_ERR, "Info: ISOL disabled"); + return -1; + case 0x82: + lprintf(LOG_ERR, "Info: ISOL activation limit reached"); + return -1; + default: + lprintf(LOG_ERR, "Error activating ISOL: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + } else { + lprintf(LOG_ERR, "Error: No response activating ISOL"); + return -1; + } + + /* response contain 4 additional bytes : 80 01 32 ff + Don't know what to use them for yet... */ + + printf("[SOL Session operational. Use %c? for help]\r\n", + ISOL_ESCAPE_CHARACTER); + + /* + * At this point we are good to go with our SOL session. We + * need to listen to + * 1) STDIN for user input + * 2) The FD for incoming SOL packets + */ + if (ipmi_isol_red_pill(intf)) { + lprintf(LOG_ERR, "Error in SOL session"); + return -1; + } + + return 0; +} + +static void print_isol_set_usage(void) { + lprintf(LOG_NOTICE, "\nISOL set parameters and values: \n"); + lprintf(LOG_NOTICE, " enabled true | false"); + lprintf(LOG_NOTICE, " privilege-level user | operator | admin | oem"); + lprintf(LOG_NOTICE, " bit-rate " + "9.6 | 19.2 | 38.4 | 57.6 | 115.2"); + lprintf(LOG_NOTICE, ""); +} + +static void print_isol_usage(void) { + lprintf(LOG_NOTICE, "ISOL Commands: info"); + lprintf(LOG_NOTICE, " set "); + lprintf(LOG_NOTICE, " activate"); +} + +int ipmi_isol_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int ret = 0; + + /* + * Help + */ + if (!argc || !strncmp(argv[0], "help", 4)) + print_isol_usage(); + + /* + * Info + */ + else if (!strncmp(argv[0], "info", 4)) { + ret = ipmi_print_isol_info(intf); + } + + /* + * Set a parameter value + */ + else if (!strncmp(argv[0], "set", 3)) { + if (argc < 3) { + print_isol_set_usage(); + return -1; } + ret = ipmi_isol_set_param(intf, argv[1], argv[2]); + } + + /* + * Activate + */ + else if (!strncmp(argv[0], "activate", 8)) { + ret = ipmi_isol_activate(intf); + } + + else { + print_isol_usage(); + ret = -1; } + return ret; } diff -urN ipmitool-1.8.9.orig/lib/ipmi_isol.c.orig ipmitool-1.8.9/lib/ipmi_isol.c.orig --- ipmitool-1.8.9.orig/lib/ipmi_isol.c.orig 1970-01-01 01:00:00.000000000 +0100 +++ ipmitool-1.8.9/lib/ipmi_isol.c.orig 2007-02-22 08:38:56.000000000 +0000 @@ -0,0 +1,191 @@ +/* + * 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 +#include +#include + +#include +#include +#include +#include +#include +#include + +const struct valstr ipmi_isol_baud_vals[] = { + { ISOL_BAUD_RATE_9600, "9600" }, + { ISOL_BAUD_RATE_19200, "19200" }, + { ISOL_BAUD_RATE_38400, "38400" }, + { ISOL_BAUD_RATE_57600, "57600" }, + { ISOL_BAUD_RATE_115200, "115200" }, + { 0x00, NULL } +}; + +extern int verbose; + +static int ipmi_isol_setup(struct ipmi_intf * intf, char baudsetting) +{ + struct ipmi_rs * rsp; + struct ipmi_rq req; + unsigned char data[6]; + + /* TEST FOR AVAILABILITY */ + + memset(data, 0, 6); + data[0] = 0x00; + data[1] = ISOL_ENABLE_PARAM; + data[2] = ISOL_ENABLE_FLAG; + + memset(&req, 0, sizeof(req)); + req.msg.netfn = IPMI_NETFN_ISOL; + req.msg.cmd = SET_ISOL_CONFIG; + req.msg.data = data; + req.msg.data_len = 3; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in Set ISOL Config Command"); + return -1; + } + if (rsp->ccode == 0xc1) { + lprintf(LOG_ERR, "IPMI v1.5 Serial Over Lan (ISOL) not supported!"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error in Set ISOL Config Command: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + /* GET ISOL CONFIG */ + + memset(data, 0, 6); + data[0] = 0x00; + data[1] = ISOL_AUTHENTICATION_PARAM; + data[2] = 0x00; /* block */ + data[3] = 0x00; /* selector */ + req.msg.cmd = GET_ISOL_CONFIG; + req.msg.data_len = 4; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in Get ISOL Config Command"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error in Get ISOL Config Command: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + if (verbose > 1) + printbuf(rsp->data, rsp->data_len, "ISOL Config"); + + /* SET ISOL CONFIG - AUTHENTICATION */ + + memset(data, 0, 6); + data[0] = 0x00; + data[1] = ISOL_AUTHENTICATION_PARAM; + data[2] = ISOL_PRIVILEGE_LEVEL_USER | (rsp->data[1] & 0x80); + req.msg.cmd = SET_ISOL_CONFIG; + req.msg.data_len = 3; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in Set ISOL Config (Authentication) Command"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error in Set ISOL Config (Authentication) Command: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + /* SET ISOL CONFIG - BAUD RATE */ + + memset(data, 0, 6); + data[0] = 0x00; + data[1] = ISOL_BAUD_RATE_PARAM; + data[2] = baudsetting; + req.msg.cmd = SET_ISOL_CONFIG; + req.msg.data_len = 3; + + rsp = intf->sendrecv(intf, &req); + if (rsp == NULL) { + lprintf(LOG_ERR, "Error in Set ISOL Config (Baud Rate) Command"); + return -1; + } + if (rsp->ccode > 0) { + lprintf(LOG_ERR, "Error in Set ISOL Config (Baud Rate) Command: %s", + val2str(rsp->ccode, completion_code_vals)); + return -1; + } + + printf("Set ISOL Baud Rate to %s\n", + val2str(baudsetting, ipmi_isol_baud_vals)); + + return 0; +} + +int ipmi_isol_main(struct ipmi_intf * intf, int argc, char ** argv) +{ + int ret = 0; + + if (argc < 2 || strncmp(argv[0], "help", 4) == 0) { + lprintf(LOG_NOTICE, "ISOL Commands: setup "); + lprintf(LOG_NOTICE, "ISOL Baud Rates: 9600, 19200, 38400, 57600, 115200"); + return 0; + } + + if (strncmp(argv[0], "setup", 5) == 0) { + if (strncmp(argv[1], "9600", 4) == 0) { + ret = ipmi_isol_setup(intf, ISOL_BAUD_RATE_9600); + } + else if (strncmp(argv[1], "19200", 5) == 0) { + ret = ipmi_isol_setup(intf, ISOL_BAUD_RATE_19200); + } + else if (strncmp(argv[1], "38400", 5) == 0) { + ret = ipmi_isol_setup(intf, ISOL_BAUD_RATE_38400); + } + else if (strncmp(argv[1], "57600", 5) == 0) { + ret = ipmi_isol_setup(intf, ISOL_BAUD_RATE_57600); + } + else if (strncmp(argv[1], "115200", 6) == 0) { + ret = ipmi_isol_setup(intf, ISOL_BAUD_RATE_115200); + } + else { + lprintf(LOG_ERR, "ISOL - Unsupported baud rate: %s", argv[1]); + ret = -1; + } + } + return ret; +} diff -urN ipmitool-1.8.9.orig/src/plugins/lan/lan.c ipmitool-1.8.9/src/plugins/lan/lan.c --- ipmitool-1.8.9.orig/src/plugins/lan/lan.c 2007-02-22 08:38:57.000000000 +0000 +++ ipmitool-1.8.9/src/plugins/lan/lan.c 2007-12-13 10:16:57.063986495 +0000 @@ -79,6 +79,11 @@ static struct ipmi_rs * ipmi_lan_poll_recv(struct ipmi_intf * intf); static int ipmi_lan_setup(struct ipmi_intf * intf); static int ipmi_lan_keepalive(struct ipmi_intf * intf); +static struct ipmi_rs * ipmi_lan_send_payload(struct ipmi_intf * intf, + struct ipmi_v2_payload * payload); +static struct ipmi_rs * ipmi_lan_recv_sol(struct ipmi_intf * intf); +static struct ipmi_rs * ipmi_lan_send_sol(struct ipmi_intf * intf, + struct ipmi_v2_payload * payload); static struct ipmi_rs * ipmi_lan_send_cmd(struct ipmi_intf * intf, struct ipmi_rq * req); static int ipmi_lan_send_rsp(struct ipmi_intf * intf, struct ipmi_rs * rsp); static int ipmi_lan_open(struct ipmi_intf * intf); @@ -93,6 +98,8 @@ close: ipmi_lan_close, sendrecv: ipmi_lan_send_cmd, sendrsp: ipmi_lan_send_rsp, + recv_sol: ipmi_lan_recv_sol, + send_sol: ipmi_lan_send_sol, keepalive: ipmi_lan_keepalive, target_addr: IPMI_BMC_SLAVE_ADDR, }; @@ -456,80 +463,141 @@ memcpy(&rsp->session.id, rsp->data+x, 4); x += 4; - if (intf->session->active && (rsp->session.authtype || intf->session->authtype)) - x += 16; - - rsp->session.msglen = rsp->data[x++]; - rsp->payload.ipmi_response.rq_addr = rsp->data[x++]; - rsp->payload.ipmi_response.netfn = rsp->data[x] >> 2; - rsp->payload.ipmi_response.rq_lun = rsp->data[x++] & 0x3; - x++; /* checksum */ - rsp->payload.ipmi_response.rs_addr = rsp->data[x++]; - rsp->payload.ipmi_response.rq_seq = rsp->data[x] >> 2; - rsp->payload.ipmi_response.rs_lun = rsp->data[x++] & 0x3; - rsp->payload.ipmi_response.cmd = rsp->data[x++]; - rsp->ccode = rsp->data[x++]; - - if (verbose > 2) - printbuf(rsp->data, rsp->data_len, "ipmi message header"); - - 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, "<< Sequence : 0x%08lx", - (long)rsp->session.seq); - lprintf(LOG_DEBUG+1, "<< Session ID : 0x%08lx", - (long)rsp->session.id); - 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); - - /* now see if we have outstanding entry in request list */ - entry = ipmi_req_lookup_entry(rsp->payload.ipmi_response.rq_seq, - rsp->payload.ipmi_response.cmd); - if (entry) { - lprintf(LOG_DEBUG+2, "IPMI Request Match found"); - if ((intf->target_addr != our_address) && bridge_possible) { - if ((rsp->data_len) && - (rsp->payload.ipmi_response.cmd != 0x34)) { - printbuf(&rsp->data[x], rsp->data_len-x, - "bridge command response"); - } - /* bridged command: lose extra header */ - if (rsp->payload.ipmi_response.cmd == 0x34) { - if (rsp->data_len == 38) { - entry->req.msg.cmd = entry->req.msg.target_cmd; - rsp = ipmi_lan_recv_packet(intf); - continue; + if (rsp->session.id == (intf->session->session_id + 0x10000000)) { + /* With SOL, authtype is always NONE, so we have no authcode */ + rsp->session.payloadtype = IPMI_PAYLOAD_TYPE_SOL; + + rsp->session.msglen = rsp->data[x++]; + + rsp->payload.sol_packet.packet_sequence_number = + rsp->data[x++] & 0x0F; + + rsp->payload.sol_packet.acked_packet_number = + rsp->data[x++] & 0x0F; + + rsp->payload.sol_packet.accepted_character_count = + rsp->data[x++]; + + rsp->payload.sol_packet.is_nack = + rsp->data[x] & 0x40; + + rsp->payload.sol_packet.transfer_unavailable = + rsp->data[x] & 0x20; + + rsp->payload.sol_packet.sol_inactive = + rsp->data[x] & 0x10; + + rsp->payload.sol_packet.transmit_overrun = + rsp->data[x] & 0x08; + + rsp->payload.sol_packet.break_detected = + rsp->data[x++] & 0x04; + + x++; /* On ISOL there's and additional fifth byte before the data starts */ + + 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"); + } + else + { + /* Standard IPMI 1.5 packet */ + rsp->session.payloadtype = IPMI_PAYLOAD_TYPE_IPMI; + if (intf->session->active && (rsp->session.authtype || intf->session->authtype)) + x += 16; + + rsp->session.msglen = rsp->data[x++]; + rsp->payload.ipmi_response.rq_addr = rsp->data[x++]; + rsp->payload.ipmi_response.netfn = rsp->data[x] >> 2; + rsp->payload.ipmi_response.rq_lun = rsp->data[x++] & 0x3; + x++; /* checksum */ + rsp->payload.ipmi_response.rs_addr = rsp->data[x++]; + rsp->payload.ipmi_response.rq_seq = rsp->data[x] >> 2; + rsp->payload.ipmi_response.rs_lun = rsp->data[x++] & 0x3; + rsp->payload.ipmi_response.cmd = rsp->data[x++]; + rsp->ccode = rsp->data[x++]; + + if (verbose > 2) + printbuf(rsp->data, rsp->data_len, "ipmi message header"); + + 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, "<< Sequence : 0x%08lx", + (long)rsp->session.seq); + lprintf(LOG_DEBUG+1, "<< Session ID : 0x%08lx", + (long)rsp->session.id); + 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); + + /* now see if we have outstanding entry in request list */ + entry = ipmi_req_lookup_entry(rsp->payload.ipmi_response.rq_seq, + rsp->payload.ipmi_response.cmd); + if (entry) { + lprintf(LOG_DEBUG+2, "IPMI Request Match found"); + if ((intf->target_addr != our_address) && bridge_possible) { + if ((rsp->data_len) && + (rsp->payload.ipmi_response.cmd != 0x34)) { + printbuf(&rsp->data[x], rsp->data_len-x, + "bridge command response"); + } + /* bridged command: lose extra header */ + if (rsp->payload.ipmi_response.cmd == 0x34) { + if (rsp->data_len == 38) { + entry->req.msg.cmd = entry->req.msg.target_cmd; + rsp = ipmi_lan_recv_packet(intf); + continue; + } + } else { + //x += sizeof(rsp->payload.ipmi_response); + if (rsp->data[x-1] != 0) + lprintf(LOG_DEBUG, "WARNING: Bridged " + "cmd ccode = 0x%02x", + rsp->data[x-1]); } - } else { - //x += sizeof(rsp->payload.ipmi_response); - if (rsp->data[x-1] != 0) - lprintf(LOG_DEBUG, "WARNING: Bridged " - "cmd ccode = 0x%02x", - rsp->data[x-1]); } + 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; } - 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; } break; @@ -537,7 +605,9 @@ /* shift response data to start of array */ if (rsp && rsp->data_len > x) { - rsp->data_len -= x + 1; + rsp->data_len -= x; + if (rsp->session.payloadtype == IPMI_PAYLOAD_TYPE_IPMI) + rsp->data_len -= 1; /* We don't want the checksum */ memmove(rsp->data, rsp->data + x, rsp->data_len); memset(rsp->data + rsp->data_len, 0, IPMI_BUF_SIZE - rsp->data_len); } @@ -553,9 +623,9 @@ * | rmcp.seq | * | rmcp.class | * +--------------------+ - * | session.authtype | 9 bytes - * | session.seq | - * | session.id | + * | session.authtype | 9 bytes + * | session.seq | + * | session.id | * +--------------------+ * | [session.authcode] | 16 bytes (AUTHTYPE != none) * +--------------------+ @@ -910,6 +980,430 @@ return 0; } +/* + * IPMI SOL Payload Format + * +--------------------+ + * | rmcp.ver | 4 bytes + * | rmcp.__reserved | + * | rmcp.seq | + * | rmcp.class | + * +--------------------+ + * | session.authtype | 9 bytes + * | session.seq | + * | session.id | + * +--------------------+ + * | message length | 1 byte + * +--------------------+ + * | sol.seq | 5 bytes + * | sol.ack_seq | + * | sol.acc_count | + * | sol.control | + * | sol.__reserved | + * +--------------------+ + * | [request data] | data_len bytes + * +--------------------+ + */ +uint8_t * ipmi_lan_build_sol_msg(struct ipmi_intf * intf, + struct ipmi_v2_payload * payload, + int * llen) +{ + struct rmcp_hdr rmcp = { + .ver = RMCP_VERSION_1, + .class = RMCP_CLASS_IPMI, + .seq = 0xff, + }; + struct ipmi_session * session = intf->session; + + /* 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 + 5 + // SOL header + payload->payload.sol_packet.character_count; // The actual payload + + 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 */ + msg[len++] = 0; /* SOL is always authtype = NONE */ + msg[len++] = session->in_seq & 0xff; + msg[len++] = (session->in_seq >> 8) & 0xff; + msg[len++] = (session->in_seq >> 16) & 0xff; + msg[len++] = (session->in_seq >> 24) & 0xff; + + msg[len++] = session->session_id & 0xff; + msg[len++] = (session->session_id >> 8) & 0xff; + msg[len++] = (session->session_id >> 16) & 0xff; + msg[len++] = ((session->session_id >> 24) + 0x10) & 0xff; /* Add 0x10 to MSB for SOL */ + + msg[len++] = payload->payload.sol_packet.character_count + 5; + + /* sol header */ + msg[len++] = payload->payload.sol_packet.packet_sequence_number; + msg[len++] = payload->payload.sol_packet.acked_packet_number; + msg[len++] = payload->payload.sol_packet.accepted_character_count; + msg[len] = payload->payload.sol_packet.is_nack ? 0x40 : 0; + msg[len] |= payload->payload.sol_packet.assert_ring_wor ? 0x20 : 0; + msg[len] |= payload->payload.sol_packet.generate_break ? 0x10 : 0; + msg[len] |= payload->payload.sol_packet.deassert_cts ? 0x08 : 0; + msg[len] |= payload->payload.sol_packet.deassert_dcd_dsr ? 0x04 : 0; + msg[len] |= payload->payload.sol_packet.flush_inbound ? 0x02 : 0; + msg[len++] |= payload->payload.sol_packet.flush_outbound ? 0x01 : 0; + + len++; /* On SOL there's and additional fifth byte before the data starts */ + + if (payload->payload.sol_packet.character_count) { + /* We may have data to add */ + memcpy(msg + len, + payload->payload.sol_packet.data, + payload->payload.sol_packet.character_count); + len += payload->payload.sol_packet.character_count; + } + + session->in_seq++; + if (session->in_seq == 0) + session->in_seq++; + + *llen = len; + return msg; +} + +/* + * is_sol_packet + */ +static int +is_sol_packet(struct ipmi_rs * rsp) +{ + return (rsp && + (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_lan_send_sol_payload + * + */ +static struct ipmi_rs * +ipmi_lan_send_sol_payload(struct ipmi_intf * intf, + struct ipmi_v2_payload * payload) +{ + struct ipmi_rs * rsp = NULL; + uint8_t * msg; + int len; + int try = 0; + + if (intf->opened == 0 && intf->open != NULL) { + if (intf->open(intf) < 0) + return NULL; + } + + msg = ipmi_lan_build_sol_msg(intf, payload, &len); + if (len <= 0 || msg == NULL) { + lprintf(LOG_ERR, "Invalid SOL payload packet"); + if (msg != NULL) + free(msg); + return NULL; + } + + lprintf(LOG_DEBUG, ">> SENDING A SOL MESSAGE\n"); + + for (;;) { + if (ipmi_lan_send_packet(intf, msg, len) < 0) { + try++; + usleep(5000); + continue; + } + + /* if we are set to noanswer we do not expect response */ + if (intf->noanswer) + break; + + if (payload->payload.sol_packet.packet_sequence_number == 0) { + /* We're just sending an ACK. No need to retry. */ + break; + } + + usleep(100); + + rsp = ipmi_lan_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); + } + + usleep(5000); + if (++try >= intf->session->retry) { + lprintf(LOG_DEBUG, " No response from remote controller"); + 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 + */ +static int is_sol_partial_ack(struct ipmi_v2_payload * v2_payload, + struct ipmi_rs * rsp) +{ + int chars_to_resend = 0; + + if (v2_payload && + rsp && + is_sol_packet(rsp) && + sol_response_acks_packet(rsp, v2_payload) && + (rsp->payload.sol_packet.accepted_character_count < + v2_payload->payload.sol_packet.character_count)) + { + if (rsp->payload.sol_packet.accepted_character_count == 0) { + /* We should not resend data */ + chars_to_resend = 0; + } + else + { + chars_to_resend = + v2_payload->payload.sol_packet.character_count - + rsp->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_lan_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_lan_send_sol(struct ipmi_intf * intf, + struct ipmi_v2_payload * v2_payload) +{ + struct ipmi_rs * rsp; + 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.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 */ + + rsp = ipmi_lan_send_sol_payload(intf, v2_payload); + + /* Determine if we need to resend some of our data */ + chars_to_resend = is_sol_partial_ack(v2_payload, rsp); + + while (chars_to_resend) + { + /* + * We first need to handle any new data we might have + * received in our NACK + */ + if (rsp->data_len) + intf->session->sol_data.sol_input_handler(rsp); + + 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 + + rsp->payload.sol_packet.accepted_character_count, + chars_to_resend); + + v2_payload->payload.sol_packet.character_count = chars_to_resend; + + rsp = ipmi_lan_send_sol_payload(intf, v2_payload); + + chars_to_resend = is_sol_partial_ack(v2_payload, rsp); + } + + return rsp; +} + +/* + * 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. + * + */ +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.payloadtype == IPMI_PAYLOAD_TYPE_SOL)) + + { + 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 && 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.payloadtype == IPMI_PAYLOAD_TYPE_SOL) && + (rsp->payload.sol_packet.packet_sequence_number)) + { + struct ipmi_v2_payload ack; + + memset(&ack, 0, 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_lan_send_sol_payload(intf, &ack); + } +} + +/* + * ipmi_recv_sol + * + * Receive a SOL packet and send an ACK in response. + * + */ +struct ipmi_rs * +ipmi_lan_recv_sol(struct ipmi_intf * intf) +{ + struct ipmi_rs * rsp = ipmi_lan_poll_recv(intf); + + 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; +} + /* send a get device id command to keep session active */ static int ipmi_lan_keepalive(struct ipmi_intf * intf) @@ -1411,6 +1905,8 @@ intf->abort = 1; + intf->session->sol_data.sequence_number = 1; + /* open port to BMC */ memset(&s->addr, 0, sizeof(struct sockaddr_in)); s->addr.sin_family = AF_INET;