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