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