blob: c18bf808b2b2aba8795ef33336cb0493194f9c77 [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
65 auto objPath =
66 std::string(session::sessionManagerRootPath) + "/" + channel + "/0";
67
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
Vernon Maueryae1fda42018-10-15 12:55:34 -070077std::shared_ptr<Session>
Vernon Mauery9e801a22018-10-12 13:20:49 -070078 Manager::startSession(SessionID remoteConsoleSessID, Privilege priv,
79 cipher::rakp_auth::Algorithms authAlgo,
80 cipher::integrity::Algorithms intAlgo,
81 cipher::crypt::Algorithms cryptAlgo)
Tom Joseph3e61aa02016-08-08 08:42:39 -050082{
83 std::shared_ptr<Session> session = nullptr;
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053084 SessionID bmcSessionID = 0;
Tom Joseph3e61aa02016-08-08 08:42:39 -050085 cleanStaleEntries();
Vernon Mauery2528dfb2022-01-10 15:29:35 -080086 // set up the timer for monitoring this session
87 scheduleSessionCleaner(std::chrono::microseconds(1 * 1000 * 1000));
88
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053089 uint8_t sessionHandle = 0;
Tom Joseph3e61aa02016-08-08 08:42:39 -050090
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053091 auto activeSessions = sessionsMap.size() - session::maxSessionlessCount;
92
Vernon Maueryecc8efa2021-06-12 12:52:23 -070093 if (activeSessions < maxSessionHandles)
Tom Joseph3e61aa02016-08-08 08:42:39 -050094 {
95 do
96 {
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053097 bmcSessionID = (crypto::prng::rand());
98 bmcSessionID &= session::multiIntfaceSessionIDMask;
99 // In sessionID , BIT 31 BIT30 are used for netipmid instance
P Dheeraj Srujan Kumar02d17e82021-08-18 04:01:03 +0530100 bmcSessionID |= static_cast<uint32_t>(ipmiNetworkInstance) << 30;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500101 /*
102 * Every IPMI Session has two ID's attached to it Remote Console
103 * Session ID and BMC Session ID. The remote console ID is passed
104 * along with the Open Session request command. The BMC session ID
105 * is the key for the session map and is generated using std::rand.
106 * There is a rare chance for collision of BMC session ID, so the
107 * following check validates that. In the case of collision the
Gunnar Mills62ec6222018-04-08 16:28:23 -0500108 * created session is reset and a new session is created for
Tom Joseph3e61aa02016-08-08 08:42:39 -0500109 * validating collision.
110 */
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530111 auto iterator = sessionsMap.find(bmcSessionID);
Tom Joseph3e61aa02016-08-08 08:42:39 -0500112 if (iterator != sessionsMap.end())
113 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700114 // Detected BMC Session ID collisions
Tom Joseph3e61aa02016-08-08 08:42:39 -0500115 continue;
116 }
117 else
118 {
119 break;
120 }
Vernon Mauery9e801a22018-10-12 13:20:49 -0700121 } while (1);
Tom Joseph3e61aa02016-08-08 08:42:39 -0500122
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530123 sessionHandle = storeSessionHandle(bmcSessionID);
124
125 if (!sessionHandle)
126 {
127 throw std::runtime_error(
128 "Invalid sessionHandle - No sessionID slot ");
129 }
130 sessionHandle &= session::multiIntfaceSessionHandleMask;
131 // In sessionID , BIT 31 BIT30 are used for netipmid instance
P Dheeraj Srujan Kumar02d17e82021-08-18 04:01:03 +0530132 sessionHandle |= static_cast<uint8_t>(ipmiNetworkInstance) << 6;
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530133 std::stringstream sstream;
134 sstream << std::hex << bmcSessionID;
135 std::stringstream shstream;
136 shstream << std::hex << (int)sessionHandle;
137 auto objPath = std::string(session::sessionManagerRootPath) + "/" +
138 chName + "/" + sstream.str() + "_" + shstream.str();
139 session = std::make_shared<Session>(*getSdBus(), objPath.c_str(),
140 remoteConsoleSessID, bmcSessionID,
141 static_cast<uint8_t>(priv));
142
Vernon Mauery9b307be2017-11-22 09:28:16 -0800143 // Set the Authentication Algorithm
Tom Joseph3e61aa02016-08-08 08:42:39 -0500144 switch (authAlgo)
145 {
146 case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA1:
147 {
148 session->setAuthAlgo(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700149 std::make_unique<cipher::rakp_auth::AlgoSHA1>(intAlgo,
150 cryptAlgo));
Tom Joseph3e61aa02016-08-08 08:42:39 -0500151 break;
152 }
Vernon Mauery7e9e2ef2017-11-29 08:36:29 -0800153 case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA256:
154 {
155 session->setAuthAlgo(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700156 std::make_unique<cipher::rakp_auth::AlgoSHA256>(intAlgo,
157 cryptAlgo));
Vernon Mauery7e9e2ef2017-11-29 08:36:29 -0800158 break;
159 }
Tom Joseph3e61aa02016-08-08 08:42:39 -0500160 default:
161 {
162 throw std::runtime_error("Invalid Authentication Algorithm");
163 }
164 }
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530165
166 sessionsMap.emplace(bmcSessionID, session);
167 session->sessionHandle(sessionHandle);
168
Vernon Mauery4cb73592018-10-26 10:15:03 -0700169 return session;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500170 }
Vernon Mauery4cb73592018-10-26 10:15:03 -0700171
George Liu7b7f25f2022-07-04 17:07:32 +0800172 lg2::info("No free RMCP+ sessions left");
Vernon Mauery4cb73592018-10-26 10:15:03 -0700173
Vernon Mauery4cb73592018-10-26 10:15:03 -0700174 throw std::runtime_error("No free sessions left");
Tom Joseph3e61aa02016-08-08 08:42:39 -0500175}
176
Tom Joseph9662c3a2016-12-06 17:52:16 +0530177bool Manager::stopSession(SessionID bmcSessionID)
Tom Joseph3e61aa02016-08-08 08:42:39 -0500178{
Tom Joseph9662c3a2016-12-06 17:52:16 +0530179 auto iter = sessionsMap.find(bmcSessionID);
180 if (iter != sessionsMap.end())
Tom Joseph3e61aa02016-08-08 08:42:39 -0500181 {
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530182 iter->second->state(
183 static_cast<uint8_t>(session::State::tearDownInProgress));
Tom Joseph9662c3a2016-12-06 17:52:16 +0530184 return true;
185 }
186 else
187 {
188 return false;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500189 }
190}
191
Vernon Maueryae1fda42018-10-15 12:55:34 -0700192std::shared_ptr<Session> Manager::getSession(SessionID sessionID,
193 RetrieveOption option)
Tom Joseph3e61aa02016-08-08 08:42:39 -0500194{
195 switch (option)
196 {
197 case RetrieveOption::BMC_SESSION_ID:
198 {
199 auto iter = sessionsMap.find(sessionID);
200 if (iter != sessionsMap.end())
201 {
202 return iter->second;
203 }
Tom Joseph6516cef2017-07-31 18:48:34 +0530204 break;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500205 }
206 case RetrieveOption::RC_SESSION_ID:
207 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700208 auto iter = std::find_if(
209 sessionsMap.begin(), sessionsMap.end(),
210 [sessionID](
211 const std::pair<const uint32_t, std::shared_ptr<Session>>&
212 in) -> bool {
213 return sessionID == in.second->getRCSessionID();
214 });
Tom Joseph3e61aa02016-08-08 08:42:39 -0500215
216 if (iter != sessionsMap.end())
217 {
218 return iter->second;
219 }
Tom Joseph6516cef2017-07-31 18:48:34 +0530220 break;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500221 }
222 default:
223 throw std::runtime_error("Invalid retrieval option");
224 }
225
226 throw std::runtime_error("Session ID not found");
227}
228
229void Manager::cleanStaleEntries()
230{
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700231 // with overflow = min(1, max - active sessions)
232 // active idle time in seconds = 60 / overflow^3
233 constexpr int baseIdleMicros = 60 * 1000 * 1000;
234 // no +1 for the zero session here because this is just active sessions
235 int sessionDivisor =
236 getActiveSessionCount() - session::maxSessionCountPerChannel;
237 sessionDivisor = std::max(0, sessionDivisor) + 1;
238 sessionDivisor = sessionDivisor * sessionDivisor * sessionDivisor;
239 int activeMicros = baseIdleMicros / sessionDivisor;
240
241 // with overflow = min(1, max - total sessions)
242 // setup idle time in seconds = max(3, 60 / overflow^3)
243
244 // +1 for the zero session here because size() counts that too
245 int setupDivisor =
246 sessionsMap.size() - (session::maxSessionCountPerChannel + 1);
247 setupDivisor = std::max(0, setupDivisor) + 1;
248 setupDivisor = setupDivisor * setupDivisor * setupDivisor;
249 constexpr int maxSetupMicros = 3 * 1000 * 1000;
250 int setupMicros = std::min(maxSetupMicros, baseIdleMicros / setupDivisor);
251
252 std::chrono::microseconds activeGrace(activeMicros);
253 std::chrono::microseconds setupGrace(setupMicros);
254
Vernon Mauery9e801a22018-10-12 13:20:49 -0700255 for (auto iter = sessionsMap.begin(); iter != sessionsMap.end();)
Tom Joseph3e61aa02016-08-08 08:42:39 -0500256 {
257 auto session = iter->second;
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700258 // special handling for sessionZero
259 if (session->getBMCSessionID() == session::sessionZero)
260 {
261 iter++;
262 continue;
263 }
264 if (!(session->isSessionActive(activeGrace, setupGrace)))
Tom Joseph3e61aa02016-08-08 08:42:39 -0500265 {
George Liu7b7f25f2022-07-04 17:07:32 +0800266 lg2::info(
267 "Removing idle IPMI LAN session, id: {ID}, handler: {HANDLE}",
268 "ID", session->getBMCSessionID(), "HANDLE",
269 getSessionHandle(session->getBMCSessionID()));
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530270 sessionHandleMap[getSessionHandle(session->getBMCSessionID())] = 0;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500271 iter = sessionsMap.erase(iter);
272 }
273 else
274 {
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700275 iter++;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500276 }
277 }
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700278 if (sessionsMap.size() > 1)
279 {
Vernon Mauery2528dfb2022-01-10 15:29:35 -0800280 constexpr int maxCleanupDelay = 1 * 1000 * 1000;
281 std::chrono::microseconds cleanupDelay(
282 std::min(setupMicros, maxCleanupDelay));
283 scheduleSessionCleaner(cleanupDelay);
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700284 }
Tom Joseph3e61aa02016-08-08 08:42:39 -0500285}
286
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530287uint8_t Manager::storeSessionHandle(SessionID bmcSessionID)
288{
289 // Handler index 0 is reserved for invalid session.
290 // index starts with 1, for direct usage. Index 0 reserved
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700291 for (size_t i = 1; i < session::maxSessionHandles; i++)
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530292 {
293 if (sessionHandleMap[i] == 0)
294 {
295 sessionHandleMap[i] = bmcSessionID;
296 return i;
297 }
298 }
299 return 0;
300}
301
302uint32_t Manager::getSessionIDbyHandle(uint8_t sessionHandle) const
303{
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700304 if (sessionHandle < session::maxSessionHandles)
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530305 {
306 return sessionHandleMap[sessionHandle];
307 }
308 return 0;
309}
310
311uint8_t Manager::getSessionHandle(SessionID bmcSessionID) const
312{
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530313 // Handler index 0 is reserved for invalid session.
314 // index starts with 1, for direct usage. Index 0 reserved
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700315 for (size_t i = 1; i < session::maxSessionHandles; i++)
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530316 {
317 if (sessionHandleMap[i] == bmcSessionID)
318 {
319 return (i);
320 }
321 }
322 return 0;
323}
324uint8_t Manager::getActiveSessionCount() const
325{
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530326 return (std::count_if(
327 sessionsMap.begin(), sessionsMap.end(),
328 [](const std::pair<const uint32_t, std::shared_ptr<Session>>& in)
329 -> bool {
330 return in.second->state() ==
331 static_cast<uint8_t>(session::State::active);
332 }));
333}
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700334
335void Manager::scheduleSessionCleaner(const std::chrono::microseconds& when)
336{
Ed Tanous6d206812023-03-06 14:04:00 -0800337 std::chrono::duration expTime =
338 timer.expiry() - boost::asio::steady_timer::clock_type::now();
Vernon Mauery2528dfb2022-01-10 15:29:35 -0800339 if (expTime > std::chrono::microseconds(0) && expTime < when)
340 {
George Liubc8958f2022-07-04 09:29:49 +0800341 // if timer has not already expired AND requested timeout is greater
342 // than current timeout then ignore this new requested timeout
Vernon Mauery2528dfb2022-01-10 15:29:35 -0800343 return;
344 }
Ed Tanous6d206812023-03-06 14:04:00 -0800345 timer.expires_after(when);
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700346 timer.async_wait([this](const boost::system::error_code& ec) {
347 if (!ec)
348 {
349 cleanStaleEntries();
350 }
351 });
352}
353
Tom Joseph3e61aa02016-08-08 08:42:39 -0500354} // namespace session