xref: /openbmc/bmcweb/features/redfish/lib/ethernet.hpp (revision 179db1d7c76af721cb9390cde1c2befff9e9c685)
19391bb9cSRapkiewicz, Pawel /*
29391bb9cSRapkiewicz, Pawel // Copyright (c) 2018 Intel Corporation
39391bb9cSRapkiewicz, Pawel //
49391bb9cSRapkiewicz, Pawel // Licensed under the Apache License, Version 2.0 (the "License");
59391bb9cSRapkiewicz, Pawel // you may not use this file except in compliance with the License.
69391bb9cSRapkiewicz, Pawel // You may obtain a copy of the License at
79391bb9cSRapkiewicz, Pawel //
89391bb9cSRapkiewicz, Pawel //      http://www.apache.org/licenses/LICENSE-2.0
99391bb9cSRapkiewicz, Pawel //
109391bb9cSRapkiewicz, Pawel // Unless required by applicable law or agreed to in writing, software
119391bb9cSRapkiewicz, Pawel // distributed under the License is distributed on an "AS IS" BASIS,
129391bb9cSRapkiewicz, Pawel // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139391bb9cSRapkiewicz, Pawel // See the License for the specific language governing permissions and
149391bb9cSRapkiewicz, Pawel // limitations under the License.
159391bb9cSRapkiewicz, Pawel */
169391bb9cSRapkiewicz, Pawel #pragma once
179391bb9cSRapkiewicz, Pawel 
18*179db1d7SKowalski, Kamil #include <dbus_singleton.hpp>
19588c3f0dSKowalski, Kamil #include <error_messages.hpp>
20*179db1d7SKowalski, Kamil #include <node.hpp>
21588c3f0dSKowalski, Kamil #include <utils/json_utils.hpp>
229391bb9cSRapkiewicz, Pawel #include <boost/container/flat_map.hpp>
239391bb9cSRapkiewicz, Pawel 
249391bb9cSRapkiewicz, Pawel namespace redfish {
259391bb9cSRapkiewicz, Pawel 
269391bb9cSRapkiewicz, Pawel /**
279391bb9cSRapkiewicz, Pawel  * DBus types primitives for several generic DBus interfaces
289391bb9cSRapkiewicz, Pawel  * TODO(Pawel) consider move this to separate file into boost::dbus
299391bb9cSRapkiewicz, Pawel  */
30aa2e59c1SEd Tanous using PropertiesMapType = boost::container::flat_map<
31aa2e59c1SEd Tanous     std::string,
32aa2e59c1SEd Tanous     sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
33aa2e59c1SEd Tanous                                 int32_t, uint32_t, int64_t, uint64_t, double>>;
349391bb9cSRapkiewicz, Pawel 
359391bb9cSRapkiewicz, Pawel using GetManagedObjectsType = boost::container::flat_map<
36aa2e59c1SEd Tanous     sdbusplus::message::object_path,
37aa2e59c1SEd Tanous     boost::container::flat_map<
38aa2e59c1SEd Tanous         std::string,
39aa2e59c1SEd Tanous         boost::container::flat_map<
40aa2e59c1SEd Tanous             std::string, sdbusplus::message::variant<
41aa2e59c1SEd Tanous                              std::string, bool, uint8_t, int16_t, uint16_t,
42aa2e59c1SEd Tanous                              int32_t, uint32_t, int64_t, uint64_t, double>>>>;
439391bb9cSRapkiewicz, Pawel 
449391bb9cSRapkiewicz, Pawel /**
459391bb9cSRapkiewicz, Pawel  * Structure for keeping IPv4 data required by Redfish
469391bb9cSRapkiewicz, Pawel  * TODO(Pawel) consider change everything to ptr, or to non-ptr values.
479391bb9cSRapkiewicz, Pawel  */
489391bb9cSRapkiewicz, Pawel struct IPv4AddressData {
49*179db1d7SKowalski, Kamil   std::string id;
509391bb9cSRapkiewicz, Pawel   const std::string *address;
519391bb9cSRapkiewicz, Pawel   const std::string *domain;
529391bb9cSRapkiewicz, Pawel   const std::string *gateway;
539391bb9cSRapkiewicz, Pawel   std::string netmask;
549391bb9cSRapkiewicz, Pawel   std::string origin;
559391bb9cSRapkiewicz, Pawel   bool global;
56*179db1d7SKowalski, Kamil   /**
57*179db1d7SKowalski, Kamil    * @brief Operator< to enable sorting
58*179db1d7SKowalski, Kamil    *
59*179db1d7SKowalski, Kamil    * @param[in] obj   Object to compare with
60*179db1d7SKowalski, Kamil    *
61*179db1d7SKowalski, Kamil    * @return This object id < supplied object id
62*179db1d7SKowalski, Kamil    */
63*179db1d7SKowalski, Kamil   bool operator<(const IPv4AddressData &obj) const { return (id < obj.id); }
649391bb9cSRapkiewicz, Pawel };
659391bb9cSRapkiewicz, Pawel 
669391bb9cSRapkiewicz, Pawel /**
679391bb9cSRapkiewicz, Pawel  * Structure for keeping basic single Ethernet Interface information
689391bb9cSRapkiewicz, Pawel  * available from DBus
699391bb9cSRapkiewicz, Pawel  */
709391bb9cSRapkiewicz, Pawel struct EthernetInterfaceData {
719391bb9cSRapkiewicz, Pawel   const unsigned int *speed;
729391bb9cSRapkiewicz, Pawel   const bool *auto_neg;
739391bb9cSRapkiewicz, Pawel   const std::string *hostname;
749391bb9cSRapkiewicz, Pawel   const std::string *default_gateway;
759391bb9cSRapkiewicz, Pawel   const std::string *mac_address;
76c7070ac2SKowalski, Kamil   const unsigned int *vlan_id;
779391bb9cSRapkiewicz, Pawel };
789391bb9cSRapkiewicz, Pawel 
799391bb9cSRapkiewicz, Pawel /**
809391bb9cSRapkiewicz, Pawel  * OnDemandEthernetProvider
81274fad5aSGunnar Mills  * Ethernet provider class that retrieves data directly from dbus, before
82274fad5aSGunnar Mills  * setting it into JSON output. This does not cache any data.
839391bb9cSRapkiewicz, Pawel  *
849391bb9cSRapkiewicz, Pawel  * TODO(Pawel)
859391bb9cSRapkiewicz, Pawel  * This perhaps shall be different file, which has to be chosen on compile time
869391bb9cSRapkiewicz, Pawel  * depending on OEM needs
879391bb9cSRapkiewicz, Pawel  */
889391bb9cSRapkiewicz, Pawel class OnDemandEthernetProvider {
899391bb9cSRapkiewicz, Pawel  private:
909391bb9cSRapkiewicz, Pawel   // Consts that may have influence on EthernetProvider performance/memory usage
919391bb9cSRapkiewicz, Pawel   const size_t MAX_IPV4_ADDRESSES_PER_INTERFACE = 10;
929391bb9cSRapkiewicz, Pawel 
939391bb9cSRapkiewicz, Pawel   // Helper function that allows to extract GetAllPropertiesType from
949391bb9cSRapkiewicz, Pawel   // GetManagedObjectsType, based on object path, and interface name
959391bb9cSRapkiewicz, Pawel   const PropertiesMapType *extractInterfaceProperties(
96aa2e59c1SEd Tanous       const sdbusplus::message::object_path &objpath,
97aa2e59c1SEd Tanous       const std::string &interface, const GetManagedObjectsType &dbus_data) {
989391bb9cSRapkiewicz, Pawel     const auto &dbus_obj = dbus_data.find(objpath);
999391bb9cSRapkiewicz, Pawel     if (dbus_obj != dbus_data.end()) {
1009391bb9cSRapkiewicz, Pawel       const auto &iface = dbus_obj->second.find(interface);
1019391bb9cSRapkiewicz, Pawel       if (iface != dbus_obj->second.end()) {
1029391bb9cSRapkiewicz, Pawel         return &iface->second;
1039391bb9cSRapkiewicz, Pawel       }
1049391bb9cSRapkiewicz, Pawel     }
1059391bb9cSRapkiewicz, Pawel     return nullptr;
1069391bb9cSRapkiewicz, Pawel   }
1079391bb9cSRapkiewicz, Pawel 
1089391bb9cSRapkiewicz, Pawel   // Helper Wrapper that does inline object_path conversion from string
109aa2e59c1SEd Tanous   // into sdbusplus::message::object_path type
1109391bb9cSRapkiewicz, Pawel   inline const PropertiesMapType *extractInterfaceProperties(
1119391bb9cSRapkiewicz, Pawel       const std::string &objpath, const std::string &interface,
1129391bb9cSRapkiewicz, Pawel       const GetManagedObjectsType &dbus_data) {
113aa2e59c1SEd Tanous     const auto &dbus_obj = sdbusplus::message::object_path{objpath};
1149391bb9cSRapkiewicz, Pawel     return extractInterfaceProperties(dbus_obj, interface, dbus_data);
1159391bb9cSRapkiewicz, Pawel   }
1169391bb9cSRapkiewicz, Pawel 
1179391bb9cSRapkiewicz, Pawel   // Helper function that allows to get pointer to the property from
1189391bb9cSRapkiewicz, Pawel   // GetAllPropertiesType native, or extracted by GetAllPropertiesType
1199391bb9cSRapkiewicz, Pawel   template <typename T>
120aa2e59c1SEd Tanous   inline T const *const extractProperty(const PropertiesMapType &properties,
1219391bb9cSRapkiewicz, Pawel                                         const std::string &name) {
1229391bb9cSRapkiewicz, Pawel     const auto &property = properties.find(name);
1239391bb9cSRapkiewicz, Pawel     if (property != properties.end()) {
124aa2e59c1SEd Tanous       return mapbox::get_ptr<const T>(property->second);
1259391bb9cSRapkiewicz, Pawel     }
1269391bb9cSRapkiewicz, Pawel     return nullptr;
1279391bb9cSRapkiewicz, Pawel   }
1289391bb9cSRapkiewicz, Pawel   // TODO(Pawel) Consider to move the above functions to dbus
1299391bb9cSRapkiewicz, Pawel   // generic_interfaces.hpp
1309391bb9cSRapkiewicz, Pawel 
1319391bb9cSRapkiewicz, Pawel   // Helper function that extracts data from several dbus objects and several
1329391bb9cSRapkiewicz, Pawel   // interfaces required by single ethernet interface instance
1339391bb9cSRapkiewicz, Pawel   void extractEthernetInterfaceData(const std::string &ethiface_id,
1349391bb9cSRapkiewicz, Pawel                                     const GetManagedObjectsType &dbus_data,
1359391bb9cSRapkiewicz, Pawel                                     EthernetInterfaceData &eth_data) {
1369391bb9cSRapkiewicz, Pawel     // Extract data that contains MAC Address
1379391bb9cSRapkiewicz, Pawel     const PropertiesMapType *mac_properties = extractInterfaceProperties(
1389391bb9cSRapkiewicz, Pawel         "/xyz/openbmc_project/network/" + ethiface_id,
1399391bb9cSRapkiewicz, Pawel         "xyz.openbmc_project.Network.MACAddress", dbus_data);
1409391bb9cSRapkiewicz, Pawel 
1419391bb9cSRapkiewicz, Pawel     if (mac_properties != nullptr) {
1429391bb9cSRapkiewicz, Pawel       eth_data.mac_address =
1439391bb9cSRapkiewicz, Pawel           extractProperty<std::string>(*mac_properties, "MACAddress");
1449391bb9cSRapkiewicz, Pawel     }
1459391bb9cSRapkiewicz, Pawel 
146c7070ac2SKowalski, Kamil     const PropertiesMapType *vlan_properties = extractInterfaceProperties(
147c7070ac2SKowalski, Kamil         "/xyz/openbmc_project/network/" + ethiface_id,
148c7070ac2SKowalski, Kamil         "xyz.openbmc_project.Network.VLAN", dbus_data);
149c7070ac2SKowalski, Kamil 
150c7070ac2SKowalski, Kamil     if (vlan_properties != nullptr) {
151c7070ac2SKowalski, Kamil       eth_data.vlan_id = extractProperty<unsigned int>(*vlan_properties, "Id");
152c7070ac2SKowalski, Kamil     }
153c7070ac2SKowalski, Kamil 
1549391bb9cSRapkiewicz, Pawel     // Extract data that contains link information (auto negotiation and speed)
1559391bb9cSRapkiewicz, Pawel     const PropertiesMapType *eth_properties = extractInterfaceProperties(
1569391bb9cSRapkiewicz, Pawel         "/xyz/openbmc_project/network/" + ethiface_id,
1579391bb9cSRapkiewicz, Pawel         "xyz.openbmc_project.Network.EthernetInterface", dbus_data);
1589391bb9cSRapkiewicz, Pawel 
1599391bb9cSRapkiewicz, Pawel     if (eth_properties != nullptr) {
1609391bb9cSRapkiewicz, Pawel       eth_data.auto_neg = extractProperty<bool>(*eth_properties, "AutoNeg");
1619391bb9cSRapkiewicz, Pawel       eth_data.speed = extractProperty<unsigned int>(*eth_properties, "Speed");
1629391bb9cSRapkiewicz, Pawel     }
1639391bb9cSRapkiewicz, Pawel 
1649391bb9cSRapkiewicz, Pawel     // Extract data that contains network config (HostName and DefaultGW)
1659391bb9cSRapkiewicz, Pawel     const PropertiesMapType *config_properties = extractInterfaceProperties(
1669391bb9cSRapkiewicz, Pawel         "/xyz/openbmc_project/network/config",
1679391bb9cSRapkiewicz, Pawel         "xyz.openbmc_project.Network.SystemConfiguration", dbus_data);
1689391bb9cSRapkiewicz, Pawel 
1699391bb9cSRapkiewicz, Pawel     if (config_properties != nullptr) {
1709391bb9cSRapkiewicz, Pawel       eth_data.hostname =
1719391bb9cSRapkiewicz, Pawel           extractProperty<std::string>(*config_properties, "HostName");
1729391bb9cSRapkiewicz, Pawel       eth_data.default_gateway =
1739391bb9cSRapkiewicz, Pawel           extractProperty<std::string>(*config_properties, "DefaultGateway");
1749391bb9cSRapkiewicz, Pawel     }
1759391bb9cSRapkiewicz, Pawel   }
1769391bb9cSRapkiewicz, Pawel 
1779391bb9cSRapkiewicz, Pawel   // Helper function that changes bits netmask notation (i.e. /24)
1789391bb9cSRapkiewicz, Pawel   // into full dot notation
1799391bb9cSRapkiewicz, Pawel   inline std::string getNetmask(unsigned int bits) {
1809391bb9cSRapkiewicz, Pawel     uint32_t value = 0xffffffff << (32 - bits);
1819391bb9cSRapkiewicz, Pawel     std::string netmask = std::to_string((value >> 24) & 0xff) + "." +
1829391bb9cSRapkiewicz, Pawel                           std::to_string((value >> 16) & 0xff) + "." +
1839391bb9cSRapkiewicz, Pawel                           std::to_string((value >> 8) & 0xff) + "." +
1849391bb9cSRapkiewicz, Pawel                           std::to_string(value & 0xff);
1859391bb9cSRapkiewicz, Pawel     return netmask;
1869391bb9cSRapkiewicz, Pawel   }
1879391bb9cSRapkiewicz, Pawel 
1889391bb9cSRapkiewicz, Pawel   // Helper function that extracts data for single ethernet ipv4 address
1899391bb9cSRapkiewicz, Pawel   void extractIPv4Data(const std::string &ethiface_id,
1909391bb9cSRapkiewicz, Pawel                        const GetManagedObjectsType &dbus_data,
1919391bb9cSRapkiewicz, Pawel                        std::vector<IPv4AddressData> &ipv4_config) {
192*179db1d7SKowalski, Kamil     const std::string pathStart =
193*179db1d7SKowalski, Kamil         "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/";
194*179db1d7SKowalski, Kamil 
1959391bb9cSRapkiewicz, Pawel     // Since there might be several IPv4 configurations aligned with
1969391bb9cSRapkiewicz, Pawel     // single ethernet interface, loop over all of them
1979391bb9cSRapkiewicz, Pawel     for (auto &objpath : dbus_data) {
198274fad5aSGunnar Mills       // Check if proper patter for object path appears
1999391bb9cSRapkiewicz, Pawel       if (boost::starts_with(
200daf36e2eSEd Tanous               static_cast<const std::string &>(objpath.first),
2019391bb9cSRapkiewicz, Pawel               "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/")) {
2029391bb9cSRapkiewicz, Pawel         // and get approrpiate interface
2039391bb9cSRapkiewicz, Pawel         const auto &interface =
2049391bb9cSRapkiewicz, Pawel             objpath.second.find("xyz.openbmc_project.Network.IP");
2059391bb9cSRapkiewicz, Pawel         if (interface != objpath.second.end()) {
2069391bb9cSRapkiewicz, Pawel           // Make a properties 'shortcut', to make everything more readable
2079391bb9cSRapkiewicz, Pawel           const PropertiesMapType &properties = interface->second;
2089391bb9cSRapkiewicz, Pawel           // Instance IPv4AddressData structure, and set as appropriate
2099391bb9cSRapkiewicz, Pawel           IPv4AddressData ipv4_address;
210*179db1d7SKowalski, Kamil 
211*179db1d7SKowalski, Kamil           ipv4_address.id = static_cast<const std::string &>(objpath.first)
212*179db1d7SKowalski, Kamil                                 .substr(pathStart.size());
213*179db1d7SKowalski, Kamil 
2149391bb9cSRapkiewicz, Pawel           // IPv4 address
2159391bb9cSRapkiewicz, Pawel           ipv4_address.address =
2169391bb9cSRapkiewicz, Pawel               extractProperty<std::string>(properties, "Address");
2179391bb9cSRapkiewicz, Pawel           // IPv4 gateway
2189391bb9cSRapkiewicz, Pawel           ipv4_address.gateway =
2199391bb9cSRapkiewicz, Pawel               extractProperty<std::string>(properties, "Gateway");
2209391bb9cSRapkiewicz, Pawel 
2219391bb9cSRapkiewicz, Pawel           // Origin is kind of DBus object so fetch pointer...
2229391bb9cSRapkiewicz, Pawel           const std::string *origin =
2239391bb9cSRapkiewicz, Pawel               extractProperty<std::string>(properties, "Origin");
2249391bb9cSRapkiewicz, Pawel           if (origin != nullptr) {
225*179db1d7SKowalski, Kamil             ipv4_address.origin =
226*179db1d7SKowalski, Kamil                 translateAddressOriginBetweenDBusAndRedfish(origin, true, true);
2279391bb9cSRapkiewicz, Pawel           }
2289391bb9cSRapkiewicz, Pawel 
2299391bb9cSRapkiewicz, Pawel           // Netmask is presented as PrefixLength
2309391bb9cSRapkiewicz, Pawel           const auto *mask =
2319391bb9cSRapkiewicz, Pawel               extractProperty<uint8_t>(properties, "PrefixLength");
2329391bb9cSRapkiewicz, Pawel           if (mask != nullptr) {
2339391bb9cSRapkiewicz, Pawel             // convert it to the string
2349391bb9cSRapkiewicz, Pawel             ipv4_address.netmask = getNetmask(*mask);
2359391bb9cSRapkiewicz, Pawel           }
2369391bb9cSRapkiewicz, Pawel 
2379391bb9cSRapkiewicz, Pawel           // Attach IPv4 only if address is present
2389391bb9cSRapkiewicz, Pawel           if (ipv4_address.address != nullptr) {
239274fad5aSGunnar Mills             // Check if given address is local, or global
2409391bb9cSRapkiewicz, Pawel             if (boost::starts_with(*ipv4_address.address, "169.254")) {
2419391bb9cSRapkiewicz, Pawel               ipv4_address.global = false;
2429391bb9cSRapkiewicz, Pawel             } else {
2439391bb9cSRapkiewicz, Pawel               ipv4_address.global = true;
2449391bb9cSRapkiewicz, Pawel             }
2459391bb9cSRapkiewicz, Pawel             ipv4_config.emplace_back(std::move(ipv4_address));
2469391bb9cSRapkiewicz, Pawel           }
2479391bb9cSRapkiewicz, Pawel         }
2489391bb9cSRapkiewicz, Pawel       }
2499391bb9cSRapkiewicz, Pawel     }
250*179db1d7SKowalski, Kamil 
251*179db1d7SKowalski, Kamil     /**
252*179db1d7SKowalski, Kamil      * We have to sort this vector and ensure that order of IPv4 addresses
253*179db1d7SKowalski, Kamil      * is consistent between GETs to allow modification and deletion in PATCHes
254*179db1d7SKowalski, Kamil      */
255*179db1d7SKowalski, Kamil     std::sort(ipv4_config.begin(), ipv4_config.end());
2569391bb9cSRapkiewicz, Pawel   }
2579391bb9cSRapkiewicz, Pawel 
258*179db1d7SKowalski, Kamil   static const constexpr int ipV4AddressSectionsCount = 4;
259*179db1d7SKowalski, Kamil 
2609391bb9cSRapkiewicz, Pawel  public:
2619391bb9cSRapkiewicz, Pawel   /**
262588c3f0dSKowalski, Kamil    * @brief Creates VLAN for given interface with given Id through D-Bus
263588c3f0dSKowalski, Kamil    *
264588c3f0dSKowalski, Kamil    * @param[in] ifaceId       Id of interface for which VLAN will be created
265588c3f0dSKowalski, Kamil    * @param[in] inputVlanId   ID of the new VLAN
266588c3f0dSKowalski, Kamil    * @param[in] callback      Function that will be called after the operation
267588c3f0dSKowalski, Kamil    *
268588c3f0dSKowalski, Kamil    * @return None.
269588c3f0dSKowalski, Kamil    */
270588c3f0dSKowalski, Kamil   template <typename CallbackFunc>
271588c3f0dSKowalski, Kamil   void createVlan(const std::string &ifaceId, const uint64_t &inputVlanId,
272588c3f0dSKowalski, Kamil                   CallbackFunc &&callback) {
273588c3f0dSKowalski, Kamil     crow::connections::system_bus->async_method_call(
274588c3f0dSKowalski, Kamil         callback, "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
275588c3f0dSKowalski, Kamil         "xyz.openbmc_project.Network.VLAN.Create", "VLAN", ifaceId,
276588c3f0dSKowalski, Kamil         static_cast<uint32_t>(inputVlanId));
277588c3f0dSKowalski, Kamil   };
278588c3f0dSKowalski, Kamil 
279588c3f0dSKowalski, Kamil   /**
280588c3f0dSKowalski, Kamil    * @brief Sets given Id on the given VLAN interface through D-Bus
281588c3f0dSKowalski, Kamil    *
282588c3f0dSKowalski, Kamil    * @param[in] ifaceId       Id of VLAN interface that should be modified
283588c3f0dSKowalski, Kamil    * @param[in] inputVlanId   New ID of the VLAN
284588c3f0dSKowalski, Kamil    * @param[in] callback      Function that will be called after the operation
285588c3f0dSKowalski, Kamil    *
286588c3f0dSKowalski, Kamil    * @return None.
287588c3f0dSKowalski, Kamil    */
288588c3f0dSKowalski, Kamil   template <typename CallbackFunc>
289588c3f0dSKowalski, Kamil   void changeVlanId(const std::string &ifaceId, const uint32_t &inputVlanId,
290588c3f0dSKowalski, Kamil                     CallbackFunc &&callback) {
291588c3f0dSKowalski, Kamil     crow::connections::system_bus->async_method_call(
292588c3f0dSKowalski, Kamil         callback, "xyz.openbmc_project.Network",
293588c3f0dSKowalski, Kamil         std::string("/xyz/openbmc_project/network/") + ifaceId,
294588c3f0dSKowalski, Kamil         "org.freedesktop.DBus.Properties", "Set",
295588c3f0dSKowalski, Kamil         "xyz.openbmc_project.Network.VLAN", "Id",
296588c3f0dSKowalski, Kamil         sdbusplus::message::variant<uint32_t>(inputVlanId));
297588c3f0dSKowalski, Kamil   };
298588c3f0dSKowalski, Kamil 
299588c3f0dSKowalski, Kamil   /**
300*179db1d7SKowalski, Kamil    * @brief Helper function that verifies IP address to check if it is in
301*179db1d7SKowalski, Kamil    *        proper format. If bits pointer is provided, also calculates active
302*179db1d7SKowalski, Kamil    *        bit count for Subnet Mask.
303*179db1d7SKowalski, Kamil    *
304*179db1d7SKowalski, Kamil    * @param[in]  ip     IP that will be verified
305*179db1d7SKowalski, Kamil    * @param[out] bits   Calculated mask in bits notation
306*179db1d7SKowalski, Kamil    *
307*179db1d7SKowalski, Kamil    * @return true in case of success, false otherwise
308*179db1d7SKowalski, Kamil    */
309*179db1d7SKowalski, Kamil   bool ipv4VerifyIpAndGetBitcount(const std::string &ip,
310*179db1d7SKowalski, Kamil                                   uint8_t *bits = nullptr) {
311*179db1d7SKowalski, Kamil     std::vector<std::string> bytesInMask;
312*179db1d7SKowalski, Kamil 
313*179db1d7SKowalski, Kamil     boost::split(bytesInMask, ip, boost::is_any_of("."));
314*179db1d7SKowalski, Kamil 
315*179db1d7SKowalski, Kamil     if (bytesInMask.size() != ipV4AddressSectionsCount) {
316*179db1d7SKowalski, Kamil       return false;
317*179db1d7SKowalski, Kamil     }
318*179db1d7SKowalski, Kamil 
319*179db1d7SKowalski, Kamil     if (bits != nullptr) {
320*179db1d7SKowalski, Kamil       *bits = 0;
321*179db1d7SKowalski, Kamil     }
322*179db1d7SKowalski, Kamil 
323*179db1d7SKowalski, Kamil     char *endPtr;
324*179db1d7SKowalski, Kamil     long previousValue = 255;
325*179db1d7SKowalski, Kamil     bool firstZeroInByteHit;
326*179db1d7SKowalski, Kamil     for (uint8_t byteIdx = 0; byteIdx < ipV4AddressSectionsCount; byteIdx++) {
327*179db1d7SKowalski, Kamil       // Use strtol instead of stroi to avoid exceptions
328*179db1d7SKowalski, Kamil       long value = std::strtol(bytesInMask[byteIdx].c_str(), &endPtr, 10);
329*179db1d7SKowalski, Kamil 
330*179db1d7SKowalski, Kamil       // endPtr should point to the end of the string, otherwise given string
331*179db1d7SKowalski, Kamil       // is not 100% number
332*179db1d7SKowalski, Kamil       if (*endPtr != '\0') {
333*179db1d7SKowalski, Kamil         return false;
334*179db1d7SKowalski, Kamil       }
335*179db1d7SKowalski, Kamil 
336*179db1d7SKowalski, Kamil       // Value should be contained in byte
337*179db1d7SKowalski, Kamil       if (value < 0 || value > 255) {
338*179db1d7SKowalski, Kamil         return false;
339*179db1d7SKowalski, Kamil       }
340*179db1d7SKowalski, Kamil 
341*179db1d7SKowalski, Kamil       if (bits != nullptr) {
342*179db1d7SKowalski, Kamil         // Mask has to be continuous between bytes
343*179db1d7SKowalski, Kamil         if (previousValue != 255 && value != 0) {
344*179db1d7SKowalski, Kamil           return false;
345*179db1d7SKowalski, Kamil         }
346*179db1d7SKowalski, Kamil 
347*179db1d7SKowalski, Kamil         // Mask has to be continuous inside bytes
348*179db1d7SKowalski, Kamil         firstZeroInByteHit = false;
349*179db1d7SKowalski, Kamil 
350*179db1d7SKowalski, Kamil         // Count bits
351*179db1d7SKowalski, Kamil         for (int bitIdx = 7; bitIdx >= 0; bitIdx--) {
352*179db1d7SKowalski, Kamil           if (value & (1 << bitIdx)) {
353*179db1d7SKowalski, Kamil             if (firstZeroInByteHit) {
354*179db1d7SKowalski, Kamil               // Continuity not preserved
355*179db1d7SKowalski, Kamil               return false;
356*179db1d7SKowalski, Kamil             } else {
357*179db1d7SKowalski, Kamil               (*bits)++;
358*179db1d7SKowalski, Kamil             }
359*179db1d7SKowalski, Kamil           } else {
360*179db1d7SKowalski, Kamil             firstZeroInByteHit = true;
361*179db1d7SKowalski, Kamil           }
362*179db1d7SKowalski, Kamil         }
363*179db1d7SKowalski, Kamil       }
364*179db1d7SKowalski, Kamil 
365*179db1d7SKowalski, Kamil       previousValue = value;
366*179db1d7SKowalski, Kamil     }
367*179db1d7SKowalski, Kamil 
368*179db1d7SKowalski, Kamil     return true;
369*179db1d7SKowalski, Kamil   }
370*179db1d7SKowalski, Kamil 
371*179db1d7SKowalski, Kamil   /**
372*179db1d7SKowalski, Kamil    * @brief Changes IPv4 address type property (Address, Gateway)
373*179db1d7SKowalski, Kamil    *
374*179db1d7SKowalski, Kamil    * @param[in] ifaceId     Id of interface whose IP should be modified
375*179db1d7SKowalski, Kamil    * @param[in] ipIdx       Index of IP in input array that should be modified
376*179db1d7SKowalski, Kamil    * @param[in] ipHash      DBus Hash id of modified IP
377*179db1d7SKowalski, Kamil    * @param[in] name        Name of field in JSON representation
378*179db1d7SKowalski, Kamil    * @param[in] newValue    New value that should be written
379*179db1d7SKowalski, Kamil    * @param[io] asyncResp   Response object that will be returned to client
380*179db1d7SKowalski, Kamil    *
381*179db1d7SKowalski, Kamil    * @return true if give IP is valid and has been sent do D-Bus, false
382*179db1d7SKowalski, Kamil    * otherwise
383*179db1d7SKowalski, Kamil    */
384*179db1d7SKowalski, Kamil   void changeIPv4AddressProperty(const std::string &ifaceId, int ipIdx,
385*179db1d7SKowalski, Kamil                                  const std::string &ipHash,
386*179db1d7SKowalski, Kamil                                  const std::string &name,
387*179db1d7SKowalski, Kamil                                  const std::string &newValue,
388*179db1d7SKowalski, Kamil                                  const std::shared_ptr<AsyncResp> &asyncResp) {
389*179db1d7SKowalski, Kamil     auto callback = [
390*179db1d7SKowalski, Kamil       asyncResp, ipIdx{std::move(ipIdx)}, name{std::move(name)},
391*179db1d7SKowalski, Kamil       newValue{std::move(newValue)}
392*179db1d7SKowalski, Kamil     ](const boost::system::error_code ec) {
393*179db1d7SKowalski, Kamil       if (ec) {
394*179db1d7SKowalski, Kamil         messages::addMessageToJson(
395*179db1d7SKowalski, Kamil             asyncResp->res.json_value, messages::internalError(),
396*179db1d7SKowalski, Kamil             "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name);
397*179db1d7SKowalski, Kamil       } else {
398*179db1d7SKowalski, Kamil         asyncResp->res.json_value["IPv4Addresses"][ipIdx][name] = newValue;
399*179db1d7SKowalski, Kamil       }
400*179db1d7SKowalski, Kamil     };
401*179db1d7SKowalski, Kamil 
402*179db1d7SKowalski, Kamil     crow::connections::system_bus->async_method_call(
403*179db1d7SKowalski, Kamil         std::move(callback), "xyz.openbmc_project.Network",
404*179db1d7SKowalski, Kamil         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
405*179db1d7SKowalski, Kamil         "org.freedesktop.DBus.Properties", "Set",
406*179db1d7SKowalski, Kamil         "xyz.openbmc_project.Network.IP", name,
407*179db1d7SKowalski, Kamil         sdbusplus::message::variant<std::string>(newValue));
408*179db1d7SKowalski, Kamil   };
409*179db1d7SKowalski, Kamil 
410*179db1d7SKowalski, Kamil   /**
411*179db1d7SKowalski, Kamil    * @brief Changes IPv4 address origin property
412*179db1d7SKowalski, Kamil    *
413*179db1d7SKowalski, Kamil    * @param[in] ifaceId       Id of interface whose IP should be modified
414*179db1d7SKowalski, Kamil    * @param[in] ipIdx         Index of IP in input array that should be modified
415*179db1d7SKowalski, Kamil    * @param[in] ipHash        DBus Hash id of modified IP
416*179db1d7SKowalski, Kamil    * @param[in] newValue      New value in Redfish format
417*179db1d7SKowalski, Kamil    * @param[in] newValueDbus  New value in D-Bus format
418*179db1d7SKowalski, Kamil    * @param[io] asyncResp     Response object that will be returned to client
419*179db1d7SKowalski, Kamil    *
420*179db1d7SKowalski, Kamil    * @return true if give IP is valid and has been sent do D-Bus, false
421*179db1d7SKowalski, Kamil    * otherwise
422*179db1d7SKowalski, Kamil    */
423*179db1d7SKowalski, Kamil   void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
424*179db1d7SKowalski, Kamil                         const std::string &ipHash, const std::string &newValue,
425*179db1d7SKowalski, Kamil                         const std::string &newValueDbus,
426*179db1d7SKowalski, Kamil                         const std::shared_ptr<AsyncResp> &asyncResp) {
427*179db1d7SKowalski, Kamil     auto callback =
428*179db1d7SKowalski, Kamil         [ asyncResp, ipIdx{std::move(ipIdx)},
429*179db1d7SKowalski, Kamil           newValue{std::move(newValue)} ](const boost::system::error_code ec) {
430*179db1d7SKowalski, Kamil       if (ec) {
431*179db1d7SKowalski, Kamil         messages::addMessageToJson(
432*179db1d7SKowalski, Kamil             asyncResp->res.json_value, messages::internalError(),
433*179db1d7SKowalski, Kamil             "/IPv4Addresses/" + std::to_string(ipIdx) + "/AddressOrigin");
434*179db1d7SKowalski, Kamil       } else {
435*179db1d7SKowalski, Kamil         asyncResp->res.json_value["IPv4Addresses"][ipIdx]["AddressOrigin"] =
436*179db1d7SKowalski, Kamil             newValue;
437*179db1d7SKowalski, Kamil       }
438*179db1d7SKowalski, Kamil     };
439*179db1d7SKowalski, Kamil 
440*179db1d7SKowalski, Kamil     crow::connections::system_bus->async_method_call(
441*179db1d7SKowalski, Kamil         std::move(callback), "xyz.openbmc_project.Network",
442*179db1d7SKowalski, Kamil         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
443*179db1d7SKowalski, Kamil         "org.freedesktop.DBus.Properties", "Set",
444*179db1d7SKowalski, Kamil         "xyz.openbmc_project.Network.IP", "Origin",
445*179db1d7SKowalski, Kamil         sdbusplus::message::variant<std::string>(newValueDbus));
446*179db1d7SKowalski, Kamil   };
447*179db1d7SKowalski, Kamil 
448*179db1d7SKowalski, Kamil   /**
449*179db1d7SKowalski, Kamil    * @brief Modifies SubnetMask for given IP
450*179db1d7SKowalski, Kamil    *
451*179db1d7SKowalski, Kamil    * @param[in] ifaceId      Id of interface whose IP should be modified
452*179db1d7SKowalski, Kamil    * @param[in] ipIdx        Index of IP in input array that should be modified
453*179db1d7SKowalski, Kamil    * @param[in] ipHash       DBus Hash id of modified IP
454*179db1d7SKowalski, Kamil    * @param[in] newValueStr  Mask in dot notation as string
455*179db1d7SKowalski, Kamil    * @param[in] newValue     Mask as PrefixLength in bitcount
456*179db1d7SKowalski, Kamil    * @param[io] asyncResp   Response object that will be returned to client
457*179db1d7SKowalski, Kamil    *
458*179db1d7SKowalski, Kamil    * @return None
459*179db1d7SKowalski, Kamil    */
460*179db1d7SKowalski, Kamil   void changeIPv4SubnetMaskProperty(
461*179db1d7SKowalski, Kamil       const std::string &ifaceId, int ipIdx, const std::string &ipHash,
462*179db1d7SKowalski, Kamil       const std::string &newValueStr, uint8_t &newValue,
463*179db1d7SKowalski, Kamil       const std::shared_ptr<AsyncResp> &asyncResp) {
464*179db1d7SKowalski, Kamil     auto callback = [
465*179db1d7SKowalski, Kamil       asyncResp, ipIdx{std::move(ipIdx)}, newValueStr{std::move(newValueStr)}
466*179db1d7SKowalski, Kamil     ](const boost::system::error_code ec) {
467*179db1d7SKowalski, Kamil       if (ec) {
468*179db1d7SKowalski, Kamil         messages::addMessageToJson(
469*179db1d7SKowalski, Kamil             asyncResp->res.json_value, messages::internalError(),
470*179db1d7SKowalski, Kamil             "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask");
471*179db1d7SKowalski, Kamil       } else {
472*179db1d7SKowalski, Kamil         asyncResp->res.json_value["IPv4Addresses"][ipIdx]["SubnetMask"] =
473*179db1d7SKowalski, Kamil             newValueStr;
474*179db1d7SKowalski, Kamil       }
475*179db1d7SKowalski, Kamil     };
476*179db1d7SKowalski, Kamil 
477*179db1d7SKowalski, Kamil     crow::connections::system_bus->async_method_call(
478*179db1d7SKowalski, Kamil         std::move(callback), "xyz.openbmc_project.Network",
479*179db1d7SKowalski, Kamil         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
480*179db1d7SKowalski, Kamil         "org.freedesktop.DBus.Properties", "Set",
481*179db1d7SKowalski, Kamil         "xyz.openbmc_project.Network.IP", "PrefixLength",
482*179db1d7SKowalski, Kamil         sdbusplus::message::variant<uint8_t>(newValue));
483*179db1d7SKowalski, Kamil   };
484*179db1d7SKowalski, Kamil 
485*179db1d7SKowalski, Kamil   /**
486588c3f0dSKowalski, Kamil    * @brief Disables VLAN with given ifaceId
487588c3f0dSKowalski, Kamil    *
488588c3f0dSKowalski, Kamil    * @param[in] ifaceId   Id of VLAN interface that should be disabled
489588c3f0dSKowalski, Kamil    * @param[in] callback  Function that will be called after the operation
490588c3f0dSKowalski, Kamil    *
491588c3f0dSKowalski, Kamil    * @return None.
492588c3f0dSKowalski, Kamil    */
493588c3f0dSKowalski, Kamil   template <typename CallbackFunc>
494588c3f0dSKowalski, Kamil   void disableVlan(const std::string &ifaceId, CallbackFunc &&callback) {
495588c3f0dSKowalski, Kamil     crow::connections::system_bus->async_method_call(
496588c3f0dSKowalski, Kamil         callback, "xyz.openbmc_project.Network",
497588c3f0dSKowalski, Kamil         std::string("/xyz/openbmc_project/network/") + ifaceId,
498588c3f0dSKowalski, Kamil         "xyz.openbmc_project.Object.Delete", "Delete");
499588c3f0dSKowalski, Kamil   };
500588c3f0dSKowalski, Kamil 
501588c3f0dSKowalski, Kamil   /**
502588c3f0dSKowalski, Kamil    * @brief Sets given HostName of the machine through D-Bus
503588c3f0dSKowalski, Kamil    *
504588c3f0dSKowalski, Kamil    * @param[in] newHostname   New name that HostName will be changed to
505588c3f0dSKowalski, Kamil    * @param[in] callback      Function that will be called after the operation
506588c3f0dSKowalski, Kamil    *
507588c3f0dSKowalski, Kamil    * @return None.
508588c3f0dSKowalski, Kamil    */
509588c3f0dSKowalski, Kamil   template <typename CallbackFunc>
510588c3f0dSKowalski, Kamil   void setHostName(const std::string &newHostname, CallbackFunc &&callback) {
511588c3f0dSKowalski, Kamil     crow::connections::system_bus->async_method_call(
512588c3f0dSKowalski, Kamil         callback, "xyz.openbmc_project.Network",
513588c3f0dSKowalski, Kamil         "/xyz/openbmc_project/network/config",
514588c3f0dSKowalski, Kamil         "org.freedesktop.DBus.Properties", "Set",
515588c3f0dSKowalski, Kamil         "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
516588c3f0dSKowalski, Kamil         sdbusplus::message::variant<std::string>(newHostname));
517588c3f0dSKowalski, Kamil   };
518588c3f0dSKowalski, Kamil 
519588c3f0dSKowalski, Kamil   /**
520*179db1d7SKowalski, Kamil    * @brief Deletes given IPv4
521*179db1d7SKowalski, Kamil    *
522*179db1d7SKowalski, Kamil    * @param[in] ifaceId     Id of interface whose IP should be deleted
523*179db1d7SKowalski, Kamil    * @param[in] ipIdx       Index of IP in input array that should be deleted
524*179db1d7SKowalski, Kamil    * @param[in] ipHash      DBus Hash id of IP that should be deleted
525*179db1d7SKowalski, Kamil    * @param[io] asyncResp   Response object that will be returned to client
526*179db1d7SKowalski, Kamil    *
527*179db1d7SKowalski, Kamil    * @return None
528*179db1d7SKowalski, Kamil    */
529*179db1d7SKowalski, Kamil   void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
530*179db1d7SKowalski, Kamil                   unsigned int ipIdx,
531*179db1d7SKowalski, Kamil                   const std::shared_ptr<AsyncResp> &asyncResp) {
532*179db1d7SKowalski, Kamil     crow::connections::system_bus->async_method_call(
533*179db1d7SKowalski, Kamil         [ ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)} ](
534*179db1d7SKowalski, Kamil             const boost::system::error_code ec) {
535*179db1d7SKowalski, Kamil           if (ec) {
536*179db1d7SKowalski, Kamil             messages::addMessageToJson(
537*179db1d7SKowalski, Kamil                 asyncResp->res.json_value, messages::internalError(),
538*179db1d7SKowalski, Kamil                 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
539*179db1d7SKowalski, Kamil           } else {
540*179db1d7SKowalski, Kamil             asyncResp->res.json_value["IPv4Addresses"][ipIdx] = nullptr;
541*179db1d7SKowalski, Kamil           }
542*179db1d7SKowalski, Kamil         },
543*179db1d7SKowalski, Kamil         "xyz.openbmc_project.Network",
544*179db1d7SKowalski, Kamil         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
545*179db1d7SKowalski, Kamil         "xyz.openbmc_project.Object.Delete", "Delete");
546*179db1d7SKowalski, Kamil   }
547*179db1d7SKowalski, Kamil 
548*179db1d7SKowalski, Kamil   /**
549*179db1d7SKowalski, Kamil    * @brief Creates IPv4 with given data
550*179db1d7SKowalski, Kamil    *
551*179db1d7SKowalski, Kamil    * @param[in] ifaceId     Id of interface whose IP should be deleted
552*179db1d7SKowalski, Kamil    * @param[in] ipIdx       Index of IP in input array that should be deleted
553*179db1d7SKowalski, Kamil    * @param[in] ipHash      DBus Hash id of IP that should be deleted
554*179db1d7SKowalski, Kamil    * @param[io] asyncResp   Response object that will be returned to client
555*179db1d7SKowalski, Kamil    *
556*179db1d7SKowalski, Kamil    * @return None
557*179db1d7SKowalski, Kamil    */
558*179db1d7SKowalski, Kamil   void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
559*179db1d7SKowalski, Kamil                   uint8_t subnetMask, const std::string &gateway,
560*179db1d7SKowalski, Kamil                   const std::string &address,
561*179db1d7SKowalski, Kamil                   const std::shared_ptr<AsyncResp> &asyncResp) {
562*179db1d7SKowalski, Kamil     auto createIpHandler = [
563*179db1d7SKowalski, Kamil       ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)}
564*179db1d7SKowalski, Kamil     ](const boost::system::error_code ec) {
565*179db1d7SKowalski, Kamil       if (ec) {
566*179db1d7SKowalski, Kamil         messages::addMessageToJson(
567*179db1d7SKowalski, Kamil             asyncResp->res.json_value, messages::internalError(),
568*179db1d7SKowalski, Kamil             "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
569*179db1d7SKowalski, Kamil       }
570*179db1d7SKowalski, Kamil     };
571*179db1d7SKowalski, Kamil 
572*179db1d7SKowalski, Kamil     crow::connections::system_bus->async_method_call(
573*179db1d7SKowalski, Kamil         std::move(createIpHandler), "xyz.openbmc_project.Network",
574*179db1d7SKowalski, Kamil         "/xyz/openbmc_project/network/" + ifaceId,
575*179db1d7SKowalski, Kamil         "xyz.openbmc_project.Network.IP.Create", "IP",
576*179db1d7SKowalski, Kamil         "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
577*179db1d7SKowalski, Kamil         gateway);
578*179db1d7SKowalski, Kamil   }
579*179db1d7SKowalski, Kamil 
580*179db1d7SKowalski, Kamil   /**
581*179db1d7SKowalski, Kamil    * @brief Translates Address Origin value from D-Bus to Redfish format and
582*179db1d7SKowalski, Kamil    *        vice-versa
583*179db1d7SKowalski, Kamil    *
584*179db1d7SKowalski, Kamil    * @param[in] inputOrigin Input value that should be translated
585*179db1d7SKowalski, Kamil    * @param[in] isIPv4      True for IPv4 origins, False for IPv6
586*179db1d7SKowalski, Kamil    * @param[in] isFromDBus  True for DBus->Redfish conversion, false for reverse
587*179db1d7SKowalski, Kamil    *
588*179db1d7SKowalski, Kamil    * @return Empty string in case of failure, translated value otherwise
589*179db1d7SKowalski, Kamil    */
590*179db1d7SKowalski, Kamil   std::string translateAddressOriginBetweenDBusAndRedfish(
591*179db1d7SKowalski, Kamil       const std::string *inputOrigin, bool isIPv4, bool isFromDBus) {
592*179db1d7SKowalski, Kamil     // Invalid pointer
593*179db1d7SKowalski, Kamil     if (inputOrigin == nullptr) {
594*179db1d7SKowalski, Kamil       return "";
595*179db1d7SKowalski, Kamil     }
596*179db1d7SKowalski, Kamil 
597*179db1d7SKowalski, Kamil     static const constexpr unsigned int firstIPv4OnlyIdx = 1;
598*179db1d7SKowalski, Kamil     static const constexpr unsigned int firstIPv6OnlyIdx = 3;
599*179db1d7SKowalski, Kamil 
600*179db1d7SKowalski, Kamil     std::array<std::pair<const char *, const char *>, 6> translationTable{
601*179db1d7SKowalski, Kamil         {{"xyz.openbmc_project.Network.IP.AddressOrigin.Static", "Static"},
602*179db1d7SKowalski, Kamil          {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCP"},
603*179db1d7SKowalski, Kamil          {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
604*179db1d7SKowalski, Kamil           "IPv4LinkLocal"},
605*179db1d7SKowalski, Kamil          {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCPv6"},
606*179db1d7SKowalski, Kamil          {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
607*179db1d7SKowalski, Kamil           "LinkLocal"},
608*179db1d7SKowalski, Kamil          {"xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC", "SLAAC"}}};
609*179db1d7SKowalski, Kamil 
610*179db1d7SKowalski, Kamil     for (unsigned int i = 0; i < translationTable.size(); i++) {
611*179db1d7SKowalski, Kamil       // Skip unrelated
612*179db1d7SKowalski, Kamil       if (isIPv4 && i >= firstIPv6OnlyIdx) break;
613*179db1d7SKowalski, Kamil       if (!isIPv4 && i >= firstIPv4OnlyIdx && i < firstIPv6OnlyIdx) continue;
614*179db1d7SKowalski, Kamil 
615*179db1d7SKowalski, Kamil       // When translating D-Bus to Redfish compare input to first element
616*179db1d7SKowalski, Kamil       if (isFromDBus && translationTable[i].first == *inputOrigin)
617*179db1d7SKowalski, Kamil         return translationTable[i].second;
618*179db1d7SKowalski, Kamil 
619*179db1d7SKowalski, Kamil       // When translating Redfish to D-Bus compare input to second element
620*179db1d7SKowalski, Kamil       if (!isFromDBus && translationTable[i].second == *inputOrigin)
621*179db1d7SKowalski, Kamil         return translationTable[i].first;
622*179db1d7SKowalski, Kamil     }
623*179db1d7SKowalski, Kamil 
624*179db1d7SKowalski, Kamil     // If we are still here, that means that value has not been found
625*179db1d7SKowalski, Kamil     return "";
626*179db1d7SKowalski, Kamil   }
627*179db1d7SKowalski, Kamil 
628*179db1d7SKowalski, Kamil   /**
629*179db1d7SKowalski, Kamil    * Function that retrieves all properties for given Ethernet Interface
630*179db1d7SKowalski, Kamil    * Object
631*179db1d7SKowalski, Kamil    * from EntityManager Network Manager
632*179db1d7SKowalski, Kamil    * @param ethiface_id a eth interface id to query on DBus
633*179db1d7SKowalski, Kamil    * @param callback a function that shall be called to convert Dbus output
634*179db1d7SKowalski, Kamil    * into JSON
635*179db1d7SKowalski, Kamil    */
636*179db1d7SKowalski, Kamil   template <typename CallbackFunc>
637*179db1d7SKowalski, Kamil   void getEthernetIfaceData(const std::string &ethiface_id,
638*179db1d7SKowalski, Kamil                             CallbackFunc &&callback) {
639*179db1d7SKowalski, Kamil     crow::connections::system_bus->async_method_call(
640*179db1d7SKowalski, Kamil         [
641*179db1d7SKowalski, Kamil           this, ethiface_id{std::move(ethiface_id)},
642*179db1d7SKowalski, Kamil           callback{std::move(callback)}
643*179db1d7SKowalski, Kamil         ](const boost::system::error_code error_code,
644*179db1d7SKowalski, Kamil           const GetManagedObjectsType &resp) {
645*179db1d7SKowalski, Kamil 
646*179db1d7SKowalski, Kamil           EthernetInterfaceData eth_data{};
647*179db1d7SKowalski, Kamil           std::vector<IPv4AddressData> ipv4_data;
648*179db1d7SKowalski, Kamil           ipv4_data.reserve(MAX_IPV4_ADDRESSES_PER_INTERFACE);
649*179db1d7SKowalski, Kamil 
650*179db1d7SKowalski, Kamil           if (error_code) {
651*179db1d7SKowalski, Kamil             // Something wrong on DBus, the error_code is not important at
652*179db1d7SKowalski, Kamil             // this moment, just return success=false, and empty output. Since
653*179db1d7SKowalski, Kamil             // size of vector may vary depending on information from Network
654*179db1d7SKowalski, Kamil             // Manager, and empty output could not be treated same way as
655*179db1d7SKowalski, Kamil             // error.
656*179db1d7SKowalski, Kamil             callback(false, eth_data, ipv4_data);
657*179db1d7SKowalski, Kamil             return;
658*179db1d7SKowalski, Kamil           }
659*179db1d7SKowalski, Kamil 
660*179db1d7SKowalski, Kamil           extractEthernetInterfaceData(ethiface_id, resp, eth_data);
661*179db1d7SKowalski, Kamil           extractIPv4Data(ethiface_id, resp, ipv4_data);
662*179db1d7SKowalski, Kamil 
663*179db1d7SKowalski, Kamil           // Fix global GW
664*179db1d7SKowalski, Kamil           for (IPv4AddressData &ipv4 : ipv4_data) {
665*179db1d7SKowalski, Kamil             if ((ipv4.global) &&
666*179db1d7SKowalski, Kamil                 ((ipv4.gateway == nullptr) || (*ipv4.gateway == "0.0.0.0"))) {
667*179db1d7SKowalski, Kamil               ipv4.gateway = eth_data.default_gateway;
668*179db1d7SKowalski, Kamil             }
669*179db1d7SKowalski, Kamil           }
670*179db1d7SKowalski, Kamil 
671*179db1d7SKowalski, Kamil           // Finally make a callback with usefull data
672*179db1d7SKowalski, Kamil           callback(true, eth_data, ipv4_data);
673*179db1d7SKowalski, Kamil         },
674*179db1d7SKowalski, Kamil         "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
675*179db1d7SKowalski, Kamil         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
676*179db1d7SKowalski, Kamil   };
677*179db1d7SKowalski, Kamil 
678*179db1d7SKowalski, Kamil   /**
6799391bb9cSRapkiewicz, Pawel    * Function that retrieves all Ethernet Interfaces available through Network
6809391bb9cSRapkiewicz, Pawel    * Manager
6819391bb9cSRapkiewicz, Pawel    * @param callback a function that shall be called to convert Dbus output into
6829391bb9cSRapkiewicz, Pawel    * JSON.
6839391bb9cSRapkiewicz, Pawel    */
6849391bb9cSRapkiewicz, Pawel   template <typename CallbackFunc>
6859391bb9cSRapkiewicz, Pawel   void getEthernetIfaceList(CallbackFunc &&callback) {
6869391bb9cSRapkiewicz, Pawel     crow::connections::system_bus->async_method_call(
6879391bb9cSRapkiewicz, Pawel         [ this, callback{std::move(callback)} ](
6889391bb9cSRapkiewicz, Pawel             const boost::system::error_code error_code,
689aa2e59c1SEd Tanous             GetManagedObjectsType &resp) {
6909391bb9cSRapkiewicz, Pawel           // Callback requires vector<string> to retrieve all available ethernet
6919391bb9cSRapkiewicz, Pawel           // interfaces
6929391bb9cSRapkiewicz, Pawel           std::vector<std::string> iface_list;
6939391bb9cSRapkiewicz, Pawel           iface_list.reserve(resp.size());
6949391bb9cSRapkiewicz, Pawel           if (error_code) {
6959391bb9cSRapkiewicz, Pawel             // Something wrong on DBus, the error_code is not important at this
6969391bb9cSRapkiewicz, Pawel             // moment, just return success=false, and empty output. Since size
6979391bb9cSRapkiewicz, Pawel             // of vector may vary depending on information from Network Manager,
6989391bb9cSRapkiewicz, Pawel             // and empty output could not be treated same way as error.
6999391bb9cSRapkiewicz, Pawel             callback(false, iface_list);
7009391bb9cSRapkiewicz, Pawel             return;
7019391bb9cSRapkiewicz, Pawel           }
7029391bb9cSRapkiewicz, Pawel 
7039391bb9cSRapkiewicz, Pawel           // Iterate over all retrieved ObjectPaths.
7049391bb9cSRapkiewicz, Pawel           for (auto &objpath : resp) {
7059391bb9cSRapkiewicz, Pawel             // And all interfaces available for certain ObjectPath.
7069391bb9cSRapkiewicz, Pawel             for (auto &interface : objpath.second) {
7079391bb9cSRapkiewicz, Pawel               // If interface is xyz.openbmc_project.Network.EthernetInterface,
7089391bb9cSRapkiewicz, Pawel               // this is what we're looking for.
7099391bb9cSRapkiewicz, Pawel               if (interface.first ==
7109391bb9cSRapkiewicz, Pawel                   "xyz.openbmc_project.Network.EthernetInterface") {
711aa2e59c1SEd Tanous                 // Cut out everyting until last "/", ...
712daf36e2eSEd Tanous                 const std::string &iface_id =
713daf36e2eSEd Tanous                     static_cast<const std::string &>(objpath.first);
7149391bb9cSRapkiewicz, Pawel                 std::size_t last_pos = iface_id.rfind("/");
7159391bb9cSRapkiewicz, Pawel                 if (last_pos != std::string::npos) {
7169391bb9cSRapkiewicz, Pawel                   // and put it into output vector.
7179391bb9cSRapkiewicz, Pawel                   iface_list.emplace_back(iface_id.substr(last_pos + 1));
7189391bb9cSRapkiewicz, Pawel                 }
7199391bb9cSRapkiewicz, Pawel               }
7209391bb9cSRapkiewicz, Pawel             }
7219391bb9cSRapkiewicz, Pawel           }
722274fad5aSGunnar Mills           // Finally make a callback with useful data
7239391bb9cSRapkiewicz, Pawel           callback(true, iface_list);
7249391bb9cSRapkiewicz, Pawel         },
725aa2e59c1SEd Tanous         "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
726aa2e59c1SEd Tanous         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
7279391bb9cSRapkiewicz, Pawel   };
7289391bb9cSRapkiewicz, Pawel };
7299391bb9cSRapkiewicz, Pawel 
7309391bb9cSRapkiewicz, Pawel /**
7319391bb9cSRapkiewicz, Pawel  * EthernetCollection derived class for delivering Ethernet Collection Schema
7329391bb9cSRapkiewicz, Pawel  */
7339391bb9cSRapkiewicz, Pawel class EthernetCollection : public Node {
7349391bb9cSRapkiewicz, Pawel  public:
7359391bb9cSRapkiewicz, Pawel   template <typename CrowApp>
7369391bb9cSRapkiewicz, Pawel   // TODO(Pawel) Remove line from below, where we assume that there is only one
7379391bb9cSRapkiewicz, Pawel   // manager called openbmc This shall be generic, but requires to update
7389391bb9cSRapkiewicz, Pawel   // GetSubroutes method
7399391bb9cSRapkiewicz, Pawel   EthernetCollection(CrowApp &app)
7409391bb9cSRapkiewicz, Pawel       : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") {
7419391bb9cSRapkiewicz, Pawel     Node::json["@odata.type"] =
7429391bb9cSRapkiewicz, Pawel         "#EthernetInterfaceCollection.EthernetInterfaceCollection";
7439391bb9cSRapkiewicz, Pawel     Node::json["@odata.context"] =
7449391bb9cSRapkiewicz, Pawel         "/redfish/v1/"
7459391bb9cSRapkiewicz, Pawel         "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
7469391bb9cSRapkiewicz, Pawel     Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/EthernetInterfaces";
7479391bb9cSRapkiewicz, Pawel     Node::json["Name"] = "Ethernet Network Interface Collection";
7489391bb9cSRapkiewicz, Pawel     Node::json["Description"] =
7499391bb9cSRapkiewicz, Pawel         "Collection of EthernetInterfaces for this Manager";
7509391bb9cSRapkiewicz, Pawel 
751588c3f0dSKowalski, Kamil     entityPrivileges = {
752588c3f0dSKowalski, Kamil         {boost::beast::http::verb::get, {{"Login"}}},
753e0d918bcSEd Tanous         {boost::beast::http::verb::head, {{"Login"}}},
754e0d918bcSEd Tanous         {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
755e0d918bcSEd Tanous         {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
756e0d918bcSEd Tanous         {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
757e0d918bcSEd Tanous         {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
7589391bb9cSRapkiewicz, Pawel   }
7599391bb9cSRapkiewicz, Pawel 
7609391bb9cSRapkiewicz, Pawel  private:
7619391bb9cSRapkiewicz, Pawel   /**
7629391bb9cSRapkiewicz, Pawel    * Functions triggers appropriate requests on DBus
7639391bb9cSRapkiewicz, Pawel    */
7649391bb9cSRapkiewicz, Pawel   void doGet(crow::response &res, const crow::request &req,
7659391bb9cSRapkiewicz, Pawel              const std::vector<std::string> &params) override {
7669391bb9cSRapkiewicz, Pawel     // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
7679391bb9cSRapkiewicz, Pawel     // any Manager, not only hardcoded 'openbmc'.
7689391bb9cSRapkiewicz, Pawel     std::string manager_id = "openbmc";
7699391bb9cSRapkiewicz, Pawel 
7709391bb9cSRapkiewicz, Pawel     // Get eth interface list, and call the below callback for JSON preparation
7719391bb9cSRapkiewicz, Pawel     ethernet_provider.getEthernetIfaceList(
7729391bb9cSRapkiewicz, Pawel         [&, manager_id{std::move(manager_id)} ](
7739391bb9cSRapkiewicz, Pawel             const bool &success, const std::vector<std::string> &iface_list) {
7749391bb9cSRapkiewicz, Pawel           if (success) {
7759391bb9cSRapkiewicz, Pawel             nlohmann::json iface_array = nlohmann::json::array();
7769391bb9cSRapkiewicz, Pawel             for (const std::string &iface_item : iface_list) {
7779391bb9cSRapkiewicz, Pawel               iface_array.push_back(
7789391bb9cSRapkiewicz, Pawel                   {{"@odata.id", "/redfish/v1/Managers/" + manager_id +
7799391bb9cSRapkiewicz, Pawel                                      "/EthernetInterfaces/" + iface_item}});
7809391bb9cSRapkiewicz, Pawel             }
7819391bb9cSRapkiewicz, Pawel             Node::json["Members"] = iface_array;
7829391bb9cSRapkiewicz, Pawel             Node::json["Members@odata.count"] = iface_array.size();
7839391bb9cSRapkiewicz, Pawel             Node::json["@odata.id"] =
7849391bb9cSRapkiewicz, Pawel                 "/redfish/v1/Managers/" + manager_id + "/EthernetInterfaces";
7859391bb9cSRapkiewicz, Pawel             res.json_value = Node::json;
7869391bb9cSRapkiewicz, Pawel           } else {
7879391bb9cSRapkiewicz, Pawel             // No success, best what we can do is return INTERNALL ERROR
788e0d918bcSEd Tanous             res.result(boost::beast::http::status::internal_server_error);
7899391bb9cSRapkiewicz, Pawel           }
7909391bb9cSRapkiewicz, Pawel           res.end();
7919391bb9cSRapkiewicz, Pawel         });
7929391bb9cSRapkiewicz, Pawel   }
7939391bb9cSRapkiewicz, Pawel 
7949391bb9cSRapkiewicz, Pawel   // Ethernet Provider object
7959391bb9cSRapkiewicz, Pawel   // TODO(Pawel) consider move it to singleton
7969391bb9cSRapkiewicz, Pawel   OnDemandEthernetProvider ethernet_provider;
7979391bb9cSRapkiewicz, Pawel };
7989391bb9cSRapkiewicz, Pawel 
7999391bb9cSRapkiewicz, Pawel /**
8009391bb9cSRapkiewicz, Pawel  * EthernetInterface derived class for delivering Ethernet Schema
8019391bb9cSRapkiewicz, Pawel  */
8029391bb9cSRapkiewicz, Pawel class EthernetInterface : public Node {
8039391bb9cSRapkiewicz, Pawel  public:
8049391bb9cSRapkiewicz, Pawel   /*
8059391bb9cSRapkiewicz, Pawel    * Default Constructor
8069391bb9cSRapkiewicz, Pawel    */
8079391bb9cSRapkiewicz, Pawel   template <typename CrowApp>
8089391bb9cSRapkiewicz, Pawel   // TODO(Pawel) Remove line from below, where we assume that there is only one
8099391bb9cSRapkiewicz, Pawel   // manager called openbmc This shall be generic, but requires to update
8109391bb9cSRapkiewicz, Pawel   // GetSubroutes method
8119391bb9cSRapkiewicz, Pawel   EthernetInterface(CrowApp &app)
8129391bb9cSRapkiewicz, Pawel       : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/",
8139391bb9cSRapkiewicz, Pawel              std::string()) {
8149391bb9cSRapkiewicz, Pawel     Node::json["@odata.type"] = "#EthernetInterface.v1_2_0.EthernetInterface";
8159391bb9cSRapkiewicz, Pawel     Node::json["@odata.context"] =
8169391bb9cSRapkiewicz, Pawel         "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
8179391bb9cSRapkiewicz, Pawel     Node::json["Name"] = "Manager Ethernet Interface";
8189391bb9cSRapkiewicz, Pawel     Node::json["Description"] = "Management Network Interface";
8199391bb9cSRapkiewicz, Pawel 
820588c3f0dSKowalski, Kamil     entityPrivileges = {
821588c3f0dSKowalski, Kamil         {boost::beast::http::verb::get, {{"Login"}}},
822e0d918bcSEd Tanous         {boost::beast::http::verb::head, {{"Login"}}},
823e0d918bcSEd Tanous         {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
824e0d918bcSEd Tanous         {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
825e0d918bcSEd Tanous         {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
826e0d918bcSEd Tanous         {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
8279391bb9cSRapkiewicz, Pawel   }
8289391bb9cSRapkiewicz, Pawel 
8299391bb9cSRapkiewicz, Pawel  private:
830588c3f0dSKowalski, Kamil   void handleVlanPatch(const std::string &ifaceId, const nlohmann::json &input,
831588c3f0dSKowalski, Kamil                        const EthernetInterfaceData &eth_data,
832588c3f0dSKowalski, Kamil                        const std::shared_ptr<AsyncResp> &asyncResp) {
833588c3f0dSKowalski, Kamil     if (!input.is_object()) {
834588c3f0dSKowalski, Kamil       messages::addMessageToJson(
835588c3f0dSKowalski, Kamil           asyncResp->res.json_value,
836588c3f0dSKowalski, Kamil           messages::propertyValueTypeError(input.dump(), "VLAN"), "/VLAN");
837588c3f0dSKowalski, Kamil       return;
838588c3f0dSKowalski, Kamil     }
839588c3f0dSKowalski, Kamil 
840588c3f0dSKowalski, Kamil     bool inputVlanEnabled;
841588c3f0dSKowalski, Kamil     uint64_t inputVlanId;
842588c3f0dSKowalski, Kamil     json_util::Result inputVlanEnabledState = json_util::getBool(
843588c3f0dSKowalski, Kamil         "VLANEnable", input, inputVlanEnabled,
844588c3f0dSKowalski, Kamil         static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
845588c3f0dSKowalski, Kamil         asyncResp->res.json_value, std::string("/VLAN/VLANEnable"));
846588c3f0dSKowalski, Kamil     json_util::Result inputVlanIdState = json_util::getUnsigned(
847588c3f0dSKowalski, Kamil         "VLANId", input, inputVlanId,
848588c3f0dSKowalski, Kamil         static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
849588c3f0dSKowalski, Kamil         asyncResp->res.json_value, std::string("/VLAN/VLANId"));
850588c3f0dSKowalski, Kamil     bool inputInvalid = false;
851588c3f0dSKowalski, Kamil 
852588c3f0dSKowalski, Kamil     // Do not proceed if fields in VLAN object were of wrong type
853588c3f0dSKowalski, Kamil     if (inputVlanEnabledState == json_util::Result::WRONG_TYPE ||
854588c3f0dSKowalski, Kamil         inputVlanIdState == json_util::Result::WRONG_TYPE) {
855588c3f0dSKowalski, Kamil       return;
856588c3f0dSKowalski, Kamil     }
857588c3f0dSKowalski, Kamil 
858588c3f0dSKowalski, Kamil     // Verify input
859588c3f0dSKowalski, Kamil     if (eth_data.vlan_id == nullptr) {
860588c3f0dSKowalski, Kamil       // VLAN is currently disabled. User can only create/enable it. Change of
861588c3f0dSKowalski, Kamil       // VLANId is prohibited, and disable request (VLANEnabled == false) will
862588c3f0dSKowalski, Kamil       // not have any effect.
863588c3f0dSKowalski, Kamil       if (inputVlanEnabledState == json_util::Result::SUCCESS &&
864588c3f0dSKowalski, Kamil           inputVlanEnabled == true) {
865588c3f0dSKowalski, Kamil         // Creation requested, user should also provide ID for new VLAN
866588c3f0dSKowalski, Kamil         if (inputVlanIdState != json_util::Result::SUCCESS) {
867588c3f0dSKowalski, Kamil           messages::addMessageToJson(asyncResp->res.json_value,
868588c3f0dSKowalski, Kamil                                      messages::propertyMissing("VLANId"),
869588c3f0dSKowalski, Kamil                                      "/VLAN");
870588c3f0dSKowalski, Kamil           inputInvalid = true;
871588c3f0dSKowalski, Kamil         }
872588c3f0dSKowalski, Kamil       } else if (inputVlanIdState == json_util::Result::SUCCESS) {
873588c3f0dSKowalski, Kamil         // VLAN is disabled, but user requested modification. This is not valid.
874588c3f0dSKowalski, Kamil         messages::addMessageToJson(
875588c3f0dSKowalski, Kamil             asyncResp->res.json_value,
876588c3f0dSKowalski, Kamil             messages::actionParameterNotSupported("VLANId", "change VLAN Id"),
877588c3f0dSKowalski, Kamil             "/VLAN");
878588c3f0dSKowalski, Kamil 
879588c3f0dSKowalski, Kamil         messages::addMessageToJson(asyncResp->res.json_value,
880588c3f0dSKowalski, Kamil                                    messages::propertyMissing("VLANEnable"),
881588c3f0dSKowalski, Kamil                                    "/VLAN");
882588c3f0dSKowalski, Kamil 
883588c3f0dSKowalski, Kamil         inputInvalid = true;
884588c3f0dSKowalski, Kamil       }
885588c3f0dSKowalski, Kamil     } else {
886588c3f0dSKowalski, Kamil       // Load actual data into field values if they were not provided
887588c3f0dSKowalski, Kamil       if (inputVlanEnabledState == json_util::Result::NOT_EXIST) {
888588c3f0dSKowalski, Kamil         inputVlanEnabled = true;
889588c3f0dSKowalski, Kamil       }
890588c3f0dSKowalski, Kamil 
891588c3f0dSKowalski, Kamil       if (inputVlanIdState == json_util::Result::NOT_EXIST) {
892588c3f0dSKowalski, Kamil         inputVlanId = *eth_data.vlan_id;
893588c3f0dSKowalski, Kamil       }
894588c3f0dSKowalski, Kamil     }
895588c3f0dSKowalski, Kamil 
896588c3f0dSKowalski, Kamil     // Do not proceed if input has not been valid
897588c3f0dSKowalski, Kamil     if (inputInvalid) {
898588c3f0dSKowalski, Kamil       return;
899588c3f0dSKowalski, Kamil     }
900588c3f0dSKowalski, Kamil 
901588c3f0dSKowalski, Kamil     auto vlanEnabledAfterOperation =
902588c3f0dSKowalski, Kamil         [asyncResp](const boost::system::error_code ec) {
903588c3f0dSKowalski, Kamil           if (ec) {
904588c3f0dSKowalski, Kamil             messages::addMessageToJson(asyncResp->res.json_value,
905588c3f0dSKowalski, Kamil                                        messages::internalError(), "/VLAN");
906588c3f0dSKowalski, Kamil           } else {
907588c3f0dSKowalski, Kamil             asyncResp->res.json_value["VLAN"]["VLANEnable"] = true;
908588c3f0dSKowalski, Kamil           }
909588c3f0dSKowalski, Kamil         };
910588c3f0dSKowalski, Kamil 
911588c3f0dSKowalski, Kamil     if (eth_data.vlan_id == nullptr) {
912588c3f0dSKowalski, Kamil       if (inputVlanEnabled == true) {
913588c3f0dSKowalski, Kamil         ethernet_provider.createVlan(ifaceId, inputVlanId,
914588c3f0dSKowalski, Kamil                                      std::move(vlanEnabledAfterOperation));
915588c3f0dSKowalski, Kamil         asyncResp->res.json_value["VLAN"]["VLANId"] = inputVlanId;
916588c3f0dSKowalski, Kamil       }
917588c3f0dSKowalski, Kamil     } else {
918588c3f0dSKowalski, Kamil       // VLAN is configured on the interface
919588c3f0dSKowalski, Kamil       if (inputVlanEnabled == true && inputVlanId != *eth_data.vlan_id) {
920588c3f0dSKowalski, Kamil         // Change VLAN Id
921588c3f0dSKowalski, Kamil         asyncResp->res.json_value["VLAN"]["VLANId"] = inputVlanId;
922588c3f0dSKowalski, Kamil         ethernet_provider.changeVlanId(ifaceId,
923588c3f0dSKowalski, Kamil                                        static_cast<uint32_t>(inputVlanId),
924588c3f0dSKowalski, Kamil                                        std::move(vlanEnabledAfterOperation));
925588c3f0dSKowalski, Kamil       } else if (inputVlanEnabled == false) {
926588c3f0dSKowalski, Kamil         // Disable VLAN
927588c3f0dSKowalski, Kamil         ethernet_provider.disableVlan(
928588c3f0dSKowalski, Kamil             ifaceId, [asyncResp](const boost::system::error_code ec) {
929588c3f0dSKowalski, Kamil               if (ec) {
930588c3f0dSKowalski, Kamil                 messages::addMessageToJson(asyncResp->res.json_value,
931588c3f0dSKowalski, Kamil                                            messages::internalError(), "/VLAN");
932588c3f0dSKowalski, Kamil               } else {
933588c3f0dSKowalski, Kamil                 asyncResp->res.json_value["VLAN"]["VLANEnable"] = false;
934588c3f0dSKowalski, Kamil               }
935588c3f0dSKowalski, Kamil             });
936588c3f0dSKowalski, Kamil       }
937588c3f0dSKowalski, Kamil     }
938588c3f0dSKowalski, Kamil   }
939588c3f0dSKowalski, Kamil 
940588c3f0dSKowalski, Kamil   void handleHostnamePatch(const nlohmann::json &input,
941588c3f0dSKowalski, Kamil                            const EthernetInterfaceData &eth_data,
942588c3f0dSKowalski, Kamil                            const std::shared_ptr<AsyncResp> &asyncResp) {
943588c3f0dSKowalski, Kamil     if (input.is_string()) {
944588c3f0dSKowalski, Kamil       std::string newHostname = input.get<std::string>();
945588c3f0dSKowalski, Kamil 
946588c3f0dSKowalski, Kamil       if (eth_data.hostname == nullptr || newHostname != *eth_data.hostname) {
947588c3f0dSKowalski, Kamil         // Change hostname
948588c3f0dSKowalski, Kamil         ethernet_provider.setHostName(
949588c3f0dSKowalski, Kamil             newHostname,
950588c3f0dSKowalski, Kamil             [asyncResp, newHostname](const boost::system::error_code ec) {
951588c3f0dSKowalski, Kamil               if (ec) {
952588c3f0dSKowalski, Kamil                 messages::addMessageToJson(asyncResp->res.json_value,
953588c3f0dSKowalski, Kamil                                            messages::internalError(),
954588c3f0dSKowalski, Kamil                                            "/HostName");
955588c3f0dSKowalski, Kamil               } else {
956588c3f0dSKowalski, Kamil                 asyncResp->res.json_value["HostName"] = newHostname;
957588c3f0dSKowalski, Kamil               }
958588c3f0dSKowalski, Kamil             });
959588c3f0dSKowalski, Kamil       }
960588c3f0dSKowalski, Kamil     } else {
961588c3f0dSKowalski, Kamil       messages::addMessageToJson(
962588c3f0dSKowalski, Kamil           asyncResp->res.json_value,
963588c3f0dSKowalski, Kamil           messages::propertyValueTypeError(input.dump(), "HostName"),
964588c3f0dSKowalski, Kamil           "/HostName");
965588c3f0dSKowalski, Kamil     }
966588c3f0dSKowalski, Kamil   }
967588c3f0dSKowalski, Kamil 
968*179db1d7SKowalski, Kamil   void handleIPv4Patch(const std::string &ifaceId, const nlohmann::json &input,
969*179db1d7SKowalski, Kamil                        const std::vector<IPv4AddressData> &ipv4_data,
970*179db1d7SKowalski, Kamil                        const std::shared_ptr<AsyncResp> &asyncResp) {
971*179db1d7SKowalski, Kamil     if (!input.is_array()) {
972*179db1d7SKowalski, Kamil       messages::addMessageToJson(
973*179db1d7SKowalski, Kamil           asyncResp->res.json_value,
974*179db1d7SKowalski, Kamil           messages::propertyValueTypeError(input.dump(), "IPv4Addresses"),
975*179db1d7SKowalski, Kamil           "/IPv4Addresses");
976*179db1d7SKowalski, Kamil       return;
977*179db1d7SKowalski, Kamil     }
978*179db1d7SKowalski, Kamil 
979*179db1d7SKowalski, Kamil     // According to Redfish PATCH definition, size must be at least equal
980*179db1d7SKowalski, Kamil     if (input.size() < ipv4_data.size()) {
981*179db1d7SKowalski, Kamil       // TODO(kkowalsk) This should be a message indicating that not enough
982*179db1d7SKowalski, Kamil       // data has been provided
983*179db1d7SKowalski, Kamil       messages::addMessageToJson(asyncResp->res.json_value,
984*179db1d7SKowalski, Kamil                                  messages::internalError(), "/IPv4Addresses");
985*179db1d7SKowalski, Kamil       return;
986*179db1d7SKowalski, Kamil     }
987*179db1d7SKowalski, Kamil 
988*179db1d7SKowalski, Kamil     json_util::Result addressFieldState;
989*179db1d7SKowalski, Kamil     json_util::Result subnetMaskFieldState;
990*179db1d7SKowalski, Kamil     json_util::Result addressOriginFieldState;
991*179db1d7SKowalski, Kamil     json_util::Result gatewayFieldState;
992*179db1d7SKowalski, Kamil     const std::string *addressFieldValue;
993*179db1d7SKowalski, Kamil     const std::string *subnetMaskFieldValue;
994*179db1d7SKowalski, Kamil     const std::string *addressOriginFieldValue = nullptr;
995*179db1d7SKowalski, Kamil     const std::string *gatewayFieldValue;
996*179db1d7SKowalski, Kamil     uint8_t subnetMaskAsPrefixLength;
997*179db1d7SKowalski, Kamil     std::string addressOriginInDBusFormat;
998*179db1d7SKowalski, Kamil 
999*179db1d7SKowalski, Kamil     bool errorDetected = false;
1000*179db1d7SKowalski, Kamil     for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++) {
1001*179db1d7SKowalski, Kamil       // Check that entry is not of some unexpected type
1002*179db1d7SKowalski, Kamil       if (!input[entryIdx].is_object() && !input[entryIdx].is_null()) {
1003*179db1d7SKowalski, Kamil         // Invalid object type
1004*179db1d7SKowalski, Kamil         messages::addMessageToJson(
1005*179db1d7SKowalski, Kamil             asyncResp->res.json_value,
1006*179db1d7SKowalski, Kamil             messages::propertyValueTypeError(input[entryIdx].dump(),
1007*179db1d7SKowalski, Kamil                                              "IPv4Address"),
1008*179db1d7SKowalski, Kamil             "/IPv4Addresses/" + std::to_string(entryIdx));
1009*179db1d7SKowalski, Kamil 
1010*179db1d7SKowalski, Kamil         continue;
1011*179db1d7SKowalski, Kamil       }
1012*179db1d7SKowalski, Kamil 
1013*179db1d7SKowalski, Kamil       // Try to load fields
1014*179db1d7SKowalski, Kamil       addressFieldState = json_util::getString(
1015*179db1d7SKowalski, Kamil           "Address", input[entryIdx], addressFieldValue,
1016*179db1d7SKowalski, Kamil           static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1017*179db1d7SKowalski, Kamil           asyncResp->res.json_value,
1018*179db1d7SKowalski, Kamil           "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1019*179db1d7SKowalski, Kamil       subnetMaskFieldState = json_util::getString(
1020*179db1d7SKowalski, Kamil           "SubnetMask", input[entryIdx], subnetMaskFieldValue,
1021*179db1d7SKowalski, Kamil           static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1022*179db1d7SKowalski, Kamil           asyncResp->res.json_value,
1023*179db1d7SKowalski, Kamil           "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1024*179db1d7SKowalski, Kamil       addressOriginFieldState = json_util::getString(
1025*179db1d7SKowalski, Kamil           "AddressOrigin", input[entryIdx], addressOriginFieldValue,
1026*179db1d7SKowalski, Kamil           static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1027*179db1d7SKowalski, Kamil           asyncResp->res.json_value,
1028*179db1d7SKowalski, Kamil           "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1029*179db1d7SKowalski, Kamil       gatewayFieldState = json_util::getString(
1030*179db1d7SKowalski, Kamil           "Gateway", input[entryIdx], gatewayFieldValue,
1031*179db1d7SKowalski, Kamil           static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1032*179db1d7SKowalski, Kamil           asyncResp->res.json_value,
1033*179db1d7SKowalski, Kamil           "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1034*179db1d7SKowalski, Kamil 
1035*179db1d7SKowalski, Kamil       if (addressFieldState == json_util::Result::WRONG_TYPE ||
1036*179db1d7SKowalski, Kamil           subnetMaskFieldState == json_util::Result::WRONG_TYPE ||
1037*179db1d7SKowalski, Kamil           addressOriginFieldState == json_util::Result::WRONG_TYPE ||
1038*179db1d7SKowalski, Kamil           gatewayFieldState == json_util::Result::WRONG_TYPE) {
1039*179db1d7SKowalski, Kamil         return;
1040*179db1d7SKowalski, Kamil       }
1041*179db1d7SKowalski, Kamil 
1042*179db1d7SKowalski, Kamil       if (addressFieldState == json_util::Result::SUCCESS &&
1043*179db1d7SKowalski, Kamil           !ethernet_provider.ipv4VerifyIpAndGetBitcount(*addressFieldValue)) {
1044*179db1d7SKowalski, Kamil         errorDetected = true;
1045*179db1d7SKowalski, Kamil         messages::addMessageToJson(
1046*179db1d7SKowalski, Kamil             asyncResp->res.json_value,
1047*179db1d7SKowalski, Kamil             messages::propertyValueFormatError(*addressFieldValue, "Address"),
1048*179db1d7SKowalski, Kamil             "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1049*179db1d7SKowalski, Kamil       }
1050*179db1d7SKowalski, Kamil 
1051*179db1d7SKowalski, Kamil       if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1052*179db1d7SKowalski, Kamil           !ethernet_provider.ipv4VerifyIpAndGetBitcount(
1053*179db1d7SKowalski, Kamil               *subnetMaskFieldValue, &subnetMaskAsPrefixLength)) {
1054*179db1d7SKowalski, Kamil         errorDetected = true;
1055*179db1d7SKowalski, Kamil         messages::addMessageToJson(
1056*179db1d7SKowalski, Kamil             asyncResp->res.json_value,
1057*179db1d7SKowalski, Kamil             messages::propertyValueFormatError(*subnetMaskFieldValue,
1058*179db1d7SKowalski, Kamil                                                "SubnetMask"),
1059*179db1d7SKowalski, Kamil             "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1060*179db1d7SKowalski, Kamil       }
1061*179db1d7SKowalski, Kamil 
1062*179db1d7SKowalski, Kamil       // Get Address origin in proper format
1063*179db1d7SKowalski, Kamil       addressOriginInDBusFormat =
1064*179db1d7SKowalski, Kamil           ethernet_provider.translateAddressOriginBetweenDBusAndRedfish(
1065*179db1d7SKowalski, Kamil               addressOriginFieldValue, true, false);
1066*179db1d7SKowalski, Kamil 
1067*179db1d7SKowalski, Kamil       if (addressOriginFieldState == json_util::Result::SUCCESS &&
1068*179db1d7SKowalski, Kamil           addressOriginInDBusFormat.empty()) {
1069*179db1d7SKowalski, Kamil         errorDetected = true;
1070*179db1d7SKowalski, Kamil         messages::addMessageToJson(
1071*179db1d7SKowalski, Kamil             asyncResp->res.json_value,
1072*179db1d7SKowalski, Kamil             messages::propertyValueNotInList(*addressOriginFieldValue,
1073*179db1d7SKowalski, Kamil                                              "AddressOrigin"),
1074*179db1d7SKowalski, Kamil             "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1075*179db1d7SKowalski, Kamil       }
1076*179db1d7SKowalski, Kamil 
1077*179db1d7SKowalski, Kamil       if (gatewayFieldState == json_util::Result::SUCCESS &&
1078*179db1d7SKowalski, Kamil           !ethernet_provider.ipv4VerifyIpAndGetBitcount(*gatewayFieldValue)) {
1079*179db1d7SKowalski, Kamil         errorDetected = true;
1080*179db1d7SKowalski, Kamil         messages::addMessageToJson(
1081*179db1d7SKowalski, Kamil             asyncResp->res.json_value,
1082*179db1d7SKowalski, Kamil             messages::propertyValueFormatError(*gatewayFieldValue, "Gateway"),
1083*179db1d7SKowalski, Kamil             "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1084*179db1d7SKowalski, Kamil       }
1085*179db1d7SKowalski, Kamil 
1086*179db1d7SKowalski, Kamil       // If any error occured do not proceed with current entry, but do not
1087*179db1d7SKowalski, Kamil       // end loop
1088*179db1d7SKowalski, Kamil       if (errorDetected) {
1089*179db1d7SKowalski, Kamil         errorDetected = false;
1090*179db1d7SKowalski, Kamil         continue;
1091*179db1d7SKowalski, Kamil       }
1092*179db1d7SKowalski, Kamil 
1093*179db1d7SKowalski, Kamil       if (entryIdx >= ipv4_data.size()) {
1094*179db1d7SKowalski, Kamil         asyncResp->res.json_value["IPv4Addresses"][entryIdx] = input[entryIdx];
1095*179db1d7SKowalski, Kamil 
1096*179db1d7SKowalski, Kamil         // Verify that all field were provided
1097*179db1d7SKowalski, Kamil         if (addressFieldState == json_util::Result::NOT_EXIST) {
1098*179db1d7SKowalski, Kamil           errorDetected = true;
1099*179db1d7SKowalski, Kamil           messages::addMessageToJson(
1100*179db1d7SKowalski, Kamil               asyncResp->res.json_value, messages::propertyMissing("Address"),
1101*179db1d7SKowalski, Kamil               "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1102*179db1d7SKowalski, Kamil         }
1103*179db1d7SKowalski, Kamil 
1104*179db1d7SKowalski, Kamil         if (subnetMaskFieldState == json_util::Result::NOT_EXIST) {
1105*179db1d7SKowalski, Kamil           errorDetected = true;
1106*179db1d7SKowalski, Kamil           messages::addMessageToJson(
1107*179db1d7SKowalski, Kamil               asyncResp->res.json_value,
1108*179db1d7SKowalski, Kamil               messages::propertyMissing("SubnetMask"),
1109*179db1d7SKowalski, Kamil               "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1110*179db1d7SKowalski, Kamil         }
1111*179db1d7SKowalski, Kamil 
1112*179db1d7SKowalski, Kamil         if (addressOriginFieldState == json_util::Result::NOT_EXIST) {
1113*179db1d7SKowalski, Kamil           errorDetected = true;
1114*179db1d7SKowalski, Kamil           messages::addMessageToJson(
1115*179db1d7SKowalski, Kamil               asyncResp->res.json_value,
1116*179db1d7SKowalski, Kamil               messages::propertyMissing("AddressOrigin"),
1117*179db1d7SKowalski, Kamil               "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1118*179db1d7SKowalski, Kamil         }
1119*179db1d7SKowalski, Kamil 
1120*179db1d7SKowalski, Kamil         if (gatewayFieldState == json_util::Result::NOT_EXIST) {
1121*179db1d7SKowalski, Kamil           errorDetected = true;
1122*179db1d7SKowalski, Kamil           messages::addMessageToJson(
1123*179db1d7SKowalski, Kamil               asyncResp->res.json_value, messages::propertyMissing("Gateway"),
1124*179db1d7SKowalski, Kamil               "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1125*179db1d7SKowalski, Kamil         }
1126*179db1d7SKowalski, Kamil 
1127*179db1d7SKowalski, Kamil         // If any error occured do not proceed with current entry, but do not
1128*179db1d7SKowalski, Kamil         // end loop
1129*179db1d7SKowalski, Kamil         if (errorDetected) {
1130*179db1d7SKowalski, Kamil           errorDetected = false;
1131*179db1d7SKowalski, Kamil           continue;
1132*179db1d7SKowalski, Kamil         }
1133*179db1d7SKowalski, Kamil 
1134*179db1d7SKowalski, Kamil         // Create IPv4 with provided data
1135*179db1d7SKowalski, Kamil         ethernet_provider.createIPv4(
1136*179db1d7SKowalski, Kamil             ifaceId, entryIdx, subnetMaskAsPrefixLength, *gatewayFieldValue,
1137*179db1d7SKowalski, Kamil             *addressFieldValue, asyncResp);
1138*179db1d7SKowalski, Kamil       } else {
1139*179db1d7SKowalski, Kamil         // Existing object that should be modified/deleted/remain unchanged
1140*179db1d7SKowalski, Kamil         if (input[entryIdx].is_null()) {
1141*179db1d7SKowalski, Kamil           // Object should be deleted
1142*179db1d7SKowalski, Kamil           ethernet_provider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id,
1143*179db1d7SKowalski, Kamil                                        entryIdx, asyncResp);
1144*179db1d7SKowalski, Kamil         } else if (input[entryIdx].is_object()) {
1145*179db1d7SKowalski, Kamil           if (input[entryIdx].size() == 0) {
1146*179db1d7SKowalski, Kamil             // Object shall remain unchanged
1147*179db1d7SKowalski, Kamil             continue;
1148*179db1d7SKowalski, Kamil           }
1149*179db1d7SKowalski, Kamil 
1150*179db1d7SKowalski, Kamil           // Apply changes
1151*179db1d7SKowalski, Kamil           if (addressFieldState == json_util::Result::SUCCESS &&
1152*179db1d7SKowalski, Kamil               ipv4_data[entryIdx].address != nullptr &&
1153*179db1d7SKowalski, Kamil               *ipv4_data[entryIdx].address != *addressFieldValue) {
1154*179db1d7SKowalski, Kamil             ethernet_provider.changeIPv4AddressProperty(
1155*179db1d7SKowalski, Kamil                 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Address",
1156*179db1d7SKowalski, Kamil                 *addressFieldValue, asyncResp);
1157*179db1d7SKowalski, Kamil           }
1158*179db1d7SKowalski, Kamil 
1159*179db1d7SKowalski, Kamil           if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1160*179db1d7SKowalski, Kamil               ipv4_data[entryIdx].netmask != *subnetMaskFieldValue) {
1161*179db1d7SKowalski, Kamil             ethernet_provider.changeIPv4SubnetMaskProperty(
1162*179db1d7SKowalski, Kamil                 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1163*179db1d7SKowalski, Kamil                 *subnetMaskFieldValue, subnetMaskAsPrefixLength, asyncResp);
1164*179db1d7SKowalski, Kamil           }
1165*179db1d7SKowalski, Kamil 
1166*179db1d7SKowalski, Kamil           if (addressOriginFieldState == json_util::Result::SUCCESS &&
1167*179db1d7SKowalski, Kamil               ipv4_data[entryIdx].origin != *addressFieldValue) {
1168*179db1d7SKowalski, Kamil             ethernet_provider.changeIPv4Origin(
1169*179db1d7SKowalski, Kamil                 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1170*179db1d7SKowalski, Kamil                 *addressOriginFieldValue, addressOriginInDBusFormat, asyncResp);
1171*179db1d7SKowalski, Kamil           }
1172*179db1d7SKowalski, Kamil 
1173*179db1d7SKowalski, Kamil           if (gatewayFieldState == json_util::Result::SUCCESS &&
1174*179db1d7SKowalski, Kamil               ipv4_data[entryIdx].gateway != nullptr &&
1175*179db1d7SKowalski, Kamil               *ipv4_data[entryIdx].gateway != *gatewayFieldValue) {
1176*179db1d7SKowalski, Kamil             ethernet_provider.changeIPv4AddressProperty(
1177*179db1d7SKowalski, Kamil                 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Gateway",
1178*179db1d7SKowalski, Kamil                 *gatewayFieldValue, asyncResp);
1179*179db1d7SKowalski, Kamil           }
1180*179db1d7SKowalski, Kamil         }
1181*179db1d7SKowalski, Kamil       }
1182*179db1d7SKowalski, Kamil     }
1183*179db1d7SKowalski, Kamil   }
1184*179db1d7SKowalski, Kamil 
1185588c3f0dSKowalski, Kamil   nlohmann::json parseInterfaceData(
1186588c3f0dSKowalski, Kamil       const std::string &iface_id, const EthernetInterfaceData &eth_data,
1187588c3f0dSKowalski, Kamil       const std::vector<IPv4AddressData> &ipv4_data) {
1188588c3f0dSKowalski, Kamil     // Copy JSON object to avoid race condition
1189588c3f0dSKowalski, Kamil     nlohmann::json json_response(Node::json);
1190588c3f0dSKowalski, Kamil 
1191588c3f0dSKowalski, Kamil     // Fill out obvious data...
1192588c3f0dSKowalski, Kamil     json_response["Id"] = iface_id;
1193588c3f0dSKowalski, Kamil     json_response["@odata.id"] =
1194588c3f0dSKowalski, Kamil         "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + iface_id;
1195588c3f0dSKowalski, Kamil 
1196588c3f0dSKowalski, Kamil     // ... then the one from DBus, regarding eth iface...
1197588c3f0dSKowalski, Kamil     if (eth_data.speed != nullptr) json_response["SpeedMbps"] = *eth_data.speed;
1198588c3f0dSKowalski, Kamil 
1199588c3f0dSKowalski, Kamil     if (eth_data.mac_address != nullptr)
1200588c3f0dSKowalski, Kamil       json_response["MACAddress"] = *eth_data.mac_address;
1201588c3f0dSKowalski, Kamil 
1202588c3f0dSKowalski, Kamil     if (eth_data.hostname != nullptr)
1203588c3f0dSKowalski, Kamil       json_response["HostName"] = *eth_data.hostname;
1204588c3f0dSKowalski, Kamil 
1205588c3f0dSKowalski, Kamil     if (eth_data.vlan_id != nullptr) {
1206588c3f0dSKowalski, Kamil       nlohmann::json &vlanObj = json_response["VLAN"];
1207588c3f0dSKowalski, Kamil       vlanObj["VLANEnable"] = true;
1208588c3f0dSKowalski, Kamil       vlanObj["VLANId"] = *eth_data.vlan_id;
1209588c3f0dSKowalski, Kamil     }
1210588c3f0dSKowalski, Kamil 
1211588c3f0dSKowalski, Kamil     // ... at last, check if there are IPv4 data and prepare appropriate
1212588c3f0dSKowalski, Kamil     // collection
1213588c3f0dSKowalski, Kamil     if (ipv4_data.size() > 0) {
1214588c3f0dSKowalski, Kamil       nlohmann::json ipv4_array = nlohmann::json::array();
1215588c3f0dSKowalski, Kamil       for (auto &ipv4_config : ipv4_data) {
1216588c3f0dSKowalski, Kamil         nlohmann::json json_ipv4;
1217588c3f0dSKowalski, Kamil         if (ipv4_config.address != nullptr) {
1218588c3f0dSKowalski, Kamil           json_ipv4["Address"] = *ipv4_config.address;
1219588c3f0dSKowalski, Kamil           if (ipv4_config.gateway != nullptr)
1220588c3f0dSKowalski, Kamil             json_ipv4["Gateway"] = *ipv4_config.gateway;
1221588c3f0dSKowalski, Kamil 
1222588c3f0dSKowalski, Kamil           json_ipv4["AddressOrigin"] = ipv4_config.origin;
1223588c3f0dSKowalski, Kamil           json_ipv4["SubnetMask"] = ipv4_config.netmask;
1224588c3f0dSKowalski, Kamil 
1225588c3f0dSKowalski, Kamil           ipv4_array.push_back(std::move(json_ipv4));
1226588c3f0dSKowalski, Kamil         }
1227588c3f0dSKowalski, Kamil       }
1228588c3f0dSKowalski, Kamil       json_response["IPv4Addresses"] = std::move(ipv4_array);
1229588c3f0dSKowalski, Kamil     }
1230588c3f0dSKowalski, Kamil 
1231588c3f0dSKowalski, Kamil     return json_response;
1232588c3f0dSKowalski, Kamil   }
1233588c3f0dSKowalski, Kamil 
12349391bb9cSRapkiewicz, Pawel   /**
12359391bb9cSRapkiewicz, Pawel    * Functions triggers appropriate requests on DBus
12369391bb9cSRapkiewicz, Pawel    */
12379391bb9cSRapkiewicz, Pawel   void doGet(crow::response &res, const crow::request &req,
12389391bb9cSRapkiewicz, Pawel              const std::vector<std::string> &params) override {
12399391bb9cSRapkiewicz, Pawel     // TODO(Pawel) this shall be parametrized call (two params) to get
12409391bb9cSRapkiewicz, Pawel     // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
12419391bb9cSRapkiewicz, Pawel     // Check if there is required param, truly entering this shall be
12429391bb9cSRapkiewicz, Pawel     // impossible.
12439391bb9cSRapkiewicz, Pawel     if (params.size() != 1) {
1244e0d918bcSEd Tanous       res.result(boost::beast::http::status::internal_server_error);
12459391bb9cSRapkiewicz, Pawel       res.end();
12469391bb9cSRapkiewicz, Pawel       return;
12479391bb9cSRapkiewicz, Pawel     }
12489391bb9cSRapkiewicz, Pawel 
12499391bb9cSRapkiewicz, Pawel     const std::string &iface_id = params[0];
12509391bb9cSRapkiewicz, Pawel 
12519391bb9cSRapkiewicz, Pawel     // Get single eth interface data, and call the below callback for JSON
12529391bb9cSRapkiewicz, Pawel     // preparation
12539391bb9cSRapkiewicz, Pawel     ethernet_provider.getEthernetIfaceData(
12549391bb9cSRapkiewicz, Pawel         iface_id, [&, iface_id](const bool &success,
12559391bb9cSRapkiewicz, Pawel                                 const EthernetInterfaceData &eth_data,
12569391bb9cSRapkiewicz, Pawel                                 const std::vector<IPv4AddressData> &ipv4_data) {
12579391bb9cSRapkiewicz, Pawel           if (success) {
1258588c3f0dSKowalski, Kamil             res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data);
12599391bb9cSRapkiewicz, Pawel           } else {
12609391bb9cSRapkiewicz, Pawel             // ... otherwise return error
12619391bb9cSRapkiewicz, Pawel             // TODO(Pawel)consider distinguish between non existing object, and
12629391bb9cSRapkiewicz, Pawel             // other errors
1263e0d918bcSEd Tanous             res.result(boost::beast::http::status::not_found);
12649391bb9cSRapkiewicz, Pawel           }
12659391bb9cSRapkiewicz, Pawel           res.end();
12669391bb9cSRapkiewicz, Pawel         });
12679391bb9cSRapkiewicz, Pawel   }
12689391bb9cSRapkiewicz, Pawel 
1269588c3f0dSKowalski, Kamil   void doPatch(crow::response &res, const crow::request &req,
1270588c3f0dSKowalski, Kamil                const std::vector<std::string> &params) override {
1271588c3f0dSKowalski, Kamil     // TODO(Pawel) this shall be parametrized call (two params) to get
1272588c3f0dSKowalski, Kamil     // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1273588c3f0dSKowalski, Kamil     // Check if there is required param, truly entering this shall be
1274588c3f0dSKowalski, Kamil     // impossible.
1275588c3f0dSKowalski, Kamil     if (params.size() != 1) {
1276588c3f0dSKowalski, Kamil       res.result(boost::beast::http::status::internal_server_error);
1277588c3f0dSKowalski, Kamil       res.end();
1278588c3f0dSKowalski, Kamil       return;
1279588c3f0dSKowalski, Kamil     }
1280588c3f0dSKowalski, Kamil 
1281588c3f0dSKowalski, Kamil     const std::string &iface_id = params[0];
1282588c3f0dSKowalski, Kamil 
1283*179db1d7SKowalski, Kamil     nlohmann::json patchReq;
1284588c3f0dSKowalski, Kamil 
1285*179db1d7SKowalski, Kamil     if (!json_util::processJsonFromRequest(res, req, patchReq)) {
1286588c3f0dSKowalski, Kamil       return;
1287588c3f0dSKowalski, Kamil     }
1288588c3f0dSKowalski, Kamil 
1289588c3f0dSKowalski, Kamil     // Get single eth interface data, and call the below callback for JSON
1290588c3f0dSKowalski, Kamil     // preparation
1291588c3f0dSKowalski, Kamil     ethernet_provider.getEthernetIfaceData(
1292588c3f0dSKowalski, Kamil         iface_id,
1293588c3f0dSKowalski, Kamil         [&, iface_id, patchReq = std::move(patchReq) ](
1294588c3f0dSKowalski, Kamil             const bool &success, const EthernetInterfaceData &eth_data,
1295588c3f0dSKowalski, Kamil             const std::vector<IPv4AddressData> &ipv4_data) {
1296588c3f0dSKowalski, Kamil           if (!success) {
1297588c3f0dSKowalski, Kamil             // ... otherwise return error
1298588c3f0dSKowalski, Kamil             // TODO(Pawel)consider distinguish between non existing object, and
1299588c3f0dSKowalski, Kamil             // other errors
1300588c3f0dSKowalski, Kamil             res.result(boost::beast::http::status::not_found);
1301588c3f0dSKowalski, Kamil             res.end();
1302588c3f0dSKowalski, Kamil 
1303588c3f0dSKowalski, Kamil             return;
1304588c3f0dSKowalski, Kamil           }
1305588c3f0dSKowalski, Kamil 
1306588c3f0dSKowalski, Kamil           res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data);
1307588c3f0dSKowalski, Kamil 
1308588c3f0dSKowalski, Kamil           std::shared_ptr<AsyncResp> asyncResp =
1309588c3f0dSKowalski, Kamil               std::make_shared<AsyncResp>(res);
1310588c3f0dSKowalski, Kamil 
1311588c3f0dSKowalski, Kamil           for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end();
1312588c3f0dSKowalski, Kamil                ++propertyIt) {
1313588c3f0dSKowalski, Kamil             if (propertyIt.key() == "VLAN") {
1314588c3f0dSKowalski, Kamil               handleVlanPatch(iface_id, propertyIt.value(), eth_data,
1315588c3f0dSKowalski, Kamil                               asyncResp);
1316588c3f0dSKowalski, Kamil             } else if (propertyIt.key() == "HostName") {
1317588c3f0dSKowalski, Kamil               handleHostnamePatch(propertyIt.value(), eth_data, asyncResp);
1318*179db1d7SKowalski, Kamil             } else if (propertyIt.key() == "IPv4Addresses") {
1319*179db1d7SKowalski, Kamil               handleIPv4Patch(iface_id, propertyIt.value(), ipv4_data,
1320*179db1d7SKowalski, Kamil                               asyncResp);
1321*179db1d7SKowalski, Kamil             } else if (propertyIt.key() == "IPv6Addresses") {
1322*179db1d7SKowalski, Kamil               // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
1323*179db1d7SKowalski, Kamil               messages::addMessageToJsonRoot(
1324*179db1d7SKowalski, Kamil                   res.json_value,
1325*179db1d7SKowalski, Kamil                   messages::propertyNotWritable(propertyIt.key()));
1326588c3f0dSKowalski, Kamil             } else {
1327588c3f0dSKowalski, Kamil               auto fieldInJsonIt = res.json_value.find(propertyIt.key());
1328588c3f0dSKowalski, Kamil 
1329588c3f0dSKowalski, Kamil               if (fieldInJsonIt == res.json_value.end()) {
1330588c3f0dSKowalski, Kamil                 // Field not in scope of defined fields
1331588c3f0dSKowalski, Kamil                 messages::addMessageToJsonRoot(
1332588c3f0dSKowalski, Kamil                     res.json_value,
1333588c3f0dSKowalski, Kamil                     messages::propertyUnknown(propertyIt.key()));
1334588c3f0dSKowalski, Kamil               } else if (*fieldInJsonIt != *propertyIt) {
1335588c3f0dSKowalski, Kamil                 // User attempted to modify non-writable field
1336588c3f0dSKowalski, Kamil                 messages::addMessageToJsonRoot(
1337588c3f0dSKowalski, Kamil                     res.json_value,
1338588c3f0dSKowalski, Kamil                     messages::propertyNotWritable(propertyIt.key()));
1339588c3f0dSKowalski, Kamil               }
1340588c3f0dSKowalski, Kamil             }
1341588c3f0dSKowalski, Kamil           }
1342588c3f0dSKowalski, Kamil         });
1343588c3f0dSKowalski, Kamil   }
1344588c3f0dSKowalski, Kamil 
13459391bb9cSRapkiewicz, Pawel   // Ethernet Provider object
13469391bb9cSRapkiewicz, Pawel   // TODO(Pawel) consider move it to singleton
13479391bb9cSRapkiewicz, Pawel   OnDemandEthernetProvider ethernet_provider;
13489391bb9cSRapkiewicz, Pawel };
13499391bb9cSRapkiewicz, Pawel 
13509391bb9cSRapkiewicz, Pawel }  // namespace redfish
1351