From e7023926675030a5976dffda0825445ca0b5ef84 Mon Sep 17 00:00:00 2001 From: Rajashekar Gade Reddy Date: Wed, 10 Jul 2019 16:54:55 +0000 Subject: Implemented close session cmd in host interface This command can close any session via host interface. Tested: Close the existing valid session by session id ipmitool raw 0x6 0x3c Response : 00 // success Close the existing valid session by session handle ipmitool raw 0x6 0x3c Response : 00 // success Close the session by zero session id ipmitool raw 0x6 0x3c Response : 0x87 // inavlid session id Close the session by zero session handle ipmitool raw 0x6 0x3c Response : 0x88 // inavlid session handle Close an inactive session. ipmitool raw 0x6 0x3c Response : 0xcc // invalid data field in request Close an inactive session. ipmitool raw 0x6 0x3c Response : 0xcc // invalid data field in request Signed-off-by: Rajashekar Gade Reddy Change-Id: I8af290001d8effbbcdbbe2dd93aabf1b015e7a88 --- apphandler.cpp | 112 ++++++++++++++++++++++++++++++++ include/Makefile.am | 1 + include/ipmid/sessiondef.hpp | 3 + include/ipmid/sessionhelper.hpp | 88 +++++++++++++++++++++++++ test/Makefile.am | 18 ++++++ test/session/closesession_unittest.cpp | 114 +++++++++++++++++++++++++++++++++ 6 files changed, 336 insertions(+) create mode 100644 include/ipmid/sessionhelper.hpp create mode 100644 test/session/closesession_unittest.cpp diff --git a/apphandler.cpp b/apphandler.cpp index c5a608d..0c8eb92 100644 --- a/apphandler.cpp +++ b/apphandler.cpp @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include #include @@ -842,6 +844,112 @@ auto ipmiAppGetSystemGuid() -> ipmi::RspType> return ipmi::responseSuccess(uuid); } +/** + * @brief set the session state as teardown + * + * This function is to set the session state to tear down in progress if the + * state is active. + * + * @param[in] busp - Dbus obj + * @param[in] service - service name + * @param[in] obj - object path + * + * @return success completion code if it sets the session state to + * tearDownInProgress else return the corresponding error completion code. + **/ +uint8_t setSessionState(std::shared_ptr& busp, + const std::string& service, const std::string& obj) +{ + try + { + uint8_t sessionState = std::get(ipmi::getDbusProperty( + *busp, service, obj, session::sessionIntf, "State")); + + if (sessionState == static_cast(session::State::active)) + { + ipmi::setDbusProperty( + *busp, service, obj, session::sessionIntf, "State", + static_cast(session::State::tearDownInProgress)); + return ipmi::ccSuccess; + } + } + catch (std::exception& e) + { + log("Failed in getting session state property", + entry("service=%s", service.c_str()), + entry("object path=%s", obj.c_str()), + entry("interface=%s", session::sessionIntf)); + return ipmi::ccUnspecifiedError; + } + + return ipmi::ccInvalidFieldRequest; +} + +ipmi::RspType<> ipmiAppCloseSession(uint32_t reqSessionId, + std::optional requestSessionHandle) +{ + auto busp = getSdBus(); + uint8_t reqSessionHandle = + requestSessionHandle.value_or(session::defaultSessionHandle); + + if (reqSessionId == session::sessionZero && + reqSessionHandle == session::defaultSessionHandle) + { + return ipmi::response(session::ccInvalidSessionId); + } + + if (reqSessionId == session::sessionZero && + reqSessionHandle == session::invalidSessionHandle) + { + return ipmi::response(session::ccInvalidSessionHandle); + } + + if (reqSessionId != session::sessionZero && + reqSessionHandle != session::defaultSessionHandle) + { + return ipmi::response(ipmi::ccInvalidFieldRequest); + } + + try + { + ipmi::ObjectTree objectTree = ipmi::getAllDbusObjects( + *busp, session::sessionManagerRootPath, session::sessionIntf); + + for (auto& objectTreeItr : objectTree) + { + const std::string obj = objectTreeItr.first; + + if (isSessionObjectMatched(obj, reqSessionId, reqSessionHandle)) + { + auto& serviceMap = objectTreeItr.second; + + // Session id and session handle are unique for each session. + // Session id and handler are retrived from the object path and + // object path will be unique for each session. Checking if + // multiple objects exist with same object path under multiple + // services. + if (serviceMap.size() != 1) + { + return ipmi::responseUnspecifiedError(); + } + + auto itr = serviceMap.begin(); + const std::string service = itr->first; + return ipmi::response(setSessionState(busp, service, obj)); + } + } + } + catch (sdbusplus::exception::SdBusError& e) + { + log("Failed to fetch object from dbus", + entry("INTERFACE=%s", session::sessionIntf), + entry("ERRMSG=%s", e.what())); + return ipmi::responseUnspecifiedError(); + } + + return ipmi::responseInvalidFieldRequest(); +} + static std::unique_ptr sysInfoParamStore; static std::string sysInfoReadSystemName() @@ -1175,6 +1283,10 @@ void register_netfn_app_functions() ipmi::app::cmdSetWatchdogTimer, ipmi::Privilege::Operator, ipmiSetWatchdogTimer); + ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, + ipmi::app::cmdCloseSession, ipmi::Privilege::Callback, + ipmiAppCloseSession); + // ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, ipmi::app::cmdGetWatchdogTimer, diff --git a/include/Makefile.am b/include/Makefile.am index 996c41a..08824c4 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -2,6 +2,7 @@ nobase_include_HEADERS = \ ipmid/api.hpp \ ipmid/api-types.hpp \ ipmid/sessiondef.hpp \ + ipmid/sessionhelper.hpp \ ipmid/filter.hpp \ ipmid/handler.hpp \ ipmid/message.hpp \ diff --git a/include/ipmid/sessiondef.hpp b/include/ipmid/sessiondef.hpp index 9fce256..48ee4c6 100644 --- a/include/ipmid/sessiondef.hpp +++ b/include/ipmid/sessiondef.hpp @@ -28,7 +28,10 @@ static constexpr size_t sessionZero = 0; static constexpr size_t maxSessionlessCount = 1; static constexpr uint8_t invalidSessionID = 0; static constexpr uint8_t invalidSessionHandle = 0; +static constexpr uint8_t defaultSessionHandle = 0xFF; static constexpr uint8_t maxNetworkInstanceSupported = 4; +static constexpr uint8_t ccInvalidSessionId = 0x87; +static constexpr uint8_t ccInvalidSessionHandle = 0x88; // MSB BIT 7 BIT 6 assigned for netipmid instance in session handle. static constexpr uint8_t multiIntfaceSessionHandleMask = 0x3F; diff --git a/include/ipmid/sessionhelper.hpp b/include/ipmid/sessionhelper.hpp new file mode 100644 index 0000000..a96f037 --- /dev/null +++ b/include/ipmid/sessionhelper.hpp @@ -0,0 +1,88 @@ +#include +#include + +/** + * @brief parse session input payload. + * + * This function retrives the session id and session handle from the session + * object path. + * A valid object path will be in the form + * "/xyz/openbmc_project/ipmi/session/channel/sessionId_sessionHandle" + * + * Ex: "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a" + * SessionId : 0X12a4567d + * SessionHandle: 0X8a + + * @param[in] objectPath - session object path + * @param[in] sessionId - retrived session id will be asigned. + * @param[in] sessionHandle - retrived session handle will be asigned. + * + * @return true if session id and session handle are retrived else returns + * false. + */ +bool parseCloseSessionInputPayload(const std::string& objectPath, + uint32_t& sessionId, uint8_t& sessionHandle) +{ + if (objectPath.empty()) + { + return false; + } + // getting the position of session id and session handle string from + // object path. + std::size_t ptrPosition = objectPath.rfind("/"); + uint16_t tempSessionHandle = 0; + + if (ptrPosition != std::string::npos) + { + // get the sessionid & session handle string from the session object + // path Ex: sessionIdString: "12a4567d_8a" + std::string sessionIdString = objectPath.substr(ptrPosition + 1); + std::size_t pos = sessionIdString.rfind("_"); + + if (pos != std::string::npos) + { + // extracting the session handle + std::string sessionHandleString = sessionIdString.substr(pos + 1); + // extracting the session id + sessionIdString = sessionIdString.substr(0, pos); + // converting session id string and session handle string to + // hexadecimal. + std::stringstream handle(sessionHandleString); + handle >> std::hex >> tempSessionHandle; + sessionHandle = tempSessionHandle & 0xFF; + std::stringstream idString(sessionIdString); + idString >> std::hex >> sessionId; + return true; + } + } + return false; +} + +/** + * @brief is session object matched. + * + * This function checks whether the objectPath contains reqSessionId and + * reqSessionHandle, e.g., "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a" + * matches sessionId 0x12a4567d and sessionHandle 0x8a. + * + * @param[in] objectPath - session object path + * @param[in] reqSessionId - request session id + * @param[in] reqSessionHandle - request session handle + * + * @return true if the object is matched else return false + **/ +bool isSessionObjectMatched(const std::string objectPath, + const uint32_t reqSessionId, + const uint8_t reqSessionHandle) +{ + uint32_t sessionId = 0; + uint8_t sessionHandle = 0; + + if (parseCloseSessionInputPayload(objectPath, sessionId, sessionHandle)) + { + return (reqSessionId == sessionId) || + (reqSessionHandle == sessionHandle); + } + + return false; +} diff --git a/test/Makefile.am b/test/Makefile.am index e4ff261..0faeda7 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -72,3 +72,21 @@ message_unittest_SOURCES = \ %reldir%/message/unpack.cpp \ %reldir%/message/pack.cpp check_PROGRAMS += %reldir%/message_unittest + +# Build/add closesession_unittest to test suite +session_unittest_CPPFLAGS = \ + -Igtest \ + $(GTEST_CPPFLAGS) \ + $(AM_CPPFLAGS) +session_unittest_CXXFLAGS = \ + $(PTHREAD_CFLAGS) \ + $(CODE_COVERAGE_CXXFLAGS) \ + $(CODE_COVERAGE_CFLAGS) +session_unittest_LDFLAGS = \ + -lgtest_main \ + -lgtest \ + -pthread \ + $(OESDK_TESTCASE_FLAGS) \ + $(CODE_COVERAGE_LDFLAGS) +session_unittest_SOURCES = %reldir%/session/closesession_unittest.cpp +check_PROGRAMS += %reldir%/session_unittest diff --git a/test/session/closesession_unittest.cpp b/test/session/closesession_unittest.cpp new file mode 100644 index 0000000..2b184ca --- /dev/null +++ b/test/session/closesession_unittest.cpp @@ -0,0 +1,114 @@ +#include + +#include + +TEST(parseSessionInputPayloadTest, ValidObjectPath) +{ + uint32_t sessionId = 0; + uint8_t sessionHandle = 0; + std::string objectPath = + "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a"; + + EXPECT_TRUE( + parseCloseSessionInputPayload(objectPath, sessionId, sessionHandle)); + EXPECT_EQ(0x12a4567d, sessionId); + EXPECT_EQ(0x8a, sessionHandle); +} + +TEST(parseSessionInputPayloadTest, InvalidObjectPath) +{ + uint32_t sessionId = 0; + uint8_t sessionHandle = 0; + // A valid object path will be like + // "/xyz/openbmc_project/ipmi/session/channel/sessionId_sessionHandle" + // Ex: "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a" + // SessionId : 0X12a4567d + // SessionHandle: 0X8a + std::string objectPath = "/xyz/openbmc_project/ipmi/session/eth0/12a4567d"; + + EXPECT_FALSE( + parseCloseSessionInputPayload(objectPath, sessionId, sessionHandle)); +} + +TEST(parseSessionInputPayloadTest, NoObjectPath) +{ + uint32_t sessionId = 0; + uint8_t sessionHandle = 0; + std::string objectPath; + + EXPECT_FALSE( + parseCloseSessionInputPayload(objectPath, sessionId, sessionHandle)); +} + +TEST(isSessionObjectMatchedTest, ValidSessionId) +{ + std::string objectPath = + "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a"; + uint32_t sessionId = 0x12a4567d; + uint8_t sessionHandle = 0; + + EXPECT_TRUE(isSessionObjectMatched(objectPath, sessionId, sessionHandle)); +} + +TEST(isSessionObjectMatchedTest, ValidSessionHandle) +{ + std::string objectPath = + "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a"; + uint32_t sessionId = 0; + uint8_t sessionHandle = 0x8a; + + EXPECT_TRUE(isSessionObjectMatched(objectPath, sessionId, sessionHandle)); +} + +TEST(isSessionObjectMatchedTest, InvalidSessionId) +{ + std::string objectPath = + "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a"; + uint32_t sessionId = 0x1234b67d; + uint8_t sessionHandle = 0; + + EXPECT_FALSE(isSessionObjectMatched(objectPath, sessionId, sessionHandle)); +} + +TEST(isSessionObjectMatchedTest, InvalidSessionHandle) +{ + std::string objectPath = + "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a"; + uint32_t sessionId = 0; + uint8_t sessionHandle = 0x9b; + + EXPECT_FALSE(isSessionObjectMatched(objectPath, sessionId, sessionHandle)); +} + +TEST(isSessionObjectMatchedTest, ZeroSessionId_ZeroSessionHandle) +{ + std::string objectPath = + "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a"; + uint32_t sessionId = 0; + uint8_t sessionHandle = 0; + + EXPECT_FALSE(isSessionObjectMatched(objectPath, sessionId, sessionHandle)); +} + +TEST(isSessionObjectMatchedTest, InvalidObjectPath) +{ + // A valid object path will be like + // "/xyz/openbmc_project/ipmi/session/channel/sessionId_sessionHandle" + // Ex: "/xyz/openbmc_project/ipmi/session/eth0/12a4567d_8a" + // SessionId : 0X12a4567d + // SessionHandle: 0X8a + std::string objectPath = "/xyz/openbmc_project/ipmi/session/eth0/12a4567d"; + uint32_t sessionId = 0x12a4567d; + uint8_t sessionHandle = 0; + + EXPECT_FALSE(isSessionObjectMatched(objectPath, sessionId, sessionHandle)); +} + +TEST(isSessionObjectMatchedTest, NoObjectPath) +{ + std::string objectPath; + uint32_t sessionId = 0x12a4567d; + uint8_t sessionHandle = 0x8a; + + EXPECT_FALSE(isSessionObjectMatched(objectPath, sessionId, sessionHandle)); +} -- cgit v1.2.1