Skip to content

Commit

Permalink
feat: adds event log decoder
Browse files Browse the repository at this point in the history
  • Loading branch information
matt-primrose committed May 22, 2024
1 parent 60e62db commit 7a37770
Show file tree
Hide file tree
Showing 5 changed files with 417 additions and 7 deletions.
265 changes: 265 additions & 0 deletions pkg/wsman/amt/messagelog/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@

package messagelog

import (
"bytes"
"encoding/base64"
"encoding/binary"
"fmt"
"time"
)

const (
AMTMessageLog string = "AMT_MessageLog"
GetRecords string = "GetRecords"
Expand Down Expand Up @@ -409,3 +417,260 @@ func (p PositionToFirstRecordReturnValue) String() string {

return ValueNotFound
}

func parseEventLogResult(eventlogdata []string) (records []RawEventData, err error) {
records = []RawEventData{}

for _, eventRecord := range eventlogdata {
decodedEventRecord, err := base64.StdEncoding.DecodeString(eventRecord)
if err != nil {
continue
}

eventData := RawEventData{}

buf := bytes.NewReader(decodedEventRecord)

err = binary.Read(buf, binary.LittleEndian, &eventData.TimeStamp)
if err != nil {
return records, err
}

err = binary.Read(buf, binary.LittleEndian, &eventData.DeviceAddress)
if err != nil {
return records, err
}

err = binary.Read(buf, binary.LittleEndian, &eventData.EventSensorType)
if err != nil {
return records, err
}

err = binary.Read(buf, binary.LittleEndian, &eventData.EventType)
if err != nil {
return records, err
}

err = binary.Read(buf, binary.LittleEndian, &eventData.EventOffset)
if err != nil {
return records, err
}

err = binary.Read(buf, binary.LittleEndian, &eventData.EventSourceType)
if err != nil {
return records, err
}

err = binary.Read(buf, binary.LittleEndian, &eventData.EventSeverity)
if err != nil {
return records, err
}

err = binary.Read(buf, binary.LittleEndian, &eventData.SensorNumber)
if err != nil {
return records, err
}

err = binary.Read(buf, binary.LittleEndian, &eventData.Entity)
if err != nil {
return records, err
}

err = binary.Read(buf, binary.LittleEndian, &eventData.EntityInstance)
if err != nil {
return records, err
}

for i := 13; i < 21; i++ {
var b uint8

err = binary.Read(buf, binary.LittleEndian, &b)
if err != nil {
return records, err
}

eventData.EventData = append(eventData.EventData, b)
}

records = append(records, eventData)
}

return records, err
}

func decodeEventRecord(eventLog []RawEventData) []RefinedEventData {
refinedEventData := []RefinedEventData{}

for _, event := range eventLog {
decodedEvent := RefinedEventData{
TimeStamp: time.Unix(int64(event.TimeStamp), 0),
Description: decodeEventDetailString(event.EventSensorType, event.EventOffset, event.EventData),
Entity: SystemEntityTypes[int(event.Entity)],
EventSeverity: EventSeverity[int(event.EventSeverity)],
}
refinedEventData = append(refinedEventData, decodedEvent)
}

return refinedEventData
}

func decodeEventDetailString(eventSensorType, eventOffset uint8, eventDataField []uint8) string {
switch eventSensorType {
case 6:
value := int(eventDataField[1]) + (int(eventDataField[2]) << 8)

return fmt.Sprintf("Authentication failed %d times. The system may be under attack.", value)
case 15:
{
if eventDataField[0] == 235 {
return "Invalid Data"
}

if eventOffset == 0 {
return SystemFirmwareError[int(eventDataField[1])]
}

return SystemFirmwareProgress[int(eventDataField[1])]
}
case 18:
// System watchdog event
if eventDataField[0] == 170 {
watchdog := fmt.Sprintf("%x%x%x%x-%x%x", eventDataField[4], eventDataField[3], eventDataField[2], eventDataField[1], eventDataField[6], eventDataField[5])
watchdogCurrentState := WatchdogCurrentStates[int(eventDataField[7])]

return fmt.Sprintf("Agent watchdog %s-... changed to %s", watchdog, watchdogCurrentState)
}

return "Unknown event data field"
case 30:
return "No bootable media"
case 32:
return "Operating system lockup or power interrupt"
case 35:
return "System boot failure"
case 37:
return "System firmware started (at least one CPU is properly executing)."
default:
return fmt.Sprintf("Unknown Sensor Type #%d", eventSensorType)
}
}

var EventSeverity = map[int]string{
0: "Unspecified",
1: "Monitor",
2: "Information",
4: "OK",
8: "Non-critical condition",
16: "Critical condition",
32: "Non-recoverable condition",
}

var SystemEntityTypes = map[int]string{
0: "Unspecified",
1: "Other",
2: "Unknown",
3: "Processor",
4: "Disk",
5: "Peripheral",
6: "System management module",
7: "System board",
8: "Memory module",
9: "Processor module",
10: "Power supply",
11: "Add in card",
12: "Front panel board",
13: "Back panel board",
14: "Power system board",
15: "Drive backplane",
16: "System internal expansion board",
17: "Other system board",
18: "Processor board",
19: "Power unit",
20: "Power module",
21: "Power management board",
22: "Chassis back panel board",
23: "System chassis",
24: "Sub chassis",
25: "Other chassis board",
26: "Disk drive bay",
27: "Peripheral bay",
28: "Device bay",
29: "Fan cooling",
30: "Cooling unit",
31: "Cable interconnect",
32: "Memory device",
33: "System management software",
34: "BIOS",
35: "Intel(r) ME",
36: "System bus",
37: "Group",
38: "Intel(r) ME",
39: "External environment",
40: "Battery",
41: "Processing blade",
42: "Connectivity switch",
43: "Processor/memory module",
44: "I/O module",
45: "Processor I/O module",
46: "Management controller firmware",
47: "IPMI channel",
48: "PCI bus",
49: "PCI express bus",
50: "SCSI bus",
51: "SATA/SAS bus",
52: "Processor front side bus",
}

var SystemFirmwareError = map[int]string{
0: "Unspecified.",
1: "No system memory is physically installed in the system.",
2: "No usable system memory, all installed memory has experienced an unrecoverable failure.",
3: "Unrecoverable hard-disk/ATAPI/IDE device failure.",
4: "Unrecoverable system-board failure.",
5: "Unrecoverable diskette subsystem failure.",
6: "Unrecoverable hard-disk controller failure.",
7: "Unrecoverable PS/2 or USB keyboard failure.",
8: "Removable boot media not found.",
9: "Unrecoverable video controller failure.",
10: "No video device detected.",
11: "Firmware (BIOS) ROM corruption detected.",
12: "CPU voltage mismatch (processors that share same supply have mismatched voltage requirements)",
13: "CPU speed matching failure",
}

var SystemFirmwareProgress = map[int]string{
0: "Unspecified.",
1: "Memory initialization.",
2: "Starting hard-disk initialization and test",
3: "Secondary processor(s) initialization",
4: "User authentication",
5: "User-initiated system setup",
6: "USB resource configuration",
7: "PCI resource configuration",
8: "Option ROM initialization",
9: "Video initialization",
10: "Cache initialization",
11: "SM Bus initialization",
12: "Keyboard controller initialization",
13: "Embedded controller/management controller initialization",
14: "Docking station attachment",
15: "Enabling docking station",
16: "Docking station ejection",
17: "Disabling docking station",
18: "Calling operating system wake-up vector",
19: "Starting operating system boot process",
20: "Baseboard or motherboard initialization",
21: "reserved",
22: "Floppy initialization",
23: "Keyboard test",
24: "Pointing device test",
25: "Primary processor initialization",
}

var WatchdogCurrentStates = map[int]string{
1: "Not Started",
2: "Stopped",
4: "Running",
8: "Expired",
16: "Suspended",
}
54 changes: 53 additions & 1 deletion pkg/wsman/amt/messagelog/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@

package messagelog

import "testing"
import (
"fmt"
"testing"
"time"

"github.com/stretchr/testify/assert"
)

func TestCapabilities_String(t *testing.T) {
tests := []struct {
Expand Down Expand Up @@ -301,3 +307,49 @@ func TestPositionToFirstRecordReturnValue_String(t *testing.T) {
}
}
}

func TestConvertToEventLogResult(t *testing.T) {
records := []string{"Y8iYZf8GbwVoEP8mYaoKAAAAAAAA", "IgYBZf8PbwJoAf8iAEAHAAAAAAAA", "IgYBZf8PbwJoAf8iAEAHAAAAAAAA"}
expectedResult := []RawEventData{{TimeStamp: 0x6598c863, DeviceAddress: 0xff, EventSensorType: 0x6, EventType: 0x6f, EventOffset: 0x5, EventSourceType: 0x68, EventSeverity: 0x10, SensorNumber: 0xff, Entity: 0x26, EntityInstance: 0x61, EventData: []uint8{0xaa, 0xa, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, {TimeStamp: 0x65010622, DeviceAddress: 0xff, EventSensorType: 0xf, EventType: 0x6f, EventOffset: 0x2, EventSourceType: 0x68, EventSeverity: 0x1, SensorNumber: 0xff, Entity: 0x22, EntityInstance: 0x0, EventData: []uint8{0x40, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}, {TimeStamp: 0x65010622, DeviceAddress: 0xff, EventSensorType: 0xf, EventType: 0x6f, EventOffset: 0x2, EventSourceType: 0x68, EventSeverity: 0x1, SensorNumber: 0xff, Entity: 0x22, EntityInstance: 0x0, EventData: []uint8{0x40, 0x7, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}}
expectedDecodedResult := []RefinedEventData{{TimeStamp: time.Unix(int64(0x6598c863), 0), Description: "Authentication failed 10 times. The system may be under attack.", Entity: "Intel(r) ME", EventSeverity: "Critical condition"}, {TimeStamp: time.Unix(int64(0x65010622), 0), Description: "PCI resource configuration", Entity: "BIOS", EventSeverity: "Monitor"}, {TimeStamp: time.Unix(int64(0x65010622), 0), Description: "PCI resource configuration", Entity: "BIOS", EventSeverity: "Monitor"}}

result, err := parseEventLogResult(records)
if err != nil {
t.Errorf("Error: %v", err)
}

decodedResult := decodeEventRecord(result)

assert.Equal(t, expectedResult, result)
assert.Equal(t, expectedDecodedResult, decodedResult)
}

func TestDecodeEventDetailString(t *testing.T) {
tests := []struct {
eventSensorType uint8
eventOffset uint8
eventDataField []uint8
expected string
}{
{6, 0, []uint8{0, 5, 0}, "Authentication failed 5 times. The system may be under attack."},
{6, 0, []uint8{0, 1, 1}, "Authentication failed 257 times. The system may be under attack."},
{15, 0, []uint8{235}, "Invalid Data"},
{15, 0, []uint8{0, 1}, "No system memory is physically installed in the system."},
{15, 1, []uint8{0, 2}, "Starting hard-disk initialization and test"},
{18, 0, []uint8{170, 1, 2, 3, 4, 5, 6, 1}, "Agent watchdog 4321-65-... changed to Not Started"},
{30, 0, nil, "No bootable media"},
{32, 0, nil, "Operating system lockup or power interrupt"},
{35, 0, nil, "System boot failure"},
{37, 0, nil, "System firmware started (at least one CPU is properly executing)."},
{0, 0, nil, "Unknown Sensor Type #0"},
}

for _, test := range tests {
t.Run(fmt.Sprintf("eventSensorType=%d/eventOffset=%d", test.eventSensorType, test.eventOffset), func(t *testing.T) {
result := decodeEventDetailString(test.eventSensorType, test.eventOffset, test.eventDataField)
if result != test.expected {
t.Errorf("Expected %q but got %q", test.expected, result)
}
})
}
}
13 changes: 10 additions & 3 deletions pkg/wsman/amt/messagelog/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,15 +124,22 @@ func (messageLog Service) GetRecords(identifier int) (response Response, err err
// send the message to AMT
err = messageLog.base.Execute(response.Message)
if err != nil {
return
return response, err
}
// put the xml response into the go struct
err = xml.Unmarshal([]byte(response.XMLOutput), &response)
if err != nil {
return
return response, err
}

return
response.Body.GetRecordsResponse.RawEventData, err = parseEventLogResult(response.Body.GetRecordsResponse.RecordArray)
if err != nil {
return response, err
}

response.Body.GetRecordsResponse.RefinedEventData = decodeEventRecord(response.Body.GetRecordsResponse.RawEventData)

return response, err
}

// Requests that an iteration of the MessageLog be established and that the iterator be set to the first entry in the Log. An identifier for the iterator is returned as an output parameter of the method. Regarding iteration, you have 2 choices: 1) Embed iteration data in the method call, and allow implementations to track/ store this data manually; or, 2) Iterate using a separate object (for example, class ActiveIterator) as an iteration agent. The first approach is used here for interoperability. The second requires an instance of the Iterator object for EACH iteration in progress. 2's functionality could be implemented underneath 1.
Expand Down
Loading

0 comments on commit 7a37770

Please sign in to comment.