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 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 48 uint8_t Manager::getNetworkInstance(void) 49 { 50 return ipmiNetworkInstance; 51 } 52 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> 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 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> 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 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 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 301 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 310 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 } 323 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 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