From 0b6abe8cd97e9cdfd56ef6a4154bfd87775bb4bb Mon Sep 17 00:00:00 2001 From: Alexander Amelkin Date: Wed, 1 Aug 2018 23:03:55 +0300 Subject: [PATCH] mc: guid: Fix timestamp decoding Before this commit the 'Timestamp' line was always printed for all versions of GUID, even for non-time-based ones. Plus, only the time_low field was used, and it was used as if it contained seconds since UNIX Epoch, which it didn't. In fact this field along with other time_* fields constitute a single 60-bit value representing the count of 100ns intervals since adoption of Gregorial calendar (00:00:00.00 15 Oct 1582). For non-time-based versions of GUID, the time_* fields do not represent any time at all. So, after this commit, the timestamp will be properly decoded for time-based GUID version 1 only. For other versions the 'Timestamp' line will not be displayed. A line showing the GUID version will be added to the output. Partially resolves ipmitool/ipmitool#25 Signed-off-by: Alexander Amelkin --- include/ipmitool/ipmi_mc.h | 19 ++++++++++++ lib/ipmi_mc.c | 61 +++++++++++++++++++++++++++++++------- 2 files changed, 70 insertions(+), 10 deletions(-) diff --git a/include/ipmitool/ipmi_mc.h b/include/ipmitool/ipmi_mc.h index 38b67eb..a5e5cb2 100644 --- a/include/ipmitool/ipmi_mc.h +++ b/include/ipmitool/ipmi_mc.h @@ -110,6 +110,25 @@ typedef enum { #define GUID_NODE_SZ 6 +#define GUID_VER_MASK 0x0F +#define GUID_VER_SHIFT 12 +#define GUID_VERSION(t_hi) (((t_hi) >> GUID_VER_SHIFT) & GUID_VER_MASK) +#define GUID_TIME_HI(t_hi) ((t_hi) & ~(GUID_VER_MASK << GUID_VER_SHIFT)) + +typedef enum { + GUID_VERSION_UNKNOWN = 0, /* Not valid according to any specification */ + + /* The following are according to IPMI/SMBIOS/RFC4122 */ + GUID_VERSION_TIME, /* Time-based, recommended for IPMI */ + GUID_VERSION_DCE, /* DCE Security with POSIX UIDs, not for IPMI */ + GUID_VERSION_MD5, /* Name-based, using MD5 */ + GUID_VERSION_RND, /* Randomly generated */ + GUID_VERSION_SHA1, /* Name-based, using SHA-1 */ + + GUID_VERSION_MAX = GUID_VERSION_SHA1, /* The maximum supported version */ + GUID_VERSION_COUNT /* The number of supported versions */ +} guid_version_t; + /* The structure follows IPMI v2.0, rev 1.1 * See section 20.8 */ #ifdef HAVE_PRAGMA_PACK diff --git a/lib/ipmi_mc.c b/lib/ipmi_mc.c index fbd261a..7baf0ef 100644 --- a/lib/ipmi_mc.c +++ b/lib/ipmi_mc.c @@ -540,9 +540,19 @@ ipmi_mc_print_guid(struct ipmi_intf *intf, ipmi_guid_mode_t guid_mode) 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; + uint64_t time_hi_and_version; + uint64_t time_mid; + uint64_t time_low; + + guid_version_t guid_ver; + const char *guid_ver_str[GUID_VERSION_COUNT] = { + [GUID_VERSION_UNKNOWN] = "Unknown/unsupported", + [GUID_VERSION_TIME] = "Time-based", + [GUID_VERSION_DCE] = "DCE Security with POSIX UIDs (not for IPMI)", + [GUID_VERSION_MD5] = "Name-based using MD5", + [GUID_VERSION_RND] = "Random or pseudo-random", + [GUID_VERSION_SHA1] = "Name-based using SHA-1" + }; char tbuf[40] = { 0 }; struct tm *tm; @@ -592,16 +602,47 @@ ipmi_mc_print_guid(struct ipmi_intf *intf, ipmi_guid_mode_t guid_mode) } printf("%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x\n", - time_low, time_mid, time_hi_and_version, + (int)time_low, (int)time_mid, (int)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); + guid_ver = GUID_VERSION(time_hi_and_version); + + if (guid_ver > GUID_VERSION_MAX) { + /* Reset any unsupported value fto UNKNOWN */ + guid_ver = GUID_VERSION_UNKNOWN; + } + + printf("GUID Version : %s", guid_ver_str[guid_ver]); + if (GUID_VERSION_UNKNOWN == guid_ver) + printf(" (%d)", GUID_VERSION((int)time_hi_and_version)); + printf("\n"); + + if (GUID_VERSION_TIME == guid_ver) { + /* GUID time-stamp is a 60-bit value representing the + * count of 100ns intervals since 00:00:00.00, 15 Oct 1582 */ + + const uint64_t t100ns_in_sec = 10000000LL; + + /* Seconds from 15 Oct 1582 to 1 Jan 1970 00:00:00 */ + uint64_t epoch_since_gregorian = 12219292800; + + /* 100ns intervals since 15 Oct 1582 00:00:00 */ + uint64_t gregorian = (GUID_TIME_HI(time_hi_and_version) << 48) + | (time_mid << 32) + | time_low; + time_t unixtime; /* We need timestamp in seconds since UNIX epoch */ + + gregorian /= t100ns_in_sec; /* Convert to seconds */ + unixtime = gregorian - epoch_since_gregorian; + + if(time_in_utc) + tm = gmtime(&unixtime); + else + tm = localtime(&unixtime); + strftime(tbuf, sizeof(tbuf), "%m/%d/%Y %H:%M:%S", tm); + printf("Timestamp : %s\n", tbuf); + } return 0; }