blob: b01ba4a263e79b2e07e766814d4ee87692fe53ce [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 Liubc8958f2022-07-04 09:29:49 +08006#include <phosphor-logging/log.hpp>
7#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
15using namespace phosphor::logging;
Tom Joseph3e61aa02016-08-08 08:42:39 -050016
Tom Joseph3e61aa02016-08-08 08:42:39 -050017namespace session
18{
19
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053020static std::array<uint8_t, session::maxNetworkInstanceSupported>
21 ipmiNetworkChannelNumList = {0};
22
23void Manager::setNetworkInstance(void)
24{
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053025 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 {
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053038 if (getInterfaceIndex() == ch)
39 {
40 ipmiNetworkInstance = index;
41 }
42
43 ipmiNetworkChannelNumList[index] = ch;
44 index++;
45 }
46 ch++;
47 }
48}
49
50uint8_t Manager::getNetworkInstance(void)
51{
52 return ipmiNetworkInstance;
53}
54
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053055void Manager::managerInit(const std::string& channel)
56{
Tom Joseph3e61aa02016-08-08 08:42:39 -050057 /*
58 * Session ID is 0000_0000h for messages that are sent outside the session.
59 * The session setup commands are sent on this session, so when the session
60 * manager comes up, is creates the Session ID 0000_0000h. It is active
61 * through the lifetime of the Session Manager.
62 */
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053063
64 objManager = std::make_unique<sdbusplus::server::manager::manager>(
65 *getSdBus(), session::sessionManagerRootPath);
66
67 auto objPath =
68 std::string(session::sessionManagerRootPath) + "/" + channel + "/0";
69
70 chName = channel;
71 setNetworkInstance();
72 sessionsMap.emplace(
73 0, std::make_shared<Session>(*getSdBus(), objPath.c_str(), 0, 0, 0));
Vernon Maueryecc8efa2021-06-12 12:52:23 -070074
75 // set up the timer for clearing out stale sessions
76 scheduleSessionCleaner(std::chrono::microseconds(3 * 1000 * 1000));
Tom Joseph3e61aa02016-08-08 08:42:39 -050077}
78
Vernon Maueryae1fda42018-10-15 12:55:34 -070079std::shared_ptr<Session>
Vernon Mauery9e801a22018-10-12 13:20:49 -070080 Manager::startSession(SessionID remoteConsoleSessID, Privilege priv,
81 cipher::rakp_auth::Algorithms authAlgo,
82 cipher::integrity::Algorithms intAlgo,
83 cipher::crypt::Algorithms cryptAlgo)
Tom Joseph3e61aa02016-08-08 08:42:39 -050084{
85 std::shared_ptr<Session> session = nullptr;
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053086 SessionID bmcSessionID = 0;
Tom Joseph3e61aa02016-08-08 08:42:39 -050087 cleanStaleEntries();
Vernon Mauery2528dfb2022-01-10 15:29:35 -080088 // set up the timer for monitoring this session
89 scheduleSessionCleaner(std::chrono::microseconds(1 * 1000 * 1000));
90
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053091 uint8_t sessionHandle = 0;
Tom Joseph3e61aa02016-08-08 08:42:39 -050092
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053093 auto activeSessions = sessionsMap.size() - session::maxSessionlessCount;
94
Vernon Maueryecc8efa2021-06-12 12:52:23 -070095 if (activeSessions < maxSessionHandles)
Tom Joseph3e61aa02016-08-08 08:42:39 -050096 {
97 do
98 {
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053099 bmcSessionID = (crypto::prng::rand());
100 bmcSessionID &= session::multiIntfaceSessionIDMask;
101 // In sessionID , BIT 31 BIT30 are used for netipmid instance
P Dheeraj Srujan Kumar02d17e82021-08-18 04:01:03 +0530102 bmcSessionID |= static_cast<uint32_t>(ipmiNetworkInstance) << 30;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500103 /*
104 * Every IPMI Session has two ID's attached to it Remote Console
105 * Session ID and BMC Session ID. The remote console ID is passed
106 * along with the Open Session request command. The BMC session ID
107 * is the key for the session map and is generated using std::rand.
108 * There is a rare chance for collision of BMC session ID, so the
109 * following check validates that. In the case of collision the
Gunnar Mills62ec6222018-04-08 16:28:23 -0500110 * created session is reset and a new session is created for
Tom Joseph3e61aa02016-08-08 08:42:39 -0500111 * validating collision.
112 */
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530113 auto iterator = sessionsMap.find(bmcSessionID);
Tom Joseph3e61aa02016-08-08 08:42:39 -0500114 if (iterator != sessionsMap.end())
115 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700116 // Detected BMC Session ID collisions
Tom Joseph3e61aa02016-08-08 08:42:39 -0500117 continue;
118 }
119 else
120 {
121 break;
122 }
Vernon Mauery9e801a22018-10-12 13:20:49 -0700123 } while (1);
Tom Joseph3e61aa02016-08-08 08:42:39 -0500124
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530125 sessionHandle = storeSessionHandle(bmcSessionID);
126
127 if (!sessionHandle)
128 {
129 throw std::runtime_error(
130 "Invalid sessionHandle - No sessionID slot ");
131 }
132 sessionHandle &= session::multiIntfaceSessionHandleMask;
133 // In sessionID , BIT 31 BIT30 are used for netipmid instance
P Dheeraj Srujan Kumar02d17e82021-08-18 04:01:03 +0530134 sessionHandle |= static_cast<uint8_t>(ipmiNetworkInstance) << 6;
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530135 std::stringstream sstream;
136 sstream << std::hex << bmcSessionID;
137 std::stringstream shstream;
138 shstream << std::hex << (int)sessionHandle;
139 auto objPath = std::string(session::sessionManagerRootPath) + "/" +
140 chName + "/" + sstream.str() + "_" + shstream.str();
141 session = std::make_shared<Session>(*getSdBus(), objPath.c_str(),
142 remoteConsoleSessID, bmcSessionID,
143 static_cast<uint8_t>(priv));
144
Vernon Mauery9b307be2017-11-22 09:28:16 -0800145 // Set the Authentication Algorithm
Tom Joseph3e61aa02016-08-08 08:42:39 -0500146 switch (authAlgo)
147 {
148 case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA1:
149 {
150 session->setAuthAlgo(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700151 std::make_unique<cipher::rakp_auth::AlgoSHA1>(intAlgo,
152 cryptAlgo));
Tom Joseph3e61aa02016-08-08 08:42:39 -0500153 break;
154 }
Vernon Mauery7e9e2ef2017-11-29 08:36:29 -0800155 case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA256:
156 {
157 session->setAuthAlgo(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700158 std::make_unique<cipher::rakp_auth::AlgoSHA256>(intAlgo,
159 cryptAlgo));
Vernon Mauery7e9e2ef2017-11-29 08:36:29 -0800160 break;
161 }
Tom Joseph3e61aa02016-08-08 08:42:39 -0500162 default:
163 {
164 throw std::runtime_error("Invalid Authentication Algorithm");
165 }
166 }
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530167
168 sessionsMap.emplace(bmcSessionID, session);
169 session->sessionHandle(sessionHandle);
170
Vernon Mauery4cb73592018-10-26 10:15:03 -0700171 return session;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500172 }
Vernon Mauery4cb73592018-10-26 10:15:03 -0700173
Vernon Maueryfc37e592018-12-19 14:55:15 -0800174 log<level::INFO>("No free RMCP+ sessions left");
Vernon Mauery4cb73592018-10-26 10:15:03 -0700175
Vernon Mauery4cb73592018-10-26 10:15:03 -0700176 throw std::runtime_error("No free sessions left");
Tom Joseph3e61aa02016-08-08 08:42:39 -0500177}
178
Tom Joseph9662c3a2016-12-06 17:52:16 +0530179bool Manager::stopSession(SessionID bmcSessionID)
Tom Joseph3e61aa02016-08-08 08:42:39 -0500180{
Tom Joseph9662c3a2016-12-06 17:52:16 +0530181 auto iter = sessionsMap.find(bmcSessionID);
182 if (iter != sessionsMap.end())
Tom Joseph3e61aa02016-08-08 08:42:39 -0500183 {
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530184 iter->second->state(
185 static_cast<uint8_t>(session::State::tearDownInProgress));
Tom Joseph9662c3a2016-12-06 17:52:16 +0530186 return true;
187 }
188 else
189 {
190 return false;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500191 }
192}
193
Vernon Maueryae1fda42018-10-15 12:55:34 -0700194std::shared_ptr<Session> Manager::getSession(SessionID sessionID,
195 RetrieveOption option)
Tom Joseph3e61aa02016-08-08 08:42:39 -0500196{
197 switch (option)
198 {
199 case RetrieveOption::BMC_SESSION_ID:
200 {
201 auto iter = sessionsMap.find(sessionID);
202 if (iter != sessionsMap.end())
203 {
204 return iter->second;
205 }
Tom Joseph6516cef2017-07-31 18:48:34 +0530206 break;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500207 }
208 case RetrieveOption::RC_SESSION_ID:
209 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700210 auto iter = std::find_if(
211 sessionsMap.begin(), sessionsMap.end(),
212 [sessionID](
213 const std::pair<const uint32_t, std::shared_ptr<Session>>&
214 in) -> bool {
215 return sessionID == in.second->getRCSessionID();
216 });
Tom Joseph3e61aa02016-08-08 08:42:39 -0500217
218 if (iter != sessionsMap.end())
219 {
220 return iter->second;
221 }
Tom Joseph6516cef2017-07-31 18:48:34 +0530222 break;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500223 }
224 default:
225 throw std::runtime_error("Invalid retrieval option");
226 }
227
228 throw std::runtime_error("Session ID not found");
229}
230
231void Manager::cleanStaleEntries()
232{
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700233 // with overflow = min(1, max - active sessions)
234 // active idle time in seconds = 60 / overflow^3
235 constexpr int baseIdleMicros = 60 * 1000 * 1000;
236 // no +1 for the zero session here because this is just active sessions
237 int sessionDivisor =
238 getActiveSessionCount() - session::maxSessionCountPerChannel;
239 sessionDivisor = std::max(0, sessionDivisor) + 1;
240 sessionDivisor = sessionDivisor * sessionDivisor * sessionDivisor;
241 int activeMicros = baseIdleMicros / sessionDivisor;
242
243 // with overflow = min(1, max - total sessions)
244 // setup idle time in seconds = max(3, 60 / overflow^3)
245
246 // +1 for the zero session here because size() counts that too
247 int setupDivisor =
248 sessionsMap.size() - (session::maxSessionCountPerChannel + 1);
249 setupDivisor = std::max(0, setupDivisor) + 1;
250 setupDivisor = setupDivisor * setupDivisor * setupDivisor;
251 constexpr int maxSetupMicros = 3 * 1000 * 1000;
252 int setupMicros = std::min(maxSetupMicros, baseIdleMicros / setupDivisor);
253
254 std::chrono::microseconds activeGrace(activeMicros);
255 std::chrono::microseconds setupGrace(setupMicros);
256
Vernon Mauery9e801a22018-10-12 13:20:49 -0700257 for (auto iter = sessionsMap.begin(); iter != sessionsMap.end();)
Tom Joseph3e61aa02016-08-08 08:42:39 -0500258 {
259 auto session = iter->second;
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700260 // special handling for sessionZero
261 if (session->getBMCSessionID() == session::sessionZero)
262 {
263 iter++;
264 continue;
265 }
266 if (!(session->isSessionActive(activeGrace, setupGrace)))
Tom Joseph3e61aa02016-08-08 08:42:39 -0500267 {
Vernon Mauery2528dfb2022-01-10 15:29:35 -0800268 log<level::INFO>(
269 "Removing idle IPMI LAN session",
270 entry("SESSION_ID=%x", session->getBMCSessionID()),
271 entry("HANDLE=%x",
272 getSessionHandle(session->getBMCSessionID())));
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530273 sessionHandleMap[getSessionHandle(session->getBMCSessionID())] = 0;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500274 iter = sessionsMap.erase(iter);
275 }
276 else
277 {
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700278 iter++;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500279 }
280 }
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700281 if (sessionsMap.size() > 1)
282 {
Vernon Mauery2528dfb2022-01-10 15:29:35 -0800283 constexpr int maxCleanupDelay = 1 * 1000 * 1000;
284 std::chrono::microseconds cleanupDelay(
285 std::min(setupMicros, maxCleanupDelay));
286 scheduleSessionCleaner(cleanupDelay);
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700287 }
Tom Joseph3e61aa02016-08-08 08:42:39 -0500288}
289
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530290uint8_t Manager::storeSessionHandle(SessionID bmcSessionID)
291{
292 // Handler index 0 is reserved for invalid session.
293 // index starts with 1, for direct usage. Index 0 reserved
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700294 for (size_t i = 1; i < session::maxSessionHandles; i++)
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530295 {
296 if (sessionHandleMap[i] == 0)
297 {
298 sessionHandleMap[i] = bmcSessionID;
299 return i;
300 }
301 }
302 return 0;
303}
304
305uint32_t Manager::getSessionIDbyHandle(uint8_t sessionHandle) const
306{
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700307 if (sessionHandle < session::maxSessionHandles)
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530308 {
309 return sessionHandleMap[sessionHandle];
310 }
311 return 0;
312}
313
314uint8_t Manager::getSessionHandle(SessionID bmcSessionID) const
315{
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530316 // Handler index 0 is reserved for invalid session.
317 // index starts with 1, for direct usage. Index 0 reserved
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700318 for (size_t i = 1; i < session::maxSessionHandles; i++)
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530319 {
320 if (sessionHandleMap[i] == bmcSessionID)
321 {
322 return (i);
323 }
324 }
325 return 0;
326}
327uint8_t Manager::getActiveSessionCount() const
328{
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530329 return (std::count_if(
330 sessionsMap.begin(), sessionsMap.end(),
331 [](const std::pair<const uint32_t, std::shared_ptr<Session>>& in)
332 -> bool {
333 return in.second->state() ==
334 static_cast<uint8_t>(session::State::active);
335 }));
336}
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700337
338void Manager::scheduleSessionCleaner(const std::chrono::microseconds& when)
339{
Vernon Mauery2528dfb2022-01-10 15:29:35 -0800340 std::chrono::duration expTime = timer.expires_from_now();
341 if (expTime > std::chrono::microseconds(0) && expTime < when)
342 {
George Liubc8958f2022-07-04 09:29:49 +0800343 // if timer has not already expired AND requested timeout is greater
344 // than current timeout then ignore this new requested timeout
Vernon Mauery2528dfb2022-01-10 15:29:35 -0800345 return;
346 }
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700347 timer.expires_from_now(when);
348 timer.async_wait([this](const boost::system::error_code& ec) {
349 if (!ec)
350 {
351 cleanStaleEntries();
352 }
353 });
354}
355
Tom Joseph3e61aa02016-08-08 08:42:39 -0500356} // namespace session