mc: guid: Add support for non-standard encodings

There are lots of BMC implementations that violate the IPMI
specification in regard to GUID encoding and instead encode
GUIDs according to either RFC4122 or SMBIOS specifications.

This commit restores the default behavior of `mc guid` to
SMBIOS-based decoding and adds options to allow for decoding
according to IPMI or RFC4122 specifications.

It also allows to simply dump the received GUID as is
without any parsing.

It is possible that in future versions `ipmitool` will
change default behavior to 'ipmi' instead of 'smbios'.

Partially resolves ipmitool/ipmitool#25

Signed-off-by: Alexander Amelkin <alexander@amelkin.msk.ru>
This commit is contained in:
Alexander Amelkin 2018-08-01 16:34:04 +03:00
parent b44ec2fb65
commit f43a78bfc3
No known key found for this signature in database
GPG Key ID: E893587B5B74178D
3 changed files with 177 additions and 51 deletions

View File

@ -2017,9 +2017,50 @@ The default will clear statistics on the first found LAN channel.
Instructs the BMC to perform a warm or cold reset. Instructs the BMC to perform a warm or cold reset.
.TP .TP
\fIguid\fP \fIguid\fP [\fBsmbios\fR|\fBrfc4122\fR|\fBipmi\fR|\fBdump\fR]
Display the Management Controller Globally Unique IDentifier. Display the Management Controller Globally Unique IDentifier.
.RS
.TP
\fIsmbios\fP
.br
This is the default behavior for ipmitool for the sake of compatibility
with old versions and broken BMC implementations whose number is legion.
Decode GUID as if it was sent by BMC as prescribed by SMBIOS specification.
\fBNOTE:\fR This is a violation of IPMI specification, but many BMC implementations do
it this way. If your BMC's GUID is shown correctly using this option, you
may want to inform your BMC manufacturer that they have a bug.
.TP
\fIrfc4122\fP or \fIrfc\fP
.br
Decode GUID as if it was sent by BMC as prescribed by RFC4122 specification.
\fBNOTE:\fR This is a violation of IPMI specification.
If your BMC's GUID is shown correctly using this option, you
may want to inform your BMC manufacturer that they have a bug.
.TP
\fIipmi\fP
.br
Decode GUID according to IPMI specification. It MUST show the correct GUID.
If it doesn't, try other options above and inform your BMC manufacturer of the bug.
.TP
\fIdump\fP
.br
Dump as hex the data received from BMC in response to Get Device GUID command.
No decoding or interpretation is performed. First received byte is dumped first.
.RE
.TP .TP
\fIinfo\fP \fIinfo\fP
.br .br

View File

@ -97,19 +97,32 @@ struct ipm_devid_rsp {
#define IPM_DEV_ADTL_SUPPORT_BITS (8) #define IPM_DEV_ADTL_SUPPORT_BITS (8)
/* Structure follow the IPMI V.2 Rev 1.0 /* There are lots of BMC implementations that don't follow the IPMI
* See Table 20-10 */ * specification for GUID encoding. Some send data encoded as in
* RFC4122, some follow SMBIOS specification. We support all users
* of those buggy implementations here */
typedef enum {
GUID_IPMI,
GUID_SMBIOS,
GUID_RFC4122,
GUID_DUMP
} ipmi_guid_mode_t;
#define GUID_NODE_SZ 6
/* The structure follows IPMI v2.0, rev 1.1
* See section 20.8 */
#ifdef HAVE_PRAGMA_PACK #ifdef HAVE_PRAGMA_PACK
#pragma pack(1) #pragma pack(1)
#endif #endif
struct ipmi_guid_t { struct ipmi_guid_t {
uint8_t node[6]; /* node */ uint8_t node[GUID_NODE_SZ]; /* Byte 0 is LSB */
union { union {
struct { struct {
uint8_t clock_seq_low; /* clock sequence low field */ uint8_t clock_seq_low; /* clock sequence low field */
uint8_t clock_seq_hi_variant;/* clock sequence high field and variant */ uint8_t clock_seq_hi_and_rsvd;/* clock sequence high field */
}; };
uint16_t clock_seq_variant; uint16_t clock_seq_and_rsvd;
}; };
uint16_t time_hi_and_version; /* timestamp high field and version number */ uint16_t time_hi_and_version; /* timestamp high field and version number */
uint16_t time_mid; /* timestamp middle field */ uint16_t time_mid; /* timestamp middle field */
@ -119,6 +132,28 @@ struct ipmi_guid_t {
#pragma pack(0) #pragma pack(0)
#endif #endif
/* The structure follows RFC4122 (section 4.1.2)
* and SMBIOS v3.0.0 (section 7.2.1) */
#ifdef HAVE_PRAGMA_PACK
#pragma pack(1)
#endif
struct rfc_guid_t {
uint32_t time_low; /* timestamp low field */
uint16_t time_mid; /* timestamp middle field */
uint16_t time_hi_and_version; /* timestamp high field and version number */
union {
struct {
uint8_t clock_seq_hi_and_rsvd;/* clock sequence high field */
uint8_t clock_seq_low; /* clock sequence low field */
};
uint16_t clock_seq_and_rsvd;
};
uint8_t node[GUID_NODE_SZ]; /* Byte 0 is MSB */
} ATTRIBUTE_PACKING;
#ifdef HAVE_PRAGMA_PACK
#pragma pack(0)
#endif
int _ipmi_mc_get_guid(struct ipmi_intf *, struct ipmi_guid_t *); int _ipmi_mc_get_guid(struct ipmi_intf *, struct ipmi_guid_t *);
#ifdef HAVE_PRAGMA_PACK #ifdef HAVE_PRAGMA_PACK

View File

@ -38,6 +38,8 @@
#include <stdbool.h> #include <stdbool.h>
#include <endian.h> #include <endian.h>
#include <arpa/inet.h>
#include <ipmitool/helper.h> #include <ipmitool/helper.h>
#include <ipmitool/log.h> #include <ipmitool/log.h>
#include <ipmitool/bswap.h> #include <ipmitool/bswap.h>
@ -184,7 +186,7 @@ printf_mc_usage(void)
struct bitfield_data * bf; struct bitfield_data * bf;
lprintf(LOG_NOTICE, "MC Commands:"); lprintf(LOG_NOTICE, "MC Commands:");
lprintf(LOG_NOTICE, " reset <warm|cold>"); lprintf(LOG_NOTICE, " reset <warm|cold>");
lprintf(LOG_NOTICE, " guid"); lprintf(LOG_NOTICE, " guid [smbios|rfc4122|ipmi|dump]");
lprintf(LOG_NOTICE, " info"); lprintf(LOG_NOTICE, " info");
lprintf(LOG_NOTICE, " watchdog <get|reset|off>"); lprintf(LOG_NOTICE, " watchdog <get|reset|off>");
lprintf(LOG_NOTICE, " selftest"); lprintf(LOG_NOTICE, " selftest");
@ -521,54 +523,86 @@ _ipmi_mc_get_guid(struct ipmi_intf *intf, struct ipmi_guid_t *guid)
/* ipmi_mc_print_guid - print-out given BMC GUID /* ipmi_mc_print_guid - print-out given BMC GUID
* *
* @guid - struct with GUID. * @param[in] intf - The IPMI interface to request GUID from
* @param[in] guid_mode - GUID decoding mode
* *
* returns 0 * @returns status code
* @retval 0 - Success
* @retval -1 - Error
*/ */
static int static int
ipmi_mc_print_guid(struct ipmi_guid_t guid) ipmi_mc_print_guid(struct ipmi_intf *intf, ipmi_guid_mode_t guid_mode)
{ {
char tbuf[40]; /* Field order is different for RFC4122/SMBIOS and for IPMI */
time_t s; struct ipmi_guid_t ipmi_guid;
memset(tbuf, 0, 40); struct rfc_guid_t *rfc_guid; /* Alias pointer */
uint8_t node[GUID_NODE_SZ]; /* MSB first */
/* These are host architecture specific */
uint16_t clock_seq_and_rsvd;
uint16_t time_hi_and_version;
uint16_t time_mid;
uint32_t time_low;
char tbuf[40] = { 0 };
struct tm *tm; struct tm *tm;
printf("System GUID : %08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x\n",
/* We're displaying GUID as hex integers. Thus we need them to be
* in host byte order before we display them. However, according to
* IPMI 2.0, all GUID fields are in LSB first (Little Endian)
* format. Hence the ipmiXtoh() calls:
*/
ipmi32toh(&guid.time_low),
ipmi16toh(&guid.time_mid),
ipmi16toh(&guid.time_hi_and_version),
ipmi16toh(&guid.clock_seq_variant),
/* The node part is shown as bytes, so no additional conversion */
guid.node[5], guid.node[4], guid.node[3],
guid.node[2], guid.node[1], guid.node[0]);
s = (time_t)ipmi32toh(&guid.time_low);
if(time_in_utc)
tm = gmtime(&s);
else
tm = localtime(&s);
strftime(tbuf, sizeof(tbuf), "%m/%d/%Y %H:%M:%S", tm);
printf("Timestamp : %s\n", tbuf);
return 0;
}
/* ipmi_mc_get_guid - Gets and prints-out System GUID */
int
ipmi_mc_get_guid(struct ipmi_intf *intf)
{
struct ipmi_guid_t guid;
int rc; int rc;
rc = _ipmi_mc_get_guid(intf, &guid);
rc = _ipmi_mc_get_guid(intf, &ipmi_guid);
if (eval_ccode(rc) != 0) { if (eval_ccode(rc) != 0) {
return (-1); return (-1);
} }
rc = ipmi_mc_print_guid(guid);
return rc; printf("System GUID : ");
if (GUID_DUMP == guid_mode) {
size_t i;
for (i = 0; i < sizeof(ipmi_guid); ++i) {
printf("%02X", ((uint8_t *)&ipmi_guid)[i]);
}
printf("\n");
return 0;
}
if (GUID_IPMI == guid_mode) {
/* In IPMI all fields are little-endian (LSB first) */
memcpy(node,
array_byteswap(ipmi_guid.node, GUID_NODE_SZ),
GUID_NODE_SZ);
clock_seq_and_rsvd = ipmi16toh(&ipmi_guid.clock_seq_and_rsvd);
time_low = ipmi32toh(&ipmi_guid.time_low);
time_mid = ipmi16toh(&ipmi_guid.time_mid);
time_hi_and_version = ipmi16toh(&ipmi_guid.time_hi_and_version);
} else {
/* For RFC4122 all fields are in network byte order (MSB first) */
rfc_guid = (struct rfc_guid_t *)(&ipmi_guid);
memcpy(node,
rfc_guid->node,
GUID_NODE_SZ);
clock_seq_and_rsvd = ntohs(rfc_guid->clock_seq_and_rsvd);
if (GUID_RFC4122 == guid_mode) {
time_low = ntohl(rfc_guid->time_low);
time_mid = ntohs(rfc_guid->time_mid);
time_hi_and_version = ntohs(rfc_guid->time_hi_and_version);
} else {
/* For SMBIOS time fields are little-endian (as in IPMI) */
time_low = ipmi32toh(&rfc_guid->time_low);
time_mid = ipmi16toh(&rfc_guid->time_mid);
time_hi_and_version = ipmi16toh(&rfc_guid->time_hi_and_version);
}
}
printf("%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x\n",
time_low, time_mid, time_hi_and_version,
clock_seq_and_rsvd,
node[0], node[1], node[2], node[3], node[4], node[5]);
if(time_in_utc)
tm = gmtime(&time_low);
else
tm = localtime(&time_low);
strftime(tbuf, sizeof(tbuf), "%m/%d/%Y %H:%M:%S", tm);
printf("Timestamp : %s\n", tbuf);
return 0;
} }
/* ipmi_mc_get_selftest - returns and print selftest results /* ipmi_mc_get_selftest - returns and print selftest results
@ -1102,7 +1136,23 @@ ipmi_mc_main(struct ipmi_intf * intf, int argc, char ** argv)
rc = ipmi_mc_get_deviceid(intf); rc = ipmi_mc_get_deviceid(intf);
} }
else if (strncmp(argv[0], "guid", 4) == 0) { else if (strncmp(argv[0], "guid", 4) == 0) {
rc = ipmi_mc_get_guid(intf); /* Most implementations like AMI MegaRAC do it the SMBIOS way.
* This is the legacy behavior we don't want to break yet. */
ipmi_guid_mode_t guid_mode = GUID_SMBIOS;
/* Allow for 'rfc' and 'rfc4122' */
if (argc > 1) {
if (!strncmp(argv[1], "rfc", 3)) {
guid_mode = GUID_RFC4122;
}
else if (!strcmp(argv[1], "ipmi")) {
guid_mode = GUID_IPMI;
}
else if (!strcmp(argv[1], "dump")) {
guid_mode = GUID_DUMP;
}
}
rc = ipmi_mc_print_guid(intf, guid_mode);
} }
else if (strncmp(argv[0], "getenables", 10) == 0) { else if (strncmp(argv[0], "getenables", 10) == 0) {
rc = ipmi_mc_get_enables(intf); rc = ipmi_mc_get_enables(intf);