xref: /openbmc/bmcweb/features/redfish/lib/ethernet.hpp (revision da131a9a053f304528cae6573f97bb6f3db951b2)
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 <boost/container/flat_map.hpp>
19 #include <boost/container/flat_set.hpp>
20 #include <dbus_singleton.hpp>
21 #include <error_messages.hpp>
22 #include <node.hpp>
23 #include <optional>
24 #include <utils/json_utils.hpp>
25 #include <variant>
26 
27 namespace redfish
28 {
29 
30 /**
31  * DBus types primitives for several generic DBus interfaces
32  * TODO(Pawel) consider move this to separate file into boost::dbus
33  */
34 using PropertiesMapType = boost::container::flat_map<
35     std::string, std::variant<std::string, bool, uint8_t, int16_t, uint16_t,
36                               int32_t, uint32_t, int64_t, uint64_t, double>>;
37 
38 using GetManagedObjects = std::vector<std::pair<
39     sdbusplus::message::object_path,
40     std::vector<std::pair<
41         std::string,
42         boost::container::flat_map<
43             std::string, sdbusplus::message::variant<
44                              std::string, bool, uint8_t, int16_t, uint16_t,
45                              int32_t, uint32_t, int64_t, uint64_t, double,
46                              std::vector<std::string>>>>>>>;
47 
48 enum class LinkType
49 {
50     Local,
51     Global
52 };
53 
54 /**
55  * Structure for keeping IPv4 data required by Redfish
56  */
57 struct IPv4AddressData
58 {
59     std::string id;
60     std::string address;
61     std::string domain;
62     std::string gateway;
63     std::string netmask;
64     std::string origin;
65     LinkType linktype;
66 
67     bool operator<(const IPv4AddressData &obj) const
68     {
69         return id < obj.id;
70     }
71 };
72 
73 /**
74  * Structure for keeping basic single Ethernet Interface information
75  * available from DBus
76  */
77 struct EthernetInterfaceData
78 {
79     uint32_t speed;
80     bool auto_neg;
81     bool DHCPEnabled;
82     std::string hostname;
83     std::string default_gateway;
84     std::string ipv6_default_gateway;
85     std::string mac_address;
86     std::vector<std::uint32_t> vlan_id;
87     std::vector<std::string> nameservers;
88 };
89 
90 // Helper function that changes bits netmask notation (i.e. /24)
91 // into full dot notation
92 inline std::string getNetmask(unsigned int bits)
93 {
94     uint32_t value = 0xffffffff << (32 - bits);
95     std::string netmask = std::to_string((value >> 24) & 0xff) + "." +
96                           std::to_string((value >> 16) & 0xff) + "." +
97                           std::to_string((value >> 8) & 0xff) + "." +
98                           std::to_string(value & 0xff);
99     return netmask;
100 }
101 
102 inline std::string
103     translateAddressOriginDbusToRedfish(const std::string &inputOrigin,
104                                         bool isIPv4)
105 {
106     if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.Static")
107     {
108         return "Static";
109     }
110     if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal")
111     {
112         if (isIPv4)
113         {
114             return "IPv4LinkLocal";
115         }
116         else
117         {
118             return "LinkLocal";
119         }
120     }
121     if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
122     {
123         if (isIPv4)
124         {
125             return "DHCP";
126         }
127         else
128         {
129             return "DHCPv6";
130         }
131     }
132     if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC")
133     {
134         return "SLAAC";
135     }
136     return "";
137 }
138 
139 inline std::string
140     translateAddressOriginRedfishToDbus(const std::string &inputOrigin)
141 {
142     if (inputOrigin == "Static")
143     {
144         return "xyz.openbmc_project.Network.IP.AddressOrigin.Static";
145     }
146     if (inputOrigin == "DHCP" || inputOrigin == "DHCPv6")
147     {
148         return "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP";
149     }
150     if (inputOrigin == "IPv4LinkLocal" || inputOrigin == "LinkLocal")
151     {
152         return "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal";
153     }
154     if (inputOrigin == "SLAAC")
155     {
156         return "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC";
157     }
158     return "";
159 }
160 
161 inline bool extractEthernetInterfaceData(const std::string &ethiface_id,
162                                          const GetManagedObjects &dbus_data,
163                                          EthernetInterfaceData &ethData)
164 {
165     bool idFound = false;
166     for (const auto &objpath : dbus_data)
167     {
168         for (const auto &ifacePair : objpath.second)
169         {
170             if (objpath.first == "/xyz/openbmc_project/network/" + ethiface_id)
171             {
172                 idFound = true;
173                 if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress")
174                 {
175                     for (const auto &propertyPair : ifacePair.second)
176                     {
177                         if (propertyPair.first == "MACAddress")
178                         {
179                             const std::string *mac =
180                                 std::get_if<std::string>(&propertyPair.second);
181                             if (mac != nullptr)
182                             {
183                                 ethData.mac_address = *mac;
184                             }
185                         }
186                     }
187                 }
188                 else if (ifacePair.first == "xyz.openbmc_project.Network.VLAN")
189                 {
190                     for (const auto &propertyPair : ifacePair.second)
191                     {
192                         if (propertyPair.first == "Id")
193                         {
194                             const uint32_t *id =
195                                 std::get_if<uint32_t>(&propertyPair.second);
196                             if (id != nullptr)
197                             {
198                                 ethData.vlan_id.push_back(*id);
199                             }
200                         }
201                     }
202                 }
203                 else if (ifacePair.first ==
204                          "xyz.openbmc_project.Network.EthernetInterface")
205                 {
206                     for (const auto &propertyPair : ifacePair.second)
207                     {
208                         if (propertyPair.first == "AutoNeg")
209                         {
210                             const bool *auto_neg =
211                                 std::get_if<bool>(&propertyPair.second);
212                             if (auto_neg != nullptr)
213                             {
214                                 ethData.auto_neg = *auto_neg;
215                             }
216                         }
217                         else if (propertyPair.first == "Speed")
218                         {
219                             const uint32_t *speed =
220                                 std::get_if<uint32_t>(&propertyPair.second);
221                             if (speed != nullptr)
222                             {
223                                 ethData.speed = *speed;
224                             }
225                         }
226                         else if (propertyPair.first == "Nameservers")
227                         {
228                             const std::vector<std::string> *nameservers =
229                                 sdbusplus::message::variant_ns::get_if<
230                                     std::vector<std::string>>(
231                                     &propertyPair.second);
232                             if (nameservers != nullptr)
233                             {
234                                 ethData.nameservers = std::move(*nameservers);
235                             }
236                         }
237                         else if (propertyPair.first == "DHCPEnabled")
238                         {
239                             const bool *DHCPEnabled =
240                                 std::get_if<bool>(&propertyPair.second);
241                             if (DHCPEnabled != nullptr)
242                             {
243                                 ethData.DHCPEnabled = *DHCPEnabled;
244                             }
245                         }
246                     }
247                 }
248             }
249             // System configuration shows up in the global namespace, so no need
250             // to check eth number
251             if (ifacePair.first ==
252                 "xyz.openbmc_project.Network.SystemConfiguration")
253             {
254                 for (const auto &propertyPair : ifacePair.second)
255                 {
256                     if (propertyPair.first == "HostName")
257                     {
258                         const std::string *hostname =
259                             sdbusplus::message::variant_ns::get_if<std::string>(
260                                 &propertyPair.second);
261                         if (hostname != nullptr)
262                         {
263                             ethData.hostname = *hostname;
264                         }
265                     }
266                     else if (propertyPair.first == "DefaultGateway")
267                     {
268                         const std::string *defaultGateway =
269                             sdbusplus::message::variant_ns::get_if<std::string>(
270                                 &propertyPair.second);
271                         if (defaultGateway != nullptr)
272                         {
273                             ethData.default_gateway = *defaultGateway;
274                         }
275                     }
276                     else if (propertyPair.first == "DefaultGateway6")
277                     {
278                         const std::string *defaultGateway6 =
279                             sdbusplus::message::variant_ns::get_if<std::string>(
280                                 &propertyPair.second);
281                         if (defaultGateway6 != nullptr)
282                         {
283                             ethData.ipv6_default_gateway = *defaultGateway6;
284                         }
285                     }
286                 }
287             }
288         }
289     }
290     return idFound;
291 }
292 
293 // Helper function that extracts data for single ethernet ipv4 address
294 inline void
295     extractIPData(const std::string &ethiface_id,
296                   const GetManagedObjects &dbus_data,
297                   boost::container::flat_set<IPv4AddressData> &ipv4_config)
298 {
299     const std::string ipv4PathStart =
300         "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/";
301 
302     // Since there might be several IPv4 configurations aligned with
303     // single ethernet interface, loop over all of them
304     for (const auto &objpath : dbus_data)
305     {
306         // Check if proper pattern for object path appears
307         if (boost::starts_with(objpath.first.str, ipv4PathStart))
308         {
309             for (auto &interface : objpath.second)
310             {
311                 if (interface.first == "xyz.openbmc_project.Network.IP")
312                 {
313                     // Instance IPv4AddressData structure, and set as
314                     // appropriate
315                     std::pair<
316                         boost::container::flat_set<IPv4AddressData>::iterator,
317                         bool>
318                         it = ipv4_config.insert(
319                             {objpath.first.str.substr(ipv4PathStart.size())});
320                     IPv4AddressData &ipv4_address = *it.first;
321                     for (auto &property : interface.second)
322                     {
323                         if (property.first == "Address")
324                         {
325                             const std::string *address =
326                                 std::get_if<std::string>(&property.second);
327                             if (address != nullptr)
328                             {
329                                 ipv4_address.address = *address;
330                             }
331                         }
332                         else if (property.first == "Gateway")
333                         {
334                             const std::string *gateway =
335                                 std::get_if<std::string>(&property.second);
336                             if (gateway != nullptr)
337                             {
338                                 ipv4_address.gateway = *gateway;
339                             }
340                         }
341                         else if (property.first == "Origin")
342                         {
343                             const std::string *origin =
344                                 std::get_if<std::string>(&property.second);
345                             if (origin != nullptr)
346                             {
347                                 ipv4_address.origin =
348                                     translateAddressOriginDbusToRedfish(*origin,
349                                                                         true);
350                             }
351                         }
352                         else if (property.first == "PrefixLength")
353                         {
354                             const uint8_t *mask =
355                                 std::get_if<uint8_t>(&property.second);
356                             if (mask != nullptr)
357                             {
358                                 // convert it to the string
359                                 ipv4_address.netmask = getNetmask(*mask);
360                             }
361                         }
362                         else
363                         {
364                             BMCWEB_LOG_ERROR
365                                 << "Got extra property: " << property.first
366                                 << " on the " << objpath.first.str << " object";
367                         }
368                     }
369                     // Check if given address is local, or global
370                     ipv4_address.linktype =
371                         boost::starts_with(ipv4_address.address, "169.254.")
372                             ? LinkType::Global
373                             : LinkType::Local;
374                 }
375             }
376         }
377     }
378 }
379 
380 /**
381  * @brief Sets given Id on the given VLAN interface through D-Bus
382  *
383  * @param[in] ifaceId       Id of VLAN interface that should be modified
384  * @param[in] inputVlanId   New ID of the VLAN
385  * @param[in] callback      Function that will be called after the operation
386  *
387  * @return None.
388  */
389 template <typename CallbackFunc>
390 void changeVlanId(const std::string &ifaceId, const uint32_t &inputVlanId,
391                   CallbackFunc &&callback)
392 {
393     crow::connections::systemBus->async_method_call(
394         callback, "xyz.openbmc_project.Network",
395         std::string("/xyz/openbmc_project/network/") + ifaceId,
396         "org.freedesktop.DBus.Properties", "Set",
397         "xyz.openbmc_project.Network.VLAN", "Id",
398         std::variant<uint32_t>(inputVlanId));
399 }
400 
401 /**
402  * @brief Helper function that verifies IP address to check if it is in
403  *        proper format. If bits pointer is provided, also calculates active
404  *        bit count for Subnet Mask.
405  *
406  * @param[in]  ip     IP that will be verified
407  * @param[out] bits   Calculated mask in bits notation
408  *
409  * @return true in case of success, false otherwise
410  */
411 inline bool ipv4VerifyIpAndGetBitcount(const std::string &ip,
412                                        uint8_t *bits = nullptr)
413 {
414     std::vector<std::string> bytesInMask;
415 
416     boost::split(bytesInMask, ip, boost::is_any_of("."));
417 
418     static const constexpr int ipV4AddressSectionsCount = 4;
419     if (bytesInMask.size() != ipV4AddressSectionsCount)
420     {
421         return false;
422     }
423 
424     if (bits != nullptr)
425     {
426         *bits = 0;
427     }
428 
429     char *endPtr;
430     long previousValue = 255;
431     bool firstZeroInByteHit;
432     for (const std::string &byte : bytesInMask)
433     {
434         if (byte.empty())
435         {
436             return false;
437         }
438 
439         // Use strtol instead of stroi to avoid exceptions
440         long value = std::strtol(byte.c_str(), &endPtr, 10);
441 
442         // endPtr should point to the end of the string, otherwise given string
443         // is not 100% number
444         if (*endPtr != '\0')
445         {
446             return false;
447         }
448 
449         // Value should be contained in byte
450         if (value < 0 || value > 255)
451         {
452             return false;
453         }
454 
455         if (bits != nullptr)
456         {
457             // Mask has to be continuous between bytes
458             if (previousValue != 255 && value != 0)
459             {
460                 return false;
461             }
462 
463             // Mask has to be continuous inside bytes
464             firstZeroInByteHit = false;
465 
466             // Count bits
467             for (int bitIdx = 7; bitIdx >= 0; bitIdx--)
468             {
469                 if (value & (1 << bitIdx))
470                 {
471                     if (firstZeroInByteHit)
472                     {
473                         // Continuity not preserved
474                         return false;
475                     }
476                     else
477                     {
478                         (*bits)++;
479                     }
480                 }
481                 else
482                 {
483                     firstZeroInByteHit = true;
484                 }
485             }
486         }
487 
488         previousValue = value;
489     }
490 
491     return true;
492 }
493 
494 /**
495  * @brief Changes IPv4 address type property (Address, Gateway)
496  *
497  * @param[in] ifaceId     Id of interface whose IP should be modified
498  * @param[in] ipIdx       Index of IP in input array that should be modified
499  * @param[in] ipHash      DBus Hash id of modified IP
500  * @param[in] name        Name of field in JSON representation
501  * @param[in] newValue    New value that should be written
502  * @param[io] asyncResp   Response object that will be returned to client
503  *
504  * @return true if give IP is valid and has been sent do D-Bus, false
505  * otherwise
506  */
507 inline void changeIPv4AddressProperty(
508     const std::string &ifaceId, int ipIdx, const std::string &ipHash,
509     const std::string &name, const std::string &newValue,
510     const std::shared_ptr<AsyncResp> asyncResp)
511 {
512     auto callback = [asyncResp, ipIdx, name{std::string(name)},
513                      newValue{std::move(newValue)}](
514                         const boost::system::error_code ec) {
515         if (ec)
516         {
517             messages::internalError(asyncResp->res);
518         }
519         else
520         {
521             asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] = newValue;
522         }
523     };
524 
525     crow::connections::systemBus->async_method_call(
526         std::move(callback), "xyz.openbmc_project.Network",
527         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
528         "org.freedesktop.DBus.Properties", "Set",
529         "xyz.openbmc_project.Network.IP", name,
530         std::variant<std::string>(newValue));
531 }
532 
533 /**
534  * @brief Changes IPv4 address origin property
535  *
536  * @param[in] ifaceId       Id of interface whose IP should be modified
537  * @param[in] ipIdx         Index of IP in input array that should be
538  * modified
539  * @param[in] ipHash        DBus Hash id of modified IP
540  * @param[in] newValue      New value in Redfish format
541  * @param[in] newValueDbus  New value in D-Bus format
542  * @param[io] asyncResp     Response object that will be returned to client
543  *
544  * @return true if give IP is valid and has been sent do D-Bus, false
545  * otherwise
546  */
547 inline void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
548                              const std::string &ipHash,
549                              const std::string &newValue,
550                              const std::string &newValueDbus,
551                              const std::shared_ptr<AsyncResp> asyncResp)
552 {
553     auto callback = [asyncResp, ipIdx, newValue{std::move(newValue)}](
554                         const boost::system::error_code ec) {
555         if (ec)
556         {
557             messages::internalError(asyncResp->res);
558         }
559         else
560         {
561             asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] =
562                 newValue;
563         }
564     };
565 
566     crow::connections::systemBus->async_method_call(
567         std::move(callback), "xyz.openbmc_project.Network",
568         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
569         "org.freedesktop.DBus.Properties", "Set",
570         "xyz.openbmc_project.Network.IP", "Origin",
571         std::variant<std::string>(newValueDbus));
572 }
573 
574 /**
575  * @brief Modifies SubnetMask for given IP
576  *
577  * @param[in] ifaceId      Id of interface whose IP should be modified
578  * @param[in] ipIdx        Index of IP in input array that should be
579  * modified
580  * @param[in] ipHash       DBus Hash id of modified IP
581  * @param[in] newValueStr  Mask in dot notation as string
582  * @param[in] newValue     Mask as PrefixLength in bitcount
583  * @param[io] asyncResp   Response object that will be returned to client
584  *
585  * @return None
586  */
587 inline void changeIPv4SubnetMaskProperty(const std::string &ifaceId, int ipIdx,
588                                          const std::string &ipHash,
589                                          const std::string &newValueStr,
590                                          uint8_t &newValue,
591                                          std::shared_ptr<AsyncResp> asyncResp)
592 {
593     auto callback = [asyncResp, ipIdx, newValueStr{std::move(newValueStr)}](
594                         const boost::system::error_code ec) {
595         if (ec)
596         {
597             messages::internalError(asyncResp->res);
598         }
599         else
600         {
601             asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] =
602                 newValueStr;
603         }
604     };
605 
606     crow::connections::systemBus->async_method_call(
607         std::move(callback), "xyz.openbmc_project.Network",
608         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
609         "org.freedesktop.DBus.Properties", "Set",
610         "xyz.openbmc_project.Network.IP", "PrefixLength",
611         std::variant<uint8_t>(newValue));
612 }
613 
614 /**
615  * @brief Deletes given IPv4
616  *
617  * @param[in] ifaceId     Id of interface whose IP should be deleted
618  * @param[in] ipIdx       Index of IP in input array that should be deleted
619  * @param[in] ipHash      DBus Hash id of IP that should be deleted
620  * @param[io] asyncResp   Response object that will be returned to client
621  *
622  * @return None
623  */
624 inline void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
625                        unsigned int ipIdx,
626                        const std::shared_ptr<AsyncResp> asyncResp)
627 {
628     crow::connections::systemBus->async_method_call(
629         [ipIdx, asyncResp](const boost::system::error_code ec) {
630             if (ec)
631             {
632                 messages::internalError(asyncResp->res);
633             }
634             else
635             {
636                 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr;
637             }
638         },
639         "xyz.openbmc_project.Network",
640         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
641         "xyz.openbmc_project.Object.Delete", "Delete");
642 }
643 
644 /**
645  * @brief Creates IPv4 with given data
646  *
647  * @param[in] ifaceId     Id of interface whose IP should be deleted
648  * @param[in] ipIdx       Index of IP in input array that should be deleted
649  * @param[in] ipHash      DBus Hash id of IP that should be deleted
650  * @param[io] asyncResp   Response object that will be returned to client
651  *
652  * @return None
653  */
654 inline void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
655                        uint8_t subnetMask, const std::string &gateway,
656                        const std::string &address,
657                        std::shared_ptr<AsyncResp> asyncResp)
658 {
659     auto createIpHandler = [asyncResp](const boost::system::error_code ec) {
660         if (ec)
661         {
662             messages::internalError(asyncResp->res);
663         }
664     };
665 
666     crow::connections::systemBus->async_method_call(
667         std::move(createIpHandler), "xyz.openbmc_project.Network",
668         "/xyz/openbmc_project/network/" + ifaceId,
669         "xyz.openbmc_project.Network.IP.Create", "IP",
670         "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
671         gateway);
672 }
673 using GetAllPropertiesType =
674     boost::container::flat_map<std::string, sdbusplus::message::variant<bool>>;
675 
676 inline void getDHCPConfigData(const std::shared_ptr<AsyncResp> asyncResp)
677 {
678     auto getConfig = [asyncResp](const boost::system::error_code error_code,
679                                  const GetAllPropertiesType &dbus_data) {
680         if (error_code)
681         {
682             BMCWEB_LOG_ERROR << "D-Bus response error: " << error_code;
683             messages::internalError(asyncResp->res);
684             return;
685         }
686         nlohmann::json &DHCPConfigTypeJson = asyncResp->res.jsonValue["DHCPv4"];
687         for (const auto &property : dbus_data)
688         {
689             auto value =
690                 sdbusplus::message::variant_ns::get_if<bool>(&property.second);
691 
692             if (value == nullptr)
693             {
694                 continue;
695             }
696             if (property.first == "DNSEnabled")
697             {
698                 DHCPConfigTypeJson["UseDNSServers"] = *value;
699             }
700             else if (property.first == "HostNameEnabled")
701             {
702                 DHCPConfigTypeJson["UseDomainName"] = *value;
703             }
704             else if (property.first == "NTPEnabled")
705             {
706                 DHCPConfigTypeJson["UseNTPServers"] = *value;
707             }
708         }
709     };
710     crow::connections::systemBus->async_method_call(
711         std::move(getConfig), "xyz.openbmc_project.Network",
712         "/xyz/openbmc_project/network/config/dhcp",
713         "org.freedesktop.DBus.Properties", "GetAll",
714         "xyz.openbmc_project.Network.DHCPConfiguration");
715 }
716 
717 /**
718  * Function that retrieves all properties for given Ethernet Interface
719  * Object
720  * from EntityManager Network Manager
721  * @param ethiface_id a eth interface id to query on DBus
722  * @param callback a function that shall be called to convert Dbus output
723  * into JSON
724  */
725 template <typename CallbackFunc>
726 void getEthernetIfaceData(const std::string &ethiface_id,
727                           CallbackFunc &&callback)
728 {
729     crow::connections::systemBus->async_method_call(
730         [ethiface_id{std::string{ethiface_id}}, callback{std::move(callback)}](
731             const boost::system::error_code error_code,
732             const GetManagedObjects &resp) {
733             EthernetInterfaceData ethData{};
734             boost::container::flat_set<IPv4AddressData> ipv4Data;
735 
736             if (error_code)
737             {
738                 callback(false, ethData, ipv4Data);
739                 return;
740             }
741 
742             bool found =
743                 extractEthernetInterfaceData(ethiface_id, resp, ethData);
744             if (!found)
745             {
746                 callback(false, ethData, ipv4Data);
747                 return;
748             }
749 
750             extractIPData(ethiface_id, resp, ipv4Data);
751 
752             // Fix global GW
753             for (IPv4AddressData &ipv4 : ipv4Data)
754             {
755                 if ((ipv4.linktype == LinkType::Global) &&
756                     (ipv4.gateway == "0.0.0.0"))
757                 {
758                     ipv4.gateway = ethData.default_gateway;
759                 }
760             }
761 
762             // Finally make a callback with usefull data
763             callback(true, ethData, ipv4Data);
764         },
765         "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
766         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
767 };
768 
769 /**
770  * Function that retrieves all Ethernet Interfaces available through Network
771  * Manager
772  * @param callback a function that shall be called to convert Dbus output
773  * into JSON.
774  */
775 template <typename CallbackFunc>
776 void getEthernetIfaceList(CallbackFunc &&callback)
777 {
778     crow::connections::systemBus->async_method_call(
779         [callback{std::move(callback)}](
780             const boost::system::error_code error_code,
781             GetManagedObjects &resp) {
782             // Callback requires vector<string> to retrieve all available
783             // ethernet interfaces
784             boost::container::flat_set<std::string> iface_list;
785             iface_list.reserve(resp.size());
786             if (error_code)
787             {
788                 callback(false, iface_list);
789                 return;
790             }
791 
792             // Iterate over all retrieved ObjectPaths.
793             for (const auto &objpath : resp)
794             {
795                 // And all interfaces available for certain ObjectPath.
796                 for (const auto &interface : objpath.second)
797                 {
798                     // If interface is
799                     // xyz.openbmc_project.Network.EthernetInterface, this is
800                     // what we're looking for.
801                     if (interface.first ==
802                         "xyz.openbmc_project.Network.EthernetInterface")
803                     {
804                         // Cut out everyting until last "/", ...
805                         const std::string &iface_id = objpath.first.str;
806                         std::size_t last_pos = iface_id.rfind("/");
807                         if (last_pos != std::string::npos)
808                         {
809                             // and put it into output vector.
810                             iface_list.emplace(iface_id.substr(last_pos + 1));
811                         }
812                     }
813                 }
814             }
815             // Finally make a callback with useful data
816             callback(true, iface_list);
817         },
818         "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
819         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
820 };
821 
822 /**
823  * EthernetCollection derived class for delivering Ethernet Collection Schema
824  */
825 class EthernetCollection : public Node
826 {
827   public:
828     template <typename CrowApp>
829     EthernetCollection(CrowApp &app) :
830         Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/")
831     {
832         entityPrivileges = {
833             {boost::beast::http::verb::get, {{"Login"}}},
834             {boost::beast::http::verb::head, {{"Login"}}},
835             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
836             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
837             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
838             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
839     }
840 
841   private:
842     /**
843      * Functions triggers appropriate requests on DBus
844      */
845     void doGet(crow::Response &res, const crow::Request &req,
846                const std::vector<std::string> &params) override
847     {
848         res.jsonValue["@odata.type"] =
849             "#EthernetInterfaceCollection.EthernetInterfaceCollection";
850         res.jsonValue["@odata.context"] =
851             "/redfish/v1/"
852             "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
853         res.jsonValue["@odata.id"] =
854             "/redfish/v1/Managers/bmc/EthernetInterfaces";
855         res.jsonValue["Name"] = "Ethernet Network Interface Collection";
856         res.jsonValue["Description"] =
857             "Collection of EthernetInterfaces for this Manager";
858         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
859         // Get eth interface list, and call the below callback for JSON
860         // preparation
861         getEthernetIfaceList(
862             [asyncResp](
863                 const bool &success,
864                 const boost::container::flat_set<std::string> &iface_list) {
865                 if (!success)
866                 {
867                     messages::internalError(asyncResp->res);
868                     return;
869                 }
870 
871                 nlohmann::json &iface_array =
872                     asyncResp->res.jsonValue["Members"];
873                 iface_array = nlohmann::json::array();
874                 std::string tag = "_";
875                 for (const std::string &iface_item : iface_list)
876                 {
877                     std::size_t found = iface_item.find(tag);
878                     if (found == std::string::npos)
879                     {
880                         iface_array.push_back(
881                             {{"@odata.id",
882                               "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
883                                   iface_item}});
884                     }
885                 }
886 
887                 asyncResp->res.jsonValue["Members@odata.count"] =
888                     iface_array.size();
889                 asyncResp->res.jsonValue["@odata.id"] =
890                     "/redfish/v1/Managers/bmc/EthernetInterfaces";
891             });
892     }
893 };
894 
895 /**
896  * EthernetInterface derived class for delivering Ethernet Schema
897  */
898 class EthernetInterface : public Node
899 {
900   public:
901     /*
902      * Default Constructor
903      */
904     template <typename CrowApp>
905     EthernetInterface(CrowApp &app) :
906         Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/",
907              std::string())
908     {
909         entityPrivileges = {
910             {boost::beast::http::verb::get, {{"Login"}}},
911             {boost::beast::http::verb::head, {{"Login"}}},
912             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
913             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
914             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
915             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
916     }
917 
918   private:
919     void handleHostnamePatch(const std::string &hostname,
920                              const std::shared_ptr<AsyncResp> asyncResp)
921     {
922         asyncResp->res.jsonValue["HostName"] = hostname;
923         crow::connections::systemBus->async_method_call(
924             [asyncResp](const boost::system::error_code ec) {
925                 if (ec)
926                 {
927                     messages::internalError(asyncResp->res);
928                 }
929             },
930             "xyz.openbmc_project.Network",
931             "/xyz/openbmc_project/network/config",
932             "org.freedesktop.DBus.Properties", "Set",
933             "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
934             std::variant<std::string>(hostname));
935     }
936 
937     void handleMACAddressPatch(const std::string &ifaceId,
938                                const std::string &macAddress,
939                                const std::shared_ptr<AsyncResp> &asyncResp)
940     {
941         crow::connections::systemBus->async_method_call(
942             [asyncResp, macAddress](const boost::system::error_code ec) {
943                 if (ec)
944                 {
945                     messages::internalError(asyncResp->res);
946                     return;
947                 }
948                 asyncResp->res.jsonValue["MACAddress"] = std::move(macAddress);
949             },
950             "xyz.openbmc_project.Network",
951             "/xyz/openbmc_project/network/" + ifaceId,
952             "org.freedesktop.DBus.Properties", "Set",
953             "xyz.openbmc_project.Network.MACAddress", "MACAddress",
954             std::variant<std::string>(macAddress));
955     }
956     void setDHCPEnabled(const std::string &ifaceId,
957                         const std::string &propertyName, const bool &value,
958                         const std::shared_ptr<AsyncResp> asyncResp)
959     {
960         crow::connections::systemBus->async_method_call(
961             [asyncResp](const boost::system::error_code ec) {
962                 if (ec)
963                 {
964                     BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
965                     messages::internalError(asyncResp->res);
966                     return;
967                 }
968             },
969             "xyz.openbmc_project.Network",
970             "/xyz/openbmc_project/network/" + ifaceId,
971             "org.freedesktop.DBus.Properties", "Set",
972             "xyz.openbmc_project.Network.EthernetInterface", propertyName,
973             std::variant<bool>{value});
974     }
975     void setDHCPv4Config(const std::string &propertyName, const bool &value,
976                          const std::shared_ptr<AsyncResp> asyncResp)
977     {
978         BMCWEB_LOG_DEBUG << propertyName << " = " << value;
979         crow::connections::systemBus->async_method_call(
980             [asyncResp](const boost::system::error_code ec) {
981                 if (ec)
982                 {
983                     BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
984                     messages::internalError(asyncResp->res);
985                     return;
986                 }
987             },
988             "xyz.openbmc_project.Network",
989             "/xyz/openbmc_project/network/config/dhcp",
990             "org.freedesktop.DBus.Properties", "Set",
991             "xyz.openbmc_project.Network.DHCPConfiguration", propertyName,
992             std::variant<bool>{value});
993     }
994 
995     void handleDHCPv4Patch(const std::string &ifaceId, nlohmann::json &input,
996                            const std::shared_ptr<AsyncResp> asyncResp)
997     {
998         std::optional<bool> dhcpEnabled;
999         std::optional<bool> useDNSServers;
1000         std::optional<bool> useDomainName;
1001         std::optional<bool> useNTPServers;
1002 
1003         if (!json_util::readJson(input, asyncResp->res, "DHCPEnabled",
1004                                  dhcpEnabled, "UseDNSServers", useDNSServers,
1005                                  "UseDomainName", useDomainName,
1006                                  "UseNTPServers", useNTPServers))
1007         {
1008             return;
1009         }
1010 
1011         if (dhcpEnabled)
1012         {
1013             BMCWEB_LOG_DEBUG << "set DHCPEnabled...";
1014             setDHCPEnabled(ifaceId, "DHCPEnabled", *dhcpEnabled, asyncResp);
1015         }
1016 
1017         if (useDNSServers)
1018         {
1019             BMCWEB_LOG_DEBUG << "set DNSEnabled...";
1020             setDHCPv4Config("DNSEnabled", *useDNSServers, asyncResp);
1021         }
1022 
1023         if (useDomainName)
1024         {
1025             BMCWEB_LOG_DEBUG << "set HostNameEnabled...";
1026             setDHCPv4Config("HostNameEnabled", *useDomainName, asyncResp);
1027         }
1028 
1029         if (useNTPServers)
1030         {
1031             BMCWEB_LOG_DEBUG << "set NTPEnabled...";
1032             setDHCPv4Config("NTPEnabled", *useNTPServers, asyncResp);
1033         }
1034     }
1035     void handleIPv4Patch(
1036         const std::string &ifaceId, nlohmann::json &input,
1037         const boost::container::flat_set<IPv4AddressData> &ipv4Data,
1038         const std::shared_ptr<AsyncResp> asyncResp)
1039     {
1040         if (!input.is_array())
1041         {
1042             messages::propertyValueTypeError(asyncResp->res, input.dump(),
1043                                              "IPv4Addresses");
1044             return;
1045         }
1046 
1047         int entryIdx = 0;
1048         boost::container::flat_set<IPv4AddressData>::const_iterator thisData =
1049             ipv4Data.begin();
1050         for (nlohmann::json &thisJson : input)
1051         {
1052             std::string pathString =
1053                 "IPv4Addresses/" + std::to_string(entryIdx);
1054 
1055             if (thisJson.is_null())
1056             {
1057                 if (thisData != ipv4Data.end())
1058                 {
1059                     deleteIPv4(ifaceId, thisData->id, entryIdx, asyncResp);
1060                     thisData++;
1061                 }
1062                 else
1063                 {
1064                     messages::propertyValueFormatError(
1065                         asyncResp->res, input.dump(), pathString);
1066                     return;
1067                     // TODO(ratagupt) Not sure about the property where value is
1068                     // list and if unable to update one of the
1069                     // list value then should we proceed further or
1070                     // break there, would ask in the redfish forum
1071                     // till then we stop processing the next list item.
1072                 }
1073                 entryIdx++;
1074                 continue; // not an error as per the redfish spec.
1075             }
1076 
1077             if (thisJson.empty())
1078             {
1079                 if (thisData != ipv4Data.end())
1080                 {
1081                     thisData++;
1082                 }
1083                 else
1084                 {
1085                     messages::propertyMissing(asyncResp->res,
1086                                               pathString + "/Address");
1087                     return;
1088                     // TODO(ratagupt) Not sure about the property where value is
1089                     // list and if unable to update one of the
1090                     // list value then should we proceed further or
1091                     // break there, would ask in the redfish forum
1092                     // till then we stop processing the next list item.
1093                 }
1094                 entryIdx++;
1095                 continue; // not an error as per the redfish spec.
1096             }
1097 
1098             std::optional<std::string> address;
1099             std::optional<std::string> addressOrigin;
1100             std::optional<std::string> subnetMask;
1101             std::optional<std::string> gateway;
1102 
1103             if (!json_util::readJson(thisJson, asyncResp->res, "Address",
1104                                      address, "AddressOrigin", addressOrigin,
1105                                      "SubnetMask", subnetMask, "Gateway",
1106                                      gateway))
1107             {
1108                 return;
1109             }
1110 
1111             if (address)
1112             {
1113                 if (!ipv4VerifyIpAndGetBitcount(*address))
1114                 {
1115                     messages::propertyValueFormatError(asyncResp->res, *address,
1116                                                        pathString + "/Address");
1117                     return;
1118                 }
1119             }
1120 
1121             uint8_t prefixLength = 0;
1122             if (subnetMask)
1123             {
1124                 if (!ipv4VerifyIpAndGetBitcount(*subnetMask, &prefixLength))
1125                 {
1126                     messages::propertyValueFormatError(
1127                         asyncResp->res, *subnetMask,
1128                         pathString + "/SubnetMask");
1129                     return;
1130                 }
1131             }
1132             std::string addressOriginInDBusFormat;
1133             if (addressOrigin)
1134             {
1135                 // Get Address origin in proper format
1136                 addressOriginInDBusFormat =
1137                     translateAddressOriginRedfishToDbus(*addressOrigin);
1138                 if (addressOriginInDBusFormat.empty())
1139                 {
1140                     messages::propertyValueNotInList(
1141                         asyncResp->res, *addressOrigin,
1142                         pathString + "/AddressOrigin");
1143                     return;
1144                 }
1145             }
1146 
1147             if (gateway)
1148             {
1149                 if (!ipv4VerifyIpAndGetBitcount(*gateway))
1150                 {
1151                     messages::propertyValueFormatError(asyncResp->res, *gateway,
1152                                                        pathString + "/Gateway");
1153                     return;
1154                 }
1155             }
1156 
1157             // if IP address exist then  modify it.
1158             if (thisData != ipv4Data.end())
1159             {
1160                 // Apply changes
1161                 if (address)
1162                 {
1163                     auto callback = [asyncResp, entryIdx,
1164                                      address{std::string(*address)}](
1165                                         const boost::system::error_code ec) {
1166                         if (ec)
1167                         {
1168                             messages::internalError(asyncResp->res);
1169                             return;
1170                         }
1171                         asyncResp->res
1172                             .jsonValue["IPv4Addresses"][entryIdx]["Address"] =
1173                             std::move(address);
1174                     };
1175 
1176                     crow::connections::systemBus->async_method_call(
1177                         std::move(callback), "xyz.openbmc_project.Network",
1178                         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" +
1179                             thisData->id,
1180                         "org.freedesktop.DBus.Properties", "Set",
1181                         "xyz.openbmc_project.Network.IP", "Address",
1182                         std::variant<std::string>(*address));
1183                 }
1184 
1185                 if (subnetMask)
1186                 {
1187                     changeIPv4SubnetMaskProperty(ifaceId, entryIdx,
1188                                                  thisData->id, *subnetMask,
1189                                                  prefixLength, asyncResp);
1190                 }
1191 
1192                 if (addressOrigin)
1193                 {
1194                     changeIPv4Origin(ifaceId, entryIdx, thisData->id,
1195                                      *addressOrigin, addressOriginInDBusFormat,
1196                                      asyncResp);
1197                 }
1198 
1199                 if (gateway)
1200                 {
1201                     auto callback = [asyncResp, entryIdx,
1202                                      gateway{std::string(*gateway)}](
1203                                         const boost::system::error_code ec) {
1204                         if (ec)
1205                         {
1206                             messages::internalError(asyncResp->res);
1207                             return;
1208                         }
1209                         asyncResp->res
1210                             .jsonValue["IPv4Addresses"][entryIdx]["Gateway"] =
1211                             std::move(gateway);
1212                     };
1213 
1214                     crow::connections::systemBus->async_method_call(
1215                         std::move(callback), "xyz.openbmc_project.Network",
1216                         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" +
1217                             thisData->id,
1218                         "org.freedesktop.DBus.Properties", "Set",
1219                         "xyz.openbmc_project.Network.IP", "Gateway",
1220                         std::variant<std::string>(*gateway));
1221                 }
1222 
1223                 thisData++;
1224             }
1225             else
1226             {
1227                 // Create IPv4 with provided data
1228                 if (!gateway)
1229                 {
1230                     messages::propertyMissing(asyncResp->res,
1231                                               pathString + "/Gateway");
1232                     continue;
1233                 }
1234 
1235                 if (!address)
1236                 {
1237                     messages::propertyMissing(asyncResp->res,
1238                                               pathString + "/Address");
1239                     continue;
1240                 }
1241 
1242                 if (!subnetMask)
1243                 {
1244                     messages::propertyMissing(asyncResp->res,
1245                                               pathString + "/SubnetMask");
1246                     continue;
1247                 }
1248 
1249                 createIPv4(ifaceId, entryIdx, prefixLength, *gateway, *address,
1250                            asyncResp);
1251 
1252                 nlohmann::json &ipv4AddressJson =
1253                     asyncResp->res.jsonValue["IPv4Addresses"][entryIdx];
1254                 ipv4AddressJson["Address"] = *address;
1255                 ipv4AddressJson["SubnetMask"] = *subnetMask;
1256                 ipv4AddressJson["Gateway"] = *gateway;
1257             }
1258             entryIdx++;
1259         }
1260     }
1261 
1262     void handleStaticNameServersPatch(
1263         const std::string &ifaceId,
1264         const std::vector<std::string> &updatedStaticNameServers,
1265         const std::shared_ptr<AsyncResp> &asyncResp)
1266     {
1267         crow::connections::systemBus->async_method_call(
1268             [asyncResp,
1269              updatedStaticNameServers](const boost::system::error_code ec) {
1270                 if (ec)
1271                 {
1272                     messages::internalError(asyncResp->res);
1273                     return;
1274                 }
1275                 asyncResp->res.jsonValue["NameServers"] =
1276                     updatedStaticNameServers;
1277                 asyncResp->res.jsonValue["StaticNameServers"] =
1278                     updatedStaticNameServers;
1279             },
1280             "xyz.openbmc_project.Network",
1281             "/xyz/openbmc_project/network/" + ifaceId,
1282             "org.freedesktop.DBus.Properties", "Set",
1283             "xyz.openbmc_project.Network.EthernetInterface", "Nameservers",
1284             std::variant<std::vector<std::string>>{updatedStaticNameServers});
1285     }
1286 
1287     void parseInterfaceData(
1288         nlohmann::json &json_response, const std::string &iface_id,
1289         const EthernetInterfaceData &ethData,
1290         const boost::container::flat_set<IPv4AddressData> &ipv4Data)
1291     {
1292         json_response["Id"] = iface_id;
1293         json_response["@odata.id"] =
1294             "/redfish/v1/Managers/bmc/EthernetInterfaces/" + iface_id;
1295         json_response["InterfaceEnabled"] = true;
1296         if (ethData.speed == 0)
1297         {
1298             json_response["LinkStatus"] = "NoLink";
1299             json_response["Status"] = {
1300                 {"Health", "OK"},
1301                 {"State", "Disabled"},
1302             };
1303         }
1304         else
1305         {
1306             json_response["LinkStatus"] = "LinkUp";
1307             json_response["Status"] = {
1308                 {"Health", "OK"},
1309                 {"State", "Enabled"},
1310             };
1311         }
1312         json_response["SpeedMbps"] = ethData.speed;
1313         json_response["MACAddress"] = ethData.mac_address;
1314         json_response["DHCPv4"]["DHCPEnabled"] = ethData.DHCPEnabled;
1315 
1316         if (!ethData.hostname.empty())
1317         {
1318             json_response["HostName"] = ethData.hostname;
1319         }
1320 
1321         json_response["VLANs"] = {
1322             {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1323                               iface_id + "/VLANs"}};
1324 
1325         json_response["NameServers"] = ethData.nameservers;
1326         json_response["StaticNameServers"] = ethData.nameservers;
1327 
1328         if (ipv4Data.size() > 0)
1329         {
1330             nlohmann::json &ipv4_array = json_response["IPv4Addresses"];
1331             ipv4_array = nlohmann::json::array();
1332             for (auto &ipv4_config : ipv4Data)
1333             {
1334 
1335                 std::string gatewayStr = ipv4_config.gateway;
1336                 if (gatewayStr.empty())
1337                 {
1338                     gatewayStr = "0.0.0.0";
1339                 }
1340 
1341                 ipv4_array.push_back({{"AddressOrigin", ipv4_config.origin},
1342                                       {"SubnetMask", ipv4_config.netmask},
1343                                       {"Address", ipv4_config.address},
1344                                       {"Gateway", gatewayStr}});
1345             }
1346         }
1347         json_response["IPv6DefaultGateway"] = ethData.ipv6_default_gateway;
1348     }
1349 
1350     /**
1351      * Functions triggers appropriate requests on DBus
1352      */
1353     void doGet(crow::Response &res, const crow::Request &req,
1354                const std::vector<std::string> &params) override
1355     {
1356         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1357         if (params.size() != 1)
1358         {
1359             messages::internalError(asyncResp->res);
1360             return;
1361         }
1362 
1363         getEthernetIfaceData(
1364             params[0],
1365             [this, asyncResp, iface_id{std::string(params[0])}](
1366                 const bool &success, const EthernetInterfaceData &ethData,
1367                 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1368                 if (!success)
1369                 {
1370                     // TODO(Pawel)consider distinguish between non existing
1371                     // object, and other errors
1372                     messages::resourceNotFound(asyncResp->res,
1373                                                "EthernetInterface", iface_id);
1374                     return;
1375                 }
1376 
1377                 // because this has no dependence on the interface at this
1378                 // point, it needs to be done after we know the interface
1379                 // exists, not before.
1380                 getDHCPConfigData(asyncResp);
1381 
1382                 asyncResp->res.jsonValue["@odata.type"] =
1383                     "#EthernetInterface.v1_4_1.EthernetInterface";
1384                 asyncResp->res.jsonValue["@odata.context"] =
1385                     "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
1386                 asyncResp->res.jsonValue["Name"] = "Manager Ethernet Interface";
1387                 asyncResp->res.jsonValue["Description"] =
1388                     "Management Network Interface";
1389 
1390                 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
1391                                    ipv4Data);
1392             });
1393     }
1394 
1395     void doPatch(crow::Response &res, const crow::Request &req,
1396                  const std::vector<std::string> &params) override
1397     {
1398         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1399         if (params.size() != 1)
1400         {
1401             messages::internalError(asyncResp->res);
1402             return;
1403         }
1404 
1405         const std::string &iface_id = params[0];
1406 
1407         std::optional<std::string> hostname;
1408         std::optional<std::string> macAddress;
1409         std::optional<std::string> ipv6DefaultGateway;
1410         std::optional<nlohmann::json> ipv4Addresses;
1411         std::optional<nlohmann::json> ipv6Addresses;
1412         std::optional<std::vector<std::string>> staticNameServers;
1413         std::optional<std::vector<std::string>> nameServers;
1414         std::optional<nlohmann::json> dhcpv4;
1415 
1416         if (!json_util::readJson(
1417                 req, res, "HostName", hostname, "IPv4Addresses", ipv4Addresses,
1418                 "IPv6Addresses", ipv6Addresses, "MACAddress", macAddress,
1419                 "StaticNameServers", staticNameServers, "IPv6DefaultGateway",
1420                 ipv6DefaultGateway, "NameServers", nameServers, "DHCPv4",
1421                 dhcpv4))
1422         {
1423             return;
1424         }
1425 
1426         if (dhcpv4)
1427         {
1428             handleDHCPv4Patch(iface_id, *dhcpv4, asyncResp);
1429         }
1430 
1431         // Get single eth interface data, and call the below callback for JSON
1432         // preparation
1433         getEthernetIfaceData(
1434             iface_id,
1435             [this, asyncResp, iface_id, hostname = std::move(hostname),
1436              macAddress = std::move(macAddress),
1437              ipv4Addresses = std::move(ipv4Addresses),
1438              ipv6Addresses = std::move(ipv6Addresses),
1439              ipv6DefaultGateway = std::move(ipv6DefaultGateway),
1440              staticNameServers = std::move(staticNameServers),
1441              nameServers = std::move(nameServers)](
1442                 const bool &success, const EthernetInterfaceData &ethData,
1443                 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1444                 if (!success)
1445                 {
1446                     // ... otherwise return error
1447                     // TODO(Pawel)consider distinguish between non existing
1448                     // object, and other errors
1449                     messages::resourceNotFound(asyncResp->res,
1450                                                "Ethernet Interface", iface_id);
1451                     return;
1452                 }
1453 
1454                 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
1455                                    ipv4Data);
1456 
1457                 if (hostname)
1458                 {
1459                     handleHostnamePatch(*hostname, asyncResp);
1460                 }
1461 
1462                 if (macAddress)
1463                 {
1464                     handleMACAddressPatch(iface_id, *macAddress, asyncResp);
1465                 }
1466 
1467                 if (ipv4Addresses)
1468                 {
1469                     // TODO(ed) for some reason the capture of ipv4Addresses
1470                     // above is returning a const value, not a non-const value.
1471                     // This doesn't really work for us, as we need to be able to
1472                     // efficiently move out the intermedia nlohmann::json
1473                     // objects. This makes a copy of the structure, and operates
1474                     // on that, but could be done more efficiently
1475                     nlohmann::json ipv4 = std::move(*ipv4Addresses);
1476                     handleIPv4Patch(iface_id, ipv4, ipv4Data, asyncResp);
1477                 }
1478 
1479                 if (nameServers)
1480                 {
1481                     // Data.Permissions is read-only
1482                     messages::propertyNotWritable(asyncResp->res,
1483                                                   "NameServers");
1484                 }
1485 
1486                 if (ipv6Addresses)
1487                 {
1488                     // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
1489                     messages::propertyNotWritable(asyncResp->res,
1490                                                   "IPv6Addresses");
1491                 }
1492 
1493                 if (staticNameServers)
1494                 {
1495                     handleStaticNameServersPatch(iface_id, *staticNameServers,
1496                                                  asyncResp);
1497                 }
1498 
1499                 if (ipv6DefaultGateway)
1500                 {
1501                     messages::propertyNotWritable(asyncResp->res,
1502                                                   "IPv6DefaultGateway");
1503                 }
1504             });
1505     }
1506 };
1507 
1508 /**
1509  * VlanNetworkInterface derived class for delivering VLANNetworkInterface
1510  * Schema
1511  */
1512 class VlanNetworkInterface : public Node
1513 {
1514   public:
1515     /*
1516      * Default Constructor
1517      */
1518     template <typename CrowApp>
1519     VlanNetworkInterface(CrowApp &app) :
1520         Node(app,
1521              "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/<str>",
1522              std::string(), std::string())
1523     {
1524         entityPrivileges = {
1525             {boost::beast::http::verb::get, {{"Login"}}},
1526             {boost::beast::http::verb::head, {{"Login"}}},
1527             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1528             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1529             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1530             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1531     }
1532 
1533   private:
1534     void parseInterfaceData(
1535         nlohmann::json &json_response, const std::string &parent_iface_id,
1536         const std::string &iface_id, const EthernetInterfaceData &ethData,
1537         const boost::container::flat_set<IPv4AddressData> &ipv4Data)
1538     {
1539         // Fill out obvious data...
1540         json_response["Id"] = iface_id;
1541         json_response["@odata.id"] =
1542             "/redfish/v1/Managers/bmc/EthernetInterfaces/" + parent_iface_id +
1543             "/VLANs/" + iface_id;
1544 
1545         json_response["VLANEnable"] = true;
1546         if (!ethData.vlan_id.empty())
1547         {
1548             json_response["VLANId"] = ethData.vlan_id.back();
1549         }
1550     }
1551 
1552     bool verifyNames(const std::string &parent, const std::string &iface)
1553     {
1554         if (!boost::starts_with(iface, parent + "_"))
1555         {
1556             return false;
1557         }
1558         else
1559         {
1560             return true;
1561         }
1562     }
1563 
1564     /**
1565      * Functions triggers appropriate requests on DBus
1566      */
1567     void doGet(crow::Response &res, const crow::Request &req,
1568                const std::vector<std::string> &params) override
1569     {
1570         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1571         // TODO(Pawel) this shall be parameterized call (two params) to get
1572         // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1573         // Check if there is required param, truly entering this shall be
1574         // impossible.
1575         if (params.size() != 2)
1576         {
1577             messages::internalError(res);
1578             res.end();
1579             return;
1580         }
1581 
1582         const std::string &parent_iface_id = params[0];
1583         const std::string &iface_id = params[1];
1584         res.jsonValue["@odata.type"] =
1585             "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1586         res.jsonValue["@odata.context"] =
1587             "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
1588         res.jsonValue["Name"] = "VLAN Network Interface";
1589 
1590         if (!verifyNames(parent_iface_id, iface_id))
1591         {
1592             return;
1593         }
1594 
1595         // Get single eth interface data, and call the below callback for JSON
1596         // preparation
1597         getEthernetIfaceData(
1598             params[1],
1599             [this, asyncResp, parent_iface_id{std::string(params[0])},
1600              iface_id{std::string(params[1])}](
1601                 const bool &success, const EthernetInterfaceData &ethData,
1602                 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1603                 if (success && ethData.vlan_id.size() != 0)
1604                 {
1605                     parseInterfaceData(asyncResp->res.jsonValue,
1606                                        parent_iface_id, iface_id, ethData,
1607                                        ipv4Data);
1608                 }
1609                 else
1610                 {
1611                     // ... otherwise return error
1612                     // TODO(Pawel)consider distinguish between non existing
1613                     // object, and other errors
1614                     messages::resourceNotFound(
1615                         asyncResp->res, "VLAN Network Interface", iface_id);
1616                 }
1617             });
1618     }
1619 
1620     void doPatch(crow::Response &res, const crow::Request &req,
1621                  const std::vector<std::string> &params) override
1622     {
1623         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1624         if (params.size() != 2)
1625         {
1626             messages::internalError(asyncResp->res);
1627             return;
1628         }
1629 
1630         const std::string &parentIfaceId = params[0];
1631         const std::string &ifaceId = params[1];
1632 
1633         if (!verifyNames(parentIfaceId, ifaceId))
1634         {
1635             messages::resourceNotFound(asyncResp->res, "VLAN Network Interface",
1636                                        ifaceId);
1637             return;
1638         }
1639 
1640         bool vlanEnable = false;
1641         uint64_t vlanId = 0;
1642 
1643         if (!json_util::readJson(req, res, "VLANEnable", vlanEnable, "VLANId",
1644                                  vlanId))
1645         {
1646             return;
1647         }
1648 
1649         // Get single eth interface data, and call the below callback for JSON
1650         // preparation
1651         getEthernetIfaceData(params[1], [this, asyncResp,
1652                                          parentIfaceId{std::string(params[0])},
1653                                          ifaceId{std::string(params[1])},
1654                                          &vlanEnable, &vlanId](
1655                                             const bool &success,
1656                                             const EthernetInterfaceData
1657                                                 &ethData,
1658                                             const boost::container::flat_set<
1659                                                 IPv4AddressData> &ipv4Data) {
1660             if (success && !ethData.vlan_id.empty())
1661             {
1662                 parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId,
1663                                    ifaceId, ethData, ipv4Data);
1664                 auto callback =
1665                     [asyncResp](const boost::system::error_code ec) {
1666                         if (ec)
1667                         {
1668                             messages::internalError(asyncResp->res);
1669                         }
1670                     };
1671 
1672                 if (vlanEnable == true)
1673                 {
1674                     crow::connections::systemBus->async_method_call(
1675                         std::move(callback), "xyz.openbmc_project.Network",
1676                         "/xyz/openbmc_project/network/" + ifaceId,
1677                         "org.freedesktop.DBus.Properties", "Set",
1678                         "xyz.openbmc_project.Network.VLAN", "Id",
1679                         std::variant<uint32_t>(vlanId));
1680                 }
1681                 else
1682                 {
1683                     BMCWEB_LOG_DEBUG
1684                         << "vlanEnable is false. Deleting the vlan interface";
1685                     crow::connections::systemBus->async_method_call(
1686                         std::move(callback), "xyz.openbmc_project.Network",
1687                         std::string("/xyz/openbmc_project/network/") + ifaceId,
1688                         "xyz.openbmc_project.Object.Delete", "Delete");
1689                 }
1690             }
1691             else
1692             {
1693                 // TODO(Pawel)consider distinguish between non existing
1694                 // object, and other errors
1695                 messages::resourceNotFound(asyncResp->res,
1696                                            "VLAN Network Interface", ifaceId);
1697                 return;
1698             }
1699         });
1700     }
1701 
1702     void doDelete(crow::Response &res, const crow::Request &req,
1703                   const std::vector<std::string> &params) override
1704     {
1705         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1706         if (params.size() != 2)
1707         {
1708             messages::internalError(asyncResp->res);
1709             return;
1710         }
1711 
1712         const std::string &parentIfaceId = params[0];
1713         const std::string &ifaceId = params[1];
1714 
1715         if (!verifyNames(parentIfaceId, ifaceId))
1716         {
1717             messages::resourceNotFound(asyncResp->res, "VLAN Network Interface",
1718                                        ifaceId);
1719             return;
1720         }
1721 
1722         // Get single eth interface data, and call the below callback for JSON
1723         // preparation
1724         getEthernetIfaceData(
1725             params[1],
1726             [this, asyncResp, parentIfaceId{std::string(params[0])},
1727              ifaceId{std::string(params[1])}](
1728                 const bool &success, const EthernetInterfaceData &ethData,
1729                 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1730                 if (success && !ethData.vlan_id.empty())
1731                 {
1732                     parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId,
1733                                        ifaceId, ethData, ipv4Data);
1734 
1735                     auto callback =
1736                         [asyncResp](const boost::system::error_code ec) {
1737                             if (ec)
1738                             {
1739                                 messages::internalError(asyncResp->res);
1740                             }
1741                         };
1742                     crow::connections::systemBus->async_method_call(
1743                         std::move(callback), "xyz.openbmc_project.Network",
1744                         std::string("/xyz/openbmc_project/network/") + ifaceId,
1745                         "xyz.openbmc_project.Object.Delete", "Delete");
1746                 }
1747                 else
1748                 {
1749                     // ... otherwise return error
1750                     // TODO(Pawel)consider distinguish between non existing
1751                     // object, and other errors
1752                     messages::resourceNotFound(
1753                         asyncResp->res, "VLAN Network Interface", ifaceId);
1754                 }
1755             });
1756     }
1757 };
1758 
1759 /**
1760  * VlanNetworkInterfaceCollection derived class for delivering
1761  * VLANNetworkInterface Collection Schema
1762  */
1763 class VlanNetworkInterfaceCollection : public Node
1764 {
1765   public:
1766     template <typename CrowApp>
1767     VlanNetworkInterfaceCollection(CrowApp &app) :
1768         Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/",
1769              std::string())
1770     {
1771         entityPrivileges = {
1772             {boost::beast::http::verb::get, {{"Login"}}},
1773             {boost::beast::http::verb::head, {{"Login"}}},
1774             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1775             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1776             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1777             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1778     }
1779 
1780   private:
1781     /**
1782      * Functions triggers appropriate requests on DBus
1783      */
1784     void doGet(crow::Response &res, const crow::Request &req,
1785                const std::vector<std::string> &params) override
1786     {
1787         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1788         if (params.size() != 1)
1789         {
1790             // This means there is a problem with the router
1791             messages::internalError(asyncResp->res);
1792             return;
1793         }
1794 
1795         const std::string &rootInterfaceName = params[0];
1796 
1797         // Get eth interface list, and call the below callback for JSON
1798         // preparation
1799         getEthernetIfaceList(
1800             [asyncResp, rootInterfaceName{std::string(rootInterfaceName)}](
1801                 const bool &success,
1802                 const boost::container::flat_set<std::string> &iface_list) {
1803                 if (!success)
1804                 {
1805                     messages::internalError(asyncResp->res);
1806                     return;
1807                 }
1808 
1809                 if (iface_list.find(rootInterfaceName) == iface_list.end())
1810                 {
1811                     messages::resourceNotFound(asyncResp->res,
1812                                                "VLanNetworkInterfaceCollection",
1813                                                rootInterfaceName);
1814                     return;
1815                 }
1816 
1817                 asyncResp->res.jsonValue["@odata.type"] =
1818                     "#VLanNetworkInterfaceCollection."
1819                     "VLanNetworkInterfaceCollection";
1820                 asyncResp->res.jsonValue["@odata.context"] =
1821                     "/redfish/v1/$metadata"
1822                     "#VLanNetworkInterfaceCollection."
1823                     "VLanNetworkInterfaceCollection";
1824                 asyncResp->res.jsonValue["Name"] =
1825                     "VLAN Network Interface Collection";
1826 
1827                 nlohmann::json iface_array = nlohmann::json::array();
1828 
1829                 for (const std::string &iface_item : iface_list)
1830                 {
1831                     if (boost::starts_with(iface_item, rootInterfaceName + "_"))
1832                     {
1833                         iface_array.push_back(
1834                             {{"@odata.id",
1835                               "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1836                                   rootInterfaceName + "/VLANs/" + iface_item}});
1837                     }
1838                 }
1839 
1840                 asyncResp->res.jsonValue["Members@odata.count"] =
1841                     iface_array.size();
1842                 asyncResp->res.jsonValue["Members"] = std::move(iface_array);
1843                 asyncResp->res.jsonValue["@odata.id"] =
1844                     "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1845                     rootInterfaceName + "/VLANs";
1846             });
1847     }
1848 
1849     void doPost(crow::Response &res, const crow::Request &req,
1850                 const std::vector<std::string> &params) override
1851     {
1852         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1853         if (params.size() != 1)
1854         {
1855             messages::internalError(asyncResp->res);
1856             return;
1857         }
1858         bool vlanEnable = false;
1859         uint32_t vlanId = 0;
1860         if (!json_util::readJson(req, res, "VLANId", vlanId, "VLANEnable",
1861                                  vlanEnable))
1862         {
1863             return;
1864         }
1865         // Need both vlanId and vlanEnable to service this request
1866         if (!vlanId)
1867         {
1868             messages::propertyMissing(asyncResp->res, "VLANId");
1869         }
1870         if (!vlanEnable)
1871         {
1872             messages::propertyMissing(asyncResp->res, "VLANEnable");
1873         }
1874         if (static_cast<bool>(vlanId) ^ static_cast<bool>(vlanEnable))
1875         {
1876             return;
1877         }
1878 
1879         const std::string &rootInterfaceName = params[0];
1880         auto callback = [asyncResp](const boost::system::error_code ec) {
1881             if (ec)
1882             {
1883                 // TODO(ed) make more consistent error messages based on
1884                 // phosphor-network responses
1885                 messages::internalError(asyncResp->res);
1886                 return;
1887             }
1888             messages::created(asyncResp->res);
1889         };
1890         crow::connections::systemBus->async_method_call(
1891             std::move(callback), "xyz.openbmc_project.Network",
1892             "/xyz/openbmc_project/network",
1893             "xyz.openbmc_project.Network.VLAN.Create", "VLAN",
1894             rootInterfaceName, vlanId);
1895     }
1896 };
1897 } // namespace redfish
1898