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 
77 std::shared_ptr<Session>
startSession(SessionID remoteConsoleSessID,Privilege priv,cipher::rakp_auth::Algorithms authAlgo,cipher::integrity::Algorithms intAlgo,cipher::crypt::Algorithms cryptAlgo)78     Manager::startSession(SessionID remoteConsoleSessID, Privilege priv,
79                           cipher::rakp_auth::Algorithms authAlgo,
80                           cipher::integrity::Algorithms intAlgo,
81                           cipher::crypt::Algorithms cryptAlgo)
82 {
83     std::shared_ptr<Session> session = nullptr;
84     SessionID bmcSessionID = 0;
85     cleanStaleEntries();
86     // set up the timer for monitoring this session
87     scheduleSessionCleaner(std::chrono::microseconds(1 * 1000 * 1000));
88 
89     uint8_t sessionHandle = 0;
90 
91     auto activeSessions = sessionsMap.size() - session::maxSessionlessCount;
92 
93     if (activeSessions < maxSessionHandles)
94     {
95         do
96         {
97             bmcSessionID = (crypto::prng::rand());
98             bmcSessionID &= session::multiIntfaceSessionIDMask;
99             // In sessionID , BIT 31 BIT30 are used for netipmid instance
100             bmcSessionID |= static_cast<uint32_t>(ipmiNetworkInstance) << 30;
101             /*
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
108              * created session is reset and a new session is created for
109              * validating collision.
110              */
111             auto iterator = sessionsMap.find(bmcSessionID);
112             if (iterator != sessionsMap.end())
113             {
114                 // Detected BMC Session ID collisions
115                 continue;
116             }
117             else
118             {
119                 break;
120             }
121         } while (1);
122 
123         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
132         sessionHandle |= static_cast<uint8_t>(ipmiNetworkInstance) << 6;
133         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 
143         // Set the Authentication Algorithm
144         switch (authAlgo)
145         {
146             case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA1:
147             {
148                 session->setAuthAlgo(
149                     std::make_unique<cipher::rakp_auth::AlgoSHA1>(intAlgo,
150                                                                   cryptAlgo));
151                 break;
152             }
153             case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA256:
154             {
155                 session->setAuthAlgo(
156                     std::make_unique<cipher::rakp_auth::AlgoSHA256>(intAlgo,
157                                                                     cryptAlgo));
158                 break;
159             }
160             default:
161             {
162                 throw std::runtime_error("Invalid Authentication Algorithm");
163             }
164         }
165 
166         sessionsMap.emplace(bmcSessionID, session);
167         session->sessionHandle(sessionHandle);
168 
169         return session;
170     }
171 
172     lg2::info("No free RMCP+ sessions left");
173 
174     throw std::runtime_error("No free sessions left");
175 }
176 
stopSession(SessionID bmcSessionID)177 bool Manager::stopSession(SessionID bmcSessionID)
178 {
179     auto iter = sessionsMap.find(bmcSessionID);
180     if (iter != sessionsMap.end())
181     {
182         iter->second->state(
183             static_cast<uint8_t>(session::State::tearDownInProgress));
184         return true;
185     }
186     else
187     {
188         return false;
189     }
190 }
191 
getSession(SessionID sessionID,RetrieveOption option)192 std::shared_ptr<Session> Manager::getSession(SessionID sessionID,
193                                              RetrieveOption option)
194 {
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             }
204             break;
205         }
206         case RetrieveOption::RC_SESSION_ID:
207         {
208             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             });
215 
216             if (iter != sessionsMap.end())
217             {
218                 return iter->second;
219             }
220             break;
221         }
222         default:
223             throw std::runtime_error("Invalid retrieval option");
224     }
225 
226     throw std::runtime_error("Session ID not found");
227 }
228 
cleanStaleEntries()229 void Manager::cleanStaleEntries()
230 {
231     // 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 = getActiveSessionCount() -
236                          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 = sessionsMap.size() -
246                        (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 
255     for (auto iter = sessionsMap.begin(); iter != sessionsMap.end();)
256     {
257         auto session = iter->second;
258         // special handling for sessionZero
259         if (session->getBMCSessionID() == session::sessionZero)
260         {
261             iter++;
262             continue;
263         }
264         if (!(session->isSessionActive(activeGrace, setupGrace)))
265         {
266             lg2::info(
267                 "Removing idle IPMI LAN session, id: {ID}, handler: {HANDLE}",
268                 "ID", session->getBMCSessionID(), "HANDLE",
269                 getSessionHandle(session->getBMCSessionID()));
270             sessionHandleMap[getSessionHandle(session->getBMCSessionID())] = 0;
271             iter = sessionsMap.erase(iter);
272         }
273         else
274         {
275             iter++;
276         }
277     }
278     if (sessionsMap.size() > 1)
279     {
280         constexpr int maxCleanupDelay = 1 * 1000 * 1000;
281         std::chrono::microseconds cleanupDelay(
282             std::min(setupMicros, maxCleanupDelay));
283         scheduleSessionCleaner(cleanupDelay);
284     }
285 }
286 
storeSessionHandle(SessionID bmcSessionID)287 uint8_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
291     for (size_t i = 1; i < session::maxSessionHandles; i++)
292     {
293         if (sessionHandleMap[i] == 0)
294         {
295             sessionHandleMap[i] = bmcSessionID;
296             return i;
297         }
298     }
299     return 0;
300 }
301 
getSessionIDbyHandle(uint8_t sessionHandle) const302 uint32_t Manager::getSessionIDbyHandle(uint8_t sessionHandle) const
303 {
304     if (sessionHandle < session::maxSessionHandles)
305     {
306         return sessionHandleMap[sessionHandle];
307     }
308     return 0;
309 }
310 
getSessionHandle(SessionID bmcSessionID) const311 uint8_t Manager::getSessionHandle(SessionID bmcSessionID) const
312 {
313     // Handler index 0 is reserved for invalid session.
314     // index starts with 1, for direct usage. Index 0 reserved
315     for (size_t i = 1; i < session::maxSessionHandles; i++)
316     {
317         if (sessionHandleMap[i] == bmcSessionID)
318         {
319             return (i);
320         }
321     }
322     return 0;
323 }
getActiveSessionCount() const324 uint8_t Manager::getActiveSessionCount() const
325 {
326     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 }
334 
scheduleSessionCleaner(const std::chrono::microseconds & when)335 void Manager::scheduleSessionCleaner(const std::chrono::microseconds& when)
336 {
337     std::chrono::duration expTime =
338         timer.expiry() - boost::asio::steady_timer::clock_type::now();
339     if (expTime > std::chrono::microseconds(0) && expTime < when)
340     {
341         // if timer has not already expired AND requested timeout is greater
342         // than current timeout then ignore this new requested timeout
343         return;
344     }
345     timer.expires_after(when);
346     timer.async_wait([this](const boost::system::error_code& ec) {
347         if (!ec)
348         {
349             cleanStaleEntries();
350         }
351     });
352 }
353 
354 } // namespace session
355