Message parser for parsing IPMI message
IPMI message parser is responsible for parsing IPMI messages. There is
parser for IPMI 1.5 messages and IPMI 2.0 messages.
Change-Id: I24ebb6907abb56bcc25297eee75c06e6cde8355b
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/message_parsers.cpp b/message_parsers.cpp
new file mode 100644
index 0000000..07945f0
--- /dev/null
+++ b/message_parsers.cpp
@@ -0,0 +1,223 @@
+#include "message_parsers.hpp"
+
+#include <iostream>
+#include <memory>
+
+#include "endian.hpp"
+#include "main.hpp"
+#include "message.hpp"
+#include "sessions_manager.hpp"
+
+namespace message
+{
+
+namespace parser
+{
+
+std::tuple<std::unique_ptr<Message>, SessionHeader> unflatten(
+ std::vector<uint8_t>& inPacket)
+{
+ // Check if the packet has atleast the size of the RMCP Header
+ if (inPacket.size() < sizeof(BasicHeader_t))
+ {
+ throw std::runtime_error("RMCP Header missing");
+ }
+
+ auto rmcpHeaderPtr = reinterpret_cast<BasicHeader_t*>(inPacket.data());
+
+ // Verify if the fields in the RMCP header conforms to the specification
+ if ((rmcpHeaderPtr->version != RMCP_VERSION) ||
+ (rmcpHeaderPtr->rmcpSeqNum != RMCP_SEQ) ||
+ (rmcpHeaderPtr->classOfMsg != RMCP_MESSAGE_CLASS_IPMI))
+ {
+ throw std::runtime_error("RMCP Header is invalid");
+ }
+
+ // Read the Session Header and invoke the parser corresponding to the
+ // header type
+ switch (static_cast<SessionHeader>(rmcpHeaderPtr->format.formatType))
+ {
+ case SessionHeader::IPMI15:
+ {
+ return std::make_tuple(ipmi15parser::unflatten(inPacket),
+ SessionHeader::IPMI15);
+ }
+ case SessionHeader::IPMI20:
+ {
+ return std::make_tuple(ipmi20parser::unflatten(inPacket),
+ SessionHeader::IPMI20);
+ }
+ default:
+ {
+ throw std::runtime_error("Invalid Session Header");
+ }
+ }
+}
+
+std::vector<uint8_t> flatten(Message& outMessage,
+ SessionHeader authType,
+ session::Session& session)
+{
+ // Call the flatten routine based on the header type
+ switch (authType)
+ {
+ case SessionHeader::IPMI15:
+ {
+ return ipmi15parser::flatten(outMessage, session);
+ }
+ case SessionHeader::IPMI20:
+ {
+ return ipmi20parser::flatten(outMessage, session);
+ }
+ default:
+ {
+ return {};
+ }
+ }
+}
+
+} // namespace parser
+
+namespace ipmi15parser
+{
+
+std::unique_ptr<Message> unflatten(std::vector<uint8_t>& inPacket)
+{
+ // Check if the packet has atleast the Session Header
+ if (inPacket.size() < sizeof(SessionHeader_t))
+ {
+ throw std::runtime_error("IPMI1.5 Session Header Missing");
+ }
+
+ auto message = std::make_unique<Message>();
+
+ auto header = reinterpret_cast<SessionHeader_t*>(inPacket.data());
+
+ message->payloadType = PayloadType::IPMI;
+ message->bmcSessionID = endian::from_ipmi<>(header->sessId);
+ message->sessionSeqNum = endian::from_ipmi<>(header->sessSeqNum);
+ message->isPacketEncrypted = false;
+ message->isPacketAuthenticated = false;
+
+ auto payloadLen = header->payloadLength;
+
+ (message->payload).assign(inPacket.data() + sizeof(SessionHeader_t),
+ inPacket.data() + sizeof(SessionHeader_t) +
+ payloadLen);
+
+ return message;
+}
+
+std::vector<uint8_t> flatten(Message& outMessage, session::Session& session)
+{
+ std::vector<uint8_t> packet(sizeof(SessionHeader_t));
+
+ // Insert Session Header into the Packet
+ auto header = reinterpret_cast<SessionHeader_t*>(packet.data());
+ header->base.version = parser::RMCP_VERSION;
+ header->base.reserved = 0x00;
+ header->base.rmcpSeqNum = parser::RMCP_SEQ;
+ header->base.classOfMsg = parser::RMCP_MESSAGE_CLASS_IPMI;
+ header->base.format.formatType =
+ static_cast<uint8_t>(parser::SessionHeader::IPMI15);
+ header->sessSeqNum = 0;
+ header->sessId = endian::to_ipmi<>(outMessage.rcSessionID);
+
+ header->payloadLength = static_cast<uint8_t>(outMessage.payload.size());
+
+ // Insert the Payload into the Packet
+ packet.insert(packet.end(), outMessage.payload.begin(),
+ outMessage.payload.end());
+
+ // Insert the Session Trailer
+ packet.resize(packet.size() + sizeof(SessionTrailer_t));
+ auto trailer = reinterpret_cast<SessionTrailer_t*>(packet.data() +
+ packet.size());
+ trailer->legacyPad = 0x00;
+
+ return packet;
+}
+
+} // namespace ipmi15parser
+
+namespace ipmi20parser
+{
+
+std::unique_ptr<Message> unflatten(std::vector<uint8_t>& inPacket)
+{
+ // Check if the packet has atleast the Session Header
+ if (inPacket.size() < sizeof(SessionHeader_t))
+ {
+ throw std::runtime_error("IPMI2.0 Session Header Missing");
+ }
+
+ auto message = std::make_unique<Message>();
+
+ auto header = reinterpret_cast<SessionHeader_t*>(inPacket.data());
+
+ message->payloadType = static_cast<PayloadType>
+ (header->payloadType & 0x3F);
+ message->bmcSessionID = endian::from_ipmi<>(header->sessId);
+ message->sessionSeqNum = endian::from_ipmi<>(header->sessSeqNum);
+ message->isPacketEncrypted =
+ ((header->payloadType & PAYLOAD_ENCRYPT_MASK) ? true : false);
+ message->isPacketAuthenticated =
+ ((header->payloadType & PAYLOAD_AUTH_MASK) ? true : false);
+
+ auto payloadLen = endian::from_ipmi<>(header->payloadLength);
+ message->payload.assign(inPacket.begin() + sizeof(SessionHeader_t),
+ inPacket.begin() + sizeof(SessionHeader_t) +
+ payloadLen);
+
+ return message;
+}
+
+std::vector<uint8_t> flatten(Message& outMessage, session::Session& session)
+{
+ std::vector<uint8_t> packet(sizeof(SessionHeader_t));
+
+ SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data());
+ header->base.version = parser::RMCP_VERSION;
+ header->base.reserved = 0x00;
+ header->base.rmcpSeqNum = parser::RMCP_SEQ;
+ header->base.classOfMsg = parser::RMCP_MESSAGE_CLASS_IPMI;
+ header->base.format.formatType =
+ static_cast<uint8_t>(parser::SessionHeader::IPMI20);
+ header->payloadType = static_cast<uint8_t>(outMessage.payloadType);
+ header->sessId = endian::to_ipmi<>(outMessage.rcSessionID);
+
+ // Add session sequence number
+ internal::addSequenceNumber(packet, session);
+
+ // Add Payload
+ header->payloadLength = endian::to_ipmi<>(outMessage.payload.size());
+ // Insert the Payload into the Packet
+ packet.insert(packet.end(), outMessage.payload.begin(),
+ outMessage.payload.end());
+
+ return packet;
+}
+
+namespace internal
+{
+
+void addSequenceNumber(std::vector<uint8_t>& packet, session::Session& session)
+{
+ SessionHeader_t* header = reinterpret_cast<SessionHeader_t*>(packet.data());
+
+ if (header->sessId == session::SESSION_ZERO)
+ {
+ header->sessSeqNum = 0x00;
+ }
+ else
+ {
+ auto seqNum = session.sequenceNums.increment();
+ header->sessSeqNum = endian::to_ipmi<>(seqNum);
+ }
+}
+
+} // namespace internal
+
+} // namespace ipmi20parser
+
+} // namespace message
diff --git a/message_parsers.hpp b/message_parsers.hpp
new file mode 100644
index 0000000..49a0c5d
--- /dev/null
+++ b/message_parsers.hpp
@@ -0,0 +1,178 @@
+#pragma once
+
+#include "message.hpp"
+#include "session.hpp"
+
+namespace message
+{
+
+namespace parser
+{
+
+constexpr size_t RMCP_VERSION = 6;
+
+// RMCP Messages with class=IPMI should be sent with an RMCP Sequence
+// Number of FFh to indicate that an RMCP ACK message should not be
+// generated by the message receiver.
+constexpr size_t RMCP_SEQ = 0xFF;
+
+// RMCP Message Class 7h is for IPMI
+constexpr size_t RMCP_MESSAGE_CLASS_IPMI = 7;
+
+// Maximum packet size that we'll handle
+constexpr uint32_t MESSAGE_MAX_PACKET_LENGTH = 512;
+
+enum class SessionHeader
+{
+ IPMI15 = 0x00,
+ IPMI20 = 0x06,
+ INVALID = 0xFF,
+};
+
+struct BasicHeader_t
+{
+ // RMCP Header
+ uint8_t version;
+ uint8_t reserved;
+ uint8_t rmcpSeqNum;
+ uint8_t classOfMsg;
+
+ // IPMI partial session header
+ union
+ {
+ uint8_t reserved1: 4;
+ uint8_t authType: 4;
+ uint8_t formatType;
+ } format;
+} __attribute__((packed));
+
+/*
+ * @brief Unflatten an incoming packet and prepare the IPMI message
+ *
+ * @param[in] inPacket - Incoming IPMI packet
+ *
+ * @return A tuple with IPMI message and the session header type to sent the
+ * response packet. In case of success incoming message and session
+ * header type. In case of failure nullptr and session header type
+ * would be invalid.
+ */
+std::tuple<std::unique_ptr<Message>, SessionHeader> unflatten(
+ std::vector<uint8_t>& inPacket);
+
+/*
+ * @brief Flatten an IPMI message and generate the IPMI packet with the
+ * session header
+ *
+ * @param[in] outMessage - IPMI message to be flattened
+ * @param[in] authType - Session header type to be added to the IPMI
+ * packet
+ *
+ * @return IPMI packet on success
+ */
+std::vector<uint8_t> flatten(Message& outMessage,
+ SessionHeader authType,
+ session::Session& session);
+
+} // namespace parser
+
+namespace ipmi15parser
+{
+
+struct SessionHeader_t
+{
+ struct parser::BasicHeader_t base;
+ uint32_t sessSeqNum;
+ uint32_t sessId;
+ // <Optional Field: AuthCode>
+ uint8_t payloadLength;
+} __attribute__((packed));
+
+struct SessionTrailer_t
+{
+ uint8_t legacyPad;
+} __attribute__((packed));
+
+/*
+ * @brief Unflatten an incoming packet and prepare the IPMI message
+ *
+ * @param[in] inPacket - Incoming IPMI packet
+ *
+ * @return IPMI message in the packet on success
+ */
+std::unique_ptr<Message> unflatten(std::vector<uint8_t>& inPacket);
+
+/*
+ * @brief Flatten an IPMI message and generate the IPMI packet with the
+ * session header
+ *
+ * @param[in] outMessage - IPMI message to be flattened
+ *
+ * @return IPMI packet on success
+ */
+std::vector<uint8_t> flatten(Message& outMessage, session::Session& session);
+
+} // namespace ipmi15parser
+
+namespace ipmi20parser
+{
+
+constexpr size_t MAX_INTEGRITY_DATA_LENGTH = 12;
+constexpr size_t PAYLOAD_ENCRYPT_MASK = 0x80;
+constexpr size_t PAYLOAD_AUTH_MASK = 0x40;
+
+struct SessionHeader_t
+{
+ struct parser::BasicHeader_t base;
+
+ uint8_t payloadType;
+
+ uint32_t sessId;
+ uint32_t sessSeqNum;
+ uint16_t payloadLength;
+} __attribute__((packed));
+
+struct SessionTrailer_t
+{
+ // Integrity Pad
+ uint8_t padLength;
+ uint8_t nextHeader;
+ // AuthCode (Integrity Data)
+ uint8_t authCode[MAX_INTEGRITY_DATA_LENGTH];
+} __attribute__((packed));
+
+/*
+ * @brief Unflatten an incoming packet and prepare the IPMI message
+ *
+ * @param[in] inPacket - Incoming IPMI packet
+ *
+ * @return IPMI message in the packet on success
+ */
+std::unique_ptr<Message> unflatten(std::vector<uint8_t>& inPacket);
+
+/*
+ * @brief Flatten an IPMI message and generate the IPMI packet with the
+ * session header
+ *
+ * @param[in] outMessage - IPMI message to be flattened
+ *
+ * @return IPMI packet on success
+ */
+std::vector<uint8_t> flatten(Message& outMessage, session::Session& session);
+
+namespace internal
+{
+
+/*
+ * @brief Add sequence number to the message
+ *
+ * @param[in] packet - outgoing packet to which to add sequence number
+ * @param[in] session - session handle
+ *
+ */
+void addSequenceNumber(std::vector<uint8_t>& packet, session::Session& session);
+
+} // namespace internal
+
+} // namespace ipmi20parser
+
+} // namespace message