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 = 66 std::string(session::sessionManagerRootPath) + "/" + 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> 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 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 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 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 = 236 getActiveSessionCount() - 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 = 246 sessionsMap.size() - (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 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 302 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 311 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 } 324 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 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