Add RMCP Ping support
Added support of RMCP Ping/Pong request and response
(ASF messages).
Tested: Tested using rmcpping tool to send RMCP ping.
Resolves openbmc/phosphor-net-ipmid#15
Signed-off-by: Kirill Pakhomov <k.pakhomov@yadro.com>
Change-Id: Ie5199e6af69860d9406bdd516952b62c3d05793f
diff --git a/configure.ac b/configure.ac
index 36b0df4..8e8ad57 100644
--- a/configure.ac
+++ b/configure.ac
@@ -66,6 +66,17 @@
[AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])])
AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"])
+AC_ARG_ENABLE([rmcp-ping],
+ AS_HELP_STRING([--enable-rmcp-ping], [Enable RMCP Ping support])
+ )
+ AS_IF([test "x$enable_rmcp_ping" == "xyes"],
+ AC_MSG_NOTICE([Enabling RMCP Ping])
+ [
+ cpp_flags="-DRMCP_PING"
+ ]
+ AC_SUBST([CPPFLAGS], [$cpp_flags])
+)
+
# Checks for header files.
AC_CHECK_HEADER(systemd/sd-bus.h, ,[AC_MSG_ERROR([Could not find systemd/sd-bus.h...systemd development package required])])
diff --git a/message.hpp b/message.hpp
index 738652c..f01fcc9 100644
--- a/message.hpp
+++ b/message.hpp
@@ -21,6 +21,24 @@
INVALID = 0xFF,
};
+// RMCP Classes of Message as per section 13.1.3.
+enum class ClassOfMsg : uint8_t
+{
+ RESERVED = 0x05,
+ ASF = 0x06,
+ IPMI = 0x07,
+ OEM = 0x08,
+};
+
+#ifdef RMCP_PING
+// RMCP Message Type as per section 13.1.3.
+enum class RmcpMsgType : uint8_t
+{
+ PING = 0x80,
+ PONG = 0x40,
+};
+#endif // RMCP_PING
+
namespace LAN
{
@@ -103,7 +121,8 @@
Message() :
payloadType(PayloadType::INVALID),
rcSessionID(Message::MESSAGE_INVALID_SESSION_ID),
- bmcSessionID(Message::MESSAGE_INVALID_SESSION_ID)
+ bmcSessionID(Message::MESSAGE_INVALID_SESSION_ID),
+ rmcpMsgClass(ClassOfMsg::RESERVED)
{
}
@@ -120,7 +139,7 @@
isPacketEncrypted(other.isPacketEncrypted),
isPacketAuthenticated(other.isPacketAuthenticated),
payloadType(other.payloadType), rcSessionID(other.rcSessionID),
- bmcSessionID(other.bmcSessionID)
+ bmcSessionID(other.bmcSessionID), rmcpMsgClass(other.rmcpMsgClass)
{
// special behavior for rmcp+ session creation
if (PayloadType::OPEN_SESSION_REQUEST == other.payloadType)
@@ -227,6 +246,10 @@
uint32_t rcSessionID; // Remote Client's Session ID
uint32_t bmcSessionID; // BMC's session ID
uint32_t sessionSeqNum; // Session Sequence Number
+ ClassOfMsg rmcpMsgClass; // Class of Message
+#ifdef RMCP_PING
+ uint8_t asfMsgTag; // ASF Message Tag
+#endif // RMCP_PING
/** @brief Message payload
*
diff --git a/message_handler.cpp b/message_handler.cpp
index b2b0607..6dadbfc 100644
--- a/message_handler.cpp
+++ b/message_handler.cpp
@@ -37,6 +37,11 @@
// Unflatten the packet
std::tie(inMessage, sessionHeader) = parser::unflatten(packet);
+ return true;
+}
+
+void Handler::updSessionData(std::shared_ptr<Message>& inMessage)
+{
auto session = std::get<session::Manager&>(singletonPool)
.getSession(inMessage->bmcSessionID);
@@ -48,30 +53,37 @@
uint32_t ipAddr = 0;
channel->getRemoteAddress(ipAddr);
session->remoteIPAddr(ipAddr);
-
- return true;
}
Handler::~Handler()
{
- if (outPayload)
+ try
{
- std::shared_ptr<Message> outMessage =
- inMessage->createResponse(*outPayload);
- if (!outMessage)
+#ifdef RMCP_PING
+ if (ClassOfMsg::ASF == inMessage->rmcpMsgClass)
{
- return;
+ sendASF();
}
- try
+ else
+#endif // RMCP_PING
{
- send(outMessage);
+ if (outPayload)
+ {
+ std::shared_ptr<Message> outMessage =
+ inMessage->createResponse(*outPayload);
+ if (!outMessage)
+ {
+ return;
+ }
+ send(outMessage);
+ }
}
- catch (const std::exception& e)
- {
- // send failed, most likely due to a session closure
- log<level::INFO>("Async RMCP+ reply failed",
- entry("EXCEPTION=%s", e.what()));
- }
+ }
+ catch (const std::exception& e)
+ {
+ // send failed, most likely due to a session closure
+ log<level::INFO>("Async RMCP+ reply failed",
+ entry("EXCEPTION=%s", e.what()));
}
}
@@ -83,14 +95,21 @@
return;
}
+#ifdef RMCP_PING
// Execute the Command, possibly asynchronously
- executeCommand();
+ if (ClassOfMsg::ASF != inMessage->rmcpMsgClass)
+#endif // RMCP_PING
+ {
+ updSessionData(inMessage);
+ executeCommand();
+ }
// send happens during the destructor if a payload was set
}
void Handler::executeCommand()
{
+
// Get the CommandID to map into the command table
auto command = inMessage->getCommand();
if (inMessage->payloadType == PayloadType::IPMI)
@@ -128,6 +147,26 @@
}
}
+void Handler::writeData(const std::vector<uint8_t>& packet)
+{
+ auto writeStatus = channel->write(packet);
+ if (writeStatus < 0)
+ {
+ throw std::runtime_error("Error in writing to socket");
+ }
+}
+
+#ifdef RMCP_PING
+void Handler::sendASF()
+{
+ // Flatten the packet
+ auto packet = asfparser::flatten(inMessage->asfMsgTag);
+
+ // Write the packet
+ writeData(packet);
+}
+#endif // RMCP_PING
+
void Handler::send(std::shared_ptr<Message> outMessage)
{
auto session =
@@ -137,11 +176,7 @@
auto packet = parser::flatten(outMessage, sessionHeader, session);
// Write the packet
- auto writeStatus = channel->write(packet);
- if (writeStatus < 0)
- {
- throw std::runtime_error("Error in writing to socket");
- }
+ writeData(packet);
}
void Handler::setChannelInSession() const
diff --git a/message_handler.hpp b/message_handler.hpp
index 533ed6a..2eb4737 100644
--- a/message_handler.hpp
+++ b/message_handler.hpp
@@ -93,6 +93,12 @@
bool receive();
/**
+ * @brief Get Session data from the IPMI packet
+ *
+ */
+ void updSessionData(std::shared_ptr<Message>& inMessage);
+
+ /**
* @brief Process the incoming IPMI message
*
* The incoming message payload is handled and the command handler for
@@ -110,6 +116,21 @@
*/
void send(std::shared_ptr<Message> outMessage);
+#ifdef RMCP_PING
+ /** @brief Send the outgoing ASF message
+ *
+ * The outgoing ASF message contains only ASF message header
+ * which is flattened and sent out on the socket
+ */
+ void sendASF();
+#endif // RMCP_PING
+
+ /** @brief Write the packet to the socket
+ *
+ * @param[in] packet - Outgoing packet
+ */
+ void writeData(const std::vector<uint8_t>& packet);
+
/** @brief Socket channel for communicating with the remote client.*/
std::shared_ptr<udpsocket::Channel> channel;
diff --git a/message_parsers.cpp b/message_parsers.cpp
index 6703fc3..18f8389 100644
--- a/message_parsers.cpp
+++ b/message_parsers.cpp
@@ -17,24 +17,37 @@
unflatten(std::vector<uint8_t>& inPacket)
{
// Check if the packet has atleast the size of the RMCP Header
- if (inPacket.size() < sizeof(BasicHeader_t))
+ if (inPacket.size() < sizeof(RmcpHeader_t))
{
throw std::runtime_error("RMCP Header missing");
}
- auto rmcpHeaderPtr = reinterpret_cast<BasicHeader_t*>(inPacket.data());
+ auto rmcpHeaderPtr = reinterpret_cast<RmcpHeader_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))
+ (rmcpHeaderPtr->classOfMsg < static_cast<uint8_t>(ClassOfMsg::ASF) &&
+ rmcpHeaderPtr->classOfMsg > static_cast<uint8_t>(ClassOfMsg::OEM)))
{
throw std::runtime_error("RMCP Header is invalid");
}
+ if (rmcpHeaderPtr->classOfMsg == static_cast<uint8_t>(ClassOfMsg::ASF))
+ {
+#ifndef RMCP_PING
+ throw std::runtime_error("RMCP Ping is not supported");
+#else
+ return std::make_tuple(asfparser::unflatten(inPacket),
+ SessionHeader::IPMI15);
+#endif // RMCP_PING
+ }
+
+ auto sessionHeaderPtr = reinterpret_cast<BasicHeader_t*>(inPacket.data());
+
// Read the Session Header and invoke the parser corresponding to the
// header type
- switch (static_cast<SessionHeader>(rmcpHeaderPtr->format.formatType))
+ switch (static_cast<SessionHeader>(sessionHeaderPtr->format.formatType))
{
case SessionHeader::IPMI15:
{
@@ -96,6 +109,8 @@
message->sessionSeqNum = endian::from_ipmi(header->sessSeqNum);
message->isPacketEncrypted = false;
message->isPacketAuthenticated = false;
+ message->rmcpMsgClass =
+ static_cast<ClassOfMsg>(header->base.rmcp.classOfMsg);
auto payloadLen = header->payloadLength;
@@ -120,10 +135,10 @@
// 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.rmcp.version = parser::RMCP_VERSION;
+ header->base.rmcp.reserved = 0x00;
+ header->base.rmcp.rmcpSeqNum = parser::RMCP_SEQ;
+ header->base.rmcp.classOfMsg = static_cast<uint8_t>(ClassOfMsg::IPMI);
header->base.format.formatType =
static_cast<uint8_t>(parser::SessionHeader::IPMI15);
header->sessSeqNum = 0;
@@ -168,6 +183,8 @@
((header->payloadType & PAYLOAD_ENCRYPT_MASK) ? true : false);
message->isPacketAuthenticated =
((header->payloadType & PAYLOAD_AUTH_MASK) ? true : false);
+ message->rmcpMsgClass =
+ static_cast<ClassOfMsg>(header->base.rmcp.classOfMsg);
auto payloadLen = endian::from_ipmi(header->payloadLength);
@@ -202,10 +219,10 @@
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.rmcp.version = parser::RMCP_VERSION;
+ header->base.rmcp.reserved = 0x00;
+ header->base.rmcp.rmcpSeqNum = parser::RMCP_SEQ;
+ header->base.rmcp.classOfMsg = static_cast<uint8_t>(ClassOfMsg::IPMI);
header->base.format.formatType =
static_cast<uint8_t>(parser::SessionHeader::IPMI20);
header->payloadType = static_cast<uint8_t>(outMessage->payloadType);
@@ -370,4 +387,53 @@
} // namespace ipmi20parser
+#ifdef RMCP_PING
+namespace asfparser
+{
+std::shared_ptr<Message> unflatten(std::vector<uint8_t>& inPacket)
+{
+ auto message = std::make_shared<Message>();
+
+ auto header = reinterpret_cast<AsfMessagePing_t*>(inPacket.data());
+
+ message->payloadType = PayloadType::IPMI;
+ message->rmcpMsgClass = ClassOfMsg::ASF;
+ message->asfMsgTag = header->msgTag;
+
+ return message;
+}
+
+std::vector<uint8_t> flatten(uint8_t asfMsgTag)
+{
+ std::vector<uint8_t> packet(sizeof(AsfMessagePong_t));
+
+ // Insert RMCP header into the Packet
+ auto header = reinterpret_cast<AsfMessagePong_t*>(packet.data());
+ header->ping.rmcp.version = parser::RMCP_VERSION;
+ header->ping.rmcp.reserved = 0x00;
+ header->ping.rmcp.rmcpSeqNum = parser::RMCP_SEQ;
+ header->ping.rmcp.classOfMsg = static_cast<uint8_t>(ClassOfMsg::ASF);
+
+ // No OEM-specific capabilities exist, therefore the second
+ // IANA Enterprise Number contains the same IANA(4542)
+ header->ping.iana = header->iana = endian::to_ipmi(parser::ASF_IANA);
+ header->ping.msgType = static_cast<uint8_t>(RmcpMsgType::PONG);
+ header->ping.msgTag = asfMsgTag;
+ header->ping.reserved = 0x00;
+ header->ping.dataLen =
+ parser::RMCP_ASF_PONG_DATA_LEN; // as per spec 13.2.4,
+
+ header->iana = parser::ASF_IANA;
+ header->oemDefined = 0x00;
+ header->suppEntities = parser::ASF_SUPP_ENT;
+ header->suppInteract = parser::ASF_SUPP_INT;
+ header->reserved1 = 0x00;
+ header->reserved2 = 0x00;
+
+ return packet;
+}
+
+} // namespace asfparser
+#endif // RMCP_PING
+
} // namespace message
diff --git a/message_parsers.hpp b/message_parsers.hpp
index 5738c43..b26f9a3 100644
--- a/message_parsers.hpp
+++ b/message_parsers.hpp
@@ -24,6 +24,19 @@
// RMCP Session Header Size
constexpr size_t RMCP_SESSION_HEADER_SIZE = 4;
+// RMCP/ASF Pong Message ASF Header Data Length
+// as per IPMI spec 13.2.4
+constexpr size_t RMCP_ASF_PONG_DATA_LEN = 16;
+
+// ASF IANA
+constexpr uint32_t ASF_IANA = 4542;
+
+// ASF Supported Entities
+constexpr uint32_t ASF_SUPP_ENT = 0x81;
+
+// ASF Supported Entities
+constexpr uint32_t ASF_SUPP_INT = 0x00;
+
// Maximum payload size
constexpr size_t MAX_PAYLOAD_SIZE = 255;
@@ -34,13 +47,20 @@
INVALID = 0xFF,
};
-struct BasicHeader_t
+// RMCP Header
+struct RmcpHeader_t
{
// RMCP Header
uint8_t version;
uint8_t reserved;
uint8_t rmcpSeqNum;
uint8_t classOfMsg;
+} __attribute__((packed));
+
+struct BasicHeader_t
+{
+ // RMCP Header
+ struct RmcpHeader_t rmcp;
// IPMI partial session header
union
@@ -226,4 +246,54 @@
} // namespace ipmi20parser
+#ifdef RMCP_PING
+namespace asfparser
+{
+
+// ASF message fields for RMCP Ping message
+struct AsfMessagePing_t
+{
+ struct parser::RmcpHeader_t rmcp;
+
+ uint32_t iana;
+ uint8_t msgType;
+ uint8_t msgTag;
+ uint8_t reserved;
+ uint8_t dataLen;
+} __attribute__((packed));
+
+// ASF message fields for RMCP Pong message
+struct AsfMessagePong_t
+{
+ struct AsfMessagePing_t ping;
+
+ uint32_t iana;
+ uint32_t oemDefined;
+ uint8_t suppEntities;
+ uint8_t suppInteract;
+ uint32_t reserved1;
+ uint16_t reserved2;
+} __attribute__((packed));
+
+/**
+ * @brief Unflatten an incoming packet and prepare the ASF message
+ *
+ * @param[in] inPacket - Incoming ASF packet
+ *
+ * @return ASF message in the packet on success
+ */
+std::shared_ptr<Message> unflatten(std::vector<uint8_t>& inPacket);
+
+/**
+ * @brief Generate the ASF packet with the RMCP header
+ *
+ * @param[in] asfMsgTag - ASF Message Tag from Ping request
+ *
+ * @return ASF packet on success
+ */
+std::vector<uint8_t> flatten(uint8_t asfMsgTag);
+
+} // namespace asfparser
+#endif // RMCP_PING
+
} // namespace message