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