blob: 79bf9debbc60442b2af59717f11ef6e109af8291 [file] [log] [blame]
Tom Joseph3e61aa02016-08-08 08:42:39 -05001#include "sessions_manager.hpp"
2
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +05303#include "main.hpp"
Vernon Mauery9e801a22018-10-12 13:20:49 -07004#include "session.hpp"
5
George Liu7b7f25f2022-07-04 17:07:32 +08006#include <phosphor-logging/lg2.hpp>
George Liubc8958f2022-07-04 09:29:49 +08007#include <sdbusplus/asio/connection.hpp>
8#include <user_channel/channel_layer.hpp>
9
Tom Joseph3e61aa02016-08-08 08:42:39 -050010#include <algorithm>
11#include <cstdlib>
12#include <iomanip>
Tom Joseph3e61aa02016-08-08 08:42:39 -050013#include <memory>
Vernon Maueryfc37e592018-12-19 14:55:15 -080014
Tom Joseph3e61aa02016-08-08 08:42:39 -050015namespace session
16{
17
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053018static std::array<uint8_t, session::maxNetworkInstanceSupported>
19 ipmiNetworkChannelNumList = {0};
20
21void Manager::setNetworkInstance(void)
22{
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053023 uint8_t index = 0, ch = 1;
24 // Constructing net-ipmid instances list based on channel info
25 // valid channel start from 1 to 15 and assuming max 4 LAN channel
26 // supported
27
28 while (ch < ipmi::maxIpmiChannels &&
29 index < session::maxNetworkInstanceSupported)
30 {
31 ipmi::ChannelInfo chInfo;
32 ipmi::getChannelInfo(ch, chInfo);
33 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
34 ipmi::EChannelMediumType::lan8032)
35 {
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053036 if (getInterfaceIndex() == ch)
37 {
38 ipmiNetworkInstance = index;
39 }
40
41 ipmiNetworkChannelNumList[index] = ch;
42 index++;
43 }
44 ch++;
45 }
46}
47
48uint8_t Manager::getNetworkInstance(void)
49{
50 return ipmiNetworkInstance;
51}
52
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053053void Manager::managerInit(const std::string& channel)
54{
Tom Joseph3e61aa02016-08-08 08:42:39 -050055 /*
56 * Session ID is 0000_0000h for messages that are sent outside the session.
57 * The session setup commands are sent on this session, so when the session
58 * manager comes up, is creates the Session ID 0000_0000h. It is active
59 * through the lifetime of the Session Manager.
60 */
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053061
Patrick Williams0a590622022-07-22 19:26:53 -050062 objManager = std::make_unique<sdbusplus::server::manager_t>(
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053063 *getSdBus(), session::sessionManagerRootPath);
64
Patrick Williams099fb092023-05-10 07:50:31 -050065 auto objPath = std::string(session::sessionManagerRootPath) + "/" +
66 channel + "/0";
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053067
68 chName = channel;
69 setNetworkInstance();
70 sessionsMap.emplace(
71 0, std::make_shared<Session>(*getSdBus(), objPath.c_str(), 0, 0, 0));
Vernon Maueryecc8efa2021-06-12 12:52:23 -070072
73 // set up the timer for clearing out stale sessions
74 scheduleSessionCleaner(std::chrono::microseconds(3 * 1000 * 1000));
Tom Joseph3e61aa02016-08-08 08:42:39 -050075}
76
Patrick Williams84256242024-08-16 15:20:21 -040077std::shared_ptr<Session> Manager::startSession(
78 SessionID remoteConsoleSessID, Privilege priv,
79 cipher::rakp_auth::Algorithms authAlgo,
80 cipher::integrity::Algorithms intAlgo, cipher::crypt::Algorithms cryptAlgo)
Tom Joseph3e61aa02016-08-08 08:42:39 -050081{
82 std::shared_ptr<Session> session = nullptr;
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053083 SessionID bmcSessionID = 0;
Tom Joseph3e61aa02016-08-08 08:42:39 -050084 cleanStaleEntries();
Vernon Mauery2528dfb2022-01-10 15:29:35 -080085 // set up the timer for monitoring this session
86 scheduleSessionCleaner(std::chrono::microseconds(1 * 1000 * 1000));
87
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053088 uint8_t sessionHandle = 0;
Tom Joseph3e61aa02016-08-08 08:42:39 -050089
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053090 auto activeSessions = sessionsMap.size() - session::maxSessionlessCount;
91
Vernon Maueryecc8efa2021-06-12 12:52:23 -070092 if (activeSessions < maxSessionHandles)
Tom Joseph3e61aa02016-08-08 08:42:39 -050093 {
94 do
95 {
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053096 bmcSessionID = (crypto::prng::rand());
97 bmcSessionID &= session::multiIntfaceSessionIDMask;
98 // In sessionID , BIT 31 BIT30 are used for netipmid instance
P Dheeraj Srujan Kumar02d17e82021-08-18 04:01:03 +053099 bmcSessionID |= static_cast<uint32_t>(ipmiNetworkInstance) << 30;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500100 /*
101 * Every IPMI Session has two ID's attached to it Remote Console
102 * Session ID and BMC Session ID. The remote console ID is passed
103 * along with the Open Session request command. The BMC session ID
104 * is the key for the session map and is generated using std::rand.
105 * There is a rare chance for collision of BMC session ID, so the
106 * following check validates that. In the case of collision the
Gunnar Mills62ec6222018-04-08 16:28:23 -0500107 * created session is reset and a new session is created for
Tom Joseph3e61aa02016-08-08 08:42:39 -0500108 * validating collision.
109 */
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530110 auto iterator = sessionsMap.find(bmcSessionID);
Tom Joseph3e61aa02016-08-08 08:42:39 -0500111 if (iterator != sessionsMap.end())
112 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700113 // Detected BMC Session ID collisions
Tom Joseph3e61aa02016-08-08 08:42:39 -0500114 continue;
115 }
116 else
117 {
118 break;
119 }
Vernon Mauery9e801a22018-10-12 13:20:49 -0700120 } while (1);
Tom Joseph3e61aa02016-08-08 08:42:39 -0500121
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530122 sessionHandle = storeSessionHandle(bmcSessionID);
123
124 if (!sessionHandle)
125 {
126 throw std::runtime_error(
127 "Invalid sessionHandle - No sessionID slot ");
128 }
129 sessionHandle &= session::multiIntfaceSessionHandleMask;
130 // In sessionID , BIT 31 BIT30 are used for netipmid instance
P Dheeraj Srujan Kumar02d17e82021-08-18 04:01:03 +0530131 sessionHandle |= static_cast<uint8_t>(ipmiNetworkInstance) << 6;
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530132 std::stringstream sstream;
133 sstream << std::hex << bmcSessionID;
134 std::stringstream shstream;
135 shstream << std::hex << (int)sessionHandle;
136 auto objPath = std::string(session::sessionManagerRootPath) + "/" +
137 chName + "/" + sstream.str() + "_" + shstream.str();
138 session = std::make_shared<Session>(*getSdBus(), objPath.c_str(),
139 remoteConsoleSessID, bmcSessionID,
140 static_cast<uint8_t>(priv));
141
Vernon Mauery9b307be2017-11-22 09:28:16 -0800142 // Set the Authentication Algorithm
Tom Joseph3e61aa02016-08-08 08:42:39 -0500143 switch (authAlgo)
144 {
145 case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA1:
146 {
147 session->setAuthAlgo(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700148 std::make_unique<cipher::rakp_auth::AlgoSHA1>(intAlgo,
149 cryptAlgo));
Tom Joseph3e61aa02016-08-08 08:42:39 -0500150 break;
151 }
Vernon Mauery7e9e2ef2017-11-29 08:36:29 -0800152 case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA256:
153 {
154 session->setAuthAlgo(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700155 std::make_unique<cipher::rakp_auth::AlgoSHA256>(intAlgo,
156 cryptAlgo));
Vernon Mauery7e9e2ef2017-11-29 08:36:29 -0800157 break;
158 }
Tom Joseph3e61aa02016-08-08 08:42:39 -0500159 default:
160 {
161 throw std::runtime_error("Invalid Authentication Algorithm");
162 }
163 }
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530164
165 sessionsMap.emplace(bmcSessionID, session);
166 session->sessionHandle(sessionHandle);
167
Vernon Mauery4cb73592018-10-26 10:15:03 -0700168 return session;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500169 }
Vernon Mauery4cb73592018-10-26 10:15:03 -0700170
George Liu7b7f25f2022-07-04 17:07:32 +0800171 lg2::info("No free RMCP+ sessions left");
Vernon Mauery4cb73592018-10-26 10:15:03 -0700172
Vernon Mauery4cb73592018-10-26 10:15:03 -0700173 throw std::runtime_error("No free sessions left");
Tom Joseph3e61aa02016-08-08 08:42:39 -0500174}
175
Tom Joseph9662c3a2016-12-06 17:52:16 +0530176bool Manager::stopSession(SessionID bmcSessionID)
Tom Joseph3e61aa02016-08-08 08:42:39 -0500177{
Tom Joseph9662c3a2016-12-06 17:52:16 +0530178 auto iter = sessionsMap.find(bmcSessionID);
179 if (iter != sessionsMap.end())
Tom Joseph3e61aa02016-08-08 08:42:39 -0500180 {
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530181 iter->second->state(
182 static_cast<uint8_t>(session::State::tearDownInProgress));
Tom Joseph9662c3a2016-12-06 17:52:16 +0530183 return true;
184 }
185 else
186 {
187 return false;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500188 }
189}
190
Patrick Williams84256242024-08-16 15:20:21 -0400191std::shared_ptr<Session>
192 Manager::getSession(SessionID sessionID, RetrieveOption option)
Tom Joseph3e61aa02016-08-08 08:42:39 -0500193{
194 switch (option)
195 {
196 case RetrieveOption::BMC_SESSION_ID:
197 {
198 auto iter = sessionsMap.find(sessionID);
199 if (iter != sessionsMap.end())
200 {
201 return iter->second;
202 }
Tom Joseph6516cef2017-07-31 18:48:34 +0530203 break;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500204 }
205 case RetrieveOption::RC_SESSION_ID:
206 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700207 auto iter = std::find_if(
208 sessionsMap.begin(), sessionsMap.end(),
209 [sessionID](
210 const std::pair<const uint32_t, std::shared_ptr<Session>>&
211 in) -> bool {
Patrick Williams84256242024-08-16 15:20:21 -0400212 return sessionID == in.second->getRCSessionID();
213 });
Tom Joseph3e61aa02016-08-08 08:42:39 -0500214
215 if (iter != sessionsMap.end())
216 {
217 return iter->second;
218 }
Tom Joseph6516cef2017-07-31 18:48:34 +0530219 break;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500220 }
221 default:
222 throw std::runtime_error("Invalid retrieval option");
223 }
224
225 throw std::runtime_error("Session ID not found");
226}
227
228void Manager::cleanStaleEntries()
229{
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700230 // with overflow = min(1, max - active sessions)
231 // active idle time in seconds = 60 / overflow^3
232 constexpr int baseIdleMicros = 60 * 1000 * 1000;
233 // no +1 for the zero session here because this is just active sessions
Patrick Williams84256242024-08-16 15:20:21 -0400234 int sessionDivisor =
235 getActiveSessionCount() - session::maxSessionCountPerChannel;
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700236 sessionDivisor = std::max(0, sessionDivisor) + 1;
237 sessionDivisor = sessionDivisor * sessionDivisor * sessionDivisor;
238 int activeMicros = baseIdleMicros / sessionDivisor;
239
240 // with overflow = min(1, max - total sessions)
241 // setup idle time in seconds = max(3, 60 / overflow^3)
242
243 // +1 for the zero session here because size() counts that too
Patrick Williams84256242024-08-16 15:20:21 -0400244 int setupDivisor =
245 sessionsMap.size() - (session::maxSessionCountPerChannel + 1);
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700246 setupDivisor = std::max(0, setupDivisor) + 1;
247 setupDivisor = setupDivisor * setupDivisor * setupDivisor;
248 constexpr int maxSetupMicros = 3 * 1000 * 1000;
249 int setupMicros = std::min(maxSetupMicros, baseIdleMicros / setupDivisor);
250
251 std::chrono::microseconds activeGrace(activeMicros);
252 std::chrono::microseconds setupGrace(setupMicros);
253
Vernon Mauery9e801a22018-10-12 13:20:49 -0700254 for (auto iter = sessionsMap.begin(); iter != sessionsMap.end();)
Tom Joseph3e61aa02016-08-08 08:42:39 -0500255 {
256 auto session = iter->second;
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700257 // special handling for sessionZero
258 if (session->getBMCSessionID() == session::sessionZero)
259 {
260 iter++;
261 continue;
262 }
263 if (!(session->isSessionActive(activeGrace, setupGrace)))
Tom Joseph3e61aa02016-08-08 08:42:39 -0500264 {
George Liu7b7f25f2022-07-04 17:07:32 +0800265 lg2::info(
266 "Removing idle IPMI LAN session, id: {ID}, handler: {HANDLE}",
267 "ID", session->getBMCSessionID(), "HANDLE",
268 getSessionHandle(session->getBMCSessionID()));
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530269 sessionHandleMap[getSessionHandle(session->getBMCSessionID())] = 0;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500270 iter = sessionsMap.erase(iter);
271 }
272 else
273 {
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700274 iter++;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500275 }
276 }
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700277 if (sessionsMap.size() > 1)
278 {
Vernon Mauery2528dfb2022-01-10 15:29:35 -0800279 constexpr int maxCleanupDelay = 1 * 1000 * 1000;
280 std::chrono::microseconds cleanupDelay(
281 std::min(setupMicros, maxCleanupDelay));
282 scheduleSessionCleaner(cleanupDelay);
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700283 }
Tom Joseph3e61aa02016-08-08 08:42:39 -0500284}
285
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530286uint8_t Manager::storeSessionHandle(SessionID bmcSessionID)
287{
288 // Handler index 0 is reserved for invalid session.
289 // index starts with 1, for direct usage. Index 0 reserved
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700290 for (size_t i = 1; i < session::maxSessionHandles; i++)
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530291 {
292 if (sessionHandleMap[i] == 0)
293 {
294 sessionHandleMap[i] = bmcSessionID;
295 return i;
296 }
297 }
298 return 0;
299}
300
301uint32_t Manager::getSessionIDbyHandle(uint8_t sessionHandle) const
302{
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700303 if (sessionHandle < session::maxSessionHandles)
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530304 {
305 return sessionHandleMap[sessionHandle];
306 }
307 return 0;
308}
309
310uint8_t Manager::getSessionHandle(SessionID bmcSessionID) const
311{
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530312 // Handler index 0 is reserved for invalid session.
313 // index starts with 1, for direct usage. Index 0 reserved
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700314 for (size_t i = 1; i < session::maxSessionHandles; i++)
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530315 {
316 if (sessionHandleMap[i] == bmcSessionID)
317 {
318 return (i);
319 }
320 }
321 return 0;
322}
323uint8_t Manager::getActiveSessionCount() const
324{
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530325 return (std::count_if(
326 sessionsMap.begin(), sessionsMap.end(),
327 [](const std::pair<const uint32_t, std::shared_ptr<Session>>& in)
328 -> bool {
Patrick Williams84256242024-08-16 15:20:21 -0400329 return in.second->state() ==
330 static_cast<uint8_t>(session::State::active);
331 }));
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530332}
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700333
334void Manager::scheduleSessionCleaner(const std::chrono::microseconds& when)
335{
Ed Tanous6d206812023-03-06 14:04:00 -0800336 std::chrono::duration expTime =
337 timer.expiry() - boost::asio::steady_timer::clock_type::now();
Vernon Mauery2528dfb2022-01-10 15:29:35 -0800338 if (expTime > std::chrono::microseconds(0) && expTime < when)
339 {
George Liubc8958f2022-07-04 09:29:49 +0800340 // if timer has not already expired AND requested timeout is greater
341 // than current timeout then ignore this new requested timeout
Vernon Mauery2528dfb2022-01-10 15:29:35 -0800342 return;
343 }
Ed Tanous6d206812023-03-06 14:04:00 -0800344 timer.expires_after(when);
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700345 timer.async_wait([this](const boost::system::error_code& ec) {
346 if (!ec)
347 {
348 cleanStaleEntries();
349 }
350 });
351}
352
Tom Joseph3e61aa02016-08-08 08:42:39 -0500353} // namespace session