Implement Network Settings Override
We want to implement the network settings override functionality.
This is an OEM boot parameter selector, using parameter ID 0x61.
The first byte of the parameter data is the same as parameter ID 5 (ie,
the valid flag in the top bit). Persistency doesn't matter here, as
petitboot will not clear out this setting.
The rest of the parameter data is a host network settings description.
For other BMC firmware, the BMC just treats this as opaque (the BMC will
just return the same data (including that data1 byte), in response to a
Get System Boot Option). For OpenBMC, we may want the BMC to know how to
construct that data; the spec for that is at:
http://ozlabs.org/~sam/ipmi-network-format
Closes openbmc/openbmc#267
Change-Id: I2af8776718deda97acf90d10504783085ccf7323
Signed-off-by: Ratan Gupta <ratagupt@in.ibm.com>
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_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 @@
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 @@
} 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 @@
}
- } 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 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 @@
* 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 @@
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 @@
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 @@
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.
+