1 #include "channel.hpp" 2 #include "types.hpp" 3 #include "transporthandler.hpp" 4 #include "utils.hpp" 5 6 #include <string> 7 #include <arpa/inet.h> 8 9 #include <phosphor-logging/log.hpp> 10 #include <phosphor-logging/elog-errors.hpp> 11 #include "xyz/openbmc_project/Common/error.hpp" 12 13 extern struct ChannelConfig_t channelConfig; 14 15 constexpr auto ipv4Protocol = "xyz.openbmc_project.Network.IP.Protocol.IPv4"; 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_set_channel_access(ipmi_netfn_t netfn, 41 ipmi_cmd_t cmd, 42 ipmi_request_t request, 43 ipmi_response_t response, 44 ipmi_data_len_t data_len, 45 ipmi_context_t context) 46 { 47 ipmi_ret_t rc = IPMI_CC_OK; 48 49 std::string ipaddress; 50 std::string gateway; 51 uint8_t prefix {}; 52 uint32_t vlanID {}; 53 std::string networkInterfacePath; 54 ipmi::DbusObjectInfo ipObject; 55 ipmi::DbusObjectInfo systemObject; 56 57 // Todo: parse the request data if needed. 58 // Using Set Channel cmd to apply changes of Set Lan Cmd. 59 try 60 { 61 sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); 62 63 log<level::INFO>("Network data from Cache", 64 entry("PREFIX=%s", channelConfig.netmask.c_str()), 65 entry("ADDRESS=%s", channelConfig.ipaddr.c_str()), 66 entry("GATEWAY=%s", channelConfig.gateway.c_str()), 67 entry("VLAN=%d", channelConfig.vlanID), 68 entry("IPSRC=%d", channelConfig.ipsrc)); 69 70 if (channelConfig.vlanID != ipmi::network::VLAN_ID_MASK) 71 { 72 //get the first twelve bits which is vlan id 73 //not interested in rest of the bits. 74 channelConfig.vlanID = le32toh(channelConfig.vlanID); 75 vlanID = channelConfig.vlanID & ipmi::network::VLAN_ID_MASK; 76 } 77 78 // if the asked ip src is DHCP then not interested in 79 // any given data except vlan. 80 if (channelConfig.ipsrc != ipmi::network::IPOrigin::DHCP) 81 { 82 // always get the system object 83 systemObject = ipmi::getDbusObject( 84 bus, 85 ipmi::network::SYSTEMCONFIG_INTERFACE, 86 ipmi::network::ROOT); 87 88 // the below code is to determine the mode of the interface 89 // as the handling is same, if the system is configured with 90 // DHCP or user has given all the data. 91 try 92 { 93 ipmi::ObjectTree ancestorMap; 94 95 ipmi::InterfaceList interfaces { 96 ipmi::network::ETHERNET_INTERFACE }; 97 98 // if the system is having ip object,then 99 // get the IP object. 100 ipObject = ipmi::getIPObject( 101 bus, 102 ipmi::network::IP_INTERFACE, 103 ipmi::network::ROOT, 104 ipmi::network::IP_TYPE); 105 106 // Get the parent interface of the IP object. 107 try 108 { 109 ancestorMap = ipmi::getAllAncestors(bus, 110 ipObject.first, 111 std::move(interfaces)); 112 } 113 catch (InternalFailure& e) 114 { 115 // if unable to get the parent interface 116 // then commit the error and return. 117 log<level::ERR>("Unable to get the parent interface", 118 entry("PATH=%s", ipObject.first.c_str()), 119 entry("INTERFACE=%s", 120 ipmi::network::ETHERNET_INTERFACE)); 121 commit<InternalFailure>(); 122 rc = IPMI_CC_UNSPECIFIED_ERROR; 123 channelConfig.clear(); 124 return rc; 125 } 126 127 networkInterfacePath = ancestorMap.begin()->first; 128 } 129 catch (InternalFailure& e) 130 { 131 // TODO Currently IPMI supports single interface,need to handle 132 // Multiple interface through 133 // https://github.com/openbmc/openbmc/issues/2138 134 135 // if there is no ip configured on the system,then 136 // get the network interface object. 137 auto networkInterfaceObject = ipmi::getDbusObject( 138 bus, 139 ipmi::network::ETHERNET_INTERFACE, 140 ipmi::network::ROOT, 141 ipmi::network::INTERFACE); 142 143 networkInterfacePath = std::move(networkInterfaceObject.first); 144 } 145 146 // get the configured mode on the system. 147 auto enableDHCP = ipmi::getDbusProperty( 148 bus, 149 ipmi::network::SERVICE, 150 networkInterfacePath, 151 ipmi::network::ETHERNET_INTERFACE, 152 "DHCPEnabled").get<bool>(); 153 154 // check whether user has given all the data 155 // or the configured system interface is dhcp enabled, 156 // in both of the cases get the values from the cache. 157 if ((!channelConfig.ipaddr.empty() && 158 !channelConfig.netmask.empty() && 159 !channelConfig.gateway.empty()) || 160 (enableDHCP)) // configured system interface mode = DHCP 161 { 162 //convert mask into prefix 163 ipaddress = channelConfig.ipaddr; 164 prefix = ipmi::network::toPrefix(AF_INET, channelConfig.netmask); 165 gateway = channelConfig.gateway; 166 if (channelConfig.vlanID != ipmi::network::VLAN_ID_MASK) 167 { 168 //get the first twelve bits which is vlan id 169 //not interested in rest of the bits. 170 channelConfig.vlanID = le32toh(channelConfig.vlanID); 171 vlanID = channelConfig.vlanID & ipmi::network::VLAN_ID_MASK; 172 } 173 else 174 { 175 vlanID = ipmi::network::getVLAN(networkInterfacePath); 176 } 177 178 } 179 else // asked ip src = static and configured system src = static 180 // or partially given data. 181 { 182 // We have partial filled cache so get the remaining 183 // info from the system. 184 185 // Get the network data from the system as user has 186 // not given all the data then use the data fetched from the 187 // system but it is implementation dependent,IPMI spec doesn't 188 // force it. 189 190 // if system is not having any ip object don't throw error, 191 try 192 { 193 auto properties = ipmi::getAllDbusProperties( 194 bus, 195 ipObject.second, 196 ipObject.first, 197 ipmi::network::IP_INTERFACE); 198 199 ipaddress = channelConfig.ipaddr.empty() ? 200 properties["Address"].get<std::string>() : 201 channelConfig.ipaddr; 202 203 prefix = channelConfig.netmask.empty() ? 204 properties["PrefixLength"].get<uint8_t>() : 205 ipmi::network::toPrefix(AF_INET, 206 channelConfig.netmask); 207 208 } 209 catch (InternalFailure& e) 210 { 211 log<level::INFO>("Failed to get IP object which matches", 212 entry("INTERFACE=%s", ipmi::network::IP_INTERFACE), 213 entry("MATCH=%s", ipmi::network::IP_TYPE)); 214 } 215 216 auto systemProperties = ipmi::getAllDbusProperties( 217 bus, 218 systemObject.second, 219 systemObject.first, 220 ipmi::network::SYSTEMCONFIG_INTERFACE); 221 222 gateway = channelConfig.gateway.empty() ? 223 systemProperties["DefaultGateway"].get<std::string>() : 224 channelConfig.gateway; 225 226 } 227 } 228 229 // Currently network manager doesn't support purging of all the 230 // ip addresses and the vlan interfaces from the parent interface, 231 // TODO once the support is there, will make the change here. 232 // https://github.com/openbmc/openbmc/issues/2141. 233 234 // TODO Currently IPMI supports single interface,need to handle 235 // Multiple interface through 236 // https://github.com/openbmc/openbmc/issues/2138 237 238 // instead of deleting all the vlan interfaces and 239 // all the ipv4 address,we will call reset method. 240 //delete all the vlan interfaces 241 242 ipmi::deleteAllDbusObjects(bus, 243 ipmi::network::ROOT, 244 ipmi::network::VLAN_INTERFACE); 245 246 // set the interface mode to static 247 auto networkInterfaceObject = ipmi::getDbusObject( 248 bus, 249 ipmi::network::ETHERNET_INTERFACE, 250 ipmi::network::ROOT, 251 ipmi::network::INTERFACE); 252 253 // setting the physical interface mode to static. 254 ipmi::setDbusProperty(bus, 255 ipmi::network::SERVICE, 256 networkInterfaceObject.first, 257 ipmi::network::ETHERNET_INTERFACE, 258 "DHCPEnabled", 259 false); 260 261 networkInterfacePath = networkInterfaceObject.first; 262 263 //delete all the ipv4 addresses 264 ipmi::deleteAllDbusObjects(bus, 265 ipmi::network::ROOT, 266 ipmi::network::IP_INTERFACE, 267 ipmi::network::IP_TYPE); 268 269 if (vlanID) 270 { 271 ipmi::network::createVLAN(bus, 272 ipmi::network::SERVICE, 273 ipmi::network::ROOT, 274 ipmi::network::INTERFACE, 275 vlanID); 276 277 auto networkInterfaceObject = ipmi::getDbusObject( 278 bus, 279 ipmi::network::VLAN_INTERFACE, 280 ipmi::network::ROOT); 281 282 networkInterfacePath = networkInterfaceObject.first; 283 } 284 285 if (channelConfig.ipsrc == ipmi::network::IPOrigin::DHCP) 286 { 287 ipmi::setDbusProperty(bus, 288 ipmi::network::SERVICE, 289 networkInterfacePath, 290 ipmi::network::ETHERNET_INTERFACE, 291 "DHCPEnabled", 292 true); 293 } 294 else 295 { 296 //change the mode to static 297 ipmi::setDbusProperty(bus, 298 ipmi::network::SERVICE, 299 networkInterfacePath, 300 ipmi::network::ETHERNET_INTERFACE, 301 "DHCPEnabled", 302 false); 303 304 if (!ipaddress.empty()) 305 { 306 ipmi::network::createIP(bus, 307 ipmi::network::SERVICE, 308 networkInterfacePath, 309 ipv4Protocol, 310 ipaddress, 311 prefix); 312 } 313 314 if (!gateway.empty()) 315 { 316 ipmi::setDbusProperty(bus, 317 systemObject.second, 318 systemObject.first, 319 ipmi::network::SYSTEMCONFIG_INTERFACE, 320 "DefaultGateway", 321 std::string(gateway)); 322 } 323 } 324 325 } 326 catch (InternalFailure& e) 327 { 328 log<level::ERR>("Failed to set network data", 329 entry("PREFIX=%d", prefix), 330 entry("ADDRESS=%s", ipaddress.c_str()), 331 entry("GATEWAY=%s", gateway.c_str()), 332 entry("VLANID=%d", vlanID), 333 entry("IPSRC=%d", channelConfig.ipsrc)); 334 335 commit<InternalFailure>(); 336 rc = IPMI_CC_UNSPECIFIED_ERROR; 337 } 338 339 channelConfig.clear(); 340 return rc; 341 } 342 343 ipmi_ret_t ipmi_get_channel_access(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 344 ipmi_request_t request, ipmi_response_t response, 345 ipmi_data_len_t data_len, ipmi_context_t context) 346 { 347 auto requestData = reinterpret_cast<const GetChannelAccessRequest*> 348 (request); 349 std::vector<uint8_t> outPayload(sizeof(GetChannelAccessResponse)); 350 auto responseData = reinterpret_cast<GetChannelAccessResponse*> 351 (outPayload.data()); 352 353 // Channel 1 is arbitrarily assigned to ETH0 channel 354 constexpr auto channelOne = 0x01; 355 356 /* 357 * The value Eh is used as a way to identify the current channel that 358 * the command is being received from. 359 */ 360 constexpr auto channelE = 0x0E; 361 362 if (requestData->channelNumber != channelOne && 363 requestData->channelNumber != channelE) 364 { 365 *data_len = 0; 366 return IPMI_CC_INVALID_FIELD_REQUEST; 367 } 368 369 /* 370 * [7:6] - reserved 371 * [5] - 1b = Alerting disabled 372 * [4] - 1b = per message authentication disabled 373 * [3] - 0b = User level authentication enabled 374 * [2:0] - 2h = always available 375 */ 376 constexpr auto channelSetting = 0x32; 377 378 responseData->settings = channelSetting; 379 //Defaulting the channel privilege to administrator level. 380 responseData->privilegeLimit = PRIVILEGE_ADMIN; 381 382 *data_len = outPayload.size(); 383 memcpy(response, outPayload.data(), *data_len); 384 385 return IPMI_CC_OK; 386 } 387 388 // ATTENTION: This ipmi function is very hardcoded on purpose 389 // OpenBMC does not fully support IPMI. This command is useful 390 // to have around because it enables testing of interfaces with 391 // the IPMI tool. 392 #define GET_CHANNEL_INFO_CHANNEL_OFFSET 0 393 // IPMI Table 6-2 394 #define IPMI_CHANNEL_TYPE_IPMB 1 395 // IPMI Table 6-3 396 #define IPMI_CHANNEL_MEDIUM_TYPE_OTHER 6 397 398 ipmi_ret_t ipmi_app_channel_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd, 399 ipmi_request_t request, ipmi_response_t response, 400 ipmi_data_len_t data_len, ipmi_context_t context) 401 { 402 ipmi_ret_t rc = IPMI_CC_OK; 403 uint8_t resp[] = { 404 1, 405 IPMI_CHANNEL_MEDIUM_TYPE_OTHER, 406 IPMI_CHANNEL_TYPE_IPMB, 407 1,0x41,0xA7,0x00,0,0}; 408 uint8_t *p = (uint8_t*) request; 409 410 printf("IPMI APP GET CHANNEL INFO\n"); 411 412 // The supported channels numbers are 1 and 8. 413 // Channel Number E is used as way to identify the current channel 414 // that the command is being is received from. 415 if (*p == 0xe || *p == 1 || *p == 8) { 416 417 *data_len = sizeof(resp); 418 memcpy(response, resp, *data_len); 419 420 } else { 421 rc = IPMI_CC_PARM_OUT_OF_RANGE; 422 *data_len = 0; 423 } 424 425 return rc; 426 } 427