diff options
author | Ravi Teja <raviteja28031990@gmail.com> | 2019-04-16 16:37:20 +0300 |
---|---|---|
committer | Ed Tanous <ed.tanous@intel.com> | 2019-06-06 20:16:30 +0300 |
commit | e48c0fc5fcdfebb8f70f4d176f2b1f8490cb94df (patch) | |
tree | abd4cee71b3f57eb27b72609c915288a7a3d6517 /redfish-core/lib/ethernet.hpp | |
parent | 0a86febd7fda49d692666ab4ddd9efccf352c4d8 (diff) | |
download | bmcweb-e48c0fc5fcdfebb8f70f4d176f2b1f8490cb94df.tar.xz |
Redfish(Network): Add support for IPv6Addresses and IPv6StaticAddresses
Added GET support for IPv6Addresses
Added GET and PATCH support for IPv6StaticAddresses
Tested by:
GET
PATCH -D patch.txt -d '{"IPv6StaticAddresses": [{"Address": "2002:905:150e:301:72e2:84ff:fe14:222","PrefixLength": 64}]}'
PATCH -D patch.txt -d '{"IPv6StaticAddresses": [{},{"Address": "2002:905:150e:301:72e2:84ff:fe14:333","PrefixLength": 64}]}'
PATCH -D patch.txt -d '{"IPv6StaticAddresses": [null,{},{"Address": "2002:905:150e:301:72e2:84ff:fe14:444","PrefixLength": 64}]}'
PATCH -D patch.txt -d '{"IPv6StaticAddresses": [{"Address": "2002:905:150e:301:72e2:84ff:fe14:555","PrefixLength": 64},{}]}'
PATCH -D patch.txt -d '{"IPv6StaticAddresses": [{},{"Address": "2002:905:150e:301:72e2:84ff:fe14:666"}]}'
PATCH -D patch.txt -d '{"IPv6StaticAddresses": [{},{"PrefixLength": 64}]}'
Tested with validator and no errors.
Change-Id: I7d1314a0c7843aae8425d66119f0d205a5cfac55
Signed-off-by: Ravi Teja <raviteja28031990@gmail.com>
Diffstat (limited to 'redfish-core/lib/ethernet.hpp')
-rw-r--r-- | redfish-core/lib/ethernet.hpp | 505 |
1 files changed, 445 insertions, 60 deletions
diff --git a/redfish-core/lib/ethernet.hpp b/redfish-core/lib/ethernet.hpp index 26d7ddcd0b..a47c637e45 100644 --- a/redfish-core/lib/ethernet.hpp +++ b/redfish-core/lib/ethernet.hpp @@ -71,6 +71,21 @@ struct IPv4AddressData }; /** + * Structure for keeping IPv6 data required by Redfish + */ +struct IPv6AddressData +{ + std::string id; + std::string address; + std::string origin; + uint8_t prefixLength; + + bool operator<(const IPv6AddressData &obj) const + { + return id < obj.id; + } +}; +/** * Structure for keeping basic single Ethernet Interface information * available from DBus */ @@ -290,6 +305,92 @@ inline bool extractEthernetInterfaceData(const std::string ðiface_id, return idFound; } +// Helper function that extracts data for single ethernet ipv6 address +inline void extractIPV6Data( + const std::string ðiface_id, const GetManagedObjects &dbus_data, + boost::container::flat_set<IPv6AddressData> &ipv6_config, + boost::container::flat_set<IPv6AddressData> &ipv6_static_config) +{ + const std::string ipv6PathStart = + "/xyz/openbmc_project/network/" + ethiface_id + "/ipv6/"; + + // Since there might be several IPv6 configurations aligned with + // single ethernet interface, loop over all of them + for (const auto &objpath : dbus_data) + { + // Check if proper pattern for object path appears + if (boost::starts_with(objpath.first.str, ipv6PathStart)) + { + for (auto &interface : objpath.second) + { + if (interface.first == "xyz.openbmc_project.Network.IP") + { + // Instance IPv6AddressData structure, and set as + // appropriate + std::pair< + boost::container::flat_set<IPv6AddressData>::iterator, + bool> + it = ipv6_config.insert( + {objpath.first.str.substr(ipv6PathStart.size())}); + IPv6AddressData &ipv6_address = *it.first; + for (auto &property : interface.second) + { + if (property.first == "Address") + { + const std::string *address = + std::get_if<std::string>(&property.second); + if (address != nullptr) + { + ipv6_address.address = *address; + } + } + else if (property.first == "Origin") + { + const std::string *origin = + std::get_if<std::string>(&property.second); + if (origin != nullptr) + { + ipv6_address.origin = + translateAddressOriginDbusToRedfish(*origin, + false); + } + } + else if (property.first == "PrefixLength") + { + const uint8_t *prefix = + std::get_if<uint8_t>(&property.second); + if (prefix != nullptr) + { + ipv6_address.prefixLength = *prefix; + } + } + else + { + BMCWEB_LOG_ERROR + << "Got extra property: " << property.first + << " on the " << objpath.first.str << " object"; + } + } + if (ipv6_address.origin == "Static") + { + std::pair<boost::container::flat_set< + IPv6AddressData>::iterator, + bool> + iter = ipv6_static_config.insert( + {objpath.first.str.substr( + ipv6PathStart.size())}); + IPv6AddressData &ipv6_static_address = *iter.first; + + ipv6_static_address.address = ipv6_address.address; + ipv6_static_address.prefixLength = + ipv6_address.prefixLength; + } + } + } + } + } +} + // Helper function that extracts data for single ethernet ipv4 address inline void extractIPData(const std::string ðiface_id, @@ -670,6 +771,69 @@ inline void createIPv4(const std::string &ifaceId, unsigned int ipIdx, "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask, gateway); } + +/** + * @brief Deletes given IPv6 + * + * @param[in] ifaceId Id of interface whose IP should be deleted + * @param[in] ipHash DBus Hash id of IP that should be deleted + * @param[in] ipIdx Index of IP in input array that should be deleted + * @param[io] asyncResp Response object that will be returned to client + * + * @return None + */ +inline void deleteIPv6(const std::string &ifaceId, const std::string &ipHash, + unsigned int ipIdx, + const std::shared_ptr<AsyncResp> asyncResp) +{ + crow::connections::systemBus->async_method_call( + [ipIdx, asyncResp](const boost::system::error_code ec) { + if (ec) + { + messages::internalError(asyncResp->res); + } + else + { + asyncResp->res.jsonValue["IPv6StaticAddresses"][ipIdx] = + nullptr; + } + }, + "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network/" + ifaceId + "/ipv6/" + ipHash, + "xyz.openbmc_project.Object.Delete", "Delete"); +} + +/** + * @brief Creates IPv6 with given data + * + * @param[in] ifaceId Id of interface whose IP should be added + * @param[in] ipIdx Index of IP in input array that should be added + * @param[in] prefixLength Prefix length that needs to be added + * @param[in] address IP address that needs to be added + * @param[io] asyncResp Response object that will be returned to client + * + * @return None + */ +inline void createIPv6(const std::string &ifaceId, unsigned int ipIdx, + uint8_t prefixLength, const std::string &address, + std::shared_ptr<AsyncResp> asyncResp) +{ + auto createIpHandler = [asyncResp](const boost::system::error_code ec) { + if (ec) + { + messages::internalError(asyncResp->res); + } + }; + // Passing null for gateway, as per redfish spec IPv6StaticAddresses object + // does not have assosiated gateway property + crow::connections::systemBus->async_method_call( + std::move(createIpHandler), "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network/" + ifaceId, + "xyz.openbmc_project.Network.IP.Create", "IP", + "xyz.openbmc_project.Network.IP.Protocol.IPv6", address, prefixLength, + ""); +} + using GetAllPropertiesType = boost::container::flat_map<std::string, sdbusplus::message::variant<bool>>; @@ -732,10 +896,12 @@ void getEthernetIfaceData(const std::string ðiface_id, const GetManagedObjects &resp) { EthernetInterfaceData ethData{}; boost::container::flat_set<IPv4AddressData> ipv4Data; + boost::container::flat_set<IPv6AddressData> ipv6Data; + boost::container::flat_set<IPv6AddressData> ipv6StaticData; if (error_code) { - callback(false, ethData, ipv4Data); + callback(false, ethData, ipv4Data, ipv6Data, ipv6StaticData); return; } @@ -743,12 +909,11 @@ void getEthernetIfaceData(const std::string ðiface_id, extractEthernetInterfaceData(ethiface_id, resp, ethData); if (!found) { - callback(false, ethData, ipv4Data); + callback(false, ethData, ipv4Data, ipv6Data, ipv6StaticData); return; } extractIPData(ethiface_id, resp, ipv4Data); - // Fix global GW for (IPv4AddressData &ipv4 : ipv4Data) { @@ -759,8 +924,9 @@ void getEthernetIfaceData(const std::string ðiface_id, } } + extractIPV6Data(ethiface_id, resp, ipv6Data, ipv6StaticData); // Finally make a callback with usefull data - callback(true, ethData, ipv4Data); + callback(true, ethData, ipv4Data, ipv6Data, ipv6StaticData); }, "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); @@ -1284,10 +1450,158 @@ class EthernetInterface : public Node std::variant<std::vector<std::string>>{updatedStaticNameServers}); } + void handleIPv6StaticAddressesPatch( + const std::string &ifaceId, nlohmann::json &input, + const boost::container::flat_set<IPv6AddressData> &ipv6StaticData, + const std::shared_ptr<AsyncResp> asyncResp) + { + if (!input.is_array()) + { + messages::propertyValueTypeError(asyncResp->res, input.dump(), + "IPv6StaticAddresses"); + return; + } + + int entryIdx = 0; + boost::container::flat_set<IPv6AddressData>::const_iterator thisData = + ipv6StaticData.begin(); + for (nlohmann::json &thisJson : input) + { + std::string pathString = + "IPv6StaticAddresses/" + std::to_string(entryIdx); + + if (thisJson.is_null()) + { + if (thisData != ipv6StaticData.end()) + { + deleteIPv6(ifaceId, thisData->id, entryIdx, asyncResp); + thisData++; + } + else + { + messages::propertyValueFormatError( + asyncResp->res, input.dump(), pathString); + return; + } + entryIdx++; + continue; + } + + if (thisJson.empty()) + { + if (thisData != ipv6StaticData.end()) + { + thisData++; + } + else + { + messages::propertyMissing(asyncResp->res, + pathString + "/Address"); + return; + } + entryIdx++; + continue; + } + + std::optional<std::string> address; + std::optional<uint8_t> prefixLength; + + if (!json_util::readJson(thisJson, asyncResp->res, "Address", + address, "PrefixLength", prefixLength)) + { + return; + } + + // if IP address exist then modify it. + if (thisData != ipv6StaticData.end()) + { + // Apply changes + if (address) + { + auto callback = [asyncResp, entryIdx, + address{std::string(*address)}]( + const boost::system::error_code ec) { + if (ec) + { + messages::internalError(asyncResp->res); + return; + } + asyncResp->res.jsonValue["IPv6StaticAddresses"] + [entryIdx]["Address"] = + std::move(address); + }; + + crow::connections::systemBus->async_method_call( + std::move(callback), "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network/" + ifaceId + "/ipv6/" + + thisData->id, + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.Network.IP", "Address", + std::variant<std::string>(*address)); + } + + if (prefixLength) + { + auto callback = [asyncResp, entryIdx, + prefixLength{uint8_t(*prefixLength)}]( + const boost::system::error_code ec) { + if (ec) + { + messages::internalError(asyncResp->res); + return; + } + asyncResp->res.jsonValue["IPv6StaticAddresses"] + [entryIdx]["PrefixLength"] = + std::move(prefixLength); + }; + + crow::connections::systemBus->async_method_call( + std::move(callback), "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network/" + ifaceId + "/ipv6/" + + thisData->id, + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.Network.IP", "PrefixLength", + std::variant<uint8_t>(*prefixLength)); + } + + thisData++; + } + else + { + // Create IPv6 with provided data + + if (!prefixLength) + { + messages::propertyMissing(asyncResp->res, + pathString + "/PrefixLength"); + continue; + } + + if (!address) + { + messages::propertyMissing(asyncResp->res, + pathString + "/Address"); + continue; + } + + createIPv6(ifaceId, entryIdx, *prefixLength, *address, + asyncResp); + + nlohmann::json &ipv6StaticAddressJson = + asyncResp->res.jsonValue["IPv6StaticAddresses"][entryIdx]; + ipv6StaticAddressJson["Address"] = *address; + ipv6StaticAddressJson["PrefixLength"] = *prefixLength; + } + entryIdx++; + } + } + void parseInterfaceData( nlohmann::json &json_response, const std::string &iface_id, const EthernetInterfaceData ðData, - const boost::container::flat_set<IPv4AddressData> &ipv4Data) + const boost::container::flat_set<IPv4AddressData> &ipv4Data, + const boost::container::flat_set<IPv6AddressData> &ipv6Data, + const boost::container::flat_set<IPv6AddressData> &ipv6StaticData) { json_response["Id"] = iface_id; json_response["@odata.id"] = @@ -1345,6 +1659,25 @@ class EthernetInterface : public Node } } json_response["IPv6DefaultGateway"] = ethData.ipv6_default_gateway; + + nlohmann::json &ipv6_array = json_response["IPv6Addresses"]; + ipv6_array = nlohmann::json::array(); + for (auto &ipv6_config : ipv6Data) + { + ipv6_array.push_back({{"Address", ipv6_config.address}, + {"PrefixLength", ipv6_config.prefixLength}, + {"AddressOrigin", ipv6_config.origin}}); + } + + nlohmann::json &ipv6_static_array = + json_response["IPv6StaticAddresses"]; + ipv6_static_array = nlohmann::json::array(); + for (auto &ipv6_static_config : ipv6StaticData) + { + ipv6_static_array.push_back( + {{"Address", ipv6_static_config.address}, + {"PrefixLength", ipv6_static_config.prefixLength}}); + } } /** @@ -1364,7 +1697,10 @@ class EthernetInterface : public Node params[0], [this, asyncResp, iface_id{std::string(params[0])}]( const bool &success, const EthernetInterfaceData ðData, - const boost::container::flat_set<IPv4AddressData> &ipv4Data) { + const boost::container::flat_set<IPv4AddressData> &ipv4Data, + const boost::container::flat_set<IPv6AddressData> &ipv6Data, + const boost::container::flat_set<IPv6AddressData> + &ipv6StaticData) { if (!success) { // TODO(Pawel)consider distinguish between non existing @@ -1388,7 +1724,7 @@ class EthernetInterface : public Node "Management Network Interface"; parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData, - ipv4Data); + ipv4Data, ipv6Data, ipv6StaticData); }); } @@ -1409,6 +1745,7 @@ class EthernetInterface : public Node std::optional<std::string> ipv6DefaultGateway; std::optional<nlohmann::json> ipv4Addresses; std::optional<nlohmann::json> ipv6Addresses; + std::optional<nlohmann::json> ipv6StaticAddresses; std::optional<std::vector<std::string>> staticNameServers; std::optional<std::vector<std::string>> nameServers; std::optional<nlohmann::json> dhcpv4; @@ -1417,8 +1754,8 @@ class EthernetInterface : public Node req, res, "HostName", hostname, "IPv4Addresses", ipv4Addresses, "IPv6Addresses", ipv6Addresses, "MACAddress", macAddress, "StaticNameServers", staticNameServers, "IPv6DefaultGateway", - ipv6DefaultGateway, "NameServers", nameServers, "DHCPv4", - dhcpv4)) + ipv6DefaultGateway, "IPv6StaticAddresses", ipv6StaticAddresses, + "NameServers", nameServers, "DHCPv4", dhcpv4)) { return; } @@ -1437,10 +1774,14 @@ class EthernetInterface : public Node ipv4Addresses = std::move(ipv4Addresses), ipv6Addresses = std::move(ipv6Addresses), ipv6DefaultGateway = std::move(ipv6DefaultGateway), + ipv6StaticAddresses = std::move(ipv6StaticAddresses), staticNameServers = std::move(staticNameServers), nameServers = std::move(nameServers)]( const bool &success, const EthernetInterfaceData ðData, - const boost::container::flat_set<IPv4AddressData> &ipv4Data) { + const boost::container::flat_set<IPv4AddressData> &ipv4Data, + const boost::container::flat_set<IPv6AddressData> &ipv6Data, + const boost::container::flat_set<IPv6AddressData> + &ipv6StaticData) { if (!success) { // ... otherwise return error @@ -1452,7 +1793,7 @@ class EthernetInterface : public Node } parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData, - ipv4Data); + ipv4Data, ipv6Data, ipv6StaticData); if (hostname) { @@ -1501,6 +1842,39 @@ class EthernetInterface : public Node messages::propertyNotWritable(asyncResp->res, "IPv6DefaultGateway"); } + + if (ipv6StaticAddresses) + { + nlohmann::json ipv6Static = std::move(*ipv6StaticAddresses); + handleIPv6StaticAddressesPatch(iface_id, ipv6Static, + ipv6StaticData, asyncResp); + + // call getEthernetIfaceData to populate updated static + // addresses data to "IPv6Addresses" json collection + getEthernetIfaceData( + iface_id, + [this, asyncResp, iface_id]( + const bool &success, + const EthernetInterfaceData ðData, + const boost::container::flat_set<IPv4AddressData> + &ipv4Data, + const boost::container::flat_set<IPv6AddressData> + &ipv6Data, + const boost::container::flat_set<IPv6AddressData> + &ipv6StaticData) { + if (!success) + { + messages::resourceNotFound(asyncResp->res, + "Ethernet Interface", + iface_id); + return; + } + + parseInterfaceData(asyncResp->res.jsonValue, + iface_id, ethData, ipv4Data, + ipv6Data, ipv6StaticData); + }); + } }); } }; @@ -1534,7 +1908,9 @@ class VlanNetworkInterface : public Node void parseInterfaceData( nlohmann::json &json_response, const std::string &parent_iface_id, const std::string &iface_id, const EthernetInterfaceData ðData, - const boost::container::flat_set<IPv4AddressData> &ipv4Data) + const boost::container::flat_set<IPv4AddressData> &ipv4Data, + const boost::container::flat_set<IPv6AddressData> &ipv6Data, + const boost::container::flat_set<IPv6AddressData> &ipv6StaticData) { // Fill out obvious data... json_response["Id"] = iface_id; @@ -1599,12 +1975,15 @@ class VlanNetworkInterface : public Node [this, asyncResp, parent_iface_id{std::string(params[0])}, iface_id{std::string(params[1])}]( const bool &success, const EthernetInterfaceData ðData, - const boost::container::flat_set<IPv4AddressData> &ipv4Data) { + const boost::container::flat_set<IPv4AddressData> &ipv4Data, + const boost::container::flat_set<IPv6AddressData> &ipv6Data, + const boost::container::flat_set<IPv6AddressData> + &ipv6StaticData) { if (success && ethData.vlan_id.size() != 0) { parseInterfaceData(asyncResp->res.jsonValue, parent_iface_id, iface_id, ethData, - ipv4Data); + ipv4Data, ipv6Data, ipv6StaticData); } else { @@ -1648,55 +2027,57 @@ class VlanNetworkInterface : public Node // Get single eth interface data, and call the below callback for JSON // preparation - getEthernetIfaceData(params[1], [this, asyncResp, - parentIfaceId{std::string(params[0])}, - ifaceId{std::string(params[1])}, - &vlanEnable, &vlanId]( - const bool &success, - const EthernetInterfaceData - ðData, - const boost::container::flat_set< - IPv4AddressData> &ipv4Data) { - if (success && !ethData.vlan_id.empty()) - { - parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId, - ifaceId, ethData, ipv4Data); - auto callback = - [asyncResp](const boost::system::error_code ec) { - if (ec) - { - messages::internalError(asyncResp->res); - } - }; - - if (vlanEnable == true) + getEthernetIfaceData( + params[1], + [this, asyncResp, parentIfaceId{std::string(params[0])}, + ifaceId{std::string(params[1])}, &vlanEnable, &vlanId]( + const bool &success, const EthernetInterfaceData ðData, + const boost::container::flat_set<IPv4AddressData> &ipv4Data, + const boost::container::flat_set<IPv6AddressData> &ipv6Data, + const boost::container::flat_set<IPv6AddressData> + &ipv6StaticData) { + if (success && !ethData.vlan_id.empty()) { - crow::connections::systemBus->async_method_call( - std::move(callback), "xyz.openbmc_project.Network", - "/xyz/openbmc_project/network/" + ifaceId, - "org.freedesktop.DBus.Properties", "Set", - "xyz.openbmc_project.Network.VLAN", "Id", - std::variant<uint32_t>(vlanId)); + parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId, + ifaceId, ethData, ipv4Data, ipv6Data, + ipv6StaticData); + auto callback = + [asyncResp](const boost::system::error_code ec) { + if (ec) + { + messages::internalError(asyncResp->res); + } + }; + + if (vlanEnable == true) + { + crow::connections::systemBus->async_method_call( + std::move(callback), "xyz.openbmc_project.Network", + "/xyz/openbmc_project/network/" + ifaceId, + "org.freedesktop.DBus.Properties", "Set", + "xyz.openbmc_project.Network.VLAN", "Id", + std::variant<uint32_t>(vlanId)); + } + else + { + BMCWEB_LOG_DEBUG << "vlanEnable is false. Deleting the " + "vlan interface"; + crow::connections::systemBus->async_method_call( + std::move(callback), "xyz.openbmc_project.Network", + std::string("/xyz/openbmc_project/network/") + + ifaceId, + "xyz.openbmc_project.Object.Delete", "Delete"); + } } else { - BMCWEB_LOG_DEBUG - << "vlanEnable is false. Deleting the vlan interface"; - crow::connections::systemBus->async_method_call( - std::move(callback), "xyz.openbmc_project.Network", - std::string("/xyz/openbmc_project/network/") + ifaceId, - "xyz.openbmc_project.Object.Delete", "Delete"); + // TODO(Pawel)consider distinguish between non existing + // object, and other errors + messages::resourceNotFound( + asyncResp->res, "VLAN Network Interface", ifaceId); + return; } - } - else - { - // TODO(Pawel)consider distinguish between non existing - // object, and other errors - messages::resourceNotFound(asyncResp->res, - "VLAN Network Interface", ifaceId); - return; - } - }); + }); } void doDelete(crow::Response &res, const crow::Request &req, @@ -1726,11 +2107,15 @@ class VlanNetworkInterface : public Node [this, asyncResp, parentIfaceId{std::string(params[0])}, ifaceId{std::string(params[1])}]( const bool &success, const EthernetInterfaceData ðData, - const boost::container::flat_set<IPv4AddressData> &ipv4Data) { + const boost::container::flat_set<IPv4AddressData> &ipv4Data, + const boost::container::flat_set<IPv6AddressData> &ipv6Data, + const boost::container::flat_set<IPv6AddressData> + &ipv6StaticData) { if (success && !ethData.vlan_id.empty()) { parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId, - ifaceId, ethData, ipv4Data); + ifaceId, ethData, ipv4Data, ipv6Data, + ipv6StaticData); auto callback = [asyncResp](const boost::system::error_code ec) { |