1 #include "sessions_manager.hpp" 2 3 #include "main.hpp" 4 #include "session.hpp" 5 6 #include <phosphor-logging/log.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 using namespace phosphor::logging; 16 17 namespace session 18 { 19 20 static std::array<uint8_t, session::maxNetworkInstanceSupported> 21 ipmiNetworkChannelNumList = {0}; 22 23 void Manager::setNetworkInstance(void) 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 if (getInterfaceIndex() == ch) 39 { 40 ipmiNetworkInstance = index; 41 } 42 43 ipmiNetworkChannelNumList[index] = ch; 44 index++; 45 } 46 ch++; 47 } 48 } 49 50 uint8_t Manager::getNetworkInstance(void) 51 { 52 return ipmiNetworkInstance; 53 } 54 55 void Manager::managerInit(const std::string& channel) 56 { 57 /* 58 * Session ID is 0000_0000h for messages that are sent outside the session. 59 * The session setup commands are sent on this session, so when the session 60 * manager comes up, is creates the Session ID 0000_0000h. It is active 61 * through the lifetime of the Session Manager. 62 */ 63 64 objManager = std::make_unique<sdbusplus::server::manager::manager>( 65 *getSdBus(), session::sessionManagerRootPath); 66 67 auto objPath = 68 std::string(session::sessionManagerRootPath) + "/" + channel + "/0"; 69 70 chName = channel; 71 setNetworkInstance(); 72 sessionsMap.emplace( 73 0, std::make_shared<Session>(*getSdBus(), objPath.c_str(), 0, 0, 0)); 74 75 // set up the timer for clearing out stale sessions 76 scheduleSessionCleaner(std::chrono::microseconds(3 * 1000 * 1000)); 77 } 78 79 std::shared_ptr<Session> 80 Manager::startSession(SessionID remoteConsoleSessID, Privilege priv, 81 cipher::rakp_auth::Algorithms authAlgo, 82 cipher::integrity::Algorithms intAlgo, 83 cipher::crypt::Algorithms cryptAlgo) 84 { 85 std::shared_ptr<Session> session = nullptr; 86 SessionID bmcSessionID = 0; 87 cleanStaleEntries(); 88 // set up the timer for monitoring this session 89 scheduleSessionCleaner(std::chrono::microseconds(1 * 1000 * 1000)); 90 91 uint8_t sessionHandle = 0; 92 93 auto activeSessions = sessionsMap.size() - session::maxSessionlessCount; 94 95 if (activeSessions < maxSessionHandles) 96 { 97 do 98 { 99 bmcSessionID = (crypto::prng::rand()); 100 bmcSessionID &= session::multiIntfaceSessionIDMask; 101 // In sessionID , BIT 31 BIT30 are used for netipmid instance 102 bmcSessionID |= static_cast<uint32_t>(ipmiNetworkInstance) << 30; 103 /* 104 * Every IPMI Session has two ID's attached to it Remote Console 105 * Session ID and BMC Session ID. The remote console ID is passed 106 * along with the Open Session request command. The BMC session ID 107 * is the key for the session map and is generated using std::rand. 108 * There is a rare chance for collision of BMC session ID, so the 109 * following check validates that. In the case of collision the 110 * created session is reset and a new session is created for 111 * validating collision. 112 */ 113 auto iterator = sessionsMap.find(bmcSessionID); 114 if (iterator != sessionsMap.end()) 115 { 116 // Detected BMC Session ID collisions 117 continue; 118 } 119 else 120 { 121 break; 122 } 123 } while (1); 124 125 sessionHandle = storeSessionHandle(bmcSessionID); 126 127 if (!sessionHandle) 128 { 129 throw std::runtime_error( 130 "Invalid sessionHandle - No sessionID slot "); 131 } 132 sessionHandle &= session::multiIntfaceSessionHandleMask; 133 // In sessionID , BIT 31 BIT30 are used for netipmid instance 134 sessionHandle |= static_cast<uint8_t>(ipmiNetworkInstance) << 6; 135 std::stringstream sstream; 136 sstream << std::hex << bmcSessionID; 137 std::stringstream shstream; 138 shstream << std::hex << (int)sessionHandle; 139 auto objPath = std::string(session::sessionManagerRootPath) + "/" + 140 chName + "/" + sstream.str() + "_" + shstream.str(); 141 session = std::make_shared<Session>(*getSdBus(), objPath.c_str(), 142 remoteConsoleSessID, bmcSessionID, 143 static_cast<uint8_t>(priv)); 144 145 // Set the Authentication Algorithm 146 switch (authAlgo) 147 { 148 case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA1: 149 { 150 session->setAuthAlgo( 151 std::make_unique<cipher::rakp_auth::AlgoSHA1>(intAlgo, 152 cryptAlgo)); 153 break; 154 } 155 case cipher::rakp_auth::Algorithms::RAKP_HMAC_SHA256: 156 { 157 session->setAuthAlgo( 158 std::make_unique<cipher::rakp_auth::AlgoSHA256>(intAlgo, 159 cryptAlgo)); 160 break; 161 } 162 default: 163 { 164 throw std::runtime_error("Invalid Authentication Algorithm"); 165 } 166 } 167 168 sessionsMap.emplace(bmcSessionID, session); 169 session->sessionHandle(sessionHandle); 170 171 return session; 172 } 173 174 log<level::INFO>("No free RMCP+ sessions left"); 175 176 throw std::runtime_error("No free sessions left"); 177 } 178 179 bool Manager::stopSession(SessionID bmcSessionID) 180 { 181 auto iter = sessionsMap.find(bmcSessionID); 182 if (iter != sessionsMap.end()) 183 { 184 iter->second->state( 185 static_cast<uint8_t>(session::State::tearDownInProgress)); 186 return true; 187 } 188 else 189 { 190 return false; 191 } 192 } 193 194 std::shared_ptr<Session> Manager::getSession(SessionID sessionID, 195 RetrieveOption option) 196 { 197 switch (option) 198 { 199 case RetrieveOption::BMC_SESSION_ID: 200 { 201 auto iter = sessionsMap.find(sessionID); 202 if (iter != sessionsMap.end()) 203 { 204 return iter->second; 205 } 206 break; 207 } 208 case RetrieveOption::RC_SESSION_ID: 209 { 210 auto iter = std::find_if( 211 sessionsMap.begin(), sessionsMap.end(), 212 [sessionID]( 213 const std::pair<const uint32_t, std::shared_ptr<Session>>& 214 in) -> bool { 215 return sessionID == in.second->getRCSessionID(); 216 }); 217 218 if (iter != sessionsMap.end()) 219 { 220 return iter->second; 221 } 222 break; 223 } 224 default: 225 throw std::runtime_error("Invalid retrieval option"); 226 } 227 228 throw std::runtime_error("Session ID not found"); 229 } 230 231 void Manager::cleanStaleEntries() 232 { 233 // with overflow = min(1, max - active sessions) 234 // active idle time in seconds = 60 / overflow^3 235 constexpr int baseIdleMicros = 60 * 1000 * 1000; 236 // no +1 for the zero session here because this is just active sessions 237 int sessionDivisor = 238 getActiveSessionCount() - session::maxSessionCountPerChannel; 239 sessionDivisor = std::max(0, sessionDivisor) + 1; 240 sessionDivisor = sessionDivisor * sessionDivisor * sessionDivisor; 241 int activeMicros = baseIdleMicros / sessionDivisor; 242 243 // with overflow = min(1, max - total sessions) 244 // setup idle time in seconds = max(3, 60 / overflow^3) 245 246 // +1 for the zero session here because size() counts that too 247 int setupDivisor = 248 sessionsMap.size() - (session::maxSessionCountPerChannel + 1); 249 setupDivisor = std::max(0, setupDivisor) + 1; 250 setupDivisor = setupDivisor * setupDivisor * setupDivisor; 251 constexpr int maxSetupMicros = 3 * 1000 * 1000; 252 int setupMicros = std::min(maxSetupMicros, baseIdleMicros / setupDivisor); 253 254 std::chrono::microseconds activeGrace(activeMicros); 255 std::chrono::microseconds setupGrace(setupMicros); 256 257 for (auto iter = sessionsMap.begin(); iter != sessionsMap.end();) 258 { 259 auto session = iter->second; 260 // special handling for sessionZero 261 if (session->getBMCSessionID() == session::sessionZero) 262 { 263 iter++; 264 continue; 265 } 266 if (!(session->isSessionActive(activeGrace, setupGrace))) 267 { 268 log<level::INFO>( 269 "Removing idle IPMI LAN session", 270 entry("SESSION_ID=%x", session->getBMCSessionID()), 271 entry("HANDLE=%x", 272 getSessionHandle(session->getBMCSessionID()))); 273 sessionHandleMap[getSessionHandle(session->getBMCSessionID())] = 0; 274 iter = sessionsMap.erase(iter); 275 } 276 else 277 { 278 iter++; 279 } 280 } 281 if (sessionsMap.size() > 1) 282 { 283 constexpr int maxCleanupDelay = 1 * 1000 * 1000; 284 std::chrono::microseconds cleanupDelay( 285 std::min(setupMicros, maxCleanupDelay)); 286 scheduleSessionCleaner(cleanupDelay); 287 } 288 } 289 290 uint8_t Manager::storeSessionHandle(SessionID bmcSessionID) 291 { 292 // Handler index 0 is reserved for invalid session. 293 // index starts with 1, for direct usage. Index 0 reserved 294 for (size_t i = 1; i < session::maxSessionHandles; i++) 295 { 296 if (sessionHandleMap[i] == 0) 297 { 298 sessionHandleMap[i] = bmcSessionID; 299 return i; 300 } 301 } 302 return 0; 303 } 304 305 uint32_t Manager::getSessionIDbyHandle(uint8_t sessionHandle) const 306 { 307 if (sessionHandle < session::maxSessionHandles) 308 { 309 return sessionHandleMap[sessionHandle]; 310 } 311 return 0; 312 } 313 314 uint8_t Manager::getSessionHandle(SessionID bmcSessionID) const 315 { 316 // Handler index 0 is reserved for invalid session. 317 // index starts with 1, for direct usage. Index 0 reserved 318 for (size_t i = 1; i < session::maxSessionHandles; i++) 319 { 320 if (sessionHandleMap[i] == bmcSessionID) 321 { 322 return (i); 323 } 324 } 325 return 0; 326 } 327 uint8_t Manager::getActiveSessionCount() const 328 { 329 return (std::count_if( 330 sessionsMap.begin(), sessionsMap.end(), 331 [](const std::pair<const uint32_t, std::shared_ptr<Session>>& in) 332 -> bool { 333 return in.second->state() == 334 static_cast<uint8_t>(session::State::active); 335 })); 336 } 337 338 void Manager::scheduleSessionCleaner(const std::chrono::microseconds& when) 339 { 340 std::chrono::duration expTime = timer.expires_from_now(); 341 if (expTime > std::chrono::microseconds(0) && expTime < when) 342 { 343 // if timer has not already expired AND requested timeout is greater 344 // than current timeout then ignore this new requested timeout 345 return; 346 } 347 timer.expires_from_now(when); 348 timer.async_wait([this](const boost::system::error_code& ec) { 349 if (!ec) 350 { 351 cleanStaleEntries(); 352 } 353 }); 354 } 355 356 } // namespace session 357