summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chassishandler.cpp397
-rw-r--r--chassishandler.h12
-rw-r--r--docs/ipmi-network-format.md74
3 files changed, 463 insertions, 20 deletions
diff --git a/chassishandler.cpp b/chassishandler.cpp
index 92d9752..1df4c0a 100644
--- a/chassishandler.cpp
+++ b/chassishandler.cpp
@@ -1,17 +1,45 @@
#include "chassishandler.h"
#include "ipmid-api.h"
#include <stdio.h>
-#include <string.h>
+#include <stdlib.h>
#include <stdint.h>
#include <mapper.h>
-
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <limits.h>
+#include <string.h>
+#include <endian.h>
+#include <sstream>
+#include <array>
//Defines
-#define SET_PARM_VERSION 1
-#define SET_PARM_BOOT_FLAGS_PERMANENT 0x40 //boot flags data1 7th bit on
+#define SET_PARM_VERSION 0x01
+#define SET_PARM_BOOT_FLAGS_PERMANENT 0x40 //boot flags data1 7th bit on
#define SET_PARM_BOOT_FLAGS_VALID_ONE_TIME 0x80 //boot flags data1 8th bit on
-#define SET_PARM_BOOT_FLAGS_VALID_PERMANENT 0xC0 //boot flags data1 7 & 8 bit on
-
+#define SET_PARM_BOOT_FLAGS_VALID_PERMANENT 0xC0 //boot flags data1 7 & 8 bit on
+
+constexpr size_t SIZE_MAC = 18;
+constexpr size_t SIZE_BOOT_OPTION = (uint8_t)BootOptionResponseSize::
+ OPAL_NETWORK_SETTINGS;//Maximum size of the boot option parametrs
+constexpr size_t SIZE_PREFIX = 7;
+constexpr size_t MAX_PREFIX_VALUE = 32;
+constexpr size_t SIZE_COOKIE = 4;
+constexpr size_t SIZE_VERSION = 2;
+constexpr auto MAC_ADDRESS_FORMAT = "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx";
+constexpr auto IP_ADDRESS_FORMAT = "%d.%d.%d.%d";
+constexpr auto PREFIX_FORMAT = "%d";
+constexpr auto ADDR_TYPE_FORMAT = "%hhx";
+//PetiBoot-Specific
+static constexpr uint8_t net_conf_initial_bytes[] = {0x80,0x21, 0x70 ,0x62 ,0x21,
+ 0x00 ,0x01 ,0x06 ,0x04};
+
+static constexpr size_t COOKIE_OFFSET = 1;
+static constexpr size_t VERSION_OFFSET = 5;
+static constexpr size_t MAC_OFFSET = 9;
+static constexpr size_t ADDRTYPE_OFFSET = 16;
+static constexpr size_t IPADDR_OFFSET = 17;
+static constexpr size_t PREFIX_OFFSET = 21;
+static constexpr size_t GATEWAY_OFFSET = 22;
// OpenBMC Chassis Manager dbus framework
@@ -153,14 +181,310 @@ struct get_sys_boot_options_t {
struct get_sys_boot_options_response_t {
uint8_t version;
uint8_t parm;
- uint8_t data[5];
+ uint8_t data[SIZE_BOOT_OPTION];
} __attribute__ ((packed));
struct set_sys_boot_options_t {
uint8_t parameter;
- uint8_t data[8];
+ uint8_t data[SIZE_BOOT_OPTION];
} __attribute__ ((packed));
+struct host_network_config_t {
+ std::string ipaddress;
+ std::string prefix;
+ std::string gateway;
+ std::string macaddress;
+ std::string addrType;
+
+ host_network_config_t()=default;
+};
+
+void fillNetworkConfig( host_network_config_t & host_config ,
+ const std::string& conf_str ) {
+
+ constexpr auto COMMA_DELIMITER = ",";
+ constexpr auto EQUAL_DELIMITER = "=";
+ size_t commaDelimtrPos = 0;
+ size_t equalDelimtrPos = 0,commaDelimtrPrevPos = 0;
+ std::string value;
+ while ( commaDelimtrPos < conf_str.length() ) {
+
+ commaDelimtrPos = conf_str.find(COMMA_DELIMITER,commaDelimtrPos);
+ //This condition is to extract the last
+ //Substring as we will not be having the delimeter
+ //at end. std::string::npos is -1
+
+ if ( commaDelimtrPos == std::string::npos ) {
+ commaDelimtrPos = conf_str.length();
+ }
+
+ equalDelimtrPos = conf_str.find (EQUAL_DELIMITER,commaDelimtrPrevPos);
+
+ //foo,ipaddress=1234
+ if ( equalDelimtrPos == std::string::npos ) {
+
+ commaDelimtrPos++;
+ commaDelimtrPrevPos= commaDelimtrPos;
+ continue;
+ }
+
+ value = conf_str.substr((equalDelimtrPos+1),
+ commaDelimtrPos-(equalDelimtrPos+1));
+
+#ifdef _IPMI_DEBUG_
+ printf ("Name=[%s],Value=[%s],commaDelimtrPos=[%d],\
+ commaDelimtrPrevPos=[%d],equalDelimtrPos=[%d]\n",
+ name.c_str(),value.c_str(),commaDelimtrPos,
+ commaDelimtrPrevPos,equalDelimtrPos);
+#endif
+
+ if ( 0 == conf_str.compare(commaDelimtrPrevPos,
+ equalDelimtrPos-commaDelimtrPrevPos,"ipaddress" )) {
+
+ host_config.ipaddress = std::move(value);
+ }
+ else if ( 0 == conf_str.compare(commaDelimtrPrevPos,
+ equalDelimtrPos-commaDelimtrPrevPos,"prefix" )) {
+
+ host_config.prefix = std::move(value);
+ }
+ else if ( 0 == conf_str.compare(commaDelimtrPrevPos,
+ equalDelimtrPos-commaDelimtrPrevPos, "gateway" )) {
+ host_config.gateway = std::move(value);
+ }
+ else if ( 0 == conf_str.compare(commaDelimtrPrevPos,
+ equalDelimtrPos-commaDelimtrPrevPos, "mac" )) {
+ host_config.macaddress = std::move(value);
+ }
+ else if ( 0 == conf_str.compare(commaDelimtrPrevPos,
+ equalDelimtrPos-commaDelimtrPrevPos, "addr_type" )) {
+ host_config.addrType = std::move(value);
+ }
+
+ commaDelimtrPos++;
+ commaDelimtrPrevPos= commaDelimtrPos;
+ }
+}
+
+int getHostNetworkData(get_sys_boot_options_response_t* respptr)
+{
+
+ char *prop = nullptr;
+ int rc = dbus_get_property("network_config",&prop);
+
+ if ( rc < 0 ) {
+ fprintf(stderr, "Dbus get property(boot_flags) failed\
+ for get_sys_boot_options.\n");
+ return rc;
+ }
+
+ std::string conf_str(prop);
+
+ if ( prop ) {
+
+ free(prop);
+ prop = nullptr;
+ }
+
+ /* network_config property Value would be in the form of
+ * ipaddress=1.1.1.1,prefix=16,gateway=2.2.2.2,mac=11:22:33:44:55:66,dhcp=0
+ */
+
+ /* Parsing the string and fill the hostconfig structure with the
+ * values */
+
+ printf ("Configuration String[%s]\n ",conf_str.c_str());
+
+ host_network_config_t host_config;
+
+ // Fill the host_config from the configuration string
+ fillNetworkConfig(host_config,conf_str);
+
+ //Assigning the index as intialByteLength as it is fixed and prefilled.
+ printf ("host_config.macaddress.c_str()=[%s]\n",host_config.macaddress.c_str());
+ do{
+
+ rc = sscanf(host_config.macaddress.c_str(),MAC_ADDRESS_FORMAT,
+ (respptr->data+MAC_OFFSET), (respptr->data+MAC_OFFSET+1),
+ (respptr->data+MAC_OFFSET+2),(respptr->data+MAC_OFFSET+3),
+ (respptr->data+MAC_OFFSET+4), (respptr->data+MAC_OFFSET+5));
+
+
+ if ( rc < 6 ){
+ fprintf(stderr, "sscanf Failed in extracting mac address.\n");
+ rc = -1;
+ break;
+ }
+
+ //Conevrt the dhcp,ipaddress,mask and gateway as hex number
+ respptr->data[MAC_OFFSET+6]=0x00;
+
+ rc = sscanf(host_config.addrType.c_str(),ADDR_TYPE_FORMAT,
+ (respptr->data+ADDRTYPE_OFFSET));
+
+ if ( rc <= 0 ) {
+ fprintf(stderr, "sscanf Failed in extracting address type.\n");
+ rc = -1;
+ break;
+ }
+
+ //ipaddress and gateway would be in IPv4 format
+ rc = inet_pton(AF_INET,host_config.ipaddress.c_str(),
+ (respptr->data+IPADDR_OFFSET));
+
+ if ( rc <= 0 ) {
+ fprintf(stderr, "inet_pton failed during ipaddress coneversion\n");
+ rc = -1;
+ break;
+ }
+
+ rc = sscanf(host_config.prefix.c_str(),PREFIX_FORMAT,
+ (respptr->data+PREFIX_OFFSET));
+
+ if ( rc <= 0 ) {
+ fprintf(stderr, "sscanf failed during prefix extraction.\n");
+ rc = -1;
+ break;
+ }
+
+ rc = inet_pton(AF_INET,host_config.gateway.c_str(),
+ (respptr->data+GATEWAY_OFFSET));
+
+ if ( rc <= 0 ) {
+ fprintf(stderr, "inet_pton failed during gateway conversion.\n");
+ rc = -1;
+ break;
+ }
+
+ }while (0);
+
+ if ( rc ) {
+
+ //PetiBoot-Specific
+ //If sucess then copy the first 9 bytes to the data
+ //else set the respptr to 0
+
+ memcpy(respptr->data,net_conf_initial_bytes,
+ sizeof(net_conf_initial_bytes));
+
+#ifdef _IPMI_DEBUG_
+ printf ("\n===Printing the IPMI Formatted Data========\n");
+
+ for ( uint8_t pos = 0; pos<index; pos++ )
+ printf("%02x ", respptr->data[pos]);
+#endif
+
+ }else {
+
+ memset(respptr->data,0,SIZE_BOOT_OPTION);
+ }
+
+ return rc;
+}
+
+int setHostNetworkData(set_sys_boot_options_t * reqptr)
+{
+ std::string host_network_config;
+ char mac[SIZE_MAC] = {0};
+ char ipAddress[INET_ADDRSTRLEN] = {0};
+ char gateway[INET_ADDRSTRLEN] = {0};
+ char dhcp[SIZE_PREFIX] = {0};
+ char prefix[SIZE_PREFIX] = {0};
+ int rc = 0;
+ uint32_t zeroCookie=0;
+
+ //cookie starts from second byte
+ // version starts from sixth byte
+
+ do {
+
+ // cookie == 0x21 0x70 0x62 0x21
+ if ( memcmp(&(reqptr->data[COOKIE_OFFSET]),
+ (net_conf_initial_bytes+COOKIE_OFFSET),
+ SIZE_COOKIE) != 0 ) {
+ //cookie == 0
+ if ( memcmp(&(reqptr->data[COOKIE_OFFSET]),
+ &zeroCookie,
+ SIZE_COOKIE) == 0 ) {
+ rc = 0;
+ break;
+ }
+ //Invalid cookie
+ fprintf(stderr, "Invalid Cookie\n");
+ rc = -1;
+ break;
+ }
+ // vesion == 0x00 0x01
+ if ( memcmp(&(reqptr->data[VERSION_OFFSET]),
+ (net_conf_initial_bytes+VERSION_OFFSET),
+ SIZE_VERSION) != 0 ) {
+
+ fprintf(stderr, "Invalid Version\n");
+ rc = -1;
+ break;
+ }
+
+ snprintf(mac, SIZE_MAC, MAC_ADDRESS_FORMAT,
+ reqptr->data[MAC_OFFSET],
+ reqptr->data[MAC_OFFSET+1],
+ reqptr->data[MAC_OFFSET+2],
+ reqptr->data[MAC_OFFSET+3],
+ reqptr->data[MAC_OFFSET+4],
+ reqptr->data[MAC_OFFSET+5]);
+
+ snprintf(dhcp,SIZE_PREFIX, ADDR_TYPE_FORMAT, reqptr->data[ADDRTYPE_OFFSET]);
+ //Validating the address type which could be
+ //either static or dynamic
+ if( *(reqptr->data+ADDRTYPE_OFFSET) > 1 ) {
+
+ fprintf(stderr, "Invalid Address Type\n");
+ rc = -1;
+ break;
+
+ }
+
+ snprintf(ipAddress, INET_ADDRSTRLEN, IP_ADDRESS_FORMAT,
+ reqptr->data[IPADDR_OFFSET], reqptr->data[IPADDR_OFFSET+1],
+ reqptr->data[IPADDR_OFFSET+2], reqptr->data[IPADDR_OFFSET+3]);
+
+ //validating prefix
+ if ( *(reqptr->data+PREFIX_OFFSET) > (uint8_t)MAX_PREFIX_VALUE ) {
+
+ fprintf(stderr, "Invalid Prefix\n");
+ rc = -1;
+ break;
+ }
+
+ snprintf(prefix,SIZE_PREFIX,PREFIX_FORMAT, reqptr->data[PREFIX_OFFSET]);
+
+ snprintf(gateway, INET_ADDRSTRLEN,IP_ADDRESS_FORMAT,
+ reqptr->data[GATEWAY_OFFSET], reqptr->data[GATEWAY_OFFSET+1],
+ reqptr->data[GATEWAY_OFFSET+2], reqptr->data[GATEWAY_OFFSET+3]);
+
+
+ }while(0);
+
+ if( !rc )
+ {
+ //Cookie == 0 or it is a valid cookie
+ host_network_config += "ipaddress="+std::string(ipAddress)+",prefix="+
+ std::string(prefix)+",gateway="+std::string(gateway)+
+ ",mac="+std::string(mac)+",addr_type="+std::string(dhcp);
+
+ printf ("Network configuration changed: %s\n",host_network_config.c_str());
+
+ rc = dbus_set_property("network_config",host_network_config.c_str());
+
+ if ( rc < 0 ) {
+ fprintf(stderr, "Dbus set property(network_config)\
+ failed for set_sys_boot_options.\n");
+ rc = -1;
+ }
+
+ }
+ return rc;
+}
+
ipmi_ret_t ipmi_chassis_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi_request_t request, ipmi_response_t response,
ipmi_data_len_t data_len, ipmi_context_t context)
@@ -326,14 +650,15 @@ ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
resp->parm = 5;
resp->data[0] = SET_PARM_BOOT_FLAGS_VALID_ONE_TIME;
- *data_len = sizeof(*resp);
/*
* Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
* This is the only parameter used by petitboot.
*/
- if (reqptr->parameter == 5) {
+ if ( reqptr->parameter == static_cast<uint8_t>
+ ( BootOptionParameter::BOOT_FLAGS )) {
+ *data_len = static_cast<uint8_t>(BootOptionResponseSize::BOOT_FLAGS);
/* Get the boot device */
int r = dbus_get_property("boot_flags",&p);
@@ -364,7 +689,7 @@ ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
} else {
- printf("BootPolicy is[%s]", p);
+ printf("BootPolicy is[%s]", p);
resp->data[0] = (strncmp(p,"ONETIME",strlen("ONETIME"))==0) ?
SET_PARM_BOOT_FLAGS_VALID_ONE_TIME:
SET_PARM_BOOT_FLAGS_VALID_PERMANENT;
@@ -373,13 +698,36 @@ ipmi_ret_t ipmi_chassis_get_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
}
- } else {
+ } else if ( reqptr->parameter == static_cast<uint8_t>
+ ( BootOptionParameter::OPAL_NETWORK_SETTINGS )) {
+
+ *data_len = static_cast<uint8_t>(BootOptionResponseSize::OPAL_NETWORK_SETTINGS);
+
+ resp->parm = static_cast<uint8_t>(BootOptionParameter::OPAL_NETWORK_SETTINGS);
+
+ int ret = getHostNetworkData(resp);
+
+ if (ret < 0) {
+
+ fprintf(stderr, "getHostNetworkData failed for get_sys_boot_options.\n");
+ rc = IPMI_CC_UNSPECIFIED_ERROR;
+
+ }else
+ rc = IPMI_CC_OK;
+ }
+
+ else {
fprintf(stderr, "Unsupported parameter 0x%x\n", reqptr->parameter);
}
if (p)
free(p);
+ if (rc == IPMI_CC_OK)
+ {
+ *data_len += 2;
+ }
+
return rc;
}
@@ -391,11 +739,10 @@ ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
{
ipmi_ret_t rc = IPMI_CC_OK;
char *s;
-
- printf("IPMI SET_SYS_BOOT_OPTIONS\n");
-
set_sys_boot_options_t *reqptr = (set_sys_boot_options_t *) request;
+ printf("IPMI SET_SYS_BOOT_OPTIONS reqptr->parameter =[%d]\n",reqptr->parameter);
+
// This IPMI command does not have any resposne data
*data_len = 0;
@@ -403,7 +750,8 @@ ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
* Parameter #5 means boot flags. Please refer to 28.13 of ipmi doc.
* This is the only parameter used by petitboot.
*/
- if (reqptr->parameter == 5) {
+
+ if (reqptr->parameter == (uint8_t)BootOptionParameter::BOOT_FLAGS) {
s = get_boot_option_by_ipmi(((reqptr->data[1] & 0x3C) >> 2));
@@ -421,12 +769,12 @@ ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
rc = IPMI_CC_UNSPECIFIED_ERROR;
}
}
-
+
/* setting the boot policy */
s = (char *)(((reqptr->data[0] & SET_PARM_BOOT_FLAGS_PERMANENT) ==
- SET_PARM_BOOT_FLAGS_PERMANENT) ?"PERMANENT":"ONETIME");
+ SET_PARM_BOOT_FLAGS_PERMANENT) ?"PERMANENT":"ONETIME");
- printf ( "\nBoot Policy is %s",s);
+ printf ( "\nBoot Policy is %s",s);
int r = dbus_set_property("boot_policy",s);
if (r < 0) {
@@ -434,7 +782,16 @@ ipmi_ret_t ipmi_chassis_set_sys_boot_options(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
rc = IPMI_CC_UNSPECIFIED_ERROR;
}
- } else {
+ } else if (reqptr->parameter ==
+ (uint8_t)BootOptionParameter::OPAL_NETWORK_SETTINGS) {
+
+ int ret = setHostNetworkData(reqptr);
+ if (ret < 0) {
+ fprintf(stderr, "setHostNetworkData failed for set_sys_boot_options.\n");
+ rc = IPMI_CC_UNSPECIFIED_ERROR;
+ }
+ }
+ else {
fprintf(stderr, "Unsupported parameter 0x%x\n", reqptr->parameter);
rc = IPMI_CC_PARM_NOT_SUPPORTED;
}
diff --git a/chassishandler.h b/chassishandler.h
index b6f7d93..e47b4a1 100644
--- a/chassishandler.h
+++ b/chassishandler.h
@@ -2,6 +2,7 @@
#define __HOST_IPMI_CHASSIS_HANDLER_H__
#include <stdint.h>
+#include <cstddef>
// IPMI commands for Chassis net functions.
enum ipmi_netfn_app_cmds
@@ -37,5 +38,16 @@ enum ipmi_chassis_control_cmds : uint8_t
CMD_PULSE_DIAGNOSTIC_INTR = 0x04,
CMD_SOFT_OFF_VIA_OVER_TEMP = 0x05,
};
+enum class BootOptionParameter : size_t
+{
+ BOOT_FLAGS = 0x5,
+ OPAL_NETWORK_SETTINGS = 0x61
+};
+
+enum class BootOptionResponseSize : size_t
+{
+ BOOT_FLAGS = 5,
+ OPAL_NETWORK_SETTINGS = 26
+};
#endif
diff --git a/docs/ipmi-network-format.md b/docs/ipmi-network-format.md
new file mode 100644
index 0000000..74dc606
--- /dev/null
+++ b/docs/ipmi-network-format.md
@@ -0,0 +1,74 @@
+On platforms running AMI BMC firmware (Habanero and Firestone at the moment),
+the 'Get/Set' System Boot Options' command has been extended to allow overrides
+to the network configuration to be specified.
+In Petitboot this will cause any existing network configuration to be overridden
+by the specified configuration. The format of each IPMI request is similar to
+that of the usual boot options request, with a slightly different payload.
+
+The start of the request is similar to a usual boot option override, but
+specifies the fields of interest differently, ie;
+
+Specify 'chassis bootdev', field 96, data1
+0x00 0x08 0x61 0x80
+
+The rest of request format is defined by Petitboot as:
+ - 4 byte cookie value (always 0x21 0x70 0x62 0x21)
+ - 2 byte version value (always 0x00 0x01)
+ - 1 byte hardware address size (eg. 0x06 for MAC address)
+ - 1 byte IP address size (eg. 0x04 for IPv4)
+ - Hardware (MAC) address
+ - 1 byte flags for 'ignore' and 'method', where method = 0 is DHCP
+ and method = 1 is Static.
+And for static configs:
+ - IP Address
+ - 1 byte subnet value
+ - Gateway address
+
+Describing each field in more detail:
+
+Specify 'chassis bootdev', field 96, data1
+0x00 0x08 0x61 0x80
+
+Set a special cookie that Petitboot will recognise:
+0x21 0x70 0x62 0x21
+
+Specify the version (only 1 at the moment)
+0x00 0x01
+
+Specify the size of the MAC address and IP address. This is used to
+differentiate between IPv4 and IPv6 addresses, or potential future support for
+Infiniband/etc.
+0x06 0x04 (6-byte MAC address, IPv4 IP address)
+
+Set the hardware address of the interface you want to override, eg:
+0xf4 0x52 0x14 0xf3 0x01 0xdf
+
+Specify 'ignore' or 'static/dynamic' flags. The second byte specifies to use
+either DHCP or static IP configuration (0 for DHCP).
+0x00 0x01
+
+The below fields are required if setting a static configuration:
+
+Set the IP address you want to use, eg:
+0x0a 0x3d 0xa1 0x42
+
+Set the subnet mask (short notation), eg '16':
+0x10
+
+Set the gateway address, eg:
+0x0a 0x3d 0x2 0x1
+
+All together this should look like:
+0x00 0x08 0x61 0x80 0x21 0x70 0x62 0x21
+0x00 0x01 0x06 0x04 0xf4 0x52 0x14 0xf3
+0x01 0xdf 0x00 0x01 0x0a 0x3d 0xa1 0x42
+0x10 0x0a 0x3d 0x2 0x1
+
+To clear a network override, it is sufficient to clear out the request, or set
+a zero-cookie which Petitboot will reject. Eg:
+0x00 0x08 0x61 0x80 0x00 0x00 0x00 0x00
+
+You can 'Get' the override back with
+0x00 0x09 0x61 0x80 0x00
+which should return whatever is currently set.
+
OpenPOWER on IntegriCloud