1 #include "sessions_manager.hpp"
2 
3 #include "main.hpp"
4 #include "session.hpp"
5 
6 #include <algorithm>
7 #include <cstdlib>
8 #include <iomanip>
9 #include <memory>
10 #include <phosphor-logging/log.hpp>
11 #include <sdbusplus/asio/connection.hpp>
12 #include <user_channel/channel_layer.hpp>
13 
14 using namespace phosphor::logging;
15 
16 namespace session
17 {
18 
19 static std::array<uint8_t, session::maxNetworkInstanceSupported>
20     ipmiNetworkChannelNumList = {0};
21 
22 void 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 
51 uint8_t Manager::getNetworkInstance(void)
52 {
53     return ipmiNetworkInstance;
54 }
55 
56 void Manager::managerInit(const std::string& channel)
57 {
58 
59     /*
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      */
65 
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));
76 
77     // set up the timer for clearing out stale sessions
78     scheduleSessionCleaner(std::chrono::microseconds(3 * 1000 * 1000));
79 }
80 
81 std::shared_ptr<Session>
82     Manager::startSession(SessionID remoteConsoleSessID, Privilege priv,
83                           cipher::rakp_auth::Algorithms authAlgo,
84                           cipher::integrity::Algorithms intAlgo,
85                           cipher::crypt::Algorithms cryptAlgo)
86 {
87     std::shared_ptr<Session> session = nullptr;
88     SessionID bmcSessionID = 0;
89     cleanStaleEntries();
90     // set up the timer for monitoring this session
91     scheduleSessionCleaner(std::chrono::microseconds(1 * 1000 * 1000));
92 
93     uint8_t sessionHandle = 0;
94 
95     auto activeSessions = sessionsMap.size() - session::maxSessionlessCount;
96 
97     if (activeSessions < maxSessionHandles)
98     {
99         do
100         {
101             bmcSessionID = (crypto::prng::rand());
102             bmcSessionID &= session::multiIntfaceSessionIDMask;
103             // In sessionID , BIT 31 BIT30 are used for netipmid instance
104             bmcSessionID |= static_cast<uint32_t>(ipmiNetworkInstance) << 30;
105             /*
106              * Every IPMI Session has two ID's attached to it Remote Console
107              * Session ID and BMC Session ID. The remote console ID is passed
108              * along with the Open Session request command. The BMC session ID
109              * is the key for the session map and is generated using std::rand.
110              * There is a rare chance for collision of BMC session ID, so the
111              * following check validates that. In the case of collision the
112              * created session is reset and a new session is created for
113              * validating collision.
114              */
115             auto iterator = sessionsMap.find(bmcSessionID);
116             if (iterator != sessionsMap.end())
117             {
118                 // Detected BMC Session ID collisions
119                 continue;
120             }
121             else
122             {
123                 break;
124             }
125         } while (1);
126 
127         sessionHandle = storeSessionHandle(bmcSessionID);
128 
129         if (!sessionHandle)
130         {
131             throw std::runtime_error(
132                 "Invalid sessionHandle - No sessionID slot ");
133         }
134         sessionHandle &= session::multiIntfaceSessionHandleMask;
135         // In sessionID , BIT 31 BIT30 are used for netipmid instance
136         sessionHandle |= static_cast<uint8_t>(ipmiNetworkInstance) << 6;
137         std::stringstream sstream;
138         sstream << std::hex << bmcSessionID;
139         std::stringstream shstream;
140         shstream << std::hex << (int)sessionHandle;
141         auto objPath = std::string(session::sessionManagerRootPath) + "/" +
142                        chName + "/" + sstream.str() + "_" + shstream.str();
143         session = std::make_shared<Session>(*getSdBus(), objPath.c_str(),
144                                             remoteConsoleSessID, bmcSessionID,
145                                             static_cast<uint8_t>(priv));
146 
147         // Set the Authentication Algorithm
148         switch (authAlgo)
149         {
150             case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA1:
151             {
152                 session->setAuthAlgo(
153                     std::make_unique<cipher::rakp_auth::AlgoSHA1>(intAlgo,
154                                                                   cryptAlgo));
155                 break;
156             }
157             case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA256:
158             {
159                 session->setAuthAlgo(
160                     std::make_unique<cipher::rakp_auth::AlgoSHA256>(intAlgo,
161                                                                     cryptAlgo));
162                 break;
163             }
164             default:
165             {
166                 throw std::runtime_error("Invalid Authentication Algorithm");
167             }
168         }
169 
170         sessionsMap.emplace(bmcSessionID, session);
171         session->sessionHandle(sessionHandle);
172 
173         return session;
174     }
175 
176     log<level::INFO>("No free RMCP+ sessions left");
177 
178     throw std::runtime_error("No free sessions left");
179 }
180 
181 bool Manager::stopSession(SessionID bmcSessionID)
182 {
183     auto iter = sessionsMap.find(bmcSessionID);
184     if (iter != sessionsMap.end())
185     {
186         iter->second->state(
187             static_cast<uint8_t>(session::State::tearDownInProgress));
188         return true;
189     }
190     else
191     {
192         return false;
193     }
194 }
195 
196 std::shared_ptr<Session> Manager::getSession(SessionID sessionID,
197                                              RetrieveOption option)
198 {
199     switch (option)
200     {
201         case RetrieveOption::BMC_SESSION_ID:
202         {
203             auto iter = sessionsMap.find(sessionID);
204             if (iter != sessionsMap.end())
205             {
206                 return iter->second;
207             }
208             break;
209         }
210         case RetrieveOption::RC_SESSION_ID:
211         {
212             auto iter = std::find_if(
213                 sessionsMap.begin(), sessionsMap.end(),
214                 [sessionID](
215                     const std::pair<const uint32_t, std::shared_ptr<Session>>&
216                         in) -> bool {
217                     return sessionID == in.second->getRCSessionID();
218                 });
219 
220             if (iter != sessionsMap.end())
221             {
222                 return iter->second;
223             }
224             break;
225         }
226         default:
227             throw std::runtime_error("Invalid retrieval option");
228     }
229 
230     throw std::runtime_error("Session ID not found");
231 }
232 
233 void Manager::cleanStaleEntries()
234 {
235     // with overflow = min(1, max - active sessions)
236     // active idle time in seconds = 60 / overflow^3
237     constexpr int baseIdleMicros = 60 * 1000 * 1000;
238     // no +1 for the zero session here because this is just active sessions
239     int sessionDivisor =
240         getActiveSessionCount() - session::maxSessionCountPerChannel;
241     sessionDivisor = std::max(0, sessionDivisor) + 1;
242     sessionDivisor = sessionDivisor * sessionDivisor * sessionDivisor;
243     int activeMicros = baseIdleMicros / sessionDivisor;
244 
245     // with overflow = min(1, max - total sessions)
246     // setup idle time in seconds = max(3, 60 / overflow^3)
247 
248     // +1 for the zero session here because size() counts that too
249     int setupDivisor =
250         sessionsMap.size() - (session::maxSessionCountPerChannel + 1);
251     setupDivisor = std::max(0, setupDivisor) + 1;
252     setupDivisor = setupDivisor * setupDivisor * setupDivisor;
253     constexpr int maxSetupMicros = 3 * 1000 * 1000;
254     int setupMicros = std::min(maxSetupMicros, baseIdleMicros / setupDivisor);
255 
256     std::chrono::microseconds activeGrace(activeMicros);
257     std::chrono::microseconds setupGrace(setupMicros);
258 
259     for (auto iter = sessionsMap.begin(); iter != sessionsMap.end();)
260     {
261         auto session = iter->second;
262         // special handling for sessionZero
263         if (session->getBMCSessionID() == session::sessionZero)
264         {
265             iter++;
266             continue;
267         }
268         if (!(session->isSessionActive(activeGrace, setupGrace)))
269         {
270             log<level::INFO>(
271                 "Removing idle IPMI LAN session",
272                 entry("SESSION_ID=%x", session->getBMCSessionID()),
273                 entry("HANDLE=%x",
274                       getSessionHandle(session->getBMCSessionID())));
275             sessionHandleMap[getSessionHandle(session->getBMCSessionID())] = 0;
276             iter = sessionsMap.erase(iter);
277         }
278         else
279         {
280             iter++;
281         }
282     }
283     if (sessionsMap.size() > 1)
284     {
285         constexpr int maxCleanupDelay = 1 * 1000 * 1000;
286         std::chrono::microseconds cleanupDelay(
287             std::min(setupMicros, maxCleanupDelay));
288         scheduleSessionCleaner(cleanupDelay);
289     }
290 }
291 
292 uint8_t Manager::storeSessionHandle(SessionID bmcSessionID)
293 {
294     // Handler index 0 is  reserved for invalid session.
295     // index starts with 1, for direct usage. Index 0 reserved
296     for (size_t i = 1; i < session::maxSessionHandles; i++)
297     {
298         if (sessionHandleMap[i] == 0)
299         {
300             sessionHandleMap[i] = bmcSessionID;
301             return i;
302         }
303     }
304     return 0;
305 }
306 
307 uint32_t Manager::getSessionIDbyHandle(uint8_t sessionHandle) const
308 {
309     if (sessionHandle < session::maxSessionHandles)
310     {
311         return sessionHandleMap[sessionHandle];
312     }
313     return 0;
314 }
315 
316 uint8_t Manager::getSessionHandle(SessionID bmcSessionID) const
317 {
318 
319     // Handler index 0 is reserved for invalid session.
320     // index starts with 1, for direct usage. Index 0 reserved
321     for (size_t i = 1; i < session::maxSessionHandles; i++)
322     {
323         if (sessionHandleMap[i] == bmcSessionID)
324         {
325             return (i);
326         }
327     }
328     return 0;
329 }
330 uint8_t Manager::getActiveSessionCount() const
331 {
332 
333     return (std::count_if(
334         sessionsMap.begin(), sessionsMap.end(),
335         [](const std::pair<const uint32_t, std::shared_ptr<Session>>& in)
336             -> bool {
337             return in.second->state() ==
338                    static_cast<uint8_t>(session::State::active);
339         }));
340 }
341 
342 void Manager::scheduleSessionCleaner(const std::chrono::microseconds& when)
343 {
344     std::chrono::duration expTime = timer.expires_from_now();
345     if (expTime > std::chrono::microseconds(0) && expTime < when)
346     {
347         // if timer has not already expired AND
348         //    requested timeout is greater than current timeout
349         // then ignore this new requested timeout
350         return;
351     }
352     timer.expires_from_now(when);
353     timer.async_wait([this](const boost::system::error_code& ec) {
354         if (!ec)
355         {
356             cleanStaleEntries();
357         }
358     });
359 }
360 
361 } // namespace session
362