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