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     uint8_t sessionHandle = 0;
91 
92     auto activeSessions = sessionsMap.size() - session::maxSessionlessCount;
93 
94     if (activeSessions < maxSessionHandles)
95     {
96         do
97         {
98             bmcSessionID = (crypto::prng::rand());
99             bmcSessionID &= session::multiIntfaceSessionIDMask;
100             // In sessionID , BIT 31 BIT30 are used for netipmid instance
101             bmcSessionID |= static_cast<uint32_t>(ipmiNetworkInstance) << 30;
102             /*
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
109              * created session is reset and a new session is created for
110              * validating collision.
111              */
112             auto iterator = sessionsMap.find(bmcSessionID);
113             if (iterator != sessionsMap.end())
114             {
115                 // Detected BMC Session ID collisions
116                 continue;
117             }
118             else
119             {
120                 break;
121             }
122         } while (1);
123 
124         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 |= static_cast<uint8_t>(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 
144         // Set the Authentication Algorithm
145         switch (authAlgo)
146         {
147             case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA1:
148             {
149                 session->setAuthAlgo(
150                     std::make_unique<cipher::rakp_auth::AlgoSHA1>(intAlgo,
151                                                                   cryptAlgo));
152                 break;
153             }
154             case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA256:
155             {
156                 session->setAuthAlgo(
157                     std::make_unique<cipher::rakp_auth::AlgoSHA256>(intAlgo,
158                                                                     cryptAlgo));
159                 break;
160             }
161             default:
162             {
163                 throw std::runtime_error("Invalid Authentication Algorithm");
164             }
165         }
166 
167         sessionsMap.emplace(bmcSessionID, session);
168         session->sessionHandle(sessionHandle);
169 
170         return session;
171     }
172 
173     log<level::INFO>("No free RMCP+ sessions left");
174 
175     throw std::runtime_error("No free sessions left");
176 }
177 
178 bool Manager::stopSession(SessionID bmcSessionID)
179 {
180     auto iter = sessionsMap.find(bmcSessionID);
181     if (iter != sessionsMap.end())
182     {
183         iter->second->state(
184             static_cast<uint8_t>(session::State::tearDownInProgress));
185         return true;
186     }
187     else
188     {
189         return false;
190     }
191 }
192 
193 std::shared_ptr<Session> Manager::getSession(SessionID sessionID,
194                                              RetrieveOption option)
195 {
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             }
205             break;
206         }
207         case RetrieveOption::RC_SESSION_ID:
208         {
209             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                 });
216 
217             if (iter != sessionsMap.end())
218             {
219                 return iter->second;
220             }
221             break;
222         }
223         default:
224             throw std::runtime_error("Invalid retrieval option");
225     }
226 
227     throw std::runtime_error("Session ID not found");
228 }
229 
230 void Manager::cleanStaleEntries()
231 {
232     // 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 
256     for (auto iter = sessionsMap.begin(); iter != sessionsMap.end();)
257     {
258         auto session = iter->second;
259         // special handling for sessionZero
260         if (session->getBMCSessionID() == session::sessionZero)
261         {
262             iter++;
263             continue;
264         }
265         if (!(session->isSessionActive(activeGrace, setupGrace)))
266         {
267             sessionHandleMap[getSessionHandle(session->getBMCSessionID())] = 0;
268             iter = sessionsMap.erase(iter);
269         }
270         else
271         {
272             iter++;
273         }
274     }
275     if (sessionsMap.size() > 1)
276     {
277         scheduleSessionCleaner(setupGrace);
278     }
279 }
280 
281 uint8_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
285     for (size_t i = 1; i < session::maxSessionHandles; i++)
286     {
287         if (sessionHandleMap[i] == 0)
288         {
289             sessionHandleMap[i] = bmcSessionID;
290             return i;
291         }
292     }
293     return 0;
294 }
295 
296 uint32_t Manager::getSessionIDbyHandle(uint8_t sessionHandle) const
297 {
298     if (sessionHandle < session::maxSessionHandles)
299     {
300         return sessionHandleMap[sessionHandle];
301     }
302     return 0;
303 }
304 
305 uint8_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
310     for (size_t i = 1; i < session::maxSessionHandles; i++)
311     {
312         if (sessionHandleMap[i] == bmcSessionID)
313         {
314             return (i);
315         }
316     }
317     return 0;
318 }
319 uint8_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 }
330 
331 void 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 
342 } // namespace session
343