blob: ac206215115ac232a930a476bf5d03e217738045 [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();
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053090 uint8_t sessionHandle = 0;
Tom Joseph3e61aa02016-08-08 08:42:39 -050091
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053092 auto activeSessions = sessionsMap.size() - session::maxSessionlessCount;
93
Vernon Maueryecc8efa2021-06-12 12:52:23 -070094 if (activeSessions < maxSessionHandles)
Tom Joseph3e61aa02016-08-08 08:42:39 -050095 {
96 do
97 {
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +053098 bmcSessionID = (crypto::prng::rand());
99 bmcSessionID &= session::multiIntfaceSessionIDMask;
100 // In sessionID , BIT 31 BIT30 are used for netipmid instance
101 bmcSessionID |= ipmiNetworkInstance << 30;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500102 /*
103 * Every IPMI Session has two ID's attached to it Remote Console
104 * Session ID and BMC Session ID. The remote console ID is passed
105 * along with the Open Session request command. The BMC session ID
106 * is the key for the session map and is generated using std::rand.
107 * There is a rare chance for collision of BMC session ID, so the
108 * following check validates that. In the case of collision the
Gunnar Mills62ec6222018-04-08 16:28:23 -0500109 * created session is reset and a new session is created for
Tom Joseph3e61aa02016-08-08 08:42:39 -0500110 * validating collision.
111 */
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530112 auto iterator = sessionsMap.find(bmcSessionID);
Tom Joseph3e61aa02016-08-08 08:42:39 -0500113 if (iterator != sessionsMap.end())
114 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700115 // Detected BMC Session ID collisions
Tom Joseph3e61aa02016-08-08 08:42:39 -0500116 continue;
117 }
118 else
119 {
120 break;
121 }
Vernon Mauery9e801a22018-10-12 13:20:49 -0700122 } while (1);
Tom Joseph3e61aa02016-08-08 08:42:39 -0500123
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530124 sessionHandle = storeSessionHandle(bmcSessionID);
125
126 if (!sessionHandle)
127 {
128 throw std::runtime_error(
129 "Invalid sessionHandle - No sessionID slot ");
130 }
131 sessionHandle &= session::multiIntfaceSessionHandleMask;
132 // In sessionID , BIT 31 BIT30 are used for netipmid instance
133 sessionHandle |= ipmiNetworkInstance << 6;
134 std::stringstream sstream;
135 sstream << std::hex << bmcSessionID;
136 std::stringstream shstream;
137 shstream << std::hex << (int)sessionHandle;
138 auto objPath = std::string(session::sessionManagerRootPath) + "/" +
139 chName + "/" + sstream.str() + "_" + shstream.str();
140 session = std::make_shared<Session>(*getSdBus(), objPath.c_str(),
141 remoteConsoleSessID, bmcSessionID,
142 static_cast<uint8_t>(priv));
143
Vernon Mauery9b307be2017-11-22 09:28:16 -0800144 // Set the Authentication Algorithm
Tom Joseph3e61aa02016-08-08 08:42:39 -0500145 switch (authAlgo)
146 {
147 case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA1:
148 {
149 session->setAuthAlgo(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700150 std::make_unique<cipher::rakp_auth::AlgoSHA1>(intAlgo,
151 cryptAlgo));
Tom Joseph3e61aa02016-08-08 08:42:39 -0500152 break;
153 }
Vernon Mauery7e9e2ef2017-11-29 08:36:29 -0800154 case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA256:
155 {
156 session->setAuthAlgo(
Vernon Mauery9e801a22018-10-12 13:20:49 -0700157 std::make_unique<cipher::rakp_auth::AlgoSHA256>(intAlgo,
158 cryptAlgo));
Vernon Mauery7e9e2ef2017-11-29 08:36:29 -0800159 break;
160 }
Tom Joseph3e61aa02016-08-08 08:42:39 -0500161 default:
162 {
163 throw std::runtime_error("Invalid Authentication Algorithm");
164 }
165 }
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530166
167 sessionsMap.emplace(bmcSessionID, session);
168 session->sessionHandle(sessionHandle);
169
Vernon Mauery4cb73592018-10-26 10:15:03 -0700170 return session;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500171 }
Vernon Mauery4cb73592018-10-26 10:15:03 -0700172
Vernon Maueryfc37e592018-12-19 14:55:15 -0800173 log<level::INFO>("No free RMCP+ sessions left");
Vernon Mauery4cb73592018-10-26 10:15:03 -0700174
Vernon Mauery4cb73592018-10-26 10:15:03 -0700175 throw std::runtime_error("No free sessions left");
Tom Joseph3e61aa02016-08-08 08:42:39 -0500176}
177
Tom Joseph9662c3a2016-12-06 17:52:16 +0530178bool Manager::stopSession(SessionID bmcSessionID)
Tom Joseph3e61aa02016-08-08 08:42:39 -0500179{
Tom Joseph9662c3a2016-12-06 17:52:16 +0530180 auto iter = sessionsMap.find(bmcSessionID);
181 if (iter != sessionsMap.end())
Tom Joseph3e61aa02016-08-08 08:42:39 -0500182 {
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530183 iter->second->state(
184 static_cast<uint8_t>(session::State::tearDownInProgress));
Tom Joseph9662c3a2016-12-06 17:52:16 +0530185 return true;
186 }
187 else
188 {
189 return false;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500190 }
191}
192
Vernon Maueryae1fda42018-10-15 12:55:34 -0700193std::shared_ptr<Session> Manager::getSession(SessionID sessionID,
194 RetrieveOption option)
Tom Joseph3e61aa02016-08-08 08:42:39 -0500195{
196 switch (option)
197 {
198 case RetrieveOption::BMC_SESSION_ID:
199 {
200 auto iter = sessionsMap.find(sessionID);
201 if (iter != sessionsMap.end())
202 {
203 return iter->second;
204 }
Tom Joseph6516cef2017-07-31 18:48:34 +0530205 break;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500206 }
207 case RetrieveOption::RC_SESSION_ID:
208 {
Vernon Mauery9e801a22018-10-12 13:20:49 -0700209 auto iter = std::find_if(
210 sessionsMap.begin(), sessionsMap.end(),
211 [sessionID](
212 const std::pair<const uint32_t, std::shared_ptr<Session>>&
213 in) -> bool {
214 return sessionID == in.second->getRCSessionID();
215 });
Tom Joseph3e61aa02016-08-08 08:42:39 -0500216
217 if (iter != sessionsMap.end())
218 {
219 return iter->second;
220 }
Tom Joseph6516cef2017-07-31 18:48:34 +0530221 break;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500222 }
223 default:
224 throw std::runtime_error("Invalid retrieval option");
225 }
226
227 throw std::runtime_error("Session ID not found");
228}
229
230void Manager::cleanStaleEntries()
231{
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700232 // with overflow = min(1, max - active sessions)
233 // active idle time in seconds = 60 / overflow^3
234 constexpr int baseIdleMicros = 60 * 1000 * 1000;
235 // no +1 for the zero session here because this is just active sessions
236 int sessionDivisor =
237 getActiveSessionCount() - session::maxSessionCountPerChannel;
238 sessionDivisor = std::max(0, sessionDivisor) + 1;
239 sessionDivisor = sessionDivisor * sessionDivisor * sessionDivisor;
240 int activeMicros = baseIdleMicros / sessionDivisor;
241
242 // with overflow = min(1, max - total sessions)
243 // setup idle time in seconds = max(3, 60 / overflow^3)
244
245 // +1 for the zero session here because size() counts that too
246 int setupDivisor =
247 sessionsMap.size() - (session::maxSessionCountPerChannel + 1);
248 setupDivisor = std::max(0, setupDivisor) + 1;
249 setupDivisor = setupDivisor * setupDivisor * setupDivisor;
250 constexpr int maxSetupMicros = 3 * 1000 * 1000;
251 int setupMicros = std::min(maxSetupMicros, baseIdleMicros / setupDivisor);
252
253 std::chrono::microseconds activeGrace(activeMicros);
254 std::chrono::microseconds setupGrace(setupMicros);
255
Vernon Mauery9e801a22018-10-12 13:20:49 -0700256 for (auto iter = sessionsMap.begin(); iter != sessionsMap.end();)
Tom Joseph3e61aa02016-08-08 08:42:39 -0500257 {
258 auto session = iter->second;
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700259 // special handling for sessionZero
260 if (session->getBMCSessionID() == session::sessionZero)
261 {
262 iter++;
263 continue;
264 }
265 if (!(session->isSessionActive(activeGrace, setupGrace)))
Tom Joseph3e61aa02016-08-08 08:42:39 -0500266 {
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530267 sessionHandleMap[getSessionHandle(session->getBMCSessionID())] = 0;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500268 iter = sessionsMap.erase(iter);
269 }
270 else
271 {
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700272 iter++;
Tom Joseph3e61aa02016-08-08 08:42:39 -0500273 }
274 }
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700275 if (sessionsMap.size() > 1)
276 {
277 scheduleSessionCleaner(setupGrace);
278 }
Tom Joseph3e61aa02016-08-08 08:42:39 -0500279}
280
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530281uint8_t Manager::storeSessionHandle(SessionID bmcSessionID)
282{
283 // Handler index 0 is reserved for invalid session.
284 // index starts with 1, for direct usage. Index 0 reserved
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700285 for (size_t i = 1; i < session::maxSessionHandles; i++)
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530286 {
287 if (sessionHandleMap[i] == 0)
288 {
289 sessionHandleMap[i] = bmcSessionID;
290 return i;
291 }
292 }
293 return 0;
294}
295
296uint32_t Manager::getSessionIDbyHandle(uint8_t sessionHandle) const
297{
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700298 if (sessionHandle < session::maxSessionHandles)
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530299 {
300 return sessionHandleMap[sessionHandle];
301 }
302 return 0;
303}
304
305uint8_t Manager::getSessionHandle(SessionID bmcSessionID) const
306{
307
308 // Handler index 0 is reserved for invalid session.
309 // index starts with 1, for direct usage. Index 0 reserved
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700310 for (size_t i = 1; i < session::maxSessionHandles; i++)
Suryakanth Sekarf8a34fc2019-06-12 20:59:18 +0530311 {
312 if (sessionHandleMap[i] == bmcSessionID)
313 {
314 return (i);
315 }
316 }
317 return 0;
318}
319uint8_t Manager::getActiveSessionCount() const
320{
321
322 return (std::count_if(
323 sessionsMap.begin(), sessionsMap.end(),
324 [](const std::pair<const uint32_t, std::shared_ptr<Session>>& in)
325 -> bool {
326 return in.second->state() ==
327 static_cast<uint8_t>(session::State::active);
328 }));
329}
Vernon Maueryecc8efa2021-06-12 12:52:23 -0700330
331void Manager::scheduleSessionCleaner(const std::chrono::microseconds& when)
332{
333 timer.expires_from_now(when);
334 timer.async_wait([this](const boost::system::error_code& ec) {
335 if (!ec)
336 {
337 cleanStaleEntries();
338 }
339 });
340}
341
Tom Joseph3e61aa02016-08-08 08:42:39 -0500342} // namespace session