xref: /openbmc/bmcweb/features/redfish/lib/ethernet.hpp (revision 179db1d7c76af721cb9390cde1c2befff9e9c685)
1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #pragma once
17 
18 #include <dbus_singleton.hpp>
19 #include <error_messages.hpp>
20 #include <node.hpp>
21 #include <utils/json_utils.hpp>
22 #include <boost/container/flat_map.hpp>
23 
24 namespace redfish {
25 
26 /**
27  * DBus types primitives for several generic DBus interfaces
28  * TODO(Pawel) consider move this to separate file into boost::dbus
29  */
30 using PropertiesMapType = boost::container::flat_map<
31     std::string,
32     sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
33                                 int32_t, uint32_t, int64_t, uint64_t, double>>;
34 
35 using GetManagedObjectsType = boost::container::flat_map<
36     sdbusplus::message::object_path,
37     boost::container::flat_map<
38         std::string,
39         boost::container::flat_map<
40             std::string, sdbusplus::message::variant<
41                              std::string, bool, uint8_t, int16_t, uint16_t,
42                              int32_t, uint32_t, int64_t, uint64_t, double>>>>;
43 
44 /**
45  * Structure for keeping IPv4 data required by Redfish
46  * TODO(Pawel) consider change everything to ptr, or to non-ptr values.
47  */
48 struct IPv4AddressData {
49   std::string id;
50   const std::string *address;
51   const std::string *domain;
52   const std::string *gateway;
53   std::string netmask;
54   std::string origin;
55   bool global;
56   /**
57    * @brief Operator< to enable sorting
58    *
59    * @param[in] obj   Object to compare with
60    *
61    * @return This object id < supplied object id
62    */
63   bool operator<(const IPv4AddressData &obj) const { return (id < obj.id); }
64 };
65 
66 /**
67  * Structure for keeping basic single Ethernet Interface information
68  * available from DBus
69  */
70 struct EthernetInterfaceData {
71   const unsigned int *speed;
72   const bool *auto_neg;
73   const std::string *hostname;
74   const std::string *default_gateway;
75   const std::string *mac_address;
76   const unsigned int *vlan_id;
77 };
78 
79 /**
80  * OnDemandEthernetProvider
81  * Ethernet provider class that retrieves data directly from dbus, before
82  * setting it into JSON output. This does not cache any data.
83  *
84  * TODO(Pawel)
85  * This perhaps shall be different file, which has to be chosen on compile time
86  * depending on OEM needs
87  */
88 class OnDemandEthernetProvider {
89  private:
90   // Consts that may have influence on EthernetProvider performance/memory usage
91   const size_t MAX_IPV4_ADDRESSES_PER_INTERFACE = 10;
92 
93   // Helper function that allows to extract GetAllPropertiesType from
94   // GetManagedObjectsType, based on object path, and interface name
95   const PropertiesMapType *extractInterfaceProperties(
96       const sdbusplus::message::object_path &objpath,
97       const std::string &interface, const GetManagedObjectsType &dbus_data) {
98     const auto &dbus_obj = dbus_data.find(objpath);
99     if (dbus_obj != dbus_data.end()) {
100       const auto &iface = dbus_obj->second.find(interface);
101       if (iface != dbus_obj->second.end()) {
102         return &iface->second;
103       }
104     }
105     return nullptr;
106   }
107 
108   // Helper Wrapper that does inline object_path conversion from string
109   // into sdbusplus::message::object_path type
110   inline const PropertiesMapType *extractInterfaceProperties(
111       const std::string &objpath, const std::string &interface,
112       const GetManagedObjectsType &dbus_data) {
113     const auto &dbus_obj = sdbusplus::message::object_path{objpath};
114     return extractInterfaceProperties(dbus_obj, interface, dbus_data);
115   }
116 
117   // Helper function that allows to get pointer to the property from
118   // GetAllPropertiesType native, or extracted by GetAllPropertiesType
119   template <typename T>
120   inline T const *const extractProperty(const PropertiesMapType &properties,
121                                         const std::string &name) {
122     const auto &property = properties.find(name);
123     if (property != properties.end()) {
124       return mapbox::get_ptr<const T>(property->second);
125     }
126     return nullptr;
127   }
128   // TODO(Pawel) Consider to move the above functions to dbus
129   // generic_interfaces.hpp
130 
131   // Helper function that extracts data from several dbus objects and several
132   // interfaces required by single ethernet interface instance
133   void extractEthernetInterfaceData(const std::string &ethiface_id,
134                                     const GetManagedObjectsType &dbus_data,
135                                     EthernetInterfaceData &eth_data) {
136     // Extract data that contains MAC Address
137     const PropertiesMapType *mac_properties = extractInterfaceProperties(
138         "/xyz/openbmc_project/network/" + ethiface_id,
139         "xyz.openbmc_project.Network.MACAddress", dbus_data);
140 
141     if (mac_properties != nullptr) {
142       eth_data.mac_address =
143           extractProperty<std::string>(*mac_properties, "MACAddress");
144     }
145 
146     const PropertiesMapType *vlan_properties = extractInterfaceProperties(
147         "/xyz/openbmc_project/network/" + ethiface_id,
148         "xyz.openbmc_project.Network.VLAN", dbus_data);
149 
150     if (vlan_properties != nullptr) {
151       eth_data.vlan_id = extractProperty<unsigned int>(*vlan_properties, "Id");
152     }
153 
154     // Extract data that contains link information (auto negotiation and speed)
155     const PropertiesMapType *eth_properties = extractInterfaceProperties(
156         "/xyz/openbmc_project/network/" + ethiface_id,
157         "xyz.openbmc_project.Network.EthernetInterface", dbus_data);
158 
159     if (eth_properties != nullptr) {
160       eth_data.auto_neg = extractProperty<bool>(*eth_properties, "AutoNeg");
161       eth_data.speed = extractProperty<unsigned int>(*eth_properties, "Speed");
162     }
163 
164     // Extract data that contains network config (HostName and DefaultGW)
165     const PropertiesMapType *config_properties = extractInterfaceProperties(
166         "/xyz/openbmc_project/network/config",
167         "xyz.openbmc_project.Network.SystemConfiguration", dbus_data);
168 
169     if (config_properties != nullptr) {
170       eth_data.hostname =
171           extractProperty<std::string>(*config_properties, "HostName");
172       eth_data.default_gateway =
173           extractProperty<std::string>(*config_properties, "DefaultGateway");
174     }
175   }
176 
177   // Helper function that changes bits netmask notation (i.e. /24)
178   // into full dot notation
179   inline std::string getNetmask(unsigned int bits) {
180     uint32_t value = 0xffffffff << (32 - bits);
181     std::string netmask = std::to_string((value >> 24) & 0xff) + "." +
182                           std::to_string((value >> 16) & 0xff) + "." +
183                           std::to_string((value >> 8) & 0xff) + "." +
184                           std::to_string(value & 0xff);
185     return netmask;
186   }
187 
188   // Helper function that extracts data for single ethernet ipv4 address
189   void extractIPv4Data(const std::string &ethiface_id,
190                        const GetManagedObjectsType &dbus_data,
191                        std::vector<IPv4AddressData> &ipv4_config) {
192     const std::string pathStart =
193         "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/";
194 
195     // Since there might be several IPv4 configurations aligned with
196     // single ethernet interface, loop over all of them
197     for (auto &objpath : dbus_data) {
198       // Check if proper patter for object path appears
199       if (boost::starts_with(
200               static_cast<const std::string &>(objpath.first),
201               "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/")) {
202         // and get approrpiate interface
203         const auto &interface =
204             objpath.second.find("xyz.openbmc_project.Network.IP");
205         if (interface != objpath.second.end()) {
206           // Make a properties 'shortcut', to make everything more readable
207           const PropertiesMapType &properties = interface->second;
208           // Instance IPv4AddressData structure, and set as appropriate
209           IPv4AddressData ipv4_address;
210 
211           ipv4_address.id = static_cast<const std::string &>(objpath.first)
212                                 .substr(pathStart.size());
213 
214           // IPv4 address
215           ipv4_address.address =
216               extractProperty<std::string>(properties, "Address");
217           // IPv4 gateway
218           ipv4_address.gateway =
219               extractProperty<std::string>(properties, "Gateway");
220 
221           // Origin is kind of DBus object so fetch pointer...
222           const std::string *origin =
223               extractProperty<std::string>(properties, "Origin");
224           if (origin != nullptr) {
225             ipv4_address.origin =
226                 translateAddressOriginBetweenDBusAndRedfish(origin, true, true);
227           }
228 
229           // Netmask is presented as PrefixLength
230           const auto *mask =
231               extractProperty<uint8_t>(properties, "PrefixLength");
232           if (mask != nullptr) {
233             // convert it to the string
234             ipv4_address.netmask = getNetmask(*mask);
235           }
236 
237           // Attach IPv4 only if address is present
238           if (ipv4_address.address != nullptr) {
239             // Check if given address is local, or global
240             if (boost::starts_with(*ipv4_address.address, "169.254")) {
241               ipv4_address.global = false;
242             } else {
243               ipv4_address.global = true;
244             }
245             ipv4_config.emplace_back(std::move(ipv4_address));
246           }
247         }
248       }
249     }
250 
251     /**
252      * We have to sort this vector and ensure that order of IPv4 addresses
253      * is consistent between GETs to allow modification and deletion in PATCHes
254      */
255     std::sort(ipv4_config.begin(), ipv4_config.end());
256   }
257 
258   static const constexpr int ipV4AddressSectionsCount = 4;
259 
260  public:
261   /**
262    * @brief Creates VLAN for given interface with given Id through D-Bus
263    *
264    * @param[in] ifaceId       Id of interface for which VLAN will be created
265    * @param[in] inputVlanId   ID of the new VLAN
266    * @param[in] callback      Function that will be called after the operation
267    *
268    * @return None.
269    */
270   template <typename CallbackFunc>
271   void createVlan(const std::string &ifaceId, const uint64_t &inputVlanId,
272                   CallbackFunc &&callback) {
273     crow::connections::system_bus->async_method_call(
274         callback, "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
275         "xyz.openbmc_project.Network.VLAN.Create", "VLAN", ifaceId,
276         static_cast<uint32_t>(inputVlanId));
277   };
278 
279   /**
280    * @brief Sets given Id on the given VLAN interface through D-Bus
281    *
282    * @param[in] ifaceId       Id of VLAN interface that should be modified
283    * @param[in] inputVlanId   New ID of the VLAN
284    * @param[in] callback      Function that will be called after the operation
285    *
286    * @return None.
287    */
288   template <typename CallbackFunc>
289   void changeVlanId(const std::string &ifaceId, const uint32_t &inputVlanId,
290                     CallbackFunc &&callback) {
291     crow::connections::system_bus->async_method_call(
292         callback, "xyz.openbmc_project.Network",
293         std::string("/xyz/openbmc_project/network/") + ifaceId,
294         "org.freedesktop.DBus.Properties", "Set",
295         "xyz.openbmc_project.Network.VLAN", "Id",
296         sdbusplus::message::variant<uint32_t>(inputVlanId));
297   };
298 
299   /**
300    * @brief Helper function that verifies IP address to check if it is in
301    *        proper format. If bits pointer is provided, also calculates active
302    *        bit count for Subnet Mask.
303    *
304    * @param[in]  ip     IP that will be verified
305    * @param[out] bits   Calculated mask in bits notation
306    *
307    * @return true in case of success, false otherwise
308    */
309   bool ipv4VerifyIpAndGetBitcount(const std::string &ip,
310                                   uint8_t *bits = nullptr) {
311     std::vector<std::string> bytesInMask;
312 
313     boost::split(bytesInMask, ip, boost::is_any_of("."));
314 
315     if (bytesInMask.size() != ipV4AddressSectionsCount) {
316       return false;
317     }
318 
319     if (bits != nullptr) {
320       *bits = 0;
321     }
322 
323     char *endPtr;
324     long previousValue = 255;
325     bool firstZeroInByteHit;
326     for (uint8_t byteIdx = 0; byteIdx < ipV4AddressSectionsCount; byteIdx++) {
327       // Use strtol instead of stroi to avoid exceptions
328       long value = std::strtol(bytesInMask[byteIdx].c_str(), &endPtr, 10);
329 
330       // endPtr should point to the end of the string, otherwise given string
331       // is not 100% number
332       if (*endPtr != '\0') {
333         return false;
334       }
335 
336       // Value should be contained in byte
337       if (value < 0 || value > 255) {
338         return false;
339       }
340 
341       if (bits != nullptr) {
342         // Mask has to be continuous between bytes
343         if (previousValue != 255 && value != 0) {
344           return false;
345         }
346 
347         // Mask has to be continuous inside bytes
348         firstZeroInByteHit = false;
349 
350         // Count bits
351         for (int bitIdx = 7; bitIdx >= 0; bitIdx--) {
352           if (value & (1 << bitIdx)) {
353             if (firstZeroInByteHit) {
354               // Continuity not preserved
355               return false;
356             } else {
357               (*bits)++;
358             }
359           } else {
360             firstZeroInByteHit = true;
361           }
362         }
363       }
364 
365       previousValue = value;
366     }
367 
368     return true;
369   }
370 
371   /**
372    * @brief Changes IPv4 address type property (Address, Gateway)
373    *
374    * @param[in] ifaceId     Id of interface whose IP should be modified
375    * @param[in] ipIdx       Index of IP in input array that should be modified
376    * @param[in] ipHash      DBus Hash id of modified IP
377    * @param[in] name        Name of field in JSON representation
378    * @param[in] newValue    New value that should be written
379    * @param[io] asyncResp   Response object that will be returned to client
380    *
381    * @return true if give IP is valid and has been sent do D-Bus, false
382    * otherwise
383    */
384   void changeIPv4AddressProperty(const std::string &ifaceId, int ipIdx,
385                                  const std::string &ipHash,
386                                  const std::string &name,
387                                  const std::string &newValue,
388                                  const std::shared_ptr<AsyncResp> &asyncResp) {
389     auto callback = [
390       asyncResp, ipIdx{std::move(ipIdx)}, name{std::move(name)},
391       newValue{std::move(newValue)}
392     ](const boost::system::error_code ec) {
393       if (ec) {
394         messages::addMessageToJson(
395             asyncResp->res.json_value, messages::internalError(),
396             "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name);
397       } else {
398         asyncResp->res.json_value["IPv4Addresses"][ipIdx][name] = newValue;
399       }
400     };
401 
402     crow::connections::system_bus->async_method_call(
403         std::move(callback), "xyz.openbmc_project.Network",
404         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
405         "org.freedesktop.DBus.Properties", "Set",
406         "xyz.openbmc_project.Network.IP", name,
407         sdbusplus::message::variant<std::string>(newValue));
408   };
409 
410   /**
411    * @brief Changes IPv4 address origin property
412    *
413    * @param[in] ifaceId       Id of interface whose IP should be modified
414    * @param[in] ipIdx         Index of IP in input array that should be modified
415    * @param[in] ipHash        DBus Hash id of modified IP
416    * @param[in] newValue      New value in Redfish format
417    * @param[in] newValueDbus  New value in D-Bus format
418    * @param[io] asyncResp     Response object that will be returned to client
419    *
420    * @return true if give IP is valid and has been sent do D-Bus, false
421    * otherwise
422    */
423   void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
424                         const std::string &ipHash, const std::string &newValue,
425                         const std::string &newValueDbus,
426                         const std::shared_ptr<AsyncResp> &asyncResp) {
427     auto callback =
428         [ asyncResp, ipIdx{std::move(ipIdx)},
429           newValue{std::move(newValue)} ](const boost::system::error_code ec) {
430       if (ec) {
431         messages::addMessageToJson(
432             asyncResp->res.json_value, messages::internalError(),
433             "/IPv4Addresses/" + std::to_string(ipIdx) + "/AddressOrigin");
434       } else {
435         asyncResp->res.json_value["IPv4Addresses"][ipIdx]["AddressOrigin"] =
436             newValue;
437       }
438     };
439 
440     crow::connections::system_bus->async_method_call(
441         std::move(callback), "xyz.openbmc_project.Network",
442         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
443         "org.freedesktop.DBus.Properties", "Set",
444         "xyz.openbmc_project.Network.IP", "Origin",
445         sdbusplus::message::variant<std::string>(newValueDbus));
446   };
447 
448   /**
449    * @brief Modifies SubnetMask for given IP
450    *
451    * @param[in] ifaceId      Id of interface whose IP should be modified
452    * @param[in] ipIdx        Index of IP in input array that should be modified
453    * @param[in] ipHash       DBus Hash id of modified IP
454    * @param[in] newValueStr  Mask in dot notation as string
455    * @param[in] newValue     Mask as PrefixLength in bitcount
456    * @param[io] asyncResp   Response object that will be returned to client
457    *
458    * @return None
459    */
460   void changeIPv4SubnetMaskProperty(
461       const std::string &ifaceId, int ipIdx, const std::string &ipHash,
462       const std::string &newValueStr, uint8_t &newValue,
463       const std::shared_ptr<AsyncResp> &asyncResp) {
464     auto callback = [
465       asyncResp, ipIdx{std::move(ipIdx)}, newValueStr{std::move(newValueStr)}
466     ](const boost::system::error_code ec) {
467       if (ec) {
468         messages::addMessageToJson(
469             asyncResp->res.json_value, messages::internalError(),
470             "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask");
471       } else {
472         asyncResp->res.json_value["IPv4Addresses"][ipIdx]["SubnetMask"] =
473             newValueStr;
474       }
475     };
476 
477     crow::connections::system_bus->async_method_call(
478         std::move(callback), "xyz.openbmc_project.Network",
479         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
480         "org.freedesktop.DBus.Properties", "Set",
481         "xyz.openbmc_project.Network.IP", "PrefixLength",
482         sdbusplus::message::variant<uint8_t>(newValue));
483   };
484 
485   /**
486    * @brief Disables VLAN with given ifaceId
487    *
488    * @param[in] ifaceId   Id of VLAN interface that should be disabled
489    * @param[in] callback  Function that will be called after the operation
490    *
491    * @return None.
492    */
493   template <typename CallbackFunc>
494   void disableVlan(const std::string &ifaceId, CallbackFunc &&callback) {
495     crow::connections::system_bus->async_method_call(
496         callback, "xyz.openbmc_project.Network",
497         std::string("/xyz/openbmc_project/network/") + ifaceId,
498         "xyz.openbmc_project.Object.Delete", "Delete");
499   };
500 
501   /**
502    * @brief Sets given HostName of the machine through D-Bus
503    *
504    * @param[in] newHostname   New name that HostName will be changed to
505    * @param[in] callback      Function that will be called after the operation
506    *
507    * @return None.
508    */
509   template <typename CallbackFunc>
510   void setHostName(const std::string &newHostname, CallbackFunc &&callback) {
511     crow::connections::system_bus->async_method_call(
512         callback, "xyz.openbmc_project.Network",
513         "/xyz/openbmc_project/network/config",
514         "org.freedesktop.DBus.Properties", "Set",
515         "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
516         sdbusplus::message::variant<std::string>(newHostname));
517   };
518 
519   /**
520    * @brief Deletes given IPv4
521    *
522    * @param[in] ifaceId     Id of interface whose IP should be deleted
523    * @param[in] ipIdx       Index of IP in input array that should be deleted
524    * @param[in] ipHash      DBus Hash id of IP that should be deleted
525    * @param[io] asyncResp   Response object that will be returned to client
526    *
527    * @return None
528    */
529   void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
530                   unsigned int ipIdx,
531                   const std::shared_ptr<AsyncResp> &asyncResp) {
532     crow::connections::system_bus->async_method_call(
533         [ ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)} ](
534             const boost::system::error_code ec) {
535           if (ec) {
536             messages::addMessageToJson(
537                 asyncResp->res.json_value, messages::internalError(),
538                 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
539           } else {
540             asyncResp->res.json_value["IPv4Addresses"][ipIdx] = nullptr;
541           }
542         },
543         "xyz.openbmc_project.Network",
544         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
545         "xyz.openbmc_project.Object.Delete", "Delete");
546   }
547 
548   /**
549    * @brief Creates IPv4 with given data
550    *
551    * @param[in] ifaceId     Id of interface whose IP should be deleted
552    * @param[in] ipIdx       Index of IP in input array that should be deleted
553    * @param[in] ipHash      DBus Hash id of IP that should be deleted
554    * @param[io] asyncResp   Response object that will be returned to client
555    *
556    * @return None
557    */
558   void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
559                   uint8_t subnetMask, const std::string &gateway,
560                   const std::string &address,
561                   const std::shared_ptr<AsyncResp> &asyncResp) {
562     auto createIpHandler = [
563       ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)}
564     ](const boost::system::error_code ec) {
565       if (ec) {
566         messages::addMessageToJson(
567             asyncResp->res.json_value, messages::internalError(),
568             "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
569       }
570     };
571 
572     crow::connections::system_bus->async_method_call(
573         std::move(createIpHandler), "xyz.openbmc_project.Network",
574         "/xyz/openbmc_project/network/" + ifaceId,
575         "xyz.openbmc_project.Network.IP.Create", "IP",
576         "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
577         gateway);
578   }
579 
580   /**
581    * @brief Translates Address Origin value from D-Bus to Redfish format and
582    *        vice-versa
583    *
584    * @param[in] inputOrigin Input value that should be translated
585    * @param[in] isIPv4      True for IPv4 origins, False for IPv6
586    * @param[in] isFromDBus  True for DBus->Redfish conversion, false for reverse
587    *
588    * @return Empty string in case of failure, translated value otherwise
589    */
590   std::string translateAddressOriginBetweenDBusAndRedfish(
591       const std::string *inputOrigin, bool isIPv4, bool isFromDBus) {
592     // Invalid pointer
593     if (inputOrigin == nullptr) {
594       return "";
595     }
596 
597     static const constexpr unsigned int firstIPv4OnlyIdx = 1;
598     static const constexpr unsigned int firstIPv6OnlyIdx = 3;
599 
600     std::array<std::pair<const char *, const char *>, 6> translationTable{
601         {{"xyz.openbmc_project.Network.IP.AddressOrigin.Static", "Static"},
602          {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCP"},
603          {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
604           "IPv4LinkLocal"},
605          {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCPv6"},
606          {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
607           "LinkLocal"},
608          {"xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC", "SLAAC"}}};
609 
610     for (unsigned int i = 0; i < translationTable.size(); i++) {
611       // Skip unrelated
612       if (isIPv4 && i >= firstIPv6OnlyIdx) break;
613       if (!isIPv4 && i >= firstIPv4OnlyIdx && i < firstIPv6OnlyIdx) continue;
614 
615       // When translating D-Bus to Redfish compare input to first element
616       if (isFromDBus && translationTable[i].first == *inputOrigin)
617         return translationTable[i].second;
618 
619       // When translating Redfish to D-Bus compare input to second element
620       if (!isFromDBus && translationTable[i].second == *inputOrigin)
621         return translationTable[i].first;
622     }
623 
624     // If we are still here, that means that value has not been found
625     return "";
626   }
627 
628   /**
629    * Function that retrieves all properties for given Ethernet Interface
630    * Object
631    * from EntityManager Network Manager
632    * @param ethiface_id a eth interface id to query on DBus
633    * @param callback a function that shall be called to convert Dbus output
634    * into JSON
635    */
636   template <typename CallbackFunc>
637   void getEthernetIfaceData(const std::string &ethiface_id,
638                             CallbackFunc &&callback) {
639     crow::connections::system_bus->async_method_call(
640         [
641           this, ethiface_id{std::move(ethiface_id)},
642           callback{std::move(callback)}
643         ](const boost::system::error_code error_code,
644           const GetManagedObjectsType &resp) {
645 
646           EthernetInterfaceData eth_data{};
647           std::vector<IPv4AddressData> ipv4_data;
648           ipv4_data.reserve(MAX_IPV4_ADDRESSES_PER_INTERFACE);
649 
650           if (error_code) {
651             // Something wrong on DBus, the error_code is not important at
652             // this moment, just return success=false, and empty output. Since
653             // size of vector may vary depending on information from Network
654             // Manager, and empty output could not be treated same way as
655             // error.
656             callback(false, eth_data, ipv4_data);
657             return;
658           }
659 
660           extractEthernetInterfaceData(ethiface_id, resp, eth_data);
661           extractIPv4Data(ethiface_id, resp, ipv4_data);
662 
663           // Fix global GW
664           for (IPv4AddressData &ipv4 : ipv4_data) {
665             if ((ipv4.global) &&
666                 ((ipv4.gateway == nullptr) || (*ipv4.gateway == "0.0.0.0"))) {
667               ipv4.gateway = eth_data.default_gateway;
668             }
669           }
670 
671           // Finally make a callback with usefull data
672           callback(true, eth_data, ipv4_data);
673         },
674         "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
675         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
676   };
677 
678   /**
679    * Function that retrieves all Ethernet Interfaces available through Network
680    * Manager
681    * @param callback a function that shall be called to convert Dbus output into
682    * JSON.
683    */
684   template <typename CallbackFunc>
685   void getEthernetIfaceList(CallbackFunc &&callback) {
686     crow::connections::system_bus->async_method_call(
687         [ this, callback{std::move(callback)} ](
688             const boost::system::error_code error_code,
689             GetManagedObjectsType &resp) {
690           // Callback requires vector<string> to retrieve all available ethernet
691           // interfaces
692           std::vector<std::string> iface_list;
693           iface_list.reserve(resp.size());
694           if (error_code) {
695             // Something wrong on DBus, the error_code is not important at this
696             // moment, just return success=false, and empty output. Since size
697             // of vector may vary depending on information from Network Manager,
698             // and empty output could not be treated same way as error.
699             callback(false, iface_list);
700             return;
701           }
702 
703           // Iterate over all retrieved ObjectPaths.
704           for (auto &objpath : resp) {
705             // And all interfaces available for certain ObjectPath.
706             for (auto &interface : objpath.second) {
707               // If interface is xyz.openbmc_project.Network.EthernetInterface,
708               // this is what we're looking for.
709               if (interface.first ==
710                   "xyz.openbmc_project.Network.EthernetInterface") {
711                 // Cut out everyting until last "/", ...
712                 const std::string &iface_id =
713                     static_cast<const std::string &>(objpath.first);
714                 std::size_t last_pos = iface_id.rfind("/");
715                 if (last_pos != std::string::npos) {
716                   // and put it into output vector.
717                   iface_list.emplace_back(iface_id.substr(last_pos + 1));
718                 }
719               }
720             }
721           }
722           // Finally make a callback with useful data
723           callback(true, iface_list);
724         },
725         "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
726         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
727   };
728 };
729 
730 /**
731  * EthernetCollection derived class for delivering Ethernet Collection Schema
732  */
733 class EthernetCollection : public Node {
734  public:
735   template <typename CrowApp>
736   // TODO(Pawel) Remove line from below, where we assume that there is only one
737   // manager called openbmc This shall be generic, but requires to update
738   // GetSubroutes method
739   EthernetCollection(CrowApp &app)
740       : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") {
741     Node::json["@odata.type"] =
742         "#EthernetInterfaceCollection.EthernetInterfaceCollection";
743     Node::json["@odata.context"] =
744         "/redfish/v1/"
745         "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
746     Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/EthernetInterfaces";
747     Node::json["Name"] = "Ethernet Network Interface Collection";
748     Node::json["Description"] =
749         "Collection of EthernetInterfaces for this Manager";
750 
751     entityPrivileges = {
752         {boost::beast::http::verb::get, {{"Login"}}},
753         {boost::beast::http::verb::head, {{"Login"}}},
754         {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
755         {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
756         {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
757         {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
758   }
759 
760  private:
761   /**
762    * Functions triggers appropriate requests on DBus
763    */
764   void doGet(crow::response &res, const crow::request &req,
765              const std::vector<std::string> &params) override {
766     // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
767     // any Manager, not only hardcoded 'openbmc'.
768     std::string manager_id = "openbmc";
769 
770     // Get eth interface list, and call the below callback for JSON preparation
771     ethernet_provider.getEthernetIfaceList(
772         [&, manager_id{std::move(manager_id)} ](
773             const bool &success, const std::vector<std::string> &iface_list) {
774           if (success) {
775             nlohmann::json iface_array = nlohmann::json::array();
776             for (const std::string &iface_item : iface_list) {
777               iface_array.push_back(
778                   {{"@odata.id", "/redfish/v1/Managers/" + manager_id +
779                                      "/EthernetInterfaces/" + iface_item}});
780             }
781             Node::json["Members"] = iface_array;
782             Node::json["Members@odata.count"] = iface_array.size();
783             Node::json["@odata.id"] =
784                 "/redfish/v1/Managers/" + manager_id + "/EthernetInterfaces";
785             res.json_value = Node::json;
786           } else {
787             // No success, best what we can do is return INTERNALL ERROR
788             res.result(boost::beast::http::status::internal_server_error);
789           }
790           res.end();
791         });
792   }
793 
794   // Ethernet Provider object
795   // TODO(Pawel) consider move it to singleton
796   OnDemandEthernetProvider ethernet_provider;
797 };
798 
799 /**
800  * EthernetInterface derived class for delivering Ethernet Schema
801  */
802 class EthernetInterface : public Node {
803  public:
804   /*
805    * Default Constructor
806    */
807   template <typename CrowApp>
808   // TODO(Pawel) Remove line from below, where we assume that there is only one
809   // manager called openbmc This shall be generic, but requires to update
810   // GetSubroutes method
811   EthernetInterface(CrowApp &app)
812       : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/",
813              std::string()) {
814     Node::json["@odata.type"] = "#EthernetInterface.v1_2_0.EthernetInterface";
815     Node::json["@odata.context"] =
816         "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
817     Node::json["Name"] = "Manager Ethernet Interface";
818     Node::json["Description"] = "Management Network Interface";
819 
820     entityPrivileges = {
821         {boost::beast::http::verb::get, {{"Login"}}},
822         {boost::beast::http::verb::head, {{"Login"}}},
823         {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
824         {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
825         {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
826         {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
827   }
828 
829  private:
830   void handleVlanPatch(const std::string &ifaceId, const nlohmann::json &input,
831                        const EthernetInterfaceData &eth_data,
832                        const std::shared_ptr<AsyncResp> &asyncResp) {
833     if (!input.is_object()) {
834       messages::addMessageToJson(
835           asyncResp->res.json_value,
836           messages::propertyValueTypeError(input.dump(), "VLAN"), "/VLAN");
837       return;
838     }
839 
840     bool inputVlanEnabled;
841     uint64_t inputVlanId;
842     json_util::Result inputVlanEnabledState = json_util::getBool(
843         "VLANEnable", input, inputVlanEnabled,
844         static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
845         asyncResp->res.json_value, std::string("/VLAN/VLANEnable"));
846     json_util::Result inputVlanIdState = json_util::getUnsigned(
847         "VLANId", input, inputVlanId,
848         static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
849         asyncResp->res.json_value, std::string("/VLAN/VLANId"));
850     bool inputInvalid = false;
851 
852     // Do not proceed if fields in VLAN object were of wrong type
853     if (inputVlanEnabledState == json_util::Result::WRONG_TYPE ||
854         inputVlanIdState == json_util::Result::WRONG_TYPE) {
855       return;
856     }
857 
858     // Verify input
859     if (eth_data.vlan_id == nullptr) {
860       // VLAN is currently disabled. User can only create/enable it. Change of
861       // VLANId is prohibited, and disable request (VLANEnabled == false) will
862       // not have any effect.
863       if (inputVlanEnabledState == json_util::Result::SUCCESS &&
864           inputVlanEnabled == true) {
865         // Creation requested, user should also provide ID for new VLAN
866         if (inputVlanIdState != json_util::Result::SUCCESS) {
867           messages::addMessageToJson(asyncResp->res.json_value,
868                                      messages::propertyMissing("VLANId"),
869                                      "/VLAN");
870           inputInvalid = true;
871         }
872       } else if (inputVlanIdState == json_util::Result::SUCCESS) {
873         // VLAN is disabled, but user requested modification. This is not valid.
874         messages::addMessageToJson(
875             asyncResp->res.json_value,
876             messages::actionParameterNotSupported("VLANId", "change VLAN Id"),
877             "/VLAN");
878 
879         messages::addMessageToJson(asyncResp->res.json_value,
880                                    messages::propertyMissing("VLANEnable"),
881                                    "/VLAN");
882 
883         inputInvalid = true;
884       }
885     } else {
886       // Load actual data into field values if they were not provided
887       if (inputVlanEnabledState == json_util::Result::NOT_EXIST) {
888         inputVlanEnabled = true;
889       }
890 
891       if (inputVlanIdState == json_util::Result::NOT_EXIST) {
892         inputVlanId = *eth_data.vlan_id;
893       }
894     }
895 
896     // Do not proceed if input has not been valid
897     if (inputInvalid) {
898       return;
899     }
900 
901     auto vlanEnabledAfterOperation =
902         [asyncResp](const boost::system::error_code ec) {
903           if (ec) {
904             messages::addMessageToJson(asyncResp->res.json_value,
905                                        messages::internalError(), "/VLAN");
906           } else {
907             asyncResp->res.json_value["VLAN"]["VLANEnable"] = true;
908           }
909         };
910 
911     if (eth_data.vlan_id == nullptr) {
912       if (inputVlanEnabled == true) {
913         ethernet_provider.createVlan(ifaceId, inputVlanId,
914                                      std::move(vlanEnabledAfterOperation));
915         asyncResp->res.json_value["VLAN"]["VLANId"] = inputVlanId;
916       }
917     } else {
918       // VLAN is configured on the interface
919       if (inputVlanEnabled == true && inputVlanId != *eth_data.vlan_id) {
920         // Change VLAN Id
921         asyncResp->res.json_value["VLAN"]["VLANId"] = inputVlanId;
922         ethernet_provider.changeVlanId(ifaceId,
923                                        static_cast<uint32_t>(inputVlanId),
924                                        std::move(vlanEnabledAfterOperation));
925       } else if (inputVlanEnabled == false) {
926         // Disable VLAN
927         ethernet_provider.disableVlan(
928             ifaceId, [asyncResp](const boost::system::error_code ec) {
929               if (ec) {
930                 messages::addMessageToJson(asyncResp->res.json_value,
931                                            messages::internalError(), "/VLAN");
932               } else {
933                 asyncResp->res.json_value["VLAN"]["VLANEnable"] = false;
934               }
935             });
936       }
937     }
938   }
939 
940   void handleHostnamePatch(const nlohmann::json &input,
941                            const EthernetInterfaceData &eth_data,
942                            const std::shared_ptr<AsyncResp> &asyncResp) {
943     if (input.is_string()) {
944       std::string newHostname = input.get<std::string>();
945 
946       if (eth_data.hostname == nullptr || newHostname != *eth_data.hostname) {
947         // Change hostname
948         ethernet_provider.setHostName(
949             newHostname,
950             [asyncResp, newHostname](const boost::system::error_code ec) {
951               if (ec) {
952                 messages::addMessageToJson(asyncResp->res.json_value,
953                                            messages::internalError(),
954                                            "/HostName");
955               } else {
956                 asyncResp->res.json_value["HostName"] = newHostname;
957               }
958             });
959       }
960     } else {
961       messages::addMessageToJson(
962           asyncResp->res.json_value,
963           messages::propertyValueTypeError(input.dump(), "HostName"),
964           "/HostName");
965     }
966   }
967 
968   void handleIPv4Patch(const std::string &ifaceId, const nlohmann::json &input,
969                        const std::vector<IPv4AddressData> &ipv4_data,
970                        const std::shared_ptr<AsyncResp> &asyncResp) {
971     if (!input.is_array()) {
972       messages::addMessageToJson(
973           asyncResp->res.json_value,
974           messages::propertyValueTypeError(input.dump(), "IPv4Addresses"),
975           "/IPv4Addresses");
976       return;
977     }
978 
979     // According to Redfish PATCH definition, size must be at least equal
980     if (input.size() < ipv4_data.size()) {
981       // TODO(kkowalsk) This should be a message indicating that not enough
982       // data has been provided
983       messages::addMessageToJson(asyncResp->res.json_value,
984                                  messages::internalError(), "/IPv4Addresses");
985       return;
986     }
987 
988     json_util::Result addressFieldState;
989     json_util::Result subnetMaskFieldState;
990     json_util::Result addressOriginFieldState;
991     json_util::Result gatewayFieldState;
992     const std::string *addressFieldValue;
993     const std::string *subnetMaskFieldValue;
994     const std::string *addressOriginFieldValue = nullptr;
995     const std::string *gatewayFieldValue;
996     uint8_t subnetMaskAsPrefixLength;
997     std::string addressOriginInDBusFormat;
998 
999     bool errorDetected = false;
1000     for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++) {
1001       // Check that entry is not of some unexpected type
1002       if (!input[entryIdx].is_object() && !input[entryIdx].is_null()) {
1003         // Invalid object type
1004         messages::addMessageToJson(
1005             asyncResp->res.json_value,
1006             messages::propertyValueTypeError(input[entryIdx].dump(),
1007                                              "IPv4Address"),
1008             "/IPv4Addresses/" + std::to_string(entryIdx));
1009 
1010         continue;
1011       }
1012 
1013       // Try to load fields
1014       addressFieldState = json_util::getString(
1015           "Address", input[entryIdx], addressFieldValue,
1016           static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1017           asyncResp->res.json_value,
1018           "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1019       subnetMaskFieldState = json_util::getString(
1020           "SubnetMask", input[entryIdx], subnetMaskFieldValue,
1021           static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1022           asyncResp->res.json_value,
1023           "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1024       addressOriginFieldState = json_util::getString(
1025           "AddressOrigin", input[entryIdx], addressOriginFieldValue,
1026           static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1027           asyncResp->res.json_value,
1028           "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1029       gatewayFieldState = json_util::getString(
1030           "Gateway", input[entryIdx], gatewayFieldValue,
1031           static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1032           asyncResp->res.json_value,
1033           "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1034 
1035       if (addressFieldState == json_util::Result::WRONG_TYPE ||
1036           subnetMaskFieldState == json_util::Result::WRONG_TYPE ||
1037           addressOriginFieldState == json_util::Result::WRONG_TYPE ||
1038           gatewayFieldState == json_util::Result::WRONG_TYPE) {
1039         return;
1040       }
1041 
1042       if (addressFieldState == json_util::Result::SUCCESS &&
1043           !ethernet_provider.ipv4VerifyIpAndGetBitcount(*addressFieldValue)) {
1044         errorDetected = true;
1045         messages::addMessageToJson(
1046             asyncResp->res.json_value,
1047             messages::propertyValueFormatError(*addressFieldValue, "Address"),
1048             "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1049       }
1050 
1051       if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1052           !ethernet_provider.ipv4VerifyIpAndGetBitcount(
1053               *subnetMaskFieldValue, &subnetMaskAsPrefixLength)) {
1054         errorDetected = true;
1055         messages::addMessageToJson(
1056             asyncResp->res.json_value,
1057             messages::propertyValueFormatError(*subnetMaskFieldValue,
1058                                                "SubnetMask"),
1059             "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1060       }
1061 
1062       // Get Address origin in proper format
1063       addressOriginInDBusFormat =
1064           ethernet_provider.translateAddressOriginBetweenDBusAndRedfish(
1065               addressOriginFieldValue, true, false);
1066 
1067       if (addressOriginFieldState == json_util::Result::SUCCESS &&
1068           addressOriginInDBusFormat.empty()) {
1069         errorDetected = true;
1070         messages::addMessageToJson(
1071             asyncResp->res.json_value,
1072             messages::propertyValueNotInList(*addressOriginFieldValue,
1073                                              "AddressOrigin"),
1074             "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1075       }
1076 
1077       if (gatewayFieldState == json_util::Result::SUCCESS &&
1078           !ethernet_provider.ipv4VerifyIpAndGetBitcount(*gatewayFieldValue)) {
1079         errorDetected = true;
1080         messages::addMessageToJson(
1081             asyncResp->res.json_value,
1082             messages::propertyValueFormatError(*gatewayFieldValue, "Gateway"),
1083             "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1084       }
1085 
1086       // If any error occured do not proceed with current entry, but do not
1087       // end loop
1088       if (errorDetected) {
1089         errorDetected = false;
1090         continue;
1091       }
1092 
1093       if (entryIdx >= ipv4_data.size()) {
1094         asyncResp->res.json_value["IPv4Addresses"][entryIdx] = input[entryIdx];
1095 
1096         // Verify that all field were provided
1097         if (addressFieldState == json_util::Result::NOT_EXIST) {
1098           errorDetected = true;
1099           messages::addMessageToJson(
1100               asyncResp->res.json_value, messages::propertyMissing("Address"),
1101               "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1102         }
1103 
1104         if (subnetMaskFieldState == json_util::Result::NOT_EXIST) {
1105           errorDetected = true;
1106           messages::addMessageToJson(
1107               asyncResp->res.json_value,
1108               messages::propertyMissing("SubnetMask"),
1109               "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1110         }
1111 
1112         if (addressOriginFieldState == json_util::Result::NOT_EXIST) {
1113           errorDetected = true;
1114           messages::addMessageToJson(
1115               asyncResp->res.json_value,
1116               messages::propertyMissing("AddressOrigin"),
1117               "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1118         }
1119 
1120         if (gatewayFieldState == json_util::Result::NOT_EXIST) {
1121           errorDetected = true;
1122           messages::addMessageToJson(
1123               asyncResp->res.json_value, messages::propertyMissing("Gateway"),
1124               "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1125         }
1126 
1127         // If any error occured do not proceed with current entry, but do not
1128         // end loop
1129         if (errorDetected) {
1130           errorDetected = false;
1131           continue;
1132         }
1133 
1134         // Create IPv4 with provided data
1135         ethernet_provider.createIPv4(
1136             ifaceId, entryIdx, subnetMaskAsPrefixLength, *gatewayFieldValue,
1137             *addressFieldValue, asyncResp);
1138       } else {
1139         // Existing object that should be modified/deleted/remain unchanged
1140         if (input[entryIdx].is_null()) {
1141           // Object should be deleted
1142           ethernet_provider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id,
1143                                        entryIdx, asyncResp);
1144         } else if (input[entryIdx].is_object()) {
1145           if (input[entryIdx].size() == 0) {
1146             // Object shall remain unchanged
1147             continue;
1148           }
1149 
1150           // Apply changes
1151           if (addressFieldState == json_util::Result::SUCCESS &&
1152               ipv4_data[entryIdx].address != nullptr &&
1153               *ipv4_data[entryIdx].address != *addressFieldValue) {
1154             ethernet_provider.changeIPv4AddressProperty(
1155                 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Address",
1156                 *addressFieldValue, asyncResp);
1157           }
1158 
1159           if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1160               ipv4_data[entryIdx].netmask != *subnetMaskFieldValue) {
1161             ethernet_provider.changeIPv4SubnetMaskProperty(
1162                 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1163                 *subnetMaskFieldValue, subnetMaskAsPrefixLength, asyncResp);
1164           }
1165 
1166           if (addressOriginFieldState == json_util::Result::SUCCESS &&
1167               ipv4_data[entryIdx].origin != *addressFieldValue) {
1168             ethernet_provider.changeIPv4Origin(
1169                 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1170                 *addressOriginFieldValue, addressOriginInDBusFormat, asyncResp);
1171           }
1172 
1173           if (gatewayFieldState == json_util::Result::SUCCESS &&
1174               ipv4_data[entryIdx].gateway != nullptr &&
1175               *ipv4_data[entryIdx].gateway != *gatewayFieldValue) {
1176             ethernet_provider.changeIPv4AddressProperty(
1177                 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Gateway",
1178                 *gatewayFieldValue, asyncResp);
1179           }
1180         }
1181       }
1182     }
1183   }
1184 
1185   nlohmann::json parseInterfaceData(
1186       const std::string &iface_id, const EthernetInterfaceData &eth_data,
1187       const std::vector<IPv4AddressData> &ipv4_data) {
1188     // Copy JSON object to avoid race condition
1189     nlohmann::json json_response(Node::json);
1190 
1191     // Fill out obvious data...
1192     json_response["Id"] = iface_id;
1193     json_response["@odata.id"] =
1194         "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + iface_id;
1195 
1196     // ... then the one from DBus, regarding eth iface...
1197     if (eth_data.speed != nullptr) json_response["SpeedMbps"] = *eth_data.speed;
1198 
1199     if (eth_data.mac_address != nullptr)
1200       json_response["MACAddress"] = *eth_data.mac_address;
1201 
1202     if (eth_data.hostname != nullptr)
1203       json_response["HostName"] = *eth_data.hostname;
1204 
1205     if (eth_data.vlan_id != nullptr) {
1206       nlohmann::json &vlanObj = json_response["VLAN"];
1207       vlanObj["VLANEnable"] = true;
1208       vlanObj["VLANId"] = *eth_data.vlan_id;
1209     }
1210 
1211     // ... at last, check if there are IPv4 data and prepare appropriate
1212     // collection
1213     if (ipv4_data.size() > 0) {
1214       nlohmann::json ipv4_array = nlohmann::json::array();
1215       for (auto &ipv4_config : ipv4_data) {
1216         nlohmann::json json_ipv4;
1217         if (ipv4_config.address != nullptr) {
1218           json_ipv4["Address"] = *ipv4_config.address;
1219           if (ipv4_config.gateway != nullptr)
1220             json_ipv4["Gateway"] = *ipv4_config.gateway;
1221 
1222           json_ipv4["AddressOrigin"] = ipv4_config.origin;
1223           json_ipv4["SubnetMask"] = ipv4_config.netmask;
1224 
1225           ipv4_array.push_back(std::move(json_ipv4));
1226         }
1227       }
1228       json_response["IPv4Addresses"] = std::move(ipv4_array);
1229     }
1230 
1231     return json_response;
1232   }
1233 
1234   /**
1235    * Functions triggers appropriate requests on DBus
1236    */
1237   void doGet(crow::response &res, const crow::request &req,
1238              const std::vector<std::string> &params) override {
1239     // TODO(Pawel) this shall be parametrized call (two params) to get
1240     // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1241     // Check if there is required param, truly entering this shall be
1242     // impossible.
1243     if (params.size() != 1) {
1244       res.result(boost::beast::http::status::internal_server_error);
1245       res.end();
1246       return;
1247     }
1248 
1249     const std::string &iface_id = params[0];
1250 
1251     // Get single eth interface data, and call the below callback for JSON
1252     // preparation
1253     ethernet_provider.getEthernetIfaceData(
1254         iface_id, [&, iface_id](const bool &success,
1255                                 const EthernetInterfaceData &eth_data,
1256                                 const std::vector<IPv4AddressData> &ipv4_data) {
1257           if (success) {
1258             res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data);
1259           } else {
1260             // ... otherwise return error
1261             // TODO(Pawel)consider distinguish between non existing object, and
1262             // other errors
1263             res.result(boost::beast::http::status::not_found);
1264           }
1265           res.end();
1266         });
1267   }
1268 
1269   void doPatch(crow::response &res, const crow::request &req,
1270                const std::vector<std::string> &params) override {
1271     // TODO(Pawel) this shall be parametrized call (two params) to get
1272     // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1273     // Check if there is required param, truly entering this shall be
1274     // impossible.
1275     if (params.size() != 1) {
1276       res.result(boost::beast::http::status::internal_server_error);
1277       res.end();
1278       return;
1279     }
1280 
1281     const std::string &iface_id = params[0];
1282 
1283     nlohmann::json patchReq;
1284 
1285     if (!json_util::processJsonFromRequest(res, req, patchReq)) {
1286       return;
1287     }
1288 
1289     // Get single eth interface data, and call the below callback for JSON
1290     // preparation
1291     ethernet_provider.getEthernetIfaceData(
1292         iface_id,
1293         [&, iface_id, patchReq = std::move(patchReq) ](
1294             const bool &success, const EthernetInterfaceData &eth_data,
1295             const std::vector<IPv4AddressData> &ipv4_data) {
1296           if (!success) {
1297             // ... otherwise return error
1298             // TODO(Pawel)consider distinguish between non existing object, and
1299             // other errors
1300             res.result(boost::beast::http::status::not_found);
1301             res.end();
1302 
1303             return;
1304           }
1305 
1306           res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data);
1307 
1308           std::shared_ptr<AsyncResp> asyncResp =
1309               std::make_shared<AsyncResp>(res);
1310 
1311           for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end();
1312                ++propertyIt) {
1313             if (propertyIt.key() == "VLAN") {
1314               handleVlanPatch(iface_id, propertyIt.value(), eth_data,
1315                               asyncResp);
1316             } else if (propertyIt.key() == "HostName") {
1317               handleHostnamePatch(propertyIt.value(), eth_data, asyncResp);
1318             } else if (propertyIt.key() == "IPv4Addresses") {
1319               handleIPv4Patch(iface_id, propertyIt.value(), ipv4_data,
1320                               asyncResp);
1321             } else if (propertyIt.key() == "IPv6Addresses") {
1322               // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
1323               messages::addMessageToJsonRoot(
1324                   res.json_value,
1325                   messages::propertyNotWritable(propertyIt.key()));
1326             } else {
1327               auto fieldInJsonIt = res.json_value.find(propertyIt.key());
1328 
1329               if (fieldInJsonIt == res.json_value.end()) {
1330                 // Field not in scope of defined fields
1331                 messages::addMessageToJsonRoot(
1332                     res.json_value,
1333                     messages::propertyUnknown(propertyIt.key()));
1334               } else if (*fieldInJsonIt != *propertyIt) {
1335                 // User attempted to modify non-writable field
1336                 messages::addMessageToJsonRoot(
1337                     res.json_value,
1338                     messages::propertyNotWritable(propertyIt.key()));
1339               }
1340             }
1341           }
1342         });
1343   }
1344 
1345   // Ethernet Provider object
1346   // TODO(Pawel) consider move it to singleton
1347   OnDemandEthernetProvider ethernet_provider;
1348 };
1349 
1350 }  // namespace redfish
1351