Add Message Handler

IPMI message Handler encapsulates the operations with respective to each
IPMI message.The routines are there to read the message, execute the
command and send the response.

Change-Id: I607416f723510326748b3eba73f3a6557c40dd06
Signed-off-by: Tom Joseph <tomjoseph@in.ibm.com>
diff --git a/main.hpp b/main.hpp
new file mode 100644
index 0000000..cfb80c6
--- /dev/null
+++ b/main.hpp
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <tuple>
+
+#include <command_table.hpp>
+#include <sessions_manager.hpp>
+
+extern std::tuple<session::Manager&, command::Table&> singletonPool;
+
+// Select call timeout is set arbitarily set to 30 sec
+static constexpr size_t SELECT_CALL_TIMEOUT = 30;
+static const auto IPMI_STD_PORT = 623;
diff --git a/message_handler.cpp b/message_handler.cpp
new file mode 100644
index 0000000..bfc861e
--- /dev/null
+++ b/message_handler.cpp
@@ -0,0 +1,182 @@
+#include "message_handler.hpp"
+
+#include <sys/socket.h>
+
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "command_table.hpp"
+#include "main.hpp"
+#include "message.hpp"
+#include "message_parsers.hpp"
+#include "sessions_manager.hpp"
+
+namespace message
+{
+
+std::unique_ptr<Message> Handler::receive()
+{
+    std::vector<uint8_t> packet;
+    auto readStatus = 0;
+
+    // Read the packet
+    std::tie(readStatus, packet) = channel->read();
+
+    // Read of the packet failed
+    if (readStatus < 0)
+    {
+        std::cerr << "E> Error in Read : " << std::hex << readStatus << "\n";
+        return nullptr;
+    }
+
+    // Unflatten the packet
+    std::unique_ptr<Message> message;
+    std::tie(message, sessionHeader) = parser::unflatten(packet);
+
+
+    auto session = (std::get<session::Manager&>(singletonPool).getSession(
+                   message->bmcSessionID)).lock();
+
+    sessionID = message->bmcSessionID;
+    message->rcSessionID = session->getRCSessionID();
+    session->updateLastTransactionTime();
+
+    return message;
+}
+
+template<>
+std::unique_ptr<Message> Handler::createResponse<PayloadType::IPMI>(
+        std::vector<uint8_t>& output, Message& inMessage)
+{
+    auto outMessage = std::make_unique<Message>();
+    outMessage->rcSessionID = inMessage.rcSessionID;
+
+    outMessage->payloadType = PayloadType::IPMI;
+
+    outMessage->payload.resize(sizeof(LAN::header::Response) +
+                               output.size() +
+                               sizeof(LAN::trailer::Response));
+
+    auto reqHeader = reinterpret_cast<LAN::header::Request*>
+                     (inMessage.payload.data());
+    auto respHeader = reinterpret_cast<LAN::header::Response*>
+                      (outMessage->payload.data());
+
+    // Add IPMI LAN Message Response Header
+    respHeader->rqaddr = reqHeader->rqaddr;
+    respHeader->netfn  = reqHeader->netfn | 0x04;
+    respHeader->cs     = crc8bit(&(respHeader->rqaddr), 2);
+    respHeader->rsaddr = reqHeader->rsaddr;
+    respHeader->rqseq  = reqHeader->rqseq;
+    respHeader->cmd    = reqHeader->cmd;
+
+    auto assembledSize = sizeof(LAN::header::Response);
+
+    // Copy the output by the execution of the command
+    std::copy(output.begin(), output.end(),
+              outMessage->payload.begin() + assembledSize);
+    assembledSize += output.size();
+
+    // Add the IPMI LAN Message Trailer
+    auto trailer = reinterpret_cast<LAN::trailer::Response*>
+                   (outMessage->payload.data() + assembledSize);
+    trailer->checksum = crc8bit(&respHeader->rsaddr, assembledSize - 3);
+
+    return outMessage;
+}
+
+std::unique_ptr<Message> Handler::executeCommand(Message& inMessage)
+{
+    // Get the CommandID to map into the command table
+    auto command = getCommand(inMessage);
+    std::vector<uint8_t> output{};
+
+    if (inMessage.payloadType == PayloadType::IPMI)
+    {
+        if (inMessage.payload.size() < (sizeof(LAN::header::Request) +
+                                        sizeof(LAN::trailer::Request)))
+        {
+            return nullptr;
+        }
+
+        auto start = inMessage.payload.begin() + sizeof(LAN::header::Request);
+        auto end = inMessage.payload.end() - sizeof(LAN::trailer::Request);
+        std::vector<uint8_t> inPayload(start, end);
+
+        output = std::get<command::Table&>(singletonPool).executeCommand(
+                     command,
+                     inPayload,
+                     *this);
+    }
+    else
+    {
+        output = std::get<command::Table&>(singletonPool).executeCommand(
+                     command,
+                     inMessage.payload,
+                     *this);
+    }
+
+    std::unique_ptr<Message> outMessage = nullptr;
+
+    switch (inMessage.payloadType)
+    {
+        case PayloadType::IPMI:
+            outMessage = createResponse<PayloadType::IPMI>(output, inMessage);
+            break;
+        case PayloadType::OPEN_SESSION_REQUEST:
+            outMessage = createResponse<PayloadType::OPEN_SESSION_RESPONSE>(
+                             output, inMessage);
+            break;
+        case PayloadType::RAKP1:
+            outMessage = createResponse<PayloadType::RAKP2>(output, inMessage);
+            break;
+        case PayloadType::RAKP3:
+            outMessage = createResponse<PayloadType::RAKP4>(output, inMessage);
+            break;
+        default:
+            break;
+
+    }
+
+    return outMessage;
+}
+
+uint32_t Handler::getCommand(Message& message)
+{
+    uint32_t command = 0 ;
+
+    command |= (static_cast<uint8_t>(message.payloadType) << 16);
+    if (message.payloadType == PayloadType::IPMI)
+    {
+        command |= ((reinterpret_cast<LAN::header::Request*>
+                     (message.payload.data()))->netfn) << 8;
+        command |= (reinterpret_cast<LAN::header::Request*>
+                    (message.payload.data()))->cmd;
+    }
+
+    return command;
+}
+
+int Handler::send(Message& outMessage)
+{
+    auto session = (std::get<session::Manager&>(singletonPool).getSession(
+                    sessionID)).lock();
+
+    // Flatten the packet
+    auto packet = parser::flatten(outMessage, sessionHeader, *session);
+
+    // Read the packet
+    auto writeStatus = channel->write(packet);
+    if (writeStatus < 0)
+    {
+        std::cerr << "E> Error in writing : " << std::hex << writeStatus
+                  << "\n";
+    }
+
+    return writeStatus;
+}
+
+} //namespace message
+
diff --git a/message_handler.hpp b/message_handler.hpp
new file mode 100644
index 0000000..ee8a04f
--- /dev/null
+++ b/message_handler.hpp
@@ -0,0 +1,119 @@
+#pragma once
+
+#include <iostream>
+#include <numeric>
+
+#include "message.hpp"
+#include "message_parsers.hpp"
+#include "session.hpp"
+
+namespace message
+{
+
+class Handler
+{
+    public:
+
+        explicit Handler(std::shared_ptr<udpsocket::Channel> inChannel):
+            channel(inChannel) {}
+
+        Handler() = delete;
+        ~Handler() = default;
+        Handler(const Handler&) = default;
+        Handler& operator=(const Handler&) = default;
+        Handler(Handler&&) = default;
+        Handler& operator=(Handler&&) = default;
+
+        /*
+         * @brief Receive the IPMI packet
+         *
+         * Read the data on the socket, get the parser based on the Session
+         * header type and flatten the payload and generate the IPMI message
+         *
+         * @return IPMI Message on success and nullptr on failure
+         *
+         */
+        std::unique_ptr<Message> receive();
+
+        /*
+         * @brief Process the incoming IPMI message
+         *
+         * The incoming message payload is handled and the command handler for
+         * the Network function and Command is executed and the response message
+         * is returned
+         *
+         * @param[in] inMessage - Incoming Message
+         *
+         * @return Outgoing message on success and nullptr on failure
+         */
+        std::unique_ptr<Message> executeCommand(Message& inMessage);
+
+        /*
+         * @brief Send the outgoing message
+         *
+         * The payload in the outgoing message is flattened and sent out on the
+         * socket
+         *
+         * @param[in] outMessage - Outgoing Message
+         *
+         * @return Zero on success and <0 on failure
+         */
+        int send(Message& outMessage);
+
+        // BMC Session ID for the Channel
+        session::SessionID sessionID;
+    private:
+        // Socket channel for communicating with the remote client
+        std::shared_ptr<udpsocket::Channel> channel;
+
+        // IPMI 1.5 or IPMI 2.0 Session Header
+        parser::SessionHeader sessionHeader;
+
+        /*
+         * @brief Create the response IPMI message
+         *
+         * The IPMI outgoing message is constructed out of payload and the
+         * corresponding fields are populated.For the payload type IPMI, the
+         * LAN message header and trailer are added.
+         *
+         * @tparam[in] T - Outgoing message payload type
+         * @param[in] output - Payload for outgoing message
+         * @param[in] inMessage - Incoming IPMI message
+         *
+         * @return Outgoing message on success and nullptr on failure
+         */
+        template<PayloadType T>
+        std::unique_ptr<Message> createResponse(std::vector<uint8_t>& output,
+                                                Message& inMessage)
+        {
+            auto outMessage = std::make_unique<Message>();
+            outMessage->rcSessionID = inMessage.rcSessionID;
+            outMessage->payloadType = T;
+            outMessage->payload = output;
+            return outMessage;
+        }
+
+        /*
+         * @brief Extract the command from the IPMI payload
+         *
+         * @param[in] message - Incoming message
+         *
+         * @return Command ID in the incoming message
+         */
+        uint32_t getCommand(Message& message);
+
+        /*
+         * @brief Calculate 8 bit 2's complement checksum
+         *
+         * Initialize checksum to 0. For each byte, checksum = (checksum + byte)
+         * modulo 256. Then checksum = - checksum. When the checksum and the
+         * bytes are added together, modulo 256, the result should be 0.
+         */
+        uint8_t crc8bit(const uint8_t* ptr, const size_t len)
+        {
+            return (0x100 - std::accumulate(ptr,ptr+len,0));
+        }
+
+};
+
+} //namespace message