Create an OEM Intel IPMI whitelist filter

This is a derivative of the upstream whitelist filter that also tracks
the host state as part of the filtering information.

In addition, the filter understands per-channel filtering, so a command
can be filtered out on a per-channel basis.

Tested: With the upstream filter disabled, and this filter enabled, run
        several checks:
	1) execute a command prior to host POST completete over kcs (OK)
	2) execute a command after host POST complete (denied)
	3) execute a command filtered on a lan interface
	4) execute a command whitelisted on a lan interface

Change-Id: I9b6755f4bf2c9b9e30de0289f1fed68b3709dea0
Signed-off-by: Vernon Mauery <vernon.mauery@linux.intel.com>
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 46cb8e8..f484d47 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -88,13 +88,22 @@
 find_package (OpenSSL REQUIRED)
 include_directories (SYSTEM ${OPENSSL_INCLUDE_DIR})
 
+include_directories (SYSTEM ${CMAKE_BINARY_DIR})
+add_custom_command(OUTPUT include/ipmi-whitelist.hpp
+                  COMMAND ./generate-whitelist.py
+                  ARGS ipmi-whitelist.conf ${CMAKE_BINARY_DIR}/ipmi-whitelist.hpp
+                  MAIN_DEPENDENCY ipmi-whitelist.conf
+                  DEPENDS generate-whitelist.py
+                  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+
 add_library (zinteloemcmds
              SHARED src/oemcommands.cpp src/sensorcommands.cpp
              src/storagecommands.cpp src/multinodecommands.cpp
              src/firmware-update.cpp src/appcommands.cpp src/smbioshandler.cpp
              src/smbiosmdrv2handler.cpp src/manufacturingcommands.cpp
              src/bmccontrolservices.cpp src/bridgingcommands.cpp
-             src/ipmi_to_redfish_hooks.cpp src/chassiscommands.cpp)
+             src/ipmi_to_redfish_hooks.cpp src/chassiscommands.cpp
+             src/whitelist-filter.cpp include/ipmi-whitelist.hpp)
 set_target_properties (zinteloemcmds PROPERTIES VERSION "0.1.0")
 set_target_properties (zinteloemcmds PROPERTIES SOVERSION "0")
 target_link_libraries (zinteloemcmds stdc++fs)
diff --git a/generate-whitelist.py b/generate-whitelist.py
new file mode 100755
index 0000000..c732726
--- /dev/null
+++ b/generate-whitelist.py
@@ -0,0 +1,107 @@
+#!/usr/bin/env python3
+
+import re, sys, os.path
+
+def usage():
+    sys.stderr.write("Usage: $0 whitelist-config-in whitelist-header-out\n")
+    sys.stderr.write("    Reads in whitelist config, sorting the contents\n")
+    sys.stderr.write("    and outputs a header file\n")
+    sys.exit(-1)
+
+class Error(Exception):
+    pass
+
+class DuplicateEntry(Error):
+    def __init__(self, e):
+        super(Error, self).__init__(
+             "Multiple entries with matching netfn/cmd found ({})".format(e))
+
+class ParseError(Error):
+    def __init__(self, d):
+        super(Error, self).__init__("Parse error at: '{}'".format(d))
+
+class entry:
+    linere = re.compile(
+            r'(0x[0-9a-f]{2}):(0x[0-9a-f]{2})((:(0x[0-9a-f]{4}))?)\s*((//\s*(.*))?)',
+            re.I
+        )
+    def __init__(self, data):
+        # parse data line into values:
+        # type 1, two values: netfn, cmd
+        # type 2, three values: netfn, cmd, channels
+        try:
+            m = self.linere.fullmatch(data).groups()
+        except:
+            raise ParseError(data)
+        self.netfn = int(m[0], 16)
+        self.cmd = int(m[1], 16)
+        if m[4] is not None:
+            self.channels = int(m[4], 16)
+        else:
+            # if no channel was provided, default to previous behavior, which
+            # is allow all interfaces, including the system interface (ch 15)
+            self.channels = 0xffff
+        if m[6] is not None:
+            self.comment = "// " + m[7]
+        else:
+            self.comment = "//"
+    def __str__(self):
+        return " ".join([ '{',
+            "0x{0.netfn:02x},".format(self),
+            "0x{0.cmd:02x},".format(self),
+            "0x{0.channels:04x}".format(self),
+            "},",
+            "{0.comment}".format(self),
+        ])
+    def __lt__(self, other):
+        if self.netfn == other.netfn:
+            return self.cmd < other.cmd
+        return self.netfn < other.netfn
+    def match(self, other):
+        return (self.netfn == other.netfn) and (self.cmd == other.cmd)
+
+def parse(config):
+    entries = []
+    with open(config) as f:
+        for line in f:
+            line = line.strip()
+            if len(line) == 0 or line[0] == '#':
+                continue
+            e = entry(line)
+            if any([e.match(item) for item in entries]):
+                d = DuplicateEntry(e)
+                sys.stderr.write("WARNING: {}\n".format(d))
+            else:
+                entries.append(e)
+    entries.sort()
+    return entries
+
+def output(entries, hppfile):
+    lines = [
+            "#pragma once",
+            "",
+            "// AUTOGENERATED FILE; DO NOT MODIFY",
+            "",
+            "#include <array>",
+            "#include <tuple>",
+            "",
+            "using netfncmd_tuple = std::tuple<unsigned char, unsigned char, unsigned short>;",
+            "",
+            "constexpr const std::array<netfncmd_tuple, {}> whitelist = ".format(
+                    len(entries)),
+            "{{"
+            ]
+    lines.extend(['    {}'.format(e) for e in entries])
+    lines.append("}};\n");
+
+    with open(hppfile, "w") as hpp:
+        hpp.write("\n".join(lines))
+
+
+if __name__ == "__main__":
+    if len(sys.argv) != 3:
+        usage()
+    config = sys.argv[1]
+    header = sys.argv[2]
+    entries = parse(config)
+    output(entries, header)
diff --git a/ipmi-whitelist.conf b/ipmi-whitelist.conf
new file mode 100644
index 0000000..aeafbf2
--- /dev/null
+++ b/ipmi-whitelist.conf
@@ -0,0 +1,319 @@
+#<NetFn>:<Command>:<ChannelMask> // comment
+0x00:0x00:0xffff   //<Chassis>:<Get Chassis Capabiliti>
+0x00:0x01:0xffff   //<Chassis>:<Get Chassis Status>
+0x00:0x02:0x7fff   //<Chassis>:<Chassis Control>
+0x00:0x03:0x7fff   //<Chassis>:<Chassis Reset>
+0x00:0x04:0xffff   //<Chassis>:<Chassis Identify>
+0x00:0x05:0x7fff   //<Chassis>:<Set Chassis Capabilities>
+0x00:0x06:0x7fff   //<Chassis>:<Set Power Restore Policy>
+0x00:0x07:0xffff   //<Chassis>:<Get System Restart Cause>
+0x00:0x08:0x7fff   //<Chassis>:<Set System Boot Options>
+0x00:0x09:0xffff   //<Chassis>:<Get System Boot Options>
+0x00:0x0a:0xffff   //<Chassis>:<Set Front Panel Enables>
+0x00:0x0f:0xffff   //<Chassis>:<Get POH Counter>
+0x04:0x00:0x7fff   //<Sensor/Event>:<Set Event Receiver>
+0x04:0x01:0xffff   //<Sensor/Event>:<Get Event Receiver>
+0x04:0x02:0xffff   //<Sensor/Event>:<SEL Platform event>
+0x04:0x10:0xffff   //<Sensor/Event>:<PEF Get Capabilities>
+0x04:0x11:0x7fff   //<Sensor/Event>:<Arm PEF Postpone Timer>
+0x04:0x12:0x7fff   //<Sensor/Event>:<PEF Set Configuration Parameters>
+0x04:0x13:0xffff   //<Sensor/Event>:<PEF Get Configuration Parameters>
+0x04:0x14:0x7fff   //<Sensor/Event>:<PEF Set Processed EventID>
+0x04:0x15:0xffff   //<Sensor/Event>:<PEF Get Processed EventID>
+0x04:0x16:0x7fff   //<Sensor/Event>:<Alert Immediate>
+0x04:0x17:0x7fff   //<Sensor/Event>:<PET Acknowledge>
+0x04:0x20:0xffff   //<Sensor/Event>:<Get Device SDR Info>
+0x04:0x21:0xffff   //<Sensor/Event>:<Get Device SDR>
+0x04:0x23:0xffff   //<Sensor/Event>:<Get Sensor Reading Factors>
+0x04:0x24:0x7fff   //<Sensor/Event>:<Set Sensor Hysteresis>
+0x04:0x25:0xffff   //<Sensor/Event>:<Get Sensor Hysteresis>
+0x04:0x26:0x7fff   //<Sensor/Event>:<Set Sensor Threshold>
+0x04:0x27:0xffff   //<Sensor/Event>:<Get Sensor Threshold>
+0x04:0x28:0x7fff   //<Sensor/Event>:<Set SensorEvent Enable>
+0x04:0x29:0xffff   //<Sensor/Event>:<Get SensorEvent Enable>
+0x04:0x2a:0x7fff   //<Sensor/Event>:<Re-arm Sensor Events>
+0x04:0x2b:0xffff   //<Sensor/Event>:<Get SensorEvent Status>
+0x04:0x2d:0xffff   //<Sensor/Event>:<Get SensorReading>
+0x04:0x2e:0x7fff   //<Sensor/Event>:<Get Sensor Type>
+0x04:0x2f:0xffff   //<Sensor/Event>:<Get Sensor Type>
+0x04:0x30:0x7fff   //<Sensor/Event>:<Set Sensor Reading and Event Status>
+0x06:0x01:0xffff   //<App>:<Get Device ID>
+0x06:0x02:0xffff   //<App>:<Cold Reset>
+0x06:0x04:0xffff   //<App>:<Get Self Test>
+0x06:0x05:0x7fff   //<App>:<Manufacturing Test Mode>
+0x06:0x06:0xffff   //<App>:<Set ACPI Power State>
+0x06:0x07:0xffff   //<App>:<Get ACPI Power State>
+0x06:0x08:0xffff   //<App>:<Get Device GUID>
+0x06:0x22:0xffff   //<App>:<Reset Watchdog Timer>
+0x06:0x24:0xffff   //<App>:<Set Watchdog Timer>
+0x06:0x25:0xffff   //<App>:<Get Watchdog Timer>
+0x06:0x2e:0x7fff   //<App>:<Set BMC Global Enables>
+0x06:0x2f:0xffff   //<App>:<Get BMC Global Enables>
+0x06:0x30:0xffff   //<App>:<Clear Message Flags>
+0x06:0x31:0xffff   //<App>:<Get Message Flags>
+0x06:0x33:0xffff   //<App>:<Get Message>
+0x06:0x34:0x7fff   //<App>:<Send Message>
+0x06:0x35:0xffff   //<App>:<Read Event Message Buffer>
+0x06:0x37:0xffff   //<App>:<Get System GUID>
+0x06:0x38:0xffff   //<App>:<Get Channel Authentication Capability>
+0x06:0x39:0xffff   //<App>:<Get Session Challenge>
+0x06:0x3a:0x7fff   //<App>:<Activate Session>
+0x06:0x3b:0x7fff   //<App>:<Set Session Privilege>
+0x06:0x3c:0x7fff   //<App>:<Close Session>
+0x06:0x3d:0xffff   //<App>:<Get Channel Session Info>
+0x06:0x3f:0x7fff   //<App>:<Get Authentication Code>
+0x06:0x40:0x7fff   //<App>:<Set Channel Access>
+0x06:0x41:0xffff   //<App>:<Get Channel Access>
+0x06:0x42:0xffff   //<App>:<Get Channel Info>
+0x06:0x43:0x7fff   //<App>:<Set User Access>
+0x06:0x44:0xffff   //<App>:<Get User Access>
+0x06:0x45:0x7fff   //<App>:<Set User Name>
+0x06:0x46:0xffff   //<App>:<Get User Name>
+0x06:0x47:0x7fff   //<App>:<Set User Password>
+0x06:0x48:0x7fff   //<App>:<Activate Payload>
+0x06:0x49:0x7fff   //<App>:<Deactivate Payload>
+0x06:0x4a:0xffff   //<App>:<Get Payload Activation Status>
+0x06:0x4b:0xffff   //<App>:<Get Payload Instance Info>
+0x06:0x4c:0x7fff   //<App>:<Set User Payload Access>
+0x06:0x4d:0xffff   //<App>:<Get User Payload Access>
+0x06:0x4e:0xffff   //<App>:<Get Channel Payload Support>
+0x06:0x4f:0xffff   //<App>:<Get Channel Payload Version>
+0x06:0x50:0xffff   //<App>:<Get Channel OEM Payload Info>
+0x06:0x52:0x7fff   //<App>:<Master Write Read I2C>
+0x06:0x54:0xffff   //<App>:<Get Channel Cipher Suites>
+0x06:0x55:0xffff   //<App>:<Suspend Payload Encryption>
+0x06:0x56:0xffff   //<App>:<Set Channel Security Keys>
+0x06:0x57:0xffff   //<App>:<Get System Interface Capabilities>
+0x08:0x20:0xf9be   //<Firmware>:<Get Version Information>
+0x08:0x21:0xf9be   //<Firmware>:<Security Version Information>
+0x08:0x22:0xf9be   //<Firmware>:<Firmware Update Channel Information>
+0x08:0x23:0xf9be   //<Firmware>:<BMC Execution Context>
+0x08:0x24:0xf9be   //<Firmware>:<Get Boot Certificate Info>
+0x08:0x25:0xf9be   //<Firmware>:<Get Boot Certificate Data>
+0x08:0x26:0xf9be   //<Firmware>:<Firmware Random Number Update>
+0x08:0x27:0xf9be   //<Firmware>:<Set Firmware Update Mode>
+0x08:0x28:0xf9be   //<Firmware>:<Exit Firmware Update Mode>
+0x08:0x29:0xf9be   //<Firmware>:<Firmware Update Control>
+0x08:0x2a:0xf9be   //<Firmware>:<Get Firmware Update Status>
+0x08:0x2b:0xf9be   //<Firmware>:<Set Firmware Update Options>
+0x08:0x2c:0xf9be   //<Firmware>:<Write Firmware Image>
+0x08:0x2d:0xf9be   //<Firmware>:<Get Firmware Update Status Code Message>
+0x08:0xdb:0x79be   //<Firmware>:<Enter Safe Mode>
+0x08:0xe0:0xf9be   //<Firmware>:<Get Firmware Update Error Code Message>
+0x0a:0x10:0xffff   //<Storage>:<Get FRU Inventory Area Info>
+0x0a:0x11:0xffff   //<Storage>:<Read FRU Data>
+0x0a:0x12:0x7fff   //<Storage>:<Write FRU Data>
+0x0a:0x20:0xffff   //<Storage>:<Get SDR Repository Info>
+0x0a:0x21:0xffff   //<Storage>:<Get SDR Alloc Info>
+0x0a:0x22:0xffff   //<Storage>:<Reserve SDR>
+0x0a:0x23:0xffff   //<Storage>:<Get SDR>
+0x0a:0x24:0x7fff   //<Storage>:<Add SDR>
+0x0a:0x25:0x7fff   //<Storage>:<Partial Add SDR>
+0x0a:0x26:0x7fff   //<Storage>:<Delete SDR>
+0x0a:0x27:0x7fff   //<Storage>:<Clear SDR>
+0x0a:0x28:0xffff   //<Storage>:<SEL Get Time>
+0x0a:0x2c:0x7fff   //<Storage>:<Run Init Agent>
+0x0a:0x40:0xffff   //<Storage>:<Get SEL Info>
+0x0a:0x41:0xffff   //<Storage>:<Get SEL Alloc Info>
+0x0a:0x42:0xffff   //<Storage>:<SEL Reserve>
+0x0a:0x43:0xffff   //<Storage>:<Get SEL Entry>
+0x0a:0x44:0x7fff   //<Storage>:<Add SEL Entry>
+0x0a:0x45:0x7fff   //<Storage>:<Add Partial SEL Entry>
+0x0a:0x46:0x7fff   //<Storage>:<Delete SEL Entry>
+0x0a:0x47:0x7fff   //<Storage>:<Clear SEL>
+0x0a:0x48:0xffff   //<Storage>:<Get SEL Time>
+0x0a:0x49:0xffff   //<Storage>:<Set SEL Time>
+0x0a:0x5a:0xffff   //<Storage>:<Get SEL Auxiliary Log Status>
+0x0a:0x5b:0x7fff   //<Storage>:<Set SEL Auxiliary Log Status>
+0x0a:0x5c:0xffff   //<Storage>:<Get SEL Time UTC Offset>
+0x0a:0x5d:0x7fff   //<Storage>:<Set SEL Time UTC Offset>
+0x0c:0x01:0x7fff   //<Transport>:<Set LAN Configuration Parameters>
+0x0c:0x02:0xffff   //<Transport>:<Get LAN Configuration Parameters>
+0x0c:0x03:0x7fff   //<Transport>:<Suspend BMC ARPs>
+0x0c:0x04:0xffff   //<Transport>:<Get IPUDPRMCP Statistics>
+0x0c:0x10:0x7fff   //<Transport>:<7et Serial Modem Configuration>
+0x0c:0x11:0xffff   //<Transport>:<Get Serial Modem Configuration>
+0x0c:0x12:0x7fff   //<Transport>:<SM SetSerial Modem Mux>
+0x0c:0x14:0x7fff   //<Transport>:<Set PPP UDP Proxy Transmit Data>
+0x0c:0x15:0x7fff   //<Transport>:<Get PPP UDP Proxy Transmit Data>
+0x0c:0x16:0x7fff   //<Transport>:<Send PPP UDP Proxy Packet>
+0x0c:0x17:0x7fff   //<Transport>:<Get PPP UDP Proxy Receive Data>
+0x0c:0x18:0x7fff   //<Transport>:<Serial Modem Connection Active>
+0x0c:0x19:0x7fff   //<Transport>:<Callback>
+0x0c:0x1a:0x7fff   //<Transport>:<Set User Callback Options>
+0x0c:0x1b:0x7fff   //<Transport>:<Get User Callback Options>
+0x0c:0x1c:0x7fff   //<Transport>:<Set Serial Routing Mux>
+0x0c:0x20:0x7fff   //<Transport>:<SOL Activating>
+0x0c:0x21:0x7fff   //<Transport>:<Set SOL Configuration Parameters>
+0x0c:0x22:0xffff   //<Transport>:<Get SOL Configuration Parameters>
+0x2c:0x1f:0xffff   //<Group Extension>:<Get CPU PECI Package Config Data>
+0x2c:0x20:0xffff   //<Group Extension>:<Get MDR Data Region Status>
+0x2c:0x21:0x7fff   //<Group Extension>:<Get MDR Region Update Complete>
+0x2c:0x22:0xffff   //<Group Extension>:<Read MDR Region>
+0x2c:0x23:0x7fff   //<Group Extension>:<Write MDR Region>
+0x2c:0x24:0x7fff   //<Group Extension>:<Get MDR Region Lock>
+0x2c:0x25:0xffff   //<Group Extension>:<Get System Mode>
+0x2c:0x26:0x7fff   //<Group Extension>:<Set System Mode>
+0x2c:0x29:0xffff   //<Group Extension>:<Get TPM Configuration>
+0x2c:0x30:0x7fff   //<Group Extension>:<Set TPM Configuration>
+0x2c:0x31:0x7fff   //<Group Extension>:<TPM Configuration Update Complete>
+0x2c:0x37:0xffff   //<Group Extension>:<Read PCIe Cable EEPROM Data>
+0x30:0x02:0x7fff   //<Intel General Application>:<Restore Configuration>
+0x30:0x03:0x7fff   //<Intel General Application>:<Restore SDR>
+0x30:0x04:0xffff   //<Intel General Application>:<Get NW Switch MIB>
+0x30:0x05:0xffff   //<Intel General Application>:<Get PDB FW Version>
+0x30:0x09:0xffff   //<Intel General Application>:<Get BMC Config>
+0x30:0x14:0xffff   //<Intel General Application>:<Get SM Signal>
+0x30:0x15:0x7fff   //<Intel General Application>:<Set SM Signal>
+0x30:0x19:0xffff   //<Intel General Application>:<Read LAN Channel Port Value>
+0x30:0x1a:0xffff   //<Intel General Application>:<Get NIC Info>
+0x30:0x1b:0xffff   //<Intel General Application>:<Get LAN Available>
+0x30:0x1c:0x7fff   //<Intel General Application>:<Set Master MAC>
+0x30:0x1d:0xffff   //<Intel General Application>:<Get Master MAC>
+0x30:0x1f:0xffff   //<Intel General Application>:<Get Secure Mode>
+0x30:0x20:0xffff   //<Intel General Application>:<OEM Get SEL info>
+0x30:0x21:0xffff   //<Intel General Application>:<OEM Get SEL Allocation Info>
+0x30:0x22:0xffff   //<Intel General Application>:<OEM Platform Event Message>
+0x30:0x23:0xffff   //<Intel General Application>:<OEM Get SEL Entry>
+0x30:0x24:0x7fff   //<Intel General Application>:<OEM Add SEL Entry>
+0x30:0x26:0xffff   //<Intel General Application>:<Set BIOS ID>
+0x30:0x27:0xffff   //<Intel General Application>:<Get OEM Device Information>
+0x30:0x29:0x7fff   //<Intel General Application>:<Write SSL Certificate File>
+0x30:0x2c:0x7fff   //<Intel General Application>:<Set Processor Tcontrol>
+0x30:0x2d:0x7fff   //<Intel General Application>:<Set Cold Redundancy Configuration>
+0x30:0x2e:0xffff   //<Intel General Application>:<Get Cold Redundancy Configuration>
+0x30:0x30:0xffff   //<Intel General Application>:<Get Sensor Severity>
+0x30:0x31:0xffff   //<Intel General Application>:<Get AIC Slot FRU ID SLOT POS Records>
+0x30:0x33:0xffff   //<Intel General Application>:<Get Controller Status>
+0x30:0x38:0xffff   //<Intel General Application>:<Get Satellite Firmware update status>
+0x30:0x39:0xffff   //<Intel General Application>:<HSBP Get Owner>
+0x30:0x3b:0x7fff   //<Intel General Application>:<Set AIC MAC>
+0x30:0x3c:0xffff   //<Intel General Application>:<Get AIC MAC>
+0x30:0x3d:0x7fff   //<Intel General Application>:<Clear All AIC MACs>
+0x30:0x3f:0x7fff   //<Intel General Application>:<Enable Onboard TPM>
+0x30:0x40:0x7fff   //<Intel General Application>:<Resolve IP Address>
+0x30:0x41:0xffff   //<Intel General Application>:<Set System GUID>
+0x30:0x42:0x7fff   //<Intel General Application>:<Disable BMC System Reset Action>
+0x30:0x43:0xffff   //<Intel General Application>:<Get BMC Reset Disables>
+0x30:0x44:0xffff   //<Intel General Application>:<Send Embedded Firmware Update Status>
+0x30:0x47:0xffff   //<Intel General Application>:<HSBP Get Version>
+0x30:0x48:0x7fff   //<Intel General Application>:<Internal Platform Event>
+0x30:0x54:0x7fff   //<Intel General Application>:<Set Power Restore Delay>
+0x30:0x55:0xffff   //<Intel General Application>:<Get Power Restore Delay>
+0x30:0x57:0x7fff   //<Intel General Application>:<Set Fault Indication>
+0x30:0x58:0xffff   //<Intel General Application>:<Get DIMM Fault Status>
+0x30:0x5f:0x7fff   //<Intel General Application>:<Set Special User Password>
+0x30:0x60:0x7fff   //<Intel General Application>:<Set Shutdown Policy>
+0x30:0x62:0xffff   //<Intel General Application>:<Get Shutdown Policy>
+0x30:0x63:0xffff   //<Intel General Application>:<Get Node Slot Presence>
+0x30:0x64:0x7fff   //<Intel General Application>:<Set HDD Drive Fault LED State>
+0x30:0x65:0xffff   //<Intel General Application>:<Get HDD Drive Fault LED State>
+0x30:0x66:0xffff   //<Intel General Application>:<Get Buffer Size>
+0x30:0x67:0x7fff   //<Intel General Application>:<OEM Misc>
+0x30:0x71:0xffff   //<Intel General Application>:<Get Advanced Support>
+0x30:0x72:0x7fff   //<Intel General Application>:<Set EFI Payload>
+0x30:0x73:0xffff   //<Intel General Application>:<Get EFI Payload>
+0x30:0x74:0xffff   //<Intel General Application>:<Get RMM Status>
+0x30:0x75:0xffff   //<Intel General Application>:<Get Voltage Name>
+0x30:0x80:0xffff   //<Intel General Application>:<HSBP Get Register From Memory>
+0x30:0x81:0xffff   //<Intel General Application>:<Get Power State>
+0x30:0x82:0xffff   //<Intel General Application>:<Get ACPI Config>
+0x30:0x85:0xffff   //<Intel General Application>:<Get SF PWM>
+0x30:0x87:0x7fff   //<Intel General Application>:<Set DIMM Thermal Threshold>
+0x30:0x88:0x7fff   //<Intel General Application>:<Fans-off Configuration>
+0x30:0x89:0x7fff   //<Intel General Application>:<Set Fan Control Configuration>
+0x30:0x8a:0xffff   //<Intel General Application>:<Get Fan Control Configuration>
+0x30:0x8b:0xffff   //<Intel General Application>:<Auto Fan Detect>
+0x30:0x8c:0x7fff   //<Intel General Application>:<Set fan speed offset>
+0x30:0x8d:0xffff   //<Intel General Application>:<Get fan speed offset>
+0x30:0x8e:0x7fff   //<Intel General Application>:<Set DIMM offset>
+0x30:0x8f:0xffff   //<Intel General Application>:<Get DIMM offset>
+0x30:0x90:0x7fff   //<Intel General Application>:<Set FSC Parameter>
+0x30:0x91:0xffff   //<Intel General Application>:<Get FSC Parameter>
+0x30:0x92:0xffff   //<Intel General Application>:<Get Chassis Identifier>
+0x30:0x93:0xffff   //<Intel General Application>:<Read Base Board Product ID>
+0x30:0x94:0xffff   //<Intel General Application>:<Get BMC Revision ID>
+0x30:0x95:0xffff   //<Intel General Application>:<Get Is AP CPU>
+0x30:0x9a:0xffff   //<Intel General Application>:<Get Processor Error Configuration and Status>
+0x30:0x9b:0x7fff   //<Intel General Application>:<Set Processor Error Configuration>
+0x30:0x9c:0x7fff   //<Intel General Application>:<Set Fan PWM Limit>
+0x30:0x9d:0xffff   //<Intel General Application>:<Get Fan PWM Limit>
+0x30:0xb0:0xffff   //<Intel General Application>:<Get LED Status>
+0x30:0xb1:0x7fff   //<Intel General Application>:<Control BMC Services>
+0x30:0xb2:0xffff   //<Intel General Application>:<Get BMC Service Status>
+0x30:0xb3:0xffff   //<Intel General Application>:<Get BMC Security Control Mode>
+0x30:0xb4:0x7fff   //<Intel General Application>:<Set BMC Security Control Mode>
+0x30:0xbb:0xffff   //<Intel General Application>:<Get CPLD Revision ID>
+0x30:0xc2:0xffff   //<Intel General Application>:<Get OEM Extended Sys Info>
+0x30:0xc3:0x7fff   //<Intel General Application>:<Mount Virtual USB Device>
+0x30:0xc4:0x7fff   //<Intel General Application>:<Unmount Virtual USB Device>
+0x30:0xc6:0xffff   //<Intel General Application>:<Get Partition Config>
+0x30:0xc7:0xffff   //<Intel General Application>:<Get Zone Information>
+0x30:0xc9:0xffff   //<Intel General Application>:<Get Configuration Status>
+0x30:0xca:0xffff   //<Intel General Application>:<Get Fabric Information>
+0x30:0xcb:0xffff   //<Intel General Application>:<Get EndPoints Information>
+0x30:0xcc:0xffff   //<Intel General Application>:<Get Switches Information>
+0x30:0xcd:0xffff   //<Intel General Application>:<Get Switch Collection Information>
+0x30:0xd0:0xffff   //<Intel General Application>:<Get NVMe Drive Data>
+0x30:0xd1:0xffff   //<Intel General Application>:<HSBP Statistics>
+0x30:0xd3:0x7fff   //<Intel General Application>:<Set BIOS Feature Capability>
+0x30:0xd4:0xffff   //<Intel General Application>:<Get BIOS Capsule (OOB Update)>
+0x30:0xd5:0x7fff   //<Intel General Application>:<Set Payload>
+0x30:0xd6:0xffff   //<Intel General Application>:<Get Payload>
+0x30:0xd7:0x7fff   //<Intel General Application>:<Set BIOS Password Hash Info>
+0x30:0xd8:0xffff   //<Intel General Application>:<Get Stored BIOS Password>
+0x30:0xd9:0x7fff   //<Intel General Application>:<OOB Update Status>
+0x30:0xdb:0x7fff   //<Intel General Application>:<Get/Set BMC Remote Debug Parameters>
+0x30:0xe2:0xffff   //<Intel General Application>:<OEM Get Reading>
+0x30:0xe5:0xffff   //<Intel General Application>:<Get NMI Source>
+0x30:0xe6:0x7fff   //<Intel General Application>:<Send Raw PECI>
+0x30:0xe7:0x7fff   //<Intel General Application>:<Aggregate Send Raw PECI>
+0x30:0xe8:0xffff   //<Intel General Application>:<Get PCIe SMBus Slot Card Info>
+0x30:0xe9:0xffff   //<Intel General Application>:<Get BIOS POST CODE>
+0x30:0xf9:0xffff   //<Intel General Application>:<Get POST Progress Codes>
+0x30:0xfa:0x7fff   //<Intel General Application>:<Disable PSU for ME>
+0x30:0xfb:0x7fff   //<Intel General Application>:<Restore PSU for ME>
+0x30:0xfd:0xffff   //<Intel General Application>:<Get Riser Presence>
+0x32:0x60:0xffff   //<Intel OEM Platform>:<Get PM Bus Information>
+0x32:0x63:0xffff   //<Intel OEM Platform>:<Get Tach Information>
+0x32:0x80:0xffff   //<Intel OEM Platform>:<Get SOL Log Number>
+0x32:0x81:0xffff   //<Intel OEM Platform>:<Get SOL Log Entry>
+0x32:0x83:0xffff   //<Intel OEM Platform>:<Get SOL Control>
+0x32:0x8d:0xffff   //<Intel OEM Platform>:<Get SSD Power>
+0x32:0x90:0x7fff   //<Intel OEM Platform>:<Get-Set Serial Port Speed>
+0x3e:0x02:0xffff   //<Intel Managed Data Region>:<BMC Data Region Update Event Message>
+0x3e:0x20:0x7fff   //<Intel Managed Data Region>:<BMC Region Status>
+0x3e:0x21:0x7fff   //<Intel Managed Data Region>:<BMC Region Update Complete>
+0x3e:0x22:0xffff   //<Intel Managed Data Region>:<MDR Event>
+0x3e:0x23:0xffff   //<Intel Managed Data Region>:<BMC Region Read>
+0x3e:0x24:0x7fff   //<Intel Managed Data Region>:<BMC Region Write>
+0x3e:0x25:0x7fff   //<Intel Managed Data Region>:<BMC Region Lock>
+0x3e:0x28:0xffff   //<Intel Managed Data Region>:<Get DIMM information>
+0x3e:0x30:0xffff   //<Intel Managed Data Region>:<MDR2 Status>
+0x3e:0x31:0xffff   //<Intel Managed Data Region>:<MDR2 GET Direction>
+0x3e:0x32:0xffff   //<Intel Managed Data Region>:<MDR2 Get Data Set Info>
+0x3e:0x33:0xffff   //<Intel Managed Data Region>:<MDR2 Lock Data>
+0x3e:0x34:0xffff   //<Intel Managed Data Region>:<MDR2 Unlock Data>
+0x3e:0x35:0xffff   //<Intel Managed Data Region>:<MDR2 Dget Data Block>
+0x3e:0x38:0xffff   //<Intel Managed Data Region>:<MDR2 Send Direction>
+0x3e:0x39:0xffff   //<Intel Managed Data Region>:<MDR2 Data Info Offer>
+0x3e:0x3a:0x7fff   //<Intel Managed Data Region>:<MDR2 Data Info>
+0x3e:0x3b:0x7fff   //<Intel Managed Data Region>:<MDR2 Data Start>
+0x3e:0x3c:0x7fff   //<Intel Managed Data Region>:<MDR2 Data Done>
+0x3e:0x3d:0x7fff   //<Intel Managed Data Region>:<MDR2 Data Block>
+0x3e:0x40:0x7fff   //<Intel Managed Data Region>:<Set Get LAN Failover>
+0x3e:0x41:0xffff   //<Intel Managed Data Region>:<Enter Platform Debug Log file transfer mode>
+0x3e:0x42:0xffff   //<Intel Managed Data Region>:<Read Platform Debug Log file>
+0x3e:0x43:0xffff   //<Intel Managed Data Region>:<Status of the Platform Debug Log file transfer mode>
+0x3e:0x44:0xffff   //<Intel Managed Data Region>:<Exit Platform Debug Log file transfer mode>
+0x3e:0x47:0x7fff   //<Intel Managed Data Region>:<Get/Set BMC Application Fault Management Config>
+0x3e:0x48:0x7fff   //<Intel Managed Data Region>:<Get/Set BMC Boot Fault Management Config>
+0x3e:0x50:0xffff   //<Intel Managed Data Region>:<Node IPMB slave address>
+0x3e:0x51:0xffff   //<Intel Managed Data Region>:<Slot IPMB>
+0x3e:0x52:0xffff   //<Intel Managed Data Region>:<Slot I2C Master Write Read>
+0x3e:0x70:0x7fff   //<Intel Managed Data Region>:<SDR Configuration File control>
+0x3e:0x71:0x7fff   //<Intel Managed Data Region>:<SDR Configuration File Write>
+0x3e:0x72:0x7fff   //<Intel Managed Data Region>:<SDR Configuration File Read>
+0x3e:0x73:0x7fff   //<Intel Managed Data Region>:<SDR Configuration File Management>
+0x3e:0x75:0xffff   //<Intel Managed Data Region>:<Get Remote Log IP>
+0x3e:0x76:0x7fff   //<Intel Managed Data Region>:<Get/Set Password Mode>
diff --git a/src/whitelist-filter.cpp b/src/whitelist-filter.cpp
new file mode 100644
index 0000000..099362c
--- /dev/null
+++ b/src/whitelist-filter.cpp
@@ -0,0 +1,375 @@
+#include <algorithm>
+#include <array>
+#include <ipmi-whitelist.hpp>
+#include <ipmid/api.hpp>
+#include <ipmid/utils.hpp>
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/log.hpp>
+#include <xyz/openbmc_project/Control/Security/RestrictionMode/server.hpp>
+
+using namespace phosphor::logging;
+using namespace sdbusplus::xyz::openbmc_project::Common::Error;
+using namespace sdbusplus::xyz::openbmc_project::Control::Security::server;
+
+namespace ipmi
+{
+
+// put the filter provider in an unnamed namespace
+namespace
+{
+
+/** @class WhitelistFilter
+ *
+ * Class that implements an IPMI message filter based
+ * on incoming interface and a restriction mode setting
+ */
+class WhitelistFilter
+{
+
+  public:
+    WhitelistFilter();
+    ~WhitelistFilter() = default;
+    WhitelistFilter(WhitelistFilter const&) = delete;
+    WhitelistFilter(WhitelistFilter&&) = delete;
+    WhitelistFilter& operator=(WhitelistFilter const&) = delete;
+    WhitelistFilter& operator=(WhitelistFilter&&) = delete;
+
+  private:
+    void postInit();
+    void cacheRestrictedAndPostCompleteMode();
+    void handleRestrictedModeChange(sdbusplus::message::message& m);
+    void handlePostCompleteChange(sdbusplus::message::message& m);
+    void updatePostComplete(const std::string& value);
+    void updateRestrictionMode(const std::string& value);
+    ipmi::Cc filterMessage(ipmi::message::Request::ptr request);
+
+    // the BMC KCS Policy Control Modes document uses different names
+    // than the RestrictionModes D-Bus interface; use aliases
+    static constexpr RestrictionMode::Modes restrictionModeAllowAll =
+        RestrictionMode::Modes::Provisioning;
+    static constexpr RestrictionMode::Modes restrictionModeRestricted =
+        RestrictionMode::Modes::ProvisionedHostWhitelist;
+    static constexpr RestrictionMode::Modes restrictionModeDenyAll =
+        RestrictionMode::Modes::ProvisionedHostDisabled;
+
+    RestrictionMode::Modes restrictionMode = restrictionModeRestricted;
+    bool postCompleted = false;
+    std::shared_ptr<sdbusplus::asio::connection> bus;
+    std::unique_ptr<sdbusplus::bus::match::match> modeChangeMatch;
+    std::unique_ptr<sdbusplus::bus::match::match> modeIntfAddedMatch;
+    std::unique_ptr<sdbusplus::bus::match::match> postCompleteMatch;
+    std::unique_ptr<sdbusplus::bus::match::match> postCompleteIntfAddedMatch;
+
+    static constexpr const char restrictionModeIntf[] =
+        "xyz.openbmc_project.Control.Security.RestrictionMode";
+    static constexpr const char* systemOsStatusIntf =
+        "xyz.openbmc_project.State.OperatingSystem.Status";
+};
+
+WhitelistFilter::WhitelistFilter()
+{
+    bus = getSdBus();
+
+    log<level::INFO>("Loading whitelist filter");
+
+    ipmi::registerFilter(ipmi::prioOpenBmcBase,
+                         [this](ipmi::message::Request::ptr request) {
+                             return filterMessage(request);
+                         });
+
+    // wait until io->run is going to fetch RestrictionMode
+    post_work([this]() { postInit(); });
+}
+
+void WhitelistFilter::cacheRestrictedAndPostCompleteMode()
+{
+    std::string restrictionModePath;
+    std::string restrictionModeService;
+    std::string systemOsStatusPath;
+    std::string systemOsStatusService;
+    try
+    {
+        ipmi::DbusObjectInfo restrictionObj =
+            ipmi::getDbusObject(*bus, restrictionModeIntf);
+
+        restrictionModePath = restrictionObj.first;
+        restrictionModeService = restrictionObj.second;
+
+        ipmi::DbusObjectInfo postCompleteObj =
+            ipmi::getDbusObject(*bus, systemOsStatusIntf);
+
+        systemOsStatusPath = postCompleteObj.first;
+        systemOsStatusService = postCompleteObj.second;
+    }
+    catch (const std::exception&)
+    {
+        log<level::ERR>(
+            "Could not initialize provisioning mode, defaulting to restricted",
+            entry("VALUE=%d", static_cast<int>(restrictionMode)));
+        return;
+    }
+
+    bus->async_method_call(
+        [this](boost::system::error_code ec, ipmi::Value v) {
+            if (ec)
+            {
+                log<level::ERR>(
+                    "Could not initialize provisioning mode, "
+                    "defaulting to restricted",
+                    entry("VALUE=%d", static_cast<int>(restrictionMode)));
+                return;
+            }
+            auto mode = std::get<std::string>(v);
+            restrictionMode = RestrictionMode::convertModesFromString(mode);
+            log<level::INFO>(
+                "Read restriction mode",
+                entry("VALUE=%d", static_cast<int>(restrictionMode)));
+        },
+        restrictionModeService, restrictionModePath,
+        "org.freedesktop.DBus.Properties", "Get", restrictionModeIntf,
+        "RestrictionMode");
+
+    bus->async_method_call(
+        [this](boost::system::error_code ec, const ipmi::Value& v) {
+            if (ec)
+            {
+                log<level::ERR>("Error in OperatingSystemState Get");
+                postCompleted = true;
+                return;
+            }
+            auto value = std::get<std::string>(v);
+            if (value == "Standby")
+            {
+                postCompleted = true;
+            }
+            else
+            {
+                postCompleted = false;
+            }
+            log<level::INFO>("Read POST complete value",
+                             entry("VALUE=%d", postCompleted));
+        },
+        systemOsStatusService, systemOsStatusPath,
+        "org.freedesktop.DBus.Properties", "Get", systemOsStatusIntf,
+        "OperatingSystemState");
+}
+
+void WhitelistFilter::updateRestrictionMode(const std::string& value)
+{
+    restrictionMode = RestrictionMode::convertModesFromString(value);
+    log<level::INFO>("Updated restriction mode",
+                     entry("VALUE=%d", static_cast<int>(restrictionMode)));
+}
+
+void WhitelistFilter::handleRestrictedModeChange(sdbusplus::message::message& m)
+{
+    std::string signal = m.get_member();
+    if (signal == "PropertiesChanged")
+    {
+        std::string intf;
+        std::vector<std::pair<std::string, ipmi::Value>> propertyList;
+        m.read(intf, propertyList);
+        for (const auto& property : propertyList)
+        {
+            if (property.first == "RestrictionMode")
+            {
+                updateRestrictionMode(std::get<std::string>(property.second));
+            }
+        }
+    }
+    else if (signal == "InterfacesAdded")
+    {
+        sdbusplus::message::object_path path;
+        DbusInterfaceMap restModeObj;
+        m.read(path, restModeObj);
+        auto intfItr = restModeObj.find(restrictionModeIntf);
+        if (intfItr == restModeObj.end())
+        {
+            return;
+        }
+        PropertyMap& propertyList = intfItr->second;
+        auto itr = propertyList.find("RestrictionMode");
+        if (itr == propertyList.end())
+        {
+            return;
+        }
+        updateRestrictionMode(std::get<std::string>(itr->second));
+    }
+}
+
+void WhitelistFilter::updatePostComplete(const std::string& value)
+{
+    if (value == "Standby")
+    {
+        postCompleted = true;
+    }
+    else
+    {
+        postCompleted = false;
+    }
+    log<level::INFO>(postCompleted ? "Updated to POST Complete"
+                                   : "Updated to !POST Complete");
+}
+
+void WhitelistFilter::handlePostCompleteChange(sdbusplus::message::message& m)
+{
+    std::string signal = m.get_member();
+    if (signal == "PropertiesChanged")
+    {
+        std::string intf;
+        std::vector<std::pair<std::string, ipmi::Value>> propertyList;
+        m.read(intf, propertyList);
+        for (const auto& property : propertyList)
+        {
+            if (property.first == "OperatingSystemState")
+            {
+                updatePostComplete(std::get<std::string>(property.second));
+            }
+        }
+    }
+    else if (signal == "InterfacesAdded")
+    {
+        sdbusplus::message::object_path path;
+        DbusInterfaceMap postCompleteObj;
+        m.read(path, postCompleteObj);
+        auto intfItr = postCompleteObj.find(systemOsStatusIntf);
+        if (intfItr == postCompleteObj.end())
+        {
+            return;
+        }
+        PropertyMap& propertyList = intfItr->second;
+        auto itr = propertyList.find("OperatingSystemState");
+        if (itr == propertyList.end())
+        {
+            return;
+        }
+        updatePostComplete(std::get<std::string>(itr->second));
+    }
+}
+void WhitelistFilter::postInit()
+{
+    // Wait for changes on Restricted mode
+    namespace rules = sdbusplus::bus::match::rules;
+    const std::string filterStrModeChange =
+        rules::type::signal() + rules::member("PropertiesChanged") +
+        rules::interface("org.freedesktop.DBus.Properties") +
+        rules::argN(0, restrictionModeIntf);
+
+    const std::string filterStrModeIntfAdd =
+        rules::interfacesAdded() +
+        rules::argNpath(
+            0, "/xyz/openbmc_project/control/security/restriction_mode");
+
+    const std::string filterStrPostComplete =
+        rules::type::signal() + rules::member("PropertiesChanged") +
+        rules::interface("org.freedesktop.DBus.Properties") +
+        rules::argN(0, systemOsStatusIntf);
+
+    const std::string filterStrPostIntfAdd =
+        rules::interfacesAdded() +
+        rules::argNpath(0, "/xyz/openbmc_project/state/os");
+
+    modeChangeMatch = std::make_unique<sdbusplus::bus::match::match>(
+        *bus, filterStrModeChange, [this](sdbusplus::message::message& m) {
+            handleRestrictedModeChange(m);
+        });
+    modeIntfAddedMatch = std::make_unique<sdbusplus::bus::match::match>(
+        *bus, filterStrModeIntfAdd, [this](sdbusplus::message::message& m) {
+            handleRestrictedModeChange(m);
+        });
+
+    postCompleteMatch = std::make_unique<sdbusplus::bus::match::match>(
+        *bus, filterStrPostComplete, [this](sdbusplus::message::message& m) {
+            handlePostCompleteChange(m);
+        });
+
+    postCompleteIntfAddedMatch = std::make_unique<sdbusplus::bus::match::match>(
+        *bus, filterStrPostIntfAdd, [this](sdbusplus::message::message& m) {
+            handlePostCompleteChange(m);
+        });
+
+    // Initialize restricted mode
+    cacheRestrictedAndPostCompleteMode();
+}
+
+ipmi::Cc WhitelistFilter::filterMessage(ipmi::message::Request::ptr request)
+{
+    auto channelMask = static_cast<unsigned short>(1 << request->ctx->channel);
+    bool whitelisted = std::binary_search(
+        whitelist.cbegin(), whitelist.cend(),
+        std::make_tuple(request->ctx->netFn, request->ctx->cmd, channelMask),
+        [](const netfncmd_tuple& first, const netfncmd_tuple& value) {
+            return (std::get<2>(first) & std::get<2>(value))
+                       ? first < std::make_tuple(std::get<0>(value),
+                                                 std::get<1>(value),
+                                                 std::get<2>(first))
+                       : first < value;
+        });
+
+    // no special handling for non-system-interface channels
+    if (request->ctx->channel != ipmi::channelSystemIface)
+    {
+        if (!whitelisted)
+        {
+            log<level::INFO>("Channel/NetFn/Cmd not whitelisted",
+                             entry("CHANNEL=0x%X", request->ctx->channel),
+                             entry("NETFN=0x%X", int(request->ctx->netFn)),
+                             entry("CMD=0x%X", int(request->ctx->cmd)));
+            return ipmi::ccCommandNotAvailable;
+        }
+        return ipmi::ccSuccess;
+    }
+
+    // for system interface, filtering is done as follows:
+    // Allow All:  preboot ? ccSuccess : ccSuccess
+    // Restricted: preboot ? ccSuccess :
+    //                  ( whitelist ? ccSuccess : // ccCommandNotAvailable )
+    // Deny All:   preboot ? ccSuccess : ccCommandNotAvailable
+
+    if (!postCompleted)
+    {
+        // Allow all commands, till POST is not completed
+        return ipmi::ccSuccess;
+    }
+
+    switch (restrictionMode)
+    {
+        case RestrictionMode::Modes::None:
+        case restrictionModeAllowAll:
+        {
+            // Allow All
+            return ipmi::ccSuccess;
+            break;
+        }
+        case restrictionModeRestricted:
+        {
+            // Restricted - follow whitelist
+            break;
+        }
+        case restrictionModeDenyAll:
+        {
+            // Deny All
+            whitelisted = false;
+            break;
+        }
+        default: // for whitelist and blacklist
+            return ipmi::ccCommandNotAvailable;
+    }
+
+    if (!whitelisted)
+    {
+        log<level::INFO>("Channel/NetFn/Cmd not whitelisted",
+                         entry("CHANNEL=0x%X", request->ctx->channel),
+                         entry("NETFN=0x%X", int(request->ctx->netFn)),
+                         entry("CMD=0x%X", int(request->ctx->cmd)));
+        return ipmi::ccCommandNotAvailable;
+    }
+    return ipmi::ccSuccess;
+} // namespace
+
+// instantiate the WhitelistFilter when this shared object is loaded
+WhitelistFilter whitelistFilter;
+
+} // namespace
+
+} // namespace ipmi