xref: /openbmc/phosphor-net-ipmid/sessions_manager.cpp (revision bc8958fec769e4c3f99b2fb8b64dd45eb414cf5b)
1 #include "sessions_manager.hpp"
2 
3 #include "main.hpp"
4 #include "session.hpp"
5 
6 #include <phosphor-logging/log.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 using namespace phosphor::logging;
16 
17 namespace session
18 {
19 
20 static std::array<uint8_t, session::maxNetworkInstanceSupported>
21     ipmiNetworkChannelNumList = {0};
22 
23 void Manager::setNetworkInstance(void)
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             if (getInterfaceIndex() == ch)
39             {
40                 ipmiNetworkInstance = index;
41             }
42 
43             ipmiNetworkChannelNumList[index] = ch;
44             index++;
45         }
46         ch++;
47     }
48 }
49 
50 uint8_t Manager::getNetworkInstance(void)
51 {
52     return ipmiNetworkInstance;
53 }
54 
55 void Manager::managerInit(const std::string& channel)
56 {
57     /*
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      */
63 
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));
74 
75     // set up the timer for clearing out stale sessions
76     scheduleSessionCleaner(std::chrono::microseconds(3 * 1000 * 1000));
77 }
78 
79 std::shared_ptr<Session>
80     Manager::startSession(SessionID remoteConsoleSessID, Privilege priv,
81                           cipher::rakp_auth::Algorithms authAlgo,
82                           cipher::integrity::Algorithms intAlgo,
83                           cipher::crypt::Algorithms cryptAlgo)
84 {
85     std::shared_ptr<Session> session = nullptr;
86     SessionID bmcSessionID = 0;
87     cleanStaleEntries();
88     // set up the timer for monitoring this session
89     scheduleSessionCleaner(std::chrono::microseconds(1 * 1000 * 1000));
90 
91     uint8_t sessionHandle = 0;
92 
93     auto activeSessions = sessionsMap.size() - session::maxSessionlessCount;
94 
95     if (activeSessions < maxSessionHandles)
96     {
97         do
98         {
99             bmcSessionID = (crypto::prng::rand());
100             bmcSessionID &= session::multiIntfaceSessionIDMask;
101             // In sessionID , BIT 31 BIT30 are used for netipmid instance
102             bmcSessionID |= static_cast<uint32_t>(ipmiNetworkInstance) << 30;
103             /*
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
110              * created session is reset and a new session is created for
111              * validating collision.
112              */
113             auto iterator = sessionsMap.find(bmcSessionID);
114             if (iterator != sessionsMap.end())
115             {
116                 // Detected BMC Session ID collisions
117                 continue;
118             }
119             else
120             {
121                 break;
122             }
123         } while (1);
124 
125         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
134         sessionHandle |= static_cast<uint8_t>(ipmiNetworkInstance) << 6;
135         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 
145         // Set the Authentication Algorithm
146         switch (authAlgo)
147         {
148             case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA1:
149             {
150                 session->setAuthAlgo(
151                     std::make_unique<cipher::rakp_auth::AlgoSHA1>(intAlgo,
152                                                                   cryptAlgo));
153                 break;
154             }
155             case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA256:
156             {
157                 session->setAuthAlgo(
158                     std::make_unique<cipher::rakp_auth::AlgoSHA256>(intAlgo,
159                                                                     cryptAlgo));
160                 break;
161             }
162             default:
163             {
164                 throw std::runtime_error("Invalid Authentication Algorithm");
165             }
166         }
167 
168         sessionsMap.emplace(bmcSessionID, session);
169         session->sessionHandle(sessionHandle);
170 
171         return session;
172     }
173 
174     log<level::INFO>("No free RMCP+ sessions left");
175 
176     throw std::runtime_error("No free sessions left");
177 }
178 
179 bool Manager::stopSession(SessionID bmcSessionID)
180 {
181     auto iter = sessionsMap.find(bmcSessionID);
182     if (iter != sessionsMap.end())
183     {
184         iter->second->state(
185             static_cast<uint8_t>(session::State::tearDownInProgress));
186         return true;
187     }
188     else
189     {
190         return false;
191     }
192 }
193 
194 std::shared_ptr<Session> Manager::getSession(SessionID sessionID,
195                                              RetrieveOption option)
196 {
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             }
206             break;
207         }
208         case RetrieveOption::RC_SESSION_ID:
209         {
210             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                 });
217 
218             if (iter != sessionsMap.end())
219             {
220                 return iter->second;
221             }
222             break;
223         }
224         default:
225             throw std::runtime_error("Invalid retrieval option");
226     }
227 
228     throw std::runtime_error("Session ID not found");
229 }
230 
231 void Manager::cleanStaleEntries()
232 {
233     // 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 
257     for (auto iter = sessionsMap.begin(); iter != sessionsMap.end();)
258     {
259         auto session = iter->second;
260         // special handling for sessionZero
261         if (session->getBMCSessionID() == session::sessionZero)
262         {
263             iter++;
264             continue;
265         }
266         if (!(session->isSessionActive(activeGrace, setupGrace)))
267         {
268             log<level::INFO>(
269                 "Removing idle IPMI LAN session",
270                 entry("SESSION_ID=%x", session->getBMCSessionID()),
271                 entry("HANDLE=%x",
272                       getSessionHandle(session->getBMCSessionID())));
273             sessionHandleMap[getSessionHandle(session->getBMCSessionID())] = 0;
274             iter = sessionsMap.erase(iter);
275         }
276         else
277         {
278             iter++;
279         }
280     }
281     if (sessionsMap.size() > 1)
282     {
283         constexpr int maxCleanupDelay = 1 * 1000 * 1000;
284         std::chrono::microseconds cleanupDelay(
285             std::min(setupMicros, maxCleanupDelay));
286         scheduleSessionCleaner(cleanupDelay);
287     }
288 }
289 
290 uint8_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
294     for (size_t i = 1; i < session::maxSessionHandles; i++)
295     {
296         if (sessionHandleMap[i] == 0)
297         {
298             sessionHandleMap[i] = bmcSessionID;
299             return i;
300         }
301     }
302     return 0;
303 }
304 
305 uint32_t Manager::getSessionIDbyHandle(uint8_t sessionHandle) const
306 {
307     if (sessionHandle < session::maxSessionHandles)
308     {
309         return sessionHandleMap[sessionHandle];
310     }
311     return 0;
312 }
313 
314 uint8_t Manager::getSessionHandle(SessionID bmcSessionID) const
315 {
316     // Handler index 0 is reserved for invalid session.
317     // index starts with 1, for direct usage. Index 0 reserved
318     for (size_t i = 1; i < session::maxSessionHandles; i++)
319     {
320         if (sessionHandleMap[i] == bmcSessionID)
321         {
322             return (i);
323         }
324     }
325     return 0;
326 }
327 uint8_t Manager::getActiveSessionCount() const
328 {
329     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 }
337 
338 void Manager::scheduleSessionCleaner(const std::chrono::microseconds& when)
339 {
340     std::chrono::duration expTime = timer.expires_from_now();
341     if (expTime > std::chrono::microseconds(0) && expTime < when)
342     {
343         // if timer has not already expired AND requested timeout is greater
344         // than current timeout then ignore this new requested timeout
345         return;
346     }
347     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 
356 } // namespace session
357