blob: ace75c341d89f25ed04fa9a1e6803b8100104abb [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
Tom Joseph3e61aa02016-08-08 08:42:39 -05006#include <algorithm>
7#include <cstdlib>
8#include <iomanip>
Tom Joseph3e61aa02016-08-08 08:42:39 -05009#include <memory>
Vernon Maueryfc37e592018-12-19 14:55:15 -080010#include <phosphor-logging/log.hpp>
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053011#include <sdbusplus/asio/connection.hpp>
12#include <user_channel/channel_layer.hpp>
Vernon Maueryfc37e592018-12-19 14:55:15 -080013
14using namespace phosphor::logging;
Tom Joseph3e61aa02016-08-08 08:42:39 -050015
Tom Joseph3e61aa02016-08-08 08:42:39 -050016namespace session
17{
18
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053019static std::array<uint8_t, session::maxNetworkInstanceSupported>
20 ipmiNetworkChannelNumList = {0};
21
22void Manager::setNetworkInstance(void)
23{
24
25 uint8_t index = 0, ch = 1;
26 // Constructing net-ipmid instances list based on channel info
27 // valid channel start from 1 to 15 and assuming max 4 LAN channel
28 // supported
29
30 while (ch < ipmi::maxIpmiChannels &&
31 index < session::maxNetworkInstanceSupported)
32 {
33 ipmi::ChannelInfo chInfo;
34 ipmi::getChannelInfo(ch, chInfo);
35 if (static_cast<ipmi::EChannelMediumType>(chInfo.mediumType) ==
36 ipmi::EChannelMediumType::lan8032)
37 {
38
39 if (getInterfaceIndex() == ch)
40 {
41 ipmiNetworkInstance = index;
42 }
43
44 ipmiNetworkChannelNumList[index] = ch;
45 index++;
46 }
47 ch++;
48 }
49}
50
51uint8_t Manager::getNetworkInstance(void)
52{
53 return ipmiNetworkInstance;
54}
55
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053056void Manager::managerInit(const std::string& channel)
57{
58
Tom Joseph3e61aa02016-08-08 08:42:39 -050059 /*
60 * Session ID is 0000_0000h for messages that are sent outside the session.
61 * The session setup commands are sent on this session, so when the session
62 * manager comes up, is creates the Session ID 0000_0000h. It is active
63 * through the lifetime of the Session Manager.
64 */
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053065
66 objManager = std::make_unique<sdbusplus::server::manager::manager>(
67 *getSdBus(), session::sessionManagerRootPath);
68
69 auto objPath =
70 std::string(session::sessionManagerRootPath) + "/" + channel + "/0";
71
72 chName = channel;
73 setNetworkInstance();
74 sessionsMap.emplace(
75 0, std::make_shared<Session>(*getSdBus(), objPath.c_str(), 0, 0, 0));
Vernon Maueryecc8efa2021-06-12 12:52:23 -070076
77 // set up the timer for clearing out stale sessions
78 scheduleSessionCleaner(std::chrono::microseconds(3 * 1000 * 1000));
Tom Joseph3e61aa02016-08-08 08:42:39 -050079}
80
Vernon Maueryae1fda42018-10-15 12:55:34 -070081std::shared_ptr<Session>
Vernon Mauery9e801a22018-10-12 13:20:49 -070082 Manager::startSession(SessionID remoteConsoleSessID, Privilege priv,
83 cipher::rakp_auth::Algorithms authAlgo,
84 cipher::integrity::Algorithms intAlgo,
85 cipher::crypt::Algorithms cryptAlgo)
Tom Joseph3e61aa02016-08-08 08:42:39 -050086{
87 std::shared_ptr<Session> session = nullptr;
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053088 SessionID bmcSessionID = 0;
Tom Joseph3e61aa02016-08-08 08:42:39 -050089 cleanStaleEntries();
Vernon Mauery2528dfb2022-01-10 15:29:35 -080090 // set up the timer for monitoring this session
91 scheduleSessionCleaner(std::chrono::microseconds(1 * 1000 * 1000));
92
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053093 uint8_t sessionHandle = 0;
Tom Joseph3e61aa02016-08-08 08:42:39 -050094
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053095 auto activeSessions = sessionsMap.size() - session::maxSessionlessCount;
96
Vernon Maueryecc8efa2021-06-12 12:52:23 -070097 if (activeSessions < maxSessionHandles)
Tom Joseph3e61aa02016-08-08 08:42:39 -050098 {
99 do
100 {
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530101 bmcSessionID = (crypto::prng::rand());
102 bmcSessionID &= session::multiIntfaceSessionIDMask;
103 // In sessionID , BIT 31 BIT30 are used for netipmid instance
P Dheeraj Srujan Kumar02d17e82021-08-18 04:01:03 +0530104 bmcSessionID |= static_cast<uint32_t>(ipmiNetworkInstance) << 30;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500105 /*
106 * Every IPMI Session has two ID's attached to it Remote Console
107 * Session ID and BMC Session ID. The remote console ID is passed
108 * along with the Open Session request command. The BMC session ID
109 * is the key for the session map and is generated using std::rand.
110 * There is a rare chance for collision of BMC session ID, so the
111 * following check validates that. In the case of collision the
Gunnar Mills62ec6222018-04-08 16:28:23 -0500112 * created session is reset and a new session is created for
Tom Joseph3e61aa02016-08-08 08:42:39 -0500113 * validating collision.
114 */
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530115 auto iterator = sessionsMap.find(bmcSessionID);
Tom Joseph3e61aa02016-08-08 08:42:39 -0500116 if (iterator != sessionsMap.end())
117 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700118 // Detected BMC Session ID collisions
Tom Joseph3e61aa02016-08-08 08:42:39 -0500119 continue;
120 }
121 else
122 {
123 break;
124 }
Vernon Mauery9e801a22018-10-12 13:20:49 -0700125 } while (1);
Tom Joseph3e61aa02016-08-08 08:42:39 -0500126
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530127 sessionHandle = storeSessionHandle(bmcSessionID);
128
129 if (!sessionHandle)
130 {
131 throw std::runtime_error(
132 "Invalid sessionHandle - No sessionID slot ");
133 }
134 sessionHandle &= session::multiIntfaceSessionHandleMask;
135 // In sessionID , BIT 31 BIT30 are used for netipmid instance
P Dheeraj Srujan Kumar02d17e82021-08-18 04:01:03 +0530136 sessionHandle |= static_cast<uint8_t>(ipmiNetworkInstance) << 6;
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530137 std::stringstream sstream;
138 sstream << std::hex << bmcSessionID;
139 std::stringstream shstream;
140 shstream << std::hex << (int)sessionHandle;
141 auto objPath = std::string(session::sessionManagerRootPath) + "/" +
142 chName + "/" + sstream.str() + "_" + shstream.str();
143 session = std::make_shared<Session>(*getSdBus(), objPath.c_str(),
144 remoteConsoleSessID, bmcSessionID,
145 static_cast<uint8_t>(priv));
146
Vernon Mauery9b307be2017-11-22 09:28:16 -0800147 // Set the Authentication Algorithm
Tom Joseph3e61aa02016-08-08 08:42:39 -0500148 switch (authAlgo)
149 {
150 case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA1:
151 {
152 session->setAuthAlgo(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700153 std::make_unique<cipher::rakp_auth::AlgoSHA1>(intAlgo,
154 cryptAlgo));
Tom Joseph3e61aa02016-08-08 08:42:39 -0500155 break;
156 }
Vernon Mauery7e9e2ef2017-11-29 08:36:29 -0800157 case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA256:
158 {
159 session->setAuthAlgo(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700160 std::make_unique<cipher::rakp_auth::AlgoSHA256>(intAlgo,
161 cryptAlgo));
Vernon Mauery7e9e2ef2017-11-29 08:36:29 -0800162 break;
163 }
Tom Joseph3e61aa02016-08-08 08:42:39 -0500164 default:
165 {
166 throw std::runtime_error("Invalid Authentication Algorithm");
167 }
168 }
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530169
170 sessionsMap.emplace(bmcSessionID, session);
171 session->sessionHandle(sessionHandle);
172
Vernon Mauery4cb73592018-10-26 10:15:03 -0700173 return session;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500174 }
Vernon Mauery4cb73592018-10-26 10:15:03 -0700175
Vernon Maueryfc37e592018-12-19 14:55:15 -0800176 log<level::INFO>("No free RMCP+ sessions left");
Vernon Mauery4cb73592018-10-26 10:15:03 -0700177
Vernon Mauery4cb73592018-10-26 10:15:03 -0700178 throw std::runtime_error("No free sessions left");
Tom Joseph3e61aa02016-08-08 08:42:39 -0500179}
180
Tom Joseph9662c3a2016-12-06 17:52:16 +0530181bool Manager::stopSession(SessionID bmcSessionID)
Tom Joseph3e61aa02016-08-08 08:42:39 -0500182{
Tom Joseph9662c3a2016-12-06 17:52:16 +0530183 auto iter = sessionsMap.find(bmcSessionID);
184 if (iter != sessionsMap.end())
Tom Joseph3e61aa02016-08-08 08:42:39 -0500185 {
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530186 iter->second->state(
187 static_cast<uint8_t>(session::State::tearDownInProgress));
Tom Joseph9662c3a2016-12-06 17:52:16 +0530188 return true;
189 }
190 else
191 {
192 return false;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500193 }
194}
195
Vernon Maueryae1fda42018-10-15 12:55:34 -0700196std::shared_ptr<Session> Manager::getSession(SessionID sessionID,
197 RetrieveOption option)
Tom Joseph3e61aa02016-08-08 08:42:39 -0500198{
199 switch (option)
200 {
201 case RetrieveOption::BMC_SESSION_ID:
202 {
203 auto iter = sessionsMap.find(sessionID);
204 if (iter != sessionsMap.end())
205 {
206 return iter->second;
207 }
Tom Joseph6516cef2017-07-31 18:48:34 +0530208 break;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500209 }
210 case RetrieveOption::RC_SESSION_ID:
211 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700212 auto iter = std::find_if(
213 sessionsMap.begin(), sessionsMap.end(),
214 [sessionID](
215 const std::pair<const uint32_t, std::shared_ptr<Session>>&
216 in) -> bool {
217 return sessionID == in.second->getRCSessionID();
218 });
Tom Joseph3e61aa02016-08-08 08:42:39 -0500219
220 if (iter != sessionsMap.end())
221 {
222 return iter->second;
223 }
Tom Joseph6516cef2017-07-31 18:48:34 +0530224 break;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500225 }
226 default:
227 throw std::runtime_error("Invalid retrieval option");
228 }
229
230 throw std::runtime_error("Session ID not found");
231}
232
233void Manager::cleanStaleEntries()
234{
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700235 // with overflow = min(1, max - active sessions)
236 // active idle time in seconds = 60 / overflow^3
237 constexpr int baseIdleMicros = 60 * 1000 * 1000;
238 // no +1 for the zero session here because this is just active sessions
239 int sessionDivisor =
240 getActiveSessionCount() - session::maxSessionCountPerChannel;
241 sessionDivisor = std::max(0, sessionDivisor) + 1;
242 sessionDivisor = sessionDivisor * sessionDivisor * sessionDivisor;
243 int activeMicros = baseIdleMicros / sessionDivisor;
244
245 // with overflow = min(1, max - total sessions)
246 // setup idle time in seconds = max(3, 60 / overflow^3)
247
248 // +1 for the zero session here because size() counts that too
249 int setupDivisor =
250 sessionsMap.size() - (session::maxSessionCountPerChannel + 1);
251 setupDivisor = std::max(0, setupDivisor) + 1;
252 setupDivisor = setupDivisor * setupDivisor * setupDivisor;
253 constexpr int maxSetupMicros = 3 * 1000 * 1000;
254 int setupMicros = std::min(maxSetupMicros, baseIdleMicros / setupDivisor);
255
256 std::chrono::microseconds activeGrace(activeMicros);
257 std::chrono::microseconds setupGrace(setupMicros);
258
Vernon Mauery9e801a22018-10-12 13:20:49 -0700259 for (auto iter = sessionsMap.begin(); iter != sessionsMap.end();)
Tom Joseph3e61aa02016-08-08 08:42:39 -0500260 {
261 auto session = iter->second;
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700262 // special handling for sessionZero
263 if (session->getBMCSessionID() == session::sessionZero)
264 {
265 iter++;
266 continue;
267 }
268 if (!(session->isSessionActive(activeGrace, setupGrace)))
Tom Joseph3e61aa02016-08-08 08:42:39 -0500269 {
Vernon Mauery2528dfb2022-01-10 15:29:35 -0800270 log<level::INFO>(
271 "Removing idle IPMI LAN session",
272 entry("SESSION_ID=%x", session->getBMCSessionID()),
273 entry("HANDLE=%x",
274 getSessionHandle(session->getBMCSessionID())));
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530275 sessionHandleMap[getSessionHandle(session->getBMCSessionID())] = 0;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500276 iter = sessionsMap.erase(iter);
277 }
278 else
279 {
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700280 iter++;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500281 }
282 }
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700283 if (sessionsMap.size() > 1)
284 {
Vernon Mauery2528dfb2022-01-10 15:29:35 -0800285 constexpr int maxCleanupDelay = 1 * 1000 * 1000;
286 std::chrono::microseconds cleanupDelay(
287 std::min(setupMicros, maxCleanupDelay));
288 scheduleSessionCleaner(cleanupDelay);
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700289 }
Tom Joseph3e61aa02016-08-08 08:42:39 -0500290}
291
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530292uint8_t Manager::storeSessionHandle(SessionID bmcSessionID)
293{
294 // Handler index 0 is reserved for invalid session.
295 // index starts with 1, for direct usage. Index 0 reserved
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700296 for (size_t i = 1; i < session::maxSessionHandles; i++)
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530297 {
298 if (sessionHandleMap[i] == 0)
299 {
300 sessionHandleMap[i] = bmcSessionID;
301 return i;
302 }
303 }
304 return 0;
305}
306
307uint32_t Manager::getSessionIDbyHandle(uint8_t sessionHandle) const
308{
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700309 if (sessionHandle < session::maxSessionHandles)
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530310 {
311 return sessionHandleMap[sessionHandle];
312 }
313 return 0;
314}
315
316uint8_t Manager::getSessionHandle(SessionID bmcSessionID) const
317{
318
319 // Handler index 0 is reserved for invalid session.
320 // index starts with 1, for direct usage. Index 0 reserved
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700321 for (size_t i = 1; i < session::maxSessionHandles; i++)
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530322 {
323 if (sessionHandleMap[i] == bmcSessionID)
324 {
325 return (i);
326 }
327 }
328 return 0;
329}
330uint8_t Manager::getActiveSessionCount() const
331{
332
333 return (std::count_if(
334 sessionsMap.begin(), sessionsMap.end(),
335 [](const std::pair<const uint32_t, std::shared_ptr<Session>>& in)
336 -> bool {
337 return in.second->state() ==
338 static_cast<uint8_t>(session::State::active);
339 }));
340}
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700341
342void Manager::scheduleSessionCleaner(const std::chrono::microseconds& when)
343{
Vernon Mauery2528dfb2022-01-10 15:29:35 -0800344 std::chrono::duration expTime = timer.expires_from_now();
345 if (expTime > std::chrono::microseconds(0) && expTime < when)
346 {
347 // if timer has not already expired AND
348 // requested timeout is greater than current timeout
349 // then ignore this new requested timeout
350 return;
351 }
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700352 timer.expires_from_now(when);
353 timer.async_wait([this](const boost::system::error_code& ec) {
354 if (!ec)
355 {
356 cleanStaleEntries();
357 }
358 });
359}
360
Tom Joseph3e61aa02016-08-08 08:42:39 -0500361} // namespace session