diff options
Diffstat (limited to 'transporthandler.cpp')
-rw-r--r-- | transporthandler.cpp | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/transporthandler.cpp b/transporthandler.cpp index f4326c1..bbc6ae5 100644 --- a/transporthandler.cpp +++ b/transporthandler.cpp @@ -22,7 +22,19 @@ #include <mapper.h> #endif +/** @struct SetChannelAccessRequest + * + * IPMI payload for Set Channel access command request. + */ +struct SetChannelAccessRequest +{ + uint8_t channelNumber; //!< Channel number. + uint8_t setting; //!< The setting values. + uint8_t privilegeLevelLimit; //!< The Privilege Level Limit +} __attribute__((packed)); + const int SIZE_MAC = 18; //xx:xx:xx:xx:xx:xx +constexpr auto ipv4Protocol = "xyz.openbmc_project.Network.IP.Protocol.IPv4"; std::map<int, std::unique_ptr<struct ChannelConfig_t>> channelConfig; @@ -620,6 +632,319 @@ ipmi_ret_t ipmi_transport_get_lan(ipmi_netfn_t netfn, return rc; } +ipmi_ret_t ipmi_set_channel_access(ipmi_netfn_t netfn, + ipmi_cmd_t cmd, + ipmi_request_t request, + ipmi_response_t response, + ipmi_data_len_t data_len, + ipmi_context_t context) +{ + ipmi_ret_t rc = IPMI_CC_OK; + + std::string ipaddress; + std::string gateway; + uint8_t prefix {}; + uint32_t vlanID {}; + std::string networkInterfacePath; + ipmi::DbusObjectInfo ipObject; + ipmi::DbusObjectInfo systemObject; + + if (*data_len < sizeof(SetChannelAccessRequest)) + { + return IPMI_CC_INVALID; + } + + auto requestData = reinterpret_cast<const SetChannelAccessRequest*> + (request); + int channel = requestData->channelNumber & CHANNEL_MASK; + + auto ethdevice = ipmi::network::ChanneltoEthernet(channel); + if (ethdevice.empty()) + { + return IPMI_CC_INVALID_FIELD_REQUEST; + } + auto ethIp = ethdevice + "/" + ipmi::network::IP_TYPE; + auto channelConf = getChannelConfig(channel); + + // Todo: parse the request data if needed. + // Using Set Channel cmd to apply changes of Set Lan Cmd. + try + { + sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection()); + + log<level::INFO>("Network data from Cache", + entry("PREFIX=%s", channelConf->netmask.c_str()), + entry("ADDRESS=%s", channelConf->ipaddr.c_str()), + entry("GATEWAY=%s", channelConf->gateway.c_str()), + entry("VLAN=%d", channelConf->vlanID), + entry("IPSRC=%d", channelConf->ipsrc)); + if (channelConf->vlanID != ipmi::network::VLAN_ID_MASK) + { + //get the first twelve bits which is vlan id + //not interested in rest of the bits. + channelConf->vlanID = le32toh(channelConf->vlanID); + vlanID = channelConf->vlanID & ipmi::network::VLAN_ID_MASK; + } + + // if the asked ip src is DHCP then not interested in + // any given data except vlan. + if (channelConf->ipsrc != ipmi::network::IPOrigin::DHCP) + { + // always get the system object + systemObject = ipmi::getDbusObject( + bus, + ipmi::network::SYSTEMCONFIG_INTERFACE, + ipmi::network::ROOT); + + // the below code is to determine the mode of the interface + // as the handling is same, if the system is configured with + // DHCP or user has given all the data. + try + { + ipmi::ObjectTree ancestorMap; + + ipmi::InterfaceList interfaces { + ipmi::network::ETHERNET_INTERFACE }; + + // if the system is having ip object,then + // get the IP object. + ipObject = ipmi::getDbusObject(bus, + ipmi::network::IP_INTERFACE, + ipmi::network::ROOT, + ethIp); + + // Get the parent interface of the IP object. + try + { + ancestorMap = ipmi::getAllAncestors(bus, + ipObject.first, + std::move(interfaces)); + } + catch (InternalFailure& e) + { + // if unable to get the parent interface + // then commit the error and return. + log<level::ERR>("Unable to get the parent interface", + entry("PATH=%s", ipObject.first.c_str()), + entry("INTERFACE=%s", + ipmi::network::ETHERNET_INTERFACE)); + commit<InternalFailure>(); + rc = IPMI_CC_UNSPECIFIED_ERROR; + channelConf->clear(); + return rc; + } + + networkInterfacePath = ancestorMap.begin()->first; + } + catch (InternalFailure& e) + { + // TODO Currently IPMI supports single interface,need to handle + // Multiple interface through + // https://github.com/openbmc/openbmc/issues/2138 + + // if there is no ip configured on the system,then + // get the network interface object. + auto networkInterfaceObject = ipmi::getDbusObject( + bus, + ipmi::network::ETHERNET_INTERFACE, + ipmi::network::ROOT, + ethdevice); + + networkInterfacePath = std::move(networkInterfaceObject.first); + } + + // get the configured mode on the system. + auto enableDHCP = ipmi::getDbusProperty( + bus, + ipmi::network::SERVICE, + networkInterfacePath, + ipmi::network::ETHERNET_INTERFACE, + "DHCPEnabled").get<bool>(); + + // if ip address source is not given then get the ip source mode + // from the system so that it can be applied later. + if (channelConf->ipsrc == ipmi::network::IPOrigin::UNSPECIFIED) + { + channelConf->ipsrc = (enableDHCP) ? + ipmi::network::IPOrigin::DHCP : + ipmi::network::IPOrigin::STATIC; + } + + // check whether user has given all the data + // or the configured system interface is dhcp enabled, + // in both of the cases get the values from the cache. + if ((!channelConf->ipaddr.empty() && + !channelConf->netmask.empty() && + !channelConf->gateway.empty()) || + (enableDHCP)) // configured system interface mode = DHCP + { + //convert mask into prefix + ipaddress = channelConf->ipaddr; + prefix = ipmi::network::toPrefix(AF_INET, channelConf->netmask); + gateway = channelConf->gateway; + } + else // asked ip src = static and configured system src = static + // or partially given data. + { + // We have partial filled cache so get the remaining + // info from the system. + + // Get the network data from the system as user has + // not given all the data then use the data fetched from the + // system but it is implementation dependent,IPMI spec doesn't + // force it. + + // if system is not having any ip object don't throw error, + try + { + auto properties = ipmi::getAllDbusProperties( + bus, + ipObject.second, + ipObject.first, + ipmi::network::IP_INTERFACE); + + ipaddress = channelConf->ipaddr.empty() ? + properties["Address"].get<std::string>() : + channelConf->ipaddr; + + prefix = channelConf->netmask.empty() ? + properties["PrefixLength"].get<uint8_t>() : + ipmi::network::toPrefix(AF_INET, + channelConf->netmask); + } + catch (InternalFailure& e) + { + log<level::INFO>("Failed to get IP object which matches", + entry("INTERFACE=%s", ipmi::network::IP_INTERFACE), + entry("MATCH=%s", ethIp)); + } + + auto systemProperties = ipmi::getAllDbusProperties( + bus, + systemObject.second, + systemObject.first, + ipmi::network::SYSTEMCONFIG_INTERFACE); + + gateway = channelConf->gateway.empty() ? + systemProperties["DefaultGateway"].get<std::string>() : + channelConf->gateway; + } + } + + // Currently network manager doesn't support purging of all the + // ip addresses and the vlan interfaces from the parent interface, + // TODO once the support is there, will make the change here. + // https://github.com/openbmc/openbmc/issues/2141. + + // TODO Currently IPMI supports single interface,need to handle + // Multiple interface through + // https://github.com/openbmc/openbmc/issues/2138 + + // instead of deleting all the vlan interfaces and + // all the ipv4 address,we will call reset method. + //delete all the vlan interfaces + + ipmi::deleteAllDbusObjects(bus, + ipmi::network::ROOT, + ipmi::network::VLAN_INTERFACE); + + // set the interface mode to static + auto networkInterfaceObject = ipmi::getDbusObject( + bus, + ipmi::network::ETHERNET_INTERFACE, + ipmi::network::ROOT, + ethdevice); + + // setting the physical interface mode to static. + ipmi::setDbusProperty(bus, + ipmi::network::SERVICE, + networkInterfaceObject.first, + ipmi::network::ETHERNET_INTERFACE, + "DHCPEnabled", + false); + + networkInterfacePath = networkInterfaceObject.first; + + //delete all the ipv4 addresses + ipmi::deleteAllDbusObjects(bus, + ipmi::network::ROOT, + ipmi::network::IP_INTERFACE, + ethIp); + + if (vlanID) + { + ipmi::network::createVLAN(bus, + ipmi::network::SERVICE, + ipmi::network::ROOT, + ethdevice, + vlanID); + + auto networkInterfaceObject = ipmi::getDbusObject( + bus, + ipmi::network::VLAN_INTERFACE, + ipmi::network::ROOT); + + networkInterfacePath = networkInterfaceObject.first; + } + + if (channelConf->ipsrc == ipmi::network::IPOrigin::DHCP) + { + ipmi::setDbusProperty(bus, + ipmi::network::SERVICE, + networkInterfacePath, + ipmi::network::ETHERNET_INTERFACE, + "DHCPEnabled", + true); + } + else + { + //change the mode to static + ipmi::setDbusProperty(bus, + ipmi::network::SERVICE, + networkInterfacePath, + ipmi::network::ETHERNET_INTERFACE, + "DHCPEnabled", + false); + + if (!ipaddress.empty()) + { + ipmi::network::createIP(bus, + ipmi::network::SERVICE, + networkInterfacePath, + ipv4Protocol, + ipaddress, + prefix); + } + + if (!gateway.empty()) + { + ipmi::setDbusProperty(bus, + systemObject.second, + systemObject.first, + ipmi::network::SYSTEMCONFIG_INTERFACE, + "DefaultGateway", + std::string(gateway)); + } + } + + } + catch (InternalFailure& e) + { + log<level::ERR>("Failed to set network data", + entry("PREFIX=%d", prefix), + entry("ADDRESS=%s", ipaddress.c_str()), + entry("GATEWAY=%s", gateway.c_str()), + entry("VLANID=%d", vlanID), + entry("IPSRC=%d", channelConf->ipsrc)); + + commit<InternalFailure>(); + rc = IPMI_CC_UNSPECIFIED_ERROR; + } + + channelConf->clear(); + return rc; +} + void register_netfn_transport_functions() { // <Wildcard Command> |