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>
diff --git a/redfish-core/lib/ethernet.hpp b/redfish-core/lib/ethernet.hpp
index 26d7ddc..a47c637 100644
--- a/redfish-core/lib/ethernet.hpp
+++ b/redfish-core/lib/ethernet.hpp
@@ -71,6 +71,21 @@
};
/**
+ * 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 @@
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 @@
"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 @@
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 @@
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 @@
}
}
+ 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 @@
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 @@
}
}
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 @@
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 @@
"Management Network Interface";
parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
- ipv4Data);
+ ipv4Data, ipv6Data, ipv6StaticData);
});
}
@@ -1409,6 +1745,7 @@
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 @@
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 @@
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 @@
}
parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
- ipv4Data);
+ ipv4Data, ipv6Data, ipv6StaticData);
if (hostname)
{
@@ -1501,6 +1842,39 @@
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 @@
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 @@
[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 @@
// 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 @@
[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) {