1 #include "sessions_manager.hpp"
2
3 #include "main.hpp"
4 #include "session.hpp"
5
6 #include <phosphor-logging/lg2.hpp>
7 #include <sdbusplus/asio/connection.hpp>
8 #include <user_channel/channel_layer.hpp>
9
10 #include <algorithm>
11 #include <cstdlib>
12 #include <iomanip>
13 #include <memory>
14
15 namespace session
16 {
17
18 static std::array<uint8_t, session::maxNetworkInstanceSupported>
19 ipmiNetworkChannelNumList = {0};
20
setNetworkInstance(void)21 void Manager::setNetworkInstance(void)
22 {
23 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 {
36 if (getInterfaceIndex() == ch)
37 {
38 ipmiNetworkInstance = index;
39 }
40
41 ipmiNetworkChannelNumList[index] = ch;
42 index++;
43 }
44 ch++;
45 }
46 }
47
getNetworkInstance(void)48 uint8_t Manager::getNetworkInstance(void)
49 {
50 return ipmiNetworkInstance;
51 }
52
managerInit(const std::string & channel)53 void Manager::managerInit(const std::string& channel)
54 {
55 /*
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 */
61
62 objManager = std::make_unique<sdbusplus::server::manager_t>(
63 *getSdBus(), session::sessionManagerRootPath);
64
65 auto objPath = std::string(session::sessionManagerRootPath) + "/" +
66 channel + "/0";
67
68 chName = channel;
69 setNetworkInstance();
70 sessionsMap.emplace(
71 0, std::make_shared<Session>(*getSdBus(), objPath.c_str(), 0, 0, 0));
72
73 // set up the timer for clearing out stale sessions
74 scheduleSessionCleaner(std::chrono::microseconds(3 * 1000 * 1000));
75 }
76
startSession(SessionID remoteConsoleSessID,Privilege priv,cipher::rakp_auth::Algorithms authAlgo,cipher::integrity::Algorithms intAlgo,cipher::crypt::Algorithms cryptAlgo)77 std::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)
81 {
82 std::shared_ptr<Session> session = nullptr;
83 SessionID bmcSessionID = 0;
84 cleanStaleEntries();
85 // set up the timer for monitoring this session
86 scheduleSessionCleaner(std::chrono::microseconds(1 * 1000 * 1000));
87
88 uint8_t sessionHandle = 0;
89
90 auto activeSessions = sessionsMap.size() - session::maxSessionlessCount;
91
92 if (activeSessions < maxSessionHandles)
93 {
94 do
95 {
96 bmcSessionID = (crypto::prng::rand());
97 bmcSessionID &= session::multiIntfaceSessionIDMask;
98 // In sessionID , BIT 31 BIT30 are used for netipmid instance
99 bmcSessionID |= static_cast<uint32_t>(ipmiNetworkInstance) << 30;
100 /*
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
107 * created session is reset and a new session is created for
108 * validating collision.
109 */
110 auto iterator = sessionsMap.find(bmcSessionID);
111 if (iterator != sessionsMap.end())
112 {
113 // Detected BMC Session ID collisions
114 continue;
115 }
116 else
117 {
118 break;
119 }
120 } while (1);
121
122 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
131 sessionHandle |= static_cast<uint8_t>(ipmiNetworkInstance) << 6;
132 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
142 // Set the Authentication Algorithm
143 switch (authAlgo)
144 {
145 case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA1:
146 {
147 session->setAuthAlgo(
148 std::make_unique<cipher::rakp_auth::AlgoSHA1>(intAlgo,
149 cryptAlgo));
150 break;
151 }
152 case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA256:
153 {
154 session->setAuthAlgo(
155 std::make_unique<cipher::rakp_auth::AlgoSHA256>(intAlgo,
156 cryptAlgo));
157 break;
158 }
159 default:
160 {
161 throw std::runtime_error("Invalid Authentication Algorithm");
162 }
163 }
164
165 sessionsMap.emplace(bmcSessionID, session);
166 session->sessionHandle(sessionHandle);
167
168 return session;
169 }
170
171 lg2::info("No free RMCP+ sessions left");
172
173 throw std::runtime_error("No free sessions left");
174 }
175
stopSession(SessionID bmcSessionID)176 bool Manager::stopSession(SessionID bmcSessionID)
177 {
178 auto iter = sessionsMap.find(bmcSessionID);
179 if (iter != sessionsMap.end())
180 {
181 iter->second->state(
182 static_cast<uint8_t>(session::State::tearDownInProgress));
183 return true;
184 }
185 else
186 {
187 return false;
188 }
189 }
190
191 std::shared_ptr<Session>
getSession(SessionID sessionID,RetrieveOption option)192 Manager::getSession(SessionID sessionID, RetrieveOption option)
193 {
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 }
203 break;
204 }
205 case RetrieveOption::RC_SESSION_ID:
206 {
207 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 {
212 return sessionID == in.second->getRCSessionID();
213 });
214
215 if (iter != sessionsMap.end())
216 {
217 return iter->second;
218 }
219 break;
220 }
221 default:
222 throw std::runtime_error("Invalid retrieval option");
223 }
224
225 throw std::runtime_error("Session ID not found");
226 }
227
cleanStaleEntries()228 void Manager::cleanStaleEntries()
229 {
230 // 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
234 int sessionDivisor =
235 getActiveSessionCount() - session::maxSessionCountPerChannel;
236 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
244 int setupDivisor =
245 sessionsMap.size() - (session::maxSessionCountPerChannel + 1);
246 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
254 for (auto iter = sessionsMap.begin(); iter != sessionsMap.end();)
255 {
256 auto session = iter->second;
257 // special handling for sessionZero
258 if (session->getBMCSessionID() == session::sessionZero)
259 {
260 iter++;
261 continue;
262 }
263 if (!(session->isSessionActive(activeGrace, setupGrace)))
264 {
265 lg2::info(
266 "Removing idle IPMI LAN session, id: {ID}, handler: {HANDLE}",
267 "ID", session->getBMCSessionID(), "HANDLE",
268 getSessionHandle(session->getBMCSessionID()));
269 sessionHandleMap[getSessionHandle(session->getBMCSessionID())] = 0;
270 iter = sessionsMap.erase(iter);
271 }
272 else
273 {
274 iter++;
275 }
276 }
277 if (sessionsMap.size() > 1)
278 {
279 constexpr int maxCleanupDelay = 1 * 1000 * 1000;
280 std::chrono::microseconds cleanupDelay(
281 std::min(setupMicros, maxCleanupDelay));
282 scheduleSessionCleaner(cleanupDelay);
283 }
284 }
285
storeSessionHandle(SessionID bmcSessionID)286 uint8_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
290 for (size_t i = 1; i < session::maxSessionHandles; i++)
291 {
292 if (sessionHandleMap[i] == 0)
293 {
294 sessionHandleMap[i] = bmcSessionID;
295 return i;
296 }
297 }
298 return 0;
299 }
300
getSessionIDbyHandle(uint8_t sessionHandle) const301 uint32_t Manager::getSessionIDbyHandle(uint8_t sessionHandle) const
302 {
303 if (sessionHandle < session::maxSessionHandles)
304 {
305 return sessionHandleMap[sessionHandle];
306 }
307 return 0;
308 }
309
getSessionHandle(SessionID bmcSessionID) const310 uint8_t Manager::getSessionHandle(SessionID bmcSessionID) const
311 {
312 // Handler index 0 is reserved for invalid session.
313 // index starts with 1, for direct usage. Index 0 reserved
314 for (size_t i = 1; i < session::maxSessionHandles; i++)
315 {
316 if (sessionHandleMap[i] == bmcSessionID)
317 {
318 return (i);
319 }
320 }
321 return 0;
322 }
getActiveSessionCount() const323 uint8_t Manager::getActiveSessionCount() const
324 {
325 return (std::count_if(
326 sessionsMap.begin(), sessionsMap.end(),
327 [](const std::pair<const uint32_t, std::shared_ptr<Session>>& in)
328 -> bool {
329 return in.second->state() ==
330 static_cast<uint8_t>(session::State::active);
331 }));
332 }
333
scheduleSessionCleaner(const std::chrono::microseconds & when)334 void Manager::scheduleSessionCleaner(const std::chrono::microseconds& when)
335 {
336 std::chrono::duration expTime =
337 timer.expiry() - boost::asio::steady_timer::clock_type::now();
338 if (expTime > std::chrono::microseconds(0) && expTime < when)
339 {
340 // if timer has not already expired AND requested timeout is greater
341 // than current timeout then ignore this new requested timeout
342 return;
343 }
344 timer.expires_after(when);
345 timer.async_wait([this](const boost::system::error_code& ec) {
346 if (!ec)
347 {
348 cleanStaleEntries();
349 }
350 });
351 }
352
353 } // namespace session
354