diff options
-rw-r--r-- | chassishandler.cpp | 397 | ||||
-rw-r--r-- | chassishandler.h | 12 | ||||
-rw-r--r-- | docs/ipmi-network-format.md | 74 |
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. + |