1 #include "channel.hpp" 2 3 #include "net.hpp" 4 #include "transporthandler.hpp" 5 #include "types.hpp" 6 #include "utils.hpp" 7 8 #include <arpa/inet.h> 9 10 #include <boost/process/child.hpp> 11 #include <fstream> 12 #include <phosphor-logging/elog-errors.hpp> 13 #include <phosphor-logging/log.hpp> 14 #include <string> 15 #include <xyz/openbmc_project/Common/error.hpp> 16 17 using namespace phosphor::logging; 18 using namespace sdbusplus::xyz::openbmc_project::Common::Error; 19 20 /** @struct GetChannelAccessRequest 21 * 22 * IPMI payload for Get Channel access command request. 23 */ 24 struct GetChannelAccessRequest 25 { 26 uint8_t channelNumber; //!< Channel number. 27 uint8_t volatileSetting; //!< Get non-volatile or the volatile setting. 28 } __attribute__((packed)); 29 30 /** @struct GetChannelAccessResponse 31 * 32 * IPMI payload for Get Channel access command response. 33 */ 34 struct GetChannelAccessResponse 35 { 36 uint8_t settings; //!< Channel settings. 37 uint8_t privilegeLimit; //!< Channel privilege level limit. 38 } __attribute__((packed)); 39 40 ipmi_ret_t ipmi_get_channel_access(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 41 ipmi_request_t request, 42 ipmi_response_t response, 43 ipmi_data_len_t data_len, 44 ipmi_context_t context) 45 { 46 auto requestData = 47 reinterpret_cast<const GetChannelAccessRequest*>(request); 48 std::vector<uint8_t> outPayload(sizeof(GetChannelAccessResponse)); 49 auto responseData = 50 reinterpret_cast<GetChannelAccessResponse*>(outPayload.data()); 51 52 /* 53 * The value Eh is used as a way to identify the current channel that 54 * the command is being received from. 55 */ 56 constexpr auto channelE = 0x0E; 57 int channel = requestData->channelNumber; 58 auto ethdevice = ipmi::network::ChanneltoEthernet(channel); 59 60 if (channel != channelE && ethdevice.empty()) 61 { 62 *data_len = 0; 63 return IPMI_CC_INVALID_FIELD_REQUEST; 64 } 65 66 /* 67 * [7:6] - reserved 68 * [5] - 1b = Alerting disabled 69 * [4] - 1b = per message authentication disabled 70 * [3] - 0b = User level authentication enabled 71 * [2:0] - 2h = always available 72 */ 73 constexpr auto channelSetting = 0x32; 74 75 responseData->settings = channelSetting; 76 // Defaulting the channel privilege to administrator level. 77 responseData->privilegeLimit = PRIVILEGE_ADMIN; 78 79 *data_len = outPayload.size(); 80 memcpy(response, outPayload.data(), *data_len); 81 82 return IPMI_CC_OK; 83 } 84 85 // ATTENTION: This ipmi function is very hardcoded on purpose 86 // OpenBMC does not fully support IPMI. This command is useful 87 // to have around because it enables testing of interfaces with 88 // the IPMI tool. 89 #define GET_CHANNEL_INFO_CHANNEL_OFFSET 0 90 // IPMI Table 6-2 91 #define IPMI_CHANNEL_TYPE_IPMB 1 92 // IPMI Table 6-3 93 #define IPMI_CHANNEL_MEDIUM_TYPE_OTHER 6 94 95 ipmi_ret_t ipmi_app_channel_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 96 ipmi_request_t request, 97 ipmi_response_t response, 98 ipmi_data_len_t data_len, 99 ipmi_context_t context) 100 { 101 ipmi_ret_t rc = IPMI_CC_OK; 102 auto* p = static_cast<uint8_t*>(request); 103 int channel = (*p) & CHANNEL_MASK; 104 std::string ethdevice = ipmi::network::ChanneltoEthernet(channel); 105 106 // The supported channels numbers are those which are configured. 107 // Channel Number E is used as way to identify the current channel 108 // that the command is being is received from. 109 if (channel != 0xe && ethdevice.empty()) 110 { 111 rc = IPMI_CC_PARM_OUT_OF_RANGE; 112 *data_len = 0; 113 } 114 else 115 { 116 uint8_t resp[] = {1, 117 IPMI_CHANNEL_MEDIUM_TYPE_OTHER, 118 IPMI_CHANNEL_TYPE_IPMB, 119 1, 120 0x41, 121 0xA7, 122 0x00, 123 0, 124 0}; 125 126 *data_len = sizeof(resp); 127 memcpy(response, resp, *data_len); 128 } 129 130 return rc; 131 } 132 133 namespace cipher 134 { 135 136 /** @brief Get the supported Cipher records 137 * 138 * The cipher records are read from the JSON file and converted into cipher 139 * suite record format mentioned in the IPMI specification. The records can be 140 * either OEM or standard cipher. Each json entry is parsed and converted into 141 * the cipher record format and pushed into the vector. 142 * 143 * @return vector containing all the cipher suite records. 144 * 145 */ 146 std::vector<uint8_t> getCipherRecords() 147 { 148 std::vector<uint8_t> records; 149 150 std::ifstream jsonFile(configFile); 151 if (!jsonFile.is_open()) 152 { 153 log<level::ERR>("Channel Cipher suites file not found"); 154 elog<InternalFailure>(); 155 } 156 157 auto data = Json::parse(jsonFile, nullptr, false); 158 if (data.is_discarded()) 159 { 160 log<level::ERR>("Parsing channel cipher suites JSON failed"); 161 elog<InternalFailure>(); 162 } 163 164 for (const auto& record : data) 165 { 166 if (record.find(oem) != record.end()) 167 { 168 // OEM cipher suite - 0xC1 169 records.push_back(oemCipherSuite); 170 // Cipher Suite ID 171 records.push_back(record.value(cipher, 0)); 172 // OEM IANA - 3 bytes 173 records.push_back(record.value(oem, 0)); 174 records.push_back(record.value(oem, 0) >> 8); 175 records.push_back(record.value(oem, 0) >> 16); 176 } 177 else 178 { 179 // OEM cipher suite - 0xC0 180 records.push_back(stdCipherSuite); 181 // Cipher Suite ID 182 records.push_back(record.value(cipher, 0)); 183 } 184 185 // Authentication algorithm number 186 records.push_back(record.value(auth, 0)); 187 // Integrity algorithm number 188 records.push_back(record.value(integrity, 0) | integrityTag); 189 // Confidentiality algorithm number 190 records.push_back(record.value(conf, 0) | confTag); 191 } 192 193 return records; 194 } 195 196 } // namespace cipher 197 198 ipmi_ret_t getChannelCipherSuites(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 199 ipmi_request_t request, 200 ipmi_response_t response, 201 ipmi_data_len_t data_len, 202 ipmi_context_t context) 203 { 204 static std::vector<uint8_t> records; 205 static auto recordInit = false; 206 207 auto requestData = 208 reinterpret_cast<const GetChannelCipherRequest*>(request); 209 210 if (*data_len < sizeof(GetChannelCipherRequest)) 211 { 212 *data_len = 0; 213 return IPMI_CC_REQ_DATA_LEN_INVALID; 214 } 215 216 *data_len = 0; 217 218 // Support only for list algorithms by cipher suite 219 if (cipher::listCipherSuite != 220 (requestData->listIndex & cipher::listTypeMask)) 221 { 222 return IPMI_CC_INVALID_FIELD_REQUEST; 223 } 224 225 if (!recordInit) 226 { 227 try 228 { 229 records = cipher::getCipherRecords(); 230 recordInit = true; 231 } 232 catch (const std::exception& e) 233 { 234 return IPMI_CC_UNSPECIFIED_ERROR; 235 } 236 } 237 238 // List index(00h-3Fh), 0h selects the first set of 16, 1h selects the next 239 // set of 16 and so on. 240 auto index = 241 static_cast<size_t>(requestData->listIndex & cipher::listIndexMask); 242 243 // Calculate the number of record data bytes to be returned. 244 auto start = std::min(index * cipher::respSize, records.size()); 245 auto end = 246 std::min((index * cipher::respSize) + cipher::respSize, records.size()); 247 auto size = end - start; 248 249 auto responseData = reinterpret_cast<GetChannelCipherRespHeader*>(response); 250 responseData->channelNumber = cipher::defaultChannelNumber; 251 252 if (!size) 253 { 254 *data_len = sizeof(GetChannelCipherRespHeader); 255 } 256 else 257 { 258 std::copy_n(records.data() + start, size, 259 static_cast<uint8_t*>(response) + 1); 260 *data_len = size + sizeof(GetChannelCipherRespHeader); 261 } 262 263 return IPMI_CC_OK; 264 } 265 266 template <typename... ArgTypes> 267 static int executeCmd(const char* path, ArgTypes&&... tArgs) 268 { 269 boost::process::child execProg(path, const_cast<char*>(tArgs)...); 270 execProg.wait(); 271 return execProg.exit_code(); 272 } 273 274 /** @brief Enable the network IPMI service on the specified ethernet interface. 275 * 276 * @param[in] intf - ethernet interface on which to enable IPMI 277 */ 278 void enableNetworkIPMI(const std::string& intf) 279 { 280 // Check if there is a iptable filter to drop IPMI packets for the 281 // interface. 282 auto retCode = 283 executeCmd("/usr/sbin/iptables", "-C", "INPUT", "-p", "udp", "-i", 284 intf.c_str(), "--dport", "623", "-j", "DROP"); 285 286 // If the iptable filter exists, delete the filter. 287 if (!retCode) 288 { 289 auto response = 290 executeCmd("/usr/sbin/iptables", "-D", "INPUT", "-p", "udp", "-i", 291 intf.c_str(), "--dport", "623", "-j", "DROP"); 292 293 if (response) 294 { 295 log<level::ERR>("Dropping the iptables filter failed", 296 entry("INTF=%s", intf.c_str()), 297 entry("RETURN_CODE:%d", response)); 298 } 299 } 300 } 301 302 /** @brief Disable the network IPMI service on the specified ethernet interface. 303 * 304 * @param[in] intf - ethernet interface on which to disable IPMI 305 */ 306 void disableNetworkIPMI(const std::string& intf) 307 { 308 // Check if there is a iptable filter to drop IPMI packets for the 309 // interface. 310 auto retCode = 311 executeCmd("/usr/sbin/iptables", "-C", "INPUT", "-p", "udp", "-i", 312 intf.c_str(), "--dport", "623", "-j", "DROP"); 313 314 // If the iptable filter does not exist, add filter to drop network IPMI 315 // packets 316 if (retCode) 317 { 318 auto response = 319 executeCmd("/usr/sbin/iptables", "-I", "INPUT", "-p", "udp", "-i", 320 intf.c_str(), "--dport", "623", "-j", "DROP"); 321 322 if (response) 323 { 324 log<level::ERR>("Inserting iptables filter failed", 325 entry("INTF=%s", intf.c_str()), 326 entry("RETURN_CODE:%d", response)); 327 } 328 } 329 } 330 331 /** @struct SetChannelAccessRequest 332 * 333 * IPMI payload for Set Channel access command request. 334 */ 335 struct SetChannelAccessRequest 336 { 337 uint8_t channelNumber; //!< Channel number 338 uint8_t accessMode; //!< Access mode for IPMI messaging 339 uint8_t privLevel; //!< Channel Privilege Level 340 } __attribute__((packed)); 341 342 ipmi_ret_t ipmi_set_channel_access(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 343 ipmi_request_t request, 344 ipmi_response_t response, 345 ipmi_data_len_t data_len, 346 ipmi_context_t context) 347 { 348 auto requestData = 349 reinterpret_cast<const SetChannelAccessRequest*>(request); 350 351 int channel = requestData->channelNumber; 352 // Validate the channel number corresponds to any of the network channel. 353 auto ethdevice = ipmi::network::ChanneltoEthernet(channel); 354 if (ethdevice.empty()) 355 { 356 *data_len = 0; 357 return IPMI_CC_INVALID_FIELD_REQUEST; 358 } 359 360 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 361 // Bits[2:0] indicates the Access Mode, this mask field will extract the 362 // access mode from the command data. 363 static constexpr auto accessModeMask = 0x07; 364 auto accessMode = requestData->accessMode & accessModeMask; 365 static constexpr auto disabled = 0; 366 static constexpr auto enabled = 2; 367 368 try 369 { 370 if (accessMode == enabled) 371 { 372 enableNetworkIPMI(ethdevice); 373 } 374 else if (accessMode == disabled) 375 { 376 disableNetworkIPMI(ethdevice); 377 } 378 } 379 catch (const sdbusplus::exception::SdBusError& e) 380 { 381 log<level::ERR>(e.what()); 382 *data_len = 0; 383 return IPMI_CC_UNSPECIFIED_ERROR; 384 } 385 386 return IPMI_CC_OK; 387 } 388