Implemented get session info cmd in host interface

This command can get info of any session

Tested all positive and negative test cases

Tested:

Get active session info by valid session handle
ipmitool raw 0x6 0x3d <sesssion index = search session by handle> <valid session handle>
Response : successfully gets the session info as per IPMI spec

Get active session info by valid session id
ipmitool raw 0x6 0x3d <sesssion index = search session by id> <valid session id>
Response : successfully gets the session info as per IPMI spec

Get inactive session info by valid session handle
ipmitool raw 0x6 0x3d <sesssion index = search session by handle> <valid session handle>
Response : successfully gets the session info as per IPMI spec

Get inactive session info by valid session id
ipmitool raw 0x6 0x3d <sesssion index = search session by id> <valid session id>
Response : successfully gets the session info as per IPMI spec

Get session info by invalid session handle
ipmitool raw 0x6 0x3d <sesssion index = search session by handle> <invalid session handle>
Response : 0x88  // Inavlid Session Handle

Get session info by invalid session id
ipmitool raw 0x6 0x3d <sesssion index = search session by id> <invalid session id>
Response : 0x87  // Inavlid Session Id

Signed-off-by: Rajashekar Gade Reddy <raja.sekhar.reddy.gade@linux.intel.com>
Change-Id: Ib36a9fe5eac58d15011cc9379ceed480ba0be96d
diff --git a/apphandler.cpp b/apphandler.cpp
index f187d95..03cd255 100644
--- a/apphandler.cpp
+++ b/apphandler.cpp
@@ -949,6 +949,277 @@
     return ipmi::responseInvalidFieldRequest();
 }
 
+uint8_t getTotalSessionCount()
+{
+    uint8_t count = 0, ch = 1;
+
+    while (ch < ipmi::maxIpmiChannels &&
+           count < session::maxNetworkInstanceSupported)
+    {
+        ipmi::ChannelInfo chInfo;
+        ipmi::getChannelInfo(ch, chInfo);
+        if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
+            ipmi::EChannelMediumType::lan8032)
+        {
+            count++;
+        }
+        ch++;
+    }
+    return count * session::maxSessionCountPerChannel;
+}
+
+/**
+ * @brief get session info request data.
+ *
+ * This function validates the request data and retrive request session id,
+ * session handle.
+ *
+ * @param[in] sessionIndex - request session index
+ * @param[in] payload - input payload
+ * @param[in] reqSessionId - unpacked session Id will be asigned
+ * @param[in] reqSessionHandle - unpacked session handle will be asigned
+ *
+ * @return success completion code if request data is valid
+ * else return the correcponding error completion code.
+ **/
+uint8_t getSessionInfoRequestData(const uint8_t sessionIndex,
+                                  ipmi::message::Payload& payload,
+                                  uint32_t& reqSessionId,
+                                  uint8_t& reqSessionHandle)
+{
+    if (sessionIndex == session::sessionZero ||
+        ((sessionIndex > session::maxSessionCountPerChannel) &&
+         (sessionIndex < session::searchSessionByHandle)))
+    {
+        return ipmi::ccInvalidFieldRequest;
+    }
+
+    switch (sessionIndex)
+    {
+        case session::searchSessionByHandle:
+
+            if ((payload.unpack(reqSessionHandle)) ||
+                (!payload.fullyUnpacked()))
+            {
+                return ipmi::ccReqDataLenInvalid;
+            }
+
+            if ((reqSessionHandle == session::sessionZero) ||
+                ((reqSessionHandle & session::multiIntfaceSessionHandleMask) >
+                 session::maxSessionCountPerChannel))
+            {
+                return session::ccInvalidSessionHandle;
+            }
+            break;
+
+        case session::searchSessionById:
+
+            if ((payload.unpack(reqSessionId)) || (!payload.fullyUnpacked()))
+            {
+                return ipmi::ccReqDataLenInvalid;
+            }
+
+            if (reqSessionId == session::sessionZero)
+            {
+                return session::ccInvalidSessionId;
+            }
+            break;
+
+        default:
+            if (!payload.fullyUnpacked())
+            {
+                return ipmi::ccReqDataLenInvalid;
+            }
+            break;
+    }
+    return ipmi::ccSuccess;
+}
+
+uint8_t getSessionState(std::shared_ptr<sdbusplus::asio::connection>& busp,
+                        const std::string& service, const std::string& objPath,
+                        uint8_t& sessionState)
+{
+    try
+    {
+        sessionState = std::get<uint8_t>(ipmi::getDbusProperty(
+            *busp, service, objPath, session::sessionIntf, "State"));
+    }
+    catch (sdbusplus::exception::SdBusError& e)
+    {
+        log<level::ERR>("Failed to fetch state property ",
+                        entry("SERVICE=%s", service.c_str()),
+                        entry("OBJECTPATH=%s", objPath.c_str()),
+                        entry("INTERFACE=%s", session::sessionIntf),
+                        entry("ERRMSG=%s", e.what()));
+        return ipmi::ccUnspecifiedError;
+    }
+
+    return ipmi::ccSuccess;
+}
+
+static constexpr uint8_t macAddrLen = 6;
+struct GetSessionInfoRes
+{
+    uint8_t sessionHandle;
+    uint8_t totalSessionCount;
+    uint8_t activeSessionCount;
+    uint8_t userID;
+    uint8_t privLevel;
+    uint8_t channelNumber;
+    uint32_t remoteIpAddr;
+    std::array<uint8_t, macAddrLen> macAddr = {0};
+    uint16_t remotePort;
+};
+
+uint8_t
+    fillGetSessionInfoRes(std::shared_ptr<sdbusplus::asio::connection>& busp,
+                          const std::string& service,
+                          const std::string& objPath,
+                          struct GetSessionInfoRes& resp, uint8_t& sessionState)
+{
+    try
+    {
+        ipmi::PropertyMap sessionProps = ipmi::getAllDbusProperties(
+            *busp, service, objPath, session::sessionIntf);
+
+        sessionState = std::get<uint8_t>(sessionProps.at("State"));
+        if (sessionState == static_cast<uint8_t>(session::State::active))
+        {
+            resp.sessionHandle =
+                std::get<uint8_t>(sessionProps["SessionHandle"]);
+            resp.userID = std::get<uint8_t>(sessionProps["UserID"]);
+            resp.privLevel =
+                std::get<uint8_t>(sessionProps["CurrentPrivilege"]);
+            resp.channelNumber = std::get<uint8_t>(sessionProps["ChannelNum"]);
+            resp.remoteIpAddr =
+                std::get<uint32_t>(sessionProps["RemoteIPAddr"]);
+            resp.remotePort = std::get<uint16_t>(sessionProps["RemotePort"]);
+        }
+    }
+    catch (sdbusplus::exception::SdBusError& e)
+    {
+        log<level::ERR>("Failed to fetch state property ",
+                        entry("SERVICE=%s", service.c_str()),
+                        entry("OBJECTPATH=%s", objPath.c_str()),
+                        entry("INTERFACE=%s", session::sessionIntf),
+                        entry("ERRMSG=%s", e.what()));
+        return ipmi::ccUnspecifiedError;
+    }
+
+    return ipmi::ccSuccess;
+}
+
+ipmi::RspType<
+    uint8_t,                           // session handle,
+    uint8_t,                           // total session count
+    uint8_t,                           // active session count
+    std::optional<std::tuple<uint8_t,  // user ID
+                             uint8_t,  // privilege level
+                             uint8_t,  // channel number
+                             uint32_t, // remote ip address,
+                             std::array<uint8_t, macAddrLen>, // mac address
+                             uint16_t                         // remote port
+                             >>>
+    ipmiAppGetSessionInfo(uint8_t sessionIndex, ipmi::message::Payload& payload)
+{
+    uint32_t reqSessionId = 0;
+    uint8_t reqSessionHandle = session::defaultSessionHandle;
+    // initializing state to 0xff as 0 represents state as inactive.
+    uint8_t state = 0xFF;
+
+    uint8_t completionCode = getSessionInfoRequestData(
+        sessionIndex, payload, reqSessionId, reqSessionHandle);
+
+    if (completionCode)
+    {
+        return ipmi::response(completionCode);
+    }
+    struct GetSessionInfoRes res = {0};
+    res.totalSessionCount = getTotalSessionCount();
+    res.activeSessionCount = 0;
+    auto busp = getSdBus();
+
+    try
+    {
+        uint8_t index = 0;
+        ipmi::ObjectTree objectTree = ipmi::getAllDbusObjects(
+            *busp, session::sessionManagerRootPath, session::sessionIntf);
+
+        for (auto& objectTreeItr : objectTree)
+        {
+            uint32_t sessionId = 0;
+            uint8_t sessionHandle = session::defaultSessionHandle;
+            std::string objectPath = objectTreeItr.first;
+
+            if (!parseCloseSessionInputPayload(objectPath, sessionId,
+                                               sessionHandle))
+            {
+                continue;
+            }
+            index++;
+            auto& serviceMap = objectTreeItr.second;
+            auto itr = serviceMap.begin();
+
+            if (serviceMap.size() != 1)
+            {
+                return ipmi::responseUnspecifiedError();
+            }
+
+            std::string service = itr->first;
+            uint8_t sessionState = 0;
+            completionCode =
+                getSessionState(busp, service, objectPath, sessionState);
+            if (completionCode)
+            {
+                return ipmi::response(completionCode);
+            }
+
+            if (sessionState == static_cast<uint8_t>(session::State::active))
+            {
+                res.activeSessionCount++;
+            }
+
+            if (index != sessionIndex && reqSessionId != sessionId &&
+                reqSessionHandle != sessionHandle)
+            {
+                continue;
+            }
+
+            completionCode =
+                fillGetSessionInfoRes(busp, service, objectPath, res, state);
+
+            if (completionCode)
+            {
+                return ipmi::response(completionCode);
+            }
+        }
+    }
+
+    catch (sdbusplus::exception::SdBusError& e)
+    {
+        log<level::ERR>("Failed to fetch object from dbus",
+                        entry("INTERFACE=%s", session::sessionIntf),
+                        entry("ERRMSG=%s", e.what()));
+        return ipmi::responseUnspecifiedError();
+    }
+
+    if (state == static_cast<uint8_t>(session::State::active))
+    {
+        return ipmi::responseSuccess(
+            res.sessionHandle, res.totalSessionCount, res.activeSessionCount,
+            std::make_tuple(res.userID, res.privLevel, res.channelNumber,
+                            res.remoteIpAddr, res.macAddr, res.remotePort));
+    }
+    else if (state == static_cast<uint8_t>(session::State::tearDownInProgress))
+    {
+        res.sessionHandle = 0;
+        return ipmi::responseSuccess(res.sessionHandle, res.totalSessionCount,
+                                     res.activeSessionCount, std::nullopt);
+    }
+
+    return ipmi::responseInvalidFieldRequest();
+}
+
 static std::unique_ptr<SysInfoParamStore> sysInfoParamStore;
 
 static std::string sysInfoReadSystemName()
@@ -1366,6 +1637,10 @@
                           ipmi::app::cmdResetWatchdogTimer,
                           ipmi::Privilege::Operator, ipmiAppResetWatchdogTimer);
 
+    ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
+                          ipmi::app::cmdGetSessionInfo,
+                          ipmi::Privilege::Callback, ipmiAppGetSessionInfo);
+
     // <Set Watchdog Timer>
     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
                           ipmi::app::cmdSetWatchdogTimer,