xref: /openbmc/bmcweb/features/redfish/lib/ethernet.hpp (revision e59f968c5d888f1203ac2c8a636f52a906f116db)
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 IPv6 data required by Redfish
75  */
76 struct IPv6AddressData
77 {
78     std::string id;
79     std::string address;
80     std::string origin;
81     uint8_t prefixLength;
82 
83     bool operator<(const IPv6AddressData &obj) const
84     {
85         return id < obj.id;
86     }
87 };
88 /**
89  * Structure for keeping basic single Ethernet Interface information
90  * available from DBus
91  */
92 struct EthernetInterfaceData
93 {
94     uint32_t speed;
95     bool auto_neg;
96     bool DNSEnabled;
97     bool NTPEnabled;
98     bool HostNameEnabled;
99     bool SendHostNameEnabled;
100     bool linkUp;
101     std::string DHCPEnabled;
102     std::string operatingMode;
103     std::string hostname;
104     std::string default_gateway;
105     std::string ipv6_default_gateway;
106     std::string mac_address;
107     std::vector<std::uint32_t> vlan_id;
108     std::vector<std::string> nameservers;
109     std::vector<std::string> domainnames;
110 };
111 
112 struct DHCPParameters
113 {
114     std::optional<bool> dhcpv4Enabled;
115     std::optional<bool> useDNSServers;
116     std::optional<bool> useNTPServers;
117     std::optional<bool> useUseDomainName;
118     std::optional<std::string> dhcpv6OperatingMode;
119 };
120 
121 // Helper function that changes bits netmask notation (i.e. /24)
122 // into full dot notation
123 inline std::string getNetmask(unsigned int bits)
124 {
125     uint32_t value = 0xffffffff << (32 - bits);
126     std::string netmask = std::to_string((value >> 24) & 0xff) + "." +
127                           std::to_string((value >> 16) & 0xff) + "." +
128                           std::to_string((value >> 8) & 0xff) + "." +
129                           std::to_string(value & 0xff);
130     return netmask;
131 }
132 
133 inline bool translateDHCPEnabledToBool(const std::string &inputDHCP,
134                                        bool isIPv4)
135 {
136     if (isIPv4)
137     {
138         return (
139             (inputDHCP ==
140              "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.v4") ||
141             (inputDHCP ==
142              "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.both"));
143     }
144     return ((inputDHCP ==
145              "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.v6") ||
146             (inputDHCP ==
147              "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.both"));
148 }
149 
150 inline std::string GetDHCPEnabledEnumeration(bool isIPv4, bool isIPv6)
151 {
152     if (isIPv4 && isIPv6)
153     {
154         return "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.both";
155     }
156     else if (isIPv4)
157     {
158         return "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.v4";
159     }
160     else if (isIPv6)
161     {
162         return "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.v6";
163     }
164     return "xyz.openbmc_project.Network.EthernetInterface.DHCPConf.none";
165 }
166 
167 inline std::string
168     translateAddressOriginDbusToRedfish(const std::string &inputOrigin,
169                                         bool isIPv4)
170 {
171     if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.Static")
172     {
173         return "Static";
174     }
175     if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal")
176     {
177         if (isIPv4)
178         {
179             return "IPv4LinkLocal";
180         }
181         else
182         {
183             return "LinkLocal";
184         }
185     }
186     if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
187     {
188         if (isIPv4)
189         {
190             return "DHCP";
191         }
192         else
193         {
194             return "DHCPv6";
195         }
196     }
197     if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC")
198     {
199         return "SLAAC";
200     }
201     return "";
202 }
203 
204 inline bool extractEthernetInterfaceData(const std::string &ethiface_id,
205                                          const GetManagedObjects &dbus_data,
206                                          EthernetInterfaceData &ethData)
207 {
208     bool idFound = false;
209     for (const auto &objpath : dbus_data)
210     {
211         for (const auto &ifacePair : objpath.second)
212         {
213             if (objpath.first == "/xyz/openbmc_project/network/" + ethiface_id)
214             {
215                 idFound = true;
216                 if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress")
217                 {
218                     for (const auto &propertyPair : ifacePair.second)
219                     {
220                         if (propertyPair.first == "MACAddress")
221                         {
222                             const std::string *mac =
223                                 std::get_if<std::string>(&propertyPair.second);
224                             if (mac != nullptr)
225                             {
226                                 ethData.mac_address = *mac;
227                             }
228                         }
229                     }
230                 }
231                 else if (ifacePair.first == "xyz.openbmc_project.Network.VLAN")
232                 {
233                     for (const auto &propertyPair : ifacePair.second)
234                     {
235                         if (propertyPair.first == "Id")
236                         {
237                             const uint32_t *id =
238                                 std::get_if<uint32_t>(&propertyPair.second);
239                             if (id != nullptr)
240                             {
241                                 ethData.vlan_id.push_back(*id);
242                             }
243                         }
244                     }
245                 }
246                 else if (ifacePair.first ==
247                          "xyz.openbmc_project.Network.EthernetInterface")
248                 {
249                     for (const auto &propertyPair : ifacePair.second)
250                     {
251                         if (propertyPair.first == "AutoNeg")
252                         {
253                             const bool *auto_neg =
254                                 std::get_if<bool>(&propertyPair.second);
255                             if (auto_neg != nullptr)
256                             {
257                                 ethData.auto_neg = *auto_neg;
258                             }
259                         }
260                         else if (propertyPair.first == "Speed")
261                         {
262                             const uint32_t *speed =
263                                 std::get_if<uint32_t>(&propertyPair.second);
264                             if (speed != nullptr)
265                             {
266                                 ethData.speed = *speed;
267                             }
268                         }
269                         else if (propertyPair.first == "LinkUp")
270                         {
271                             const bool *linkUp =
272                                 std::get_if<bool>(&propertyPair.second);
273                             if (linkUp != nullptr)
274                             {
275                                 ethData.linkUp = *linkUp;
276                             }
277                         }
278                         else if (propertyPair.first == "Nameservers")
279                         {
280                             const std::vector<std::string> *nameservers =
281                                 sdbusplus::message::variant_ns::get_if<
282                                     std::vector<std::string>>(
283                                     &propertyPair.second);
284                             if (nameservers != nullptr)
285                             {
286                                 ethData.nameservers = std::move(*nameservers);
287                             }
288                         }
289                         else if (propertyPair.first == "DHCPEnabled")
290                         {
291                             const std::string *DHCPEnabled =
292                                 std::get_if<std::string>(&propertyPair.second);
293                             if (DHCPEnabled != nullptr)
294                             {
295                                 ethData.DHCPEnabled = *DHCPEnabled;
296                             }
297                         }
298                         else if (propertyPair.first == "DomainName")
299                         {
300                             const std::vector<std::string> *domainNames =
301                                 sdbusplus::message::variant_ns::get_if<
302                                     std::vector<std::string>>(
303                                     &propertyPair.second);
304                             if (domainNames != nullptr)
305                             {
306                                 ethData.domainnames = std::move(*domainNames);
307                             }
308                         }
309                     }
310                 }
311             }
312 
313             if (objpath.first == "/xyz/openbmc_project/network/config/dhcp")
314             {
315                 if (ifacePair.first ==
316                     "xyz.openbmc_project.Network.DHCPConfiguration")
317                 {
318                     for (const auto &propertyPair : ifacePair.second)
319                     {
320                         if (propertyPair.first == "DNSEnabled")
321                         {
322                             const bool *DNSEnabled =
323                                 std::get_if<bool>(&propertyPair.second);
324                             if (DNSEnabled != nullptr)
325                             {
326                                 ethData.DNSEnabled = *DNSEnabled;
327                             }
328                         }
329                         else if (propertyPair.first == "NTPEnabled")
330                         {
331                             const bool *NTPEnabled =
332                                 std::get_if<bool>(&propertyPair.second);
333                             if (NTPEnabled != nullptr)
334                             {
335                                 ethData.NTPEnabled = *NTPEnabled;
336                             }
337                         }
338                         else if (propertyPair.first == "HostNameEnabled")
339                         {
340                             const bool *HostNameEnabled =
341                                 std::get_if<bool>(&propertyPair.second);
342                             if (HostNameEnabled != nullptr)
343                             {
344                                 ethData.HostNameEnabled = *HostNameEnabled;
345                             }
346                         }
347                         else if (propertyPair.first == "SendHostNameEnabled")
348                         {
349                             const bool *SendHostNameEnabled =
350                                 std::get_if<bool>(&propertyPair.second);
351                             if (SendHostNameEnabled != nullptr)
352                             {
353                                 ethData.SendHostNameEnabled =
354                                     *SendHostNameEnabled;
355                             }
356                         }
357                     }
358                 }
359             }
360             // System configuration shows up in the global namespace, so no need
361             // to check eth number
362             if (ifacePair.first ==
363                 "xyz.openbmc_project.Network.SystemConfiguration")
364             {
365                 for (const auto &propertyPair : ifacePair.second)
366                 {
367                     if (propertyPair.first == "HostName")
368                     {
369                         const std::string *hostname =
370                             sdbusplus::message::variant_ns::get_if<std::string>(
371                                 &propertyPair.second);
372                         if (hostname != nullptr)
373                         {
374                             ethData.hostname = *hostname;
375                         }
376                     }
377                     else if (propertyPair.first == "DefaultGateway")
378                     {
379                         const std::string *defaultGateway =
380                             sdbusplus::message::variant_ns::get_if<std::string>(
381                                 &propertyPair.second);
382                         if (defaultGateway != nullptr)
383                         {
384                             ethData.default_gateway = *defaultGateway;
385                         }
386                     }
387                     else if (propertyPair.first == "DefaultGateway6")
388                     {
389                         const std::string *defaultGateway6 =
390                             sdbusplus::message::variant_ns::get_if<std::string>(
391                                 &propertyPair.second);
392                         if (defaultGateway6 != nullptr)
393                         {
394                             ethData.ipv6_default_gateway = *defaultGateway6;
395                         }
396                     }
397                 }
398             }
399         }
400     }
401     return idFound;
402 }
403 
404 // Helper function that extracts data for single ethernet ipv6 address
405 inline void
406     extractIPV6Data(const std::string &ethiface_id,
407                     const GetManagedObjects &dbus_data,
408                     boost::container::flat_set<IPv6AddressData> &ipv6_config)
409 {
410     const std::string ipv6PathStart =
411         "/xyz/openbmc_project/network/" + ethiface_id + "/ipv6/";
412 
413     // Since there might be several IPv6 configurations aligned with
414     // single ethernet interface, loop over all of them
415     for (const auto &objpath : dbus_data)
416     {
417         // Check if proper pattern for object path appears
418         if (boost::starts_with(objpath.first.str, ipv6PathStart))
419         {
420             for (auto &interface : objpath.second)
421             {
422                 if (interface.first == "xyz.openbmc_project.Network.IP")
423                 {
424                     // Instance IPv6AddressData structure, and set as
425                     // appropriate
426                     std::pair<
427                         boost::container::flat_set<IPv6AddressData>::iterator,
428                         bool>
429                         it = ipv6_config.insert(IPv6AddressData{});
430                     IPv6AddressData &ipv6_address = *it.first;
431                     ipv6_address.id =
432                         objpath.first.str.substr(ipv6PathStart.size());
433                     for (auto &property : interface.second)
434                     {
435                         if (property.first == "Address")
436                         {
437                             const std::string *address =
438                                 std::get_if<std::string>(&property.second);
439                             if (address != nullptr)
440                             {
441                                 ipv6_address.address = *address;
442                             }
443                         }
444                         else if (property.first == "Origin")
445                         {
446                             const std::string *origin =
447                                 std::get_if<std::string>(&property.second);
448                             if (origin != nullptr)
449                             {
450                                 ipv6_address.origin =
451                                     translateAddressOriginDbusToRedfish(*origin,
452                                                                         false);
453                             }
454                         }
455                         else if (property.first == "PrefixLength")
456                         {
457                             const uint8_t *prefix =
458                                 std::get_if<uint8_t>(&property.second);
459                             if (prefix != nullptr)
460                             {
461                                 ipv6_address.prefixLength = *prefix;
462                             }
463                         }
464                         else
465                         {
466                             BMCWEB_LOG_ERROR
467                                 << "Got extra property: " << property.first
468                                 << " on the " << objpath.first.str << " object";
469                         }
470                     }
471                 }
472             }
473         }
474     }
475 }
476 
477 // Helper function that extracts data for single ethernet ipv4 address
478 inline void
479     extractIPData(const std::string &ethiface_id,
480                   const GetManagedObjects &dbus_data,
481                   boost::container::flat_set<IPv4AddressData> &ipv4_config)
482 {
483     const std::string ipv4PathStart =
484         "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/";
485 
486     // Since there might be several IPv4 configurations aligned with
487     // single ethernet interface, loop over all of them
488     for (const auto &objpath : dbus_data)
489     {
490         // Check if proper pattern for object path appears
491         if (boost::starts_with(objpath.first.str, ipv4PathStart))
492         {
493             for (auto &interface : objpath.second)
494             {
495                 if (interface.first == "xyz.openbmc_project.Network.IP")
496                 {
497                     // Instance IPv4AddressData structure, and set as
498                     // appropriate
499                     std::pair<
500                         boost::container::flat_set<IPv4AddressData>::iterator,
501                         bool>
502                         it = ipv4_config.insert(IPv4AddressData{});
503                     IPv4AddressData &ipv4_address = *it.first;
504                     ipv4_address.id =
505                         objpath.first.str.substr(ipv4PathStart.size());
506                     for (auto &property : interface.second)
507                     {
508                         if (property.first == "Address")
509                         {
510                             const std::string *address =
511                                 std::get_if<std::string>(&property.second);
512                             if (address != nullptr)
513                             {
514                                 ipv4_address.address = *address;
515                             }
516                         }
517                         else if (property.first == "Gateway")
518                         {
519                             const std::string *gateway =
520                                 std::get_if<std::string>(&property.second);
521                             if (gateway != nullptr)
522                             {
523                                 ipv4_address.gateway = *gateway;
524                             }
525                         }
526                         else if (property.first == "Origin")
527                         {
528                             const std::string *origin =
529                                 std::get_if<std::string>(&property.second);
530                             if (origin != nullptr)
531                             {
532                                 ipv4_address.origin =
533                                     translateAddressOriginDbusToRedfish(*origin,
534                                                                         true);
535                             }
536                         }
537                         else if (property.first == "PrefixLength")
538                         {
539                             const uint8_t *mask =
540                                 std::get_if<uint8_t>(&property.second);
541                             if (mask != nullptr)
542                             {
543                                 // convert it to the string
544                                 ipv4_address.netmask = getNetmask(*mask);
545                             }
546                         }
547                         else
548                         {
549                             BMCWEB_LOG_ERROR
550                                 << "Got extra property: " << property.first
551                                 << " on the " << objpath.first.str << " object";
552                         }
553                     }
554                     // Check if given address is local, or global
555                     ipv4_address.linktype =
556                         boost::starts_with(ipv4_address.address, "169.254.")
557                             ? LinkType::Local
558                             : LinkType::Global;
559                 }
560             }
561         }
562     }
563 }
564 
565 /**
566  * @brief Sets given Id on the given VLAN interface through D-Bus
567  *
568  * @param[in] ifaceId       Id of VLAN interface that should be modified
569  * @param[in] inputVlanId   New ID of the VLAN
570  * @param[in] callback      Function that will be called after the operation
571  *
572  * @return None.
573  */
574 template <typename CallbackFunc>
575 void changeVlanId(const std::string &ifaceId, const uint32_t &inputVlanId,
576                   CallbackFunc &&callback)
577 {
578     crow::connections::systemBus->async_method_call(
579         callback, "xyz.openbmc_project.Network",
580         std::string("/xyz/openbmc_project/network/") + ifaceId,
581         "org.freedesktop.DBus.Properties", "Set",
582         "xyz.openbmc_project.Network.VLAN", "Id",
583         std::variant<uint32_t>(inputVlanId));
584 }
585 
586 /**
587  * @brief Helper function that verifies IP address to check if it is in
588  *        proper format. If bits pointer is provided, also calculates active
589  *        bit count for Subnet Mask.
590  *
591  * @param[in]  ip     IP that will be verified
592  * @param[out] bits   Calculated mask in bits notation
593  *
594  * @return true in case of success, false otherwise
595  */
596 inline bool ipv4VerifyIpAndGetBitcount(const std::string &ip,
597                                        uint8_t *bits = nullptr)
598 {
599     std::vector<std::string> bytesInMask;
600 
601     boost::split(bytesInMask, ip, boost::is_any_of("."));
602 
603     static const constexpr int ipV4AddressSectionsCount = 4;
604     if (bytesInMask.size() != ipV4AddressSectionsCount)
605     {
606         return false;
607     }
608 
609     if (bits != nullptr)
610     {
611         *bits = 0;
612     }
613 
614     char *endPtr;
615     long previousValue = 255;
616     bool firstZeroInByteHit;
617     for (const std::string &byte : bytesInMask)
618     {
619         if (byte.empty())
620         {
621             return false;
622         }
623 
624         // Use strtol instead of stroi to avoid exceptions
625         long value = std::strtol(byte.c_str(), &endPtr, 10);
626 
627         // endPtr should point to the end of the string, otherwise given string
628         // is not 100% number
629         if (*endPtr != '\0')
630         {
631             return false;
632         }
633 
634         // Value should be contained in byte
635         if (value < 0 || value > 255)
636         {
637             return false;
638         }
639 
640         if (bits != nullptr)
641         {
642             // Mask has to be continuous between bytes
643             if (previousValue != 255 && value != 0)
644             {
645                 return false;
646             }
647 
648             // Mask has to be continuous inside bytes
649             firstZeroInByteHit = false;
650 
651             // Count bits
652             for (int bitIdx = 7; bitIdx >= 0; bitIdx--)
653             {
654                 if (value & (1 << bitIdx))
655                 {
656                     if (firstZeroInByteHit)
657                     {
658                         // Continuity not preserved
659                         return false;
660                     }
661                     else
662                     {
663                         (*bits)++;
664                     }
665                 }
666                 else
667                 {
668                     firstZeroInByteHit = true;
669                 }
670             }
671         }
672 
673         previousValue = value;
674     }
675 
676     return true;
677 }
678 
679 /**
680  * @brief Deletes given IPv4 interface
681  *
682  * @param[in] ifaceId     Id of interface whose IP should be deleted
683  * @param[in] ipHash      DBus Hash id of IP that should be deleted
684  * @param[io] asyncResp   Response object that will be returned to client
685  *
686  * @return None
687  */
688 inline void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
689                        const std::shared_ptr<AsyncResp> asyncResp)
690 {
691     crow::connections::systemBus->async_method_call(
692         [asyncResp](const boost::system::error_code ec) {
693             if (ec)
694             {
695                 messages::internalError(asyncResp->res);
696             }
697         },
698         "xyz.openbmc_project.Network",
699         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
700         "xyz.openbmc_project.Object.Delete", "Delete");
701 }
702 
703 /**
704  * @brief Creates a static IPv4 entry
705  *
706  * @param[in] ifaceId      Id of interface upon which to create the IPv4 entry
707  * @param[in] prefixLength IPv4 prefix syntax for the subnet mask
708  * @param[in] gateway      IPv4 address of this interfaces gateway
709  * @param[in] address      IPv4 address to assign to this interface
710  * @param[io] asyncResp    Response object that will be returned to client
711  *
712  * @return None
713  */
714 inline void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
715                        uint8_t prefixLength, const std::string &gateway,
716                        const std::string &address,
717                        std::shared_ptr<AsyncResp> asyncResp)
718 {
719     crow::connections::systemBus->async_method_call(
720         [asyncResp](const boost::system::error_code ec) {
721             if (ec)
722             {
723                 messages::internalError(asyncResp->res);
724             }
725         },
726         "xyz.openbmc_project.Network",
727         "/xyz/openbmc_project/network/" + ifaceId,
728         "xyz.openbmc_project.Network.IP.Create", "IP",
729         "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, prefixLength,
730         gateway);
731 }
732 
733 /**
734  * @brief Deletes the IPv4 entry for this interface and creates a replacement
735  * static IPv4 entry
736  *
737  * @param[in] ifaceId      Id of interface upon which to create the IPv4 entry
738  * @param[in] id           The unique hash entry identifying the DBus entry
739  * @param[in] prefixLength IPv4 prefix syntax for the subnet mask
740  * @param[in] gateway      IPv4 address of this interfaces gateway
741  * @param[in] address      IPv4 address to assign to this interface
742  * @param[io] asyncResp    Response object that will be returned to client
743  *
744  * @return None
745  */
746 inline void deleteAndCreateIPv4(const std::string &ifaceId,
747                                 const std::string &id, uint8_t prefixLength,
748                                 const std::string &gateway,
749                                 const std::string &address,
750                                 std::shared_ptr<AsyncResp> asyncResp)
751 {
752     crow::connections::systemBus->async_method_call(
753         [asyncResp, ifaceId, address, prefixLength,
754          gateway](const boost::system::error_code ec) {
755             if (ec)
756             {
757                 messages::internalError(asyncResp->res);
758             }
759             crow::connections::systemBus->async_method_call(
760                 [asyncResp](const boost::system::error_code ec) {
761                     if (ec)
762                     {
763                         messages::internalError(asyncResp->res);
764                     }
765                 },
766                 "xyz.openbmc_project.Network",
767                 "/xyz/openbmc_project/network/" + ifaceId,
768                 "xyz.openbmc_project.Network.IP.Create", "IP",
769                 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address,
770                 prefixLength, gateway);
771         },
772         "xyz.openbmc_project.Network",
773         +"/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + id,
774         "xyz.openbmc_project.Object.Delete", "Delete");
775 }
776 
777 /**
778  * @brief Deletes given IPv6
779  *
780  * @param[in] ifaceId     Id of interface whose IP should be deleted
781  * @param[in] ipHash      DBus Hash id of IP that should be deleted
782  * @param[io] asyncResp   Response object that will be returned to client
783  *
784  * @return None
785  */
786 inline void deleteIPv6(const std::string &ifaceId, const std::string &ipHash,
787                        const std::shared_ptr<AsyncResp> asyncResp)
788 {
789     crow::connections::systemBus->async_method_call(
790         [asyncResp](const boost::system::error_code ec) {
791             if (ec)
792             {
793                 messages::internalError(asyncResp->res);
794             }
795         },
796         "xyz.openbmc_project.Network",
797         "/xyz/openbmc_project/network/" + ifaceId + "/ipv6/" + ipHash,
798         "xyz.openbmc_project.Object.Delete", "Delete");
799 }
800 
801 /**
802  * @brief Deletes the IPv6 entry for this interface and creates a replacement
803  * static IPv6 entry
804  *
805  * @param[in] ifaceId      Id of interface upon which to create the IPv6 entry
806  * @param[in] id           The unique hash entry identifying the DBus entry
807  * @param[in] prefixLength IPv6 prefix syntax for the subnet mask
808  * @param[in] address      IPv6 address to assign to this interface
809  * @param[io] asyncResp    Response object that will be returned to client
810  *
811  * @return None
812  */
813 inline void deleteAndCreateIPv6(const std::string &ifaceId,
814                                 const std::string &id, uint8_t prefixLength,
815                                 const std::string &address,
816                                 std::shared_ptr<AsyncResp> asyncResp)
817 {
818     crow::connections::systemBus->async_method_call(
819         [asyncResp, ifaceId, address,
820          prefixLength](const boost::system::error_code ec) {
821             if (ec)
822             {
823                 messages::internalError(asyncResp->res);
824             }
825             crow::connections::systemBus->async_method_call(
826                 [asyncResp](const boost::system::error_code ec) {
827                     if (ec)
828                     {
829                         messages::internalError(asyncResp->res);
830                     }
831                 },
832                 "xyz.openbmc_project.Network",
833                 "/xyz/openbmc_project/network/" + ifaceId,
834                 "xyz.openbmc_project.Network.IP.Create", "IP",
835                 "xyz.openbmc_project.Network.IP.Protocol.IPv6", address,
836                 prefixLength, "");
837         },
838         "xyz.openbmc_project.Network",
839         +"/xyz/openbmc_project/network/" + ifaceId + "/ipv6/" + id,
840         "xyz.openbmc_project.Object.Delete", "Delete");
841 }
842 
843 /**
844  * @brief Creates IPv6 with given data
845  *
846  * @param[in] ifaceId      Id of interface whose IP should be added
847  * @param[in] prefixLength Prefix length that needs to be added
848  * @param[in] address      IP address that needs to be added
849  * @param[io] asyncResp    Response object that will be returned to client
850  *
851  * @return None
852  */
853 inline void createIPv6(const std::string &ifaceId, uint8_t prefixLength,
854                        const std::string &address,
855                        std::shared_ptr<AsyncResp> asyncResp)
856 {
857     auto createIpHandler = [asyncResp](const boost::system::error_code ec) {
858         if (ec)
859         {
860             messages::internalError(asyncResp->res);
861         }
862     };
863     // Passing null for gateway, as per redfish spec IPv6StaticAddresses object
864     // does not have assosiated gateway property
865     crow::connections::systemBus->async_method_call(
866         std::move(createIpHandler), "xyz.openbmc_project.Network",
867         "/xyz/openbmc_project/network/" + ifaceId,
868         "xyz.openbmc_project.Network.IP.Create", "IP",
869         "xyz.openbmc_project.Network.IP.Protocol.IPv6", address, prefixLength,
870         "");
871 }
872 
873 /**
874  * Function that retrieves all properties for given Ethernet Interface
875  * Object
876  * from EntityManager Network Manager
877  * @param ethiface_id a eth interface id to query on DBus
878  * @param callback a function that shall be called to convert Dbus output
879  * into JSON
880  */
881 template <typename CallbackFunc>
882 void getEthernetIfaceData(const std::string &ethiface_id,
883                           CallbackFunc &&callback)
884 {
885     crow::connections::systemBus->async_method_call(
886         [ethiface_id{std::string{ethiface_id}}, callback{std::move(callback)}](
887             const boost::system::error_code error_code,
888             const GetManagedObjects &resp) {
889             EthernetInterfaceData ethData{};
890             boost::container::flat_set<IPv4AddressData> ipv4Data;
891             boost::container::flat_set<IPv6AddressData> ipv6Data;
892 
893             if (error_code)
894             {
895                 callback(false, ethData, ipv4Data, ipv6Data);
896                 return;
897             }
898 
899             bool found =
900                 extractEthernetInterfaceData(ethiface_id, resp, ethData);
901             if (!found)
902             {
903                 callback(false, ethData, ipv4Data, ipv6Data);
904                 return;
905             }
906 
907             extractIPData(ethiface_id, resp, ipv4Data);
908             // Fix global GW
909             for (IPv4AddressData &ipv4 : ipv4Data)
910             {
911                 if (((ipv4.linktype == LinkType::Global) &&
912                      (ipv4.gateway == "0.0.0.0")) ||
913                     (ipv4.origin == "DHCP"))
914                 {
915                     ipv4.gateway = ethData.default_gateway;
916                 }
917             }
918 
919             extractIPV6Data(ethiface_id, resp, ipv6Data);
920             // Finally make a callback with usefull data
921             callback(true, ethData, ipv4Data, ipv6Data);
922         },
923         "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
924         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
925 }
926 
927 /**
928  * Function that retrieves all Ethernet Interfaces available through Network
929  * Manager
930  * @param callback a function that shall be called to convert Dbus output
931  * into JSON.
932  */
933 template <typename CallbackFunc>
934 void getEthernetIfaceList(CallbackFunc &&callback)
935 {
936     crow::connections::systemBus->async_method_call(
937         [callback{std::move(callback)}](
938             const boost::system::error_code error_code,
939             GetManagedObjects &resp) {
940             // Callback requires vector<string> to retrieve all available
941             // ethernet interfaces
942             boost::container::flat_set<std::string> iface_list;
943             iface_list.reserve(resp.size());
944             if (error_code)
945             {
946                 callback(false, iface_list);
947                 return;
948             }
949 
950             // Iterate over all retrieved ObjectPaths.
951             for (const auto &objpath : resp)
952             {
953                 // And all interfaces available for certain ObjectPath.
954                 for (const auto &interface : objpath.second)
955                 {
956                     // If interface is
957                     // xyz.openbmc_project.Network.EthernetInterface, this is
958                     // what we're looking for.
959                     if (interface.first ==
960                         "xyz.openbmc_project.Network.EthernetInterface")
961                     {
962                         // Cut out everyting until last "/", ...
963                         const std::string &iface_id = objpath.first.str;
964                         std::size_t last_pos = iface_id.rfind("/");
965                         if (last_pos != std::string::npos)
966                         {
967                             // and put it into output vector.
968                             iface_list.emplace(iface_id.substr(last_pos + 1));
969                         }
970                     }
971                 }
972             }
973             // Finally make a callback with useful data
974             callback(true, iface_list);
975         },
976         "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
977         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
978 }
979 
980 /**
981  * EthernetCollection derived class for delivering Ethernet Collection Schema
982  */
983 class EthernetCollection : public Node
984 {
985   public:
986     template <typename CrowApp>
987     EthernetCollection(CrowApp &app) :
988         Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/")
989     {
990         entityPrivileges = {
991             {boost::beast::http::verb::get, {{"Login"}}},
992             {boost::beast::http::verb::head, {{"Login"}}},
993             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
994             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
995             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
996             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
997     }
998 
999   private:
1000     /**
1001      * Functions triggers appropriate requests on DBus
1002      */
1003     void doGet(crow::Response &res, const crow::Request &req,
1004                const std::vector<std::string> &params) override
1005     {
1006         res.jsonValue["@odata.type"] =
1007             "#EthernetInterfaceCollection.EthernetInterfaceCollection";
1008         res.jsonValue["@odata.id"] =
1009             "/redfish/v1/Managers/bmc/EthernetInterfaces";
1010         res.jsonValue["Name"] = "Ethernet Network Interface Collection";
1011         res.jsonValue["Description"] =
1012             "Collection of EthernetInterfaces for this Manager";
1013         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1014         // Get eth interface list, and call the below callback for JSON
1015         // preparation
1016         getEthernetIfaceList(
1017             [asyncResp](
1018                 const bool &success,
1019                 const boost::container::flat_set<std::string> &iface_list) {
1020                 if (!success)
1021                 {
1022                     messages::internalError(asyncResp->res);
1023                     return;
1024                 }
1025 
1026                 nlohmann::json &iface_array =
1027                     asyncResp->res.jsonValue["Members"];
1028                 iface_array = nlohmann::json::array();
1029                 std::string tag = "_";
1030                 for (const std::string &iface_item : iface_list)
1031                 {
1032                     std::size_t found = iface_item.find(tag);
1033                     if (found == std::string::npos)
1034                     {
1035                         iface_array.push_back(
1036                             {{"@odata.id",
1037                               "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1038                                   iface_item}});
1039                     }
1040                 }
1041 
1042                 asyncResp->res.jsonValue["Members@odata.count"] =
1043                     iface_array.size();
1044                 asyncResp->res.jsonValue["@odata.id"] =
1045                     "/redfish/v1/Managers/bmc/EthernetInterfaces";
1046             });
1047     }
1048 };
1049 
1050 /**
1051  * EthernetInterface derived class for delivering Ethernet Schema
1052  */
1053 class EthernetInterface : public Node
1054 {
1055   public:
1056     /*
1057      * Default Constructor
1058      */
1059     template <typename CrowApp>
1060     EthernetInterface(CrowApp &app) :
1061         Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/",
1062              std::string())
1063     {
1064         entityPrivileges = {
1065             {boost::beast::http::verb::get, {{"Login"}}},
1066             {boost::beast::http::verb::head, {{"Login"}}},
1067             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1068             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1069             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1070             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1071     }
1072 
1073   private:
1074     void handleHostnamePatch(const std::string &hostname,
1075                              const std::shared_ptr<AsyncResp> asyncResp)
1076     {
1077         asyncResp->res.jsonValue["HostName"] = hostname;
1078         crow::connections::systemBus->async_method_call(
1079             [asyncResp](const boost::system::error_code ec) {
1080                 if (ec)
1081                 {
1082                     messages::internalError(asyncResp->res);
1083                 }
1084             },
1085             "xyz.openbmc_project.Network",
1086             "/xyz/openbmc_project/network/config",
1087             "org.freedesktop.DBus.Properties", "Set",
1088             "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
1089             std::variant<std::string>(hostname));
1090     }
1091 
1092     void handleMACAddressPatch(const std::string &ifaceId,
1093                                const std::string &macAddress,
1094                                const std::shared_ptr<AsyncResp> &asyncResp)
1095     {
1096         crow::connections::systemBus->async_method_call(
1097             [asyncResp, macAddress](const boost::system::error_code ec) {
1098                 if (ec)
1099                 {
1100                     messages::internalError(asyncResp->res);
1101                     return;
1102                 }
1103             },
1104             "xyz.openbmc_project.Network",
1105             "/xyz/openbmc_project/network/" + ifaceId,
1106             "org.freedesktop.DBus.Properties", "Set",
1107             "xyz.openbmc_project.Network.MACAddress", "MACAddress",
1108             std::variant<std::string>(macAddress));
1109     }
1110 
1111     void setDHCPEnabled(const std::string &ifaceId,
1112                         const std::string &propertyName, const bool v4Value,
1113                         const bool v6Value,
1114                         const std::shared_ptr<AsyncResp> asyncResp)
1115     {
1116         const std::string dhcp = GetDHCPEnabledEnumeration(v4Value, v6Value);
1117         crow::connections::systemBus->async_method_call(
1118             [asyncResp](const boost::system::error_code ec) {
1119                 if (ec)
1120                 {
1121                     BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1122                     messages::internalError(asyncResp->res);
1123                     return;
1124                 }
1125             },
1126             "xyz.openbmc_project.Network",
1127             "/xyz/openbmc_project/network/" + ifaceId,
1128             "org.freedesktop.DBus.Properties", "Set",
1129             "xyz.openbmc_project.Network.EthernetInterface", propertyName,
1130             std::variant<std::string>{dhcp});
1131     }
1132 
1133     void setDHCPv4Config(const std::string &propertyName, const bool &value,
1134                          const std::shared_ptr<AsyncResp> asyncResp)
1135     {
1136         BMCWEB_LOG_DEBUG << propertyName << " = " << value;
1137         crow::connections::systemBus->async_method_call(
1138             [asyncResp](const boost::system::error_code ec) {
1139                 if (ec)
1140                 {
1141                     BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1142                     messages::internalError(asyncResp->res);
1143                     return;
1144                 }
1145             },
1146             "xyz.openbmc_project.Network",
1147             "/xyz/openbmc_project/network/config/dhcp",
1148             "org.freedesktop.DBus.Properties", "Set",
1149             "xyz.openbmc_project.Network.DHCPConfiguration", propertyName,
1150             std::variant<bool>{value});
1151     }
1152 
1153     void handleDHCPPatch(const std::string &ifaceId,
1154                          const EthernetInterfaceData &ethData,
1155                          DHCPParameters v4dhcpParms, DHCPParameters v6dhcpParms,
1156                          const std::shared_ptr<AsyncResp> asyncResp)
1157     {
1158         bool ipv4Active = translateDHCPEnabledToBool(ethData.DHCPEnabled, true);
1159         bool ipv6Active =
1160             translateDHCPEnabledToBool(ethData.DHCPEnabled, false);
1161 
1162         bool nextv4DHCPState =
1163             v4dhcpParms.dhcpv4Enabled ? *v4dhcpParms.dhcpv4Enabled : ipv4Active;
1164 
1165         bool nextv6DHCPState{};
1166         if (v6dhcpParms.dhcpv6OperatingMode)
1167         {
1168             if ((*v6dhcpParms.dhcpv6OperatingMode != "Stateful") &&
1169                 (*v6dhcpParms.dhcpv6OperatingMode != "Stateless") &&
1170                 (*v6dhcpParms.dhcpv6OperatingMode != "Disabled"))
1171             {
1172                 messages::propertyValueFormatError(
1173                     asyncResp->res, *v6dhcpParms.dhcpv6OperatingMode,
1174                     "OperatingMode");
1175                 return;
1176             }
1177             nextv6DHCPState = (*v6dhcpParms.dhcpv6OperatingMode == "Stateful");
1178         }
1179         else
1180         {
1181             nextv6DHCPState = ipv6Active;
1182         }
1183 
1184         bool nextDNS{};
1185         if (v4dhcpParms.useDNSServers && v6dhcpParms.useDNSServers)
1186         {
1187             if (*v4dhcpParms.useDNSServers != *v6dhcpParms.useDNSServers)
1188             {
1189                 messages::generalError(asyncResp->res);
1190                 return;
1191             }
1192             nextDNS = *v4dhcpParms.useDNSServers;
1193         }
1194         else if (v4dhcpParms.useDNSServers)
1195         {
1196             nextDNS = *v4dhcpParms.useDNSServers;
1197         }
1198         else if (v6dhcpParms.useDNSServers)
1199         {
1200             nextDNS = *v6dhcpParms.useDNSServers;
1201         }
1202         else
1203         {
1204             nextDNS = ethData.DNSEnabled;
1205         }
1206 
1207         bool nextNTP{};
1208         if (v4dhcpParms.useNTPServers && v6dhcpParms.useNTPServers)
1209         {
1210             if (*v4dhcpParms.useNTPServers != *v6dhcpParms.useNTPServers)
1211             {
1212                 messages::generalError(asyncResp->res);
1213                 return;
1214             }
1215             nextNTP = *v4dhcpParms.useNTPServers;
1216         }
1217         else if (v4dhcpParms.useNTPServers)
1218         {
1219             nextNTP = *v4dhcpParms.useNTPServers;
1220         }
1221         else if (v6dhcpParms.useNTPServers)
1222         {
1223             nextNTP = *v6dhcpParms.useNTPServers;
1224         }
1225         else
1226         {
1227             nextNTP = ethData.NTPEnabled;
1228         }
1229 
1230         bool nextUseDomain{};
1231         if (v4dhcpParms.useUseDomainName && v6dhcpParms.useUseDomainName)
1232         {
1233             if (*v4dhcpParms.useUseDomainName != *v6dhcpParms.useUseDomainName)
1234             {
1235                 messages::generalError(asyncResp->res);
1236                 return;
1237             }
1238             nextUseDomain = *v4dhcpParms.useUseDomainName;
1239         }
1240         else if (v4dhcpParms.useUseDomainName)
1241         {
1242             nextUseDomain = *v4dhcpParms.useUseDomainName;
1243         }
1244         else if (v6dhcpParms.useUseDomainName)
1245         {
1246             nextUseDomain = *v6dhcpParms.useUseDomainName;
1247         }
1248         else
1249         {
1250             nextUseDomain = ethData.HostNameEnabled;
1251         }
1252 
1253         BMCWEB_LOG_DEBUG << "set DHCPEnabled...";
1254         setDHCPEnabled(ifaceId, "DHCPEnabled", nextv4DHCPState, nextv6DHCPState,
1255                        asyncResp);
1256         BMCWEB_LOG_DEBUG << "set DNSEnabled...";
1257         setDHCPv4Config("DNSEnabled", nextDNS, asyncResp);
1258         BMCWEB_LOG_DEBUG << "set NTPEnabled...";
1259         setDHCPv4Config("NTPEnabled", nextNTP, asyncResp);
1260         BMCWEB_LOG_DEBUG << "set HostNameEnabled...";
1261         setDHCPv4Config("HostNameEnabled", nextUseDomain, asyncResp);
1262     }
1263 
1264     boost::container::flat_set<IPv4AddressData>::const_iterator
1265         GetNextStaticIPEntry(
1266             boost::container::flat_set<IPv4AddressData>::const_iterator head,
1267             boost::container::flat_set<IPv4AddressData>::const_iterator end)
1268     {
1269         for (; head != end; head++)
1270         {
1271             if (head->origin == "Static")
1272             {
1273                 return head;
1274             }
1275         }
1276         return end;
1277     }
1278 
1279     boost::container::flat_set<IPv6AddressData>::const_iterator
1280         GetNextStaticIPEntry(
1281             boost::container::flat_set<IPv6AddressData>::const_iterator head,
1282             boost::container::flat_set<IPv6AddressData>::const_iterator end)
1283     {
1284         for (; head != end; head++)
1285         {
1286             if (head->origin == "Static")
1287             {
1288                 return head;
1289             }
1290         }
1291         return end;
1292     }
1293 
1294     void handleIPv4StaticPatch(
1295         const std::string &ifaceId, nlohmann::json &input,
1296         const boost::container::flat_set<IPv4AddressData> &ipv4Data,
1297         const std::shared_ptr<AsyncResp> asyncResp)
1298     {
1299         if ((!input.is_array()) || input.empty())
1300         {
1301             messages::propertyValueTypeError(asyncResp->res, input.dump(),
1302                                              "IPv4StaticAddresses");
1303             return;
1304         }
1305 
1306         unsigned entryIdx = 1;
1307         // Find the first static IP address currently active on the NIC and
1308         // match it to the first JSON element in the IPv4StaticAddresses array.
1309         // Match each subsequent JSON element to the next static IP programmed
1310         // into the NIC.
1311         boost::container::flat_set<IPv4AddressData>::const_iterator NICIPentry =
1312             GetNextStaticIPEntry(ipv4Data.cbegin(), ipv4Data.cend());
1313 
1314         for (nlohmann::json &thisJson : input)
1315         {
1316             std::string pathString =
1317                 "IPv4StaticAddresses/" + std::to_string(entryIdx);
1318 
1319             if (!thisJson.is_null() && !thisJson.empty())
1320             {
1321                 std::optional<std::string> address;
1322                 std::optional<std::string> subnetMask;
1323                 std::optional<std::string> gateway;
1324 
1325                 if (!json_util::readJson(thisJson, asyncResp->res, "Address",
1326                                          address, "SubnetMask", subnetMask,
1327                                          "Gateway", gateway))
1328                 {
1329                     messages::propertyValueFormatError(
1330                         asyncResp->res, thisJson.dump(), pathString);
1331                     return;
1332                 }
1333 
1334                 // Find the address/subnet/gateway values. Any values that are
1335                 // not explicitly provided are assumed to be unmodified from the
1336                 // current state of the interface. Merge existing state into the
1337                 // current request.
1338                 const std::string *addr = nullptr;
1339                 const std::string *gw = nullptr;
1340                 uint8_t prefixLength = 0;
1341                 bool errorInEntry = false;
1342                 if (address)
1343                 {
1344                     if (ipv4VerifyIpAndGetBitcount(*address))
1345                     {
1346                         addr = &(*address);
1347                     }
1348                     else
1349                     {
1350                         messages::propertyValueFormatError(
1351                             asyncResp->res, *address, pathString + "/Address");
1352                         errorInEntry = true;
1353                     }
1354                 }
1355                 else if (NICIPentry != ipv4Data.cend())
1356                 {
1357                     addr = &(NICIPentry->address);
1358                 }
1359                 else
1360                 {
1361                     messages::propertyMissing(asyncResp->res,
1362                                               pathString + "/Address");
1363                     errorInEntry = true;
1364                 }
1365 
1366                 if (subnetMask)
1367                 {
1368                     if (!ipv4VerifyIpAndGetBitcount(*subnetMask, &prefixLength))
1369                     {
1370                         messages::propertyValueFormatError(
1371                             asyncResp->res, *subnetMask,
1372                             pathString + "/SubnetMask");
1373                         errorInEntry = true;
1374                     }
1375                 }
1376                 else if (NICIPentry != ipv4Data.cend())
1377                 {
1378                     if (!ipv4VerifyIpAndGetBitcount(NICIPentry->netmask,
1379                                                     &prefixLength))
1380                     {
1381                         messages::propertyValueFormatError(
1382                             asyncResp->res, NICIPentry->netmask,
1383                             pathString + "/SubnetMask");
1384                         errorInEntry = true;
1385                     }
1386                 }
1387                 else
1388                 {
1389                     messages::propertyMissing(asyncResp->res,
1390                                               pathString + "/SubnetMask");
1391                     errorInEntry = true;
1392                 }
1393 
1394                 if (gateway)
1395                 {
1396                     if (ipv4VerifyIpAndGetBitcount(*gateway))
1397                     {
1398                         gw = &(*gateway);
1399                     }
1400                     else
1401                     {
1402                         messages::propertyValueFormatError(
1403                             asyncResp->res, *gateway, pathString + "/Gateway");
1404                         errorInEntry = true;
1405                     }
1406                 }
1407                 else if (NICIPentry != ipv4Data.cend())
1408                 {
1409                     gw = &NICIPentry->gateway;
1410                 }
1411                 else
1412                 {
1413                     messages::propertyMissing(asyncResp->res,
1414                                               pathString + "/Gateway");
1415                     errorInEntry = true;
1416                 }
1417 
1418                 if (errorInEntry)
1419                 {
1420                     return;
1421                 }
1422 
1423                 if (NICIPentry != ipv4Data.cend())
1424                 {
1425                     if (gw != nullptr || addr != nullptr)
1426                     {
1427                         // Shouldn't be possible based on errorInEntry, but
1428                         // it flags -wmaybe-uninitialized in the compiler,
1429                         // so defend against that
1430                         return;
1431                     }
1432                     deleteAndCreateIPv4(ifaceId, NICIPentry->id, prefixLength,
1433                                         *gw, *addr, asyncResp);
1434                     NICIPentry =
1435                         GetNextStaticIPEntry(++NICIPentry, ipv4Data.cend());
1436                 }
1437                 else
1438                 {
1439                     createIPv4(ifaceId, entryIdx, prefixLength, *gateway,
1440                                *address, asyncResp);
1441                 }
1442                 entryIdx++;
1443             }
1444             else
1445             {
1446                 if (NICIPentry == ipv4Data.cend())
1447                 {
1448                     // Requesting a DELETE/DO NOT MODIFY action for an item
1449                     // that isn't present on the eth(n) interface. Input JSON is
1450                     // in error, so bail out.
1451                     if (thisJson.is_null())
1452                     {
1453                         messages::resourceCannotBeDeleted(asyncResp->res);
1454                         return;
1455                     }
1456                     else
1457                     {
1458                         messages::propertyValueFormatError(
1459                             asyncResp->res, thisJson.dump(), pathString);
1460                         return;
1461                     }
1462                 }
1463 
1464                 if (thisJson.is_null())
1465                 {
1466                     deleteIPv4(ifaceId, NICIPentry->id, asyncResp);
1467                 }
1468                 if (NICIPentry != ipv4Data.cend())
1469                 {
1470                     NICIPentry =
1471                         GetNextStaticIPEntry(++NICIPentry, ipv4Data.cend());
1472                 }
1473                 entryIdx++;
1474             }
1475         }
1476     }
1477 
1478     void handleStaticNameServersPatch(
1479         const std::string &ifaceId,
1480         const std::vector<std::string> &updatedStaticNameServers,
1481         const std::shared_ptr<AsyncResp> &asyncResp)
1482     {
1483         crow::connections::systemBus->async_method_call(
1484             [asyncResp](const boost::system::error_code ec) {
1485                 if (ec)
1486                 {
1487                     messages::internalError(asyncResp->res);
1488                     return;
1489                 }
1490             },
1491             "xyz.openbmc_project.Network",
1492             "/xyz/openbmc_project/network/" + ifaceId,
1493             "org.freedesktop.DBus.Properties", "Set",
1494             "xyz.openbmc_project.Network.EthernetInterface", "Nameservers",
1495             std::variant<std::vector<std::string>>{updatedStaticNameServers});
1496     }
1497 
1498     void handleIPv6StaticAddressesPatch(
1499         const std::string &ifaceId, nlohmann::json &input,
1500         const boost::container::flat_set<IPv6AddressData> &ipv6Data,
1501         const std::shared_ptr<AsyncResp> asyncResp)
1502     {
1503         if (!input.is_array() || input.empty())
1504         {
1505             messages::propertyValueTypeError(asyncResp->res, input.dump(),
1506                                              "IPv6StaticAddresses");
1507             return;
1508         }
1509         size_t entryIdx = 1;
1510         boost::container::flat_set<IPv6AddressData>::const_iterator NICIPentry =
1511             GetNextStaticIPEntry(ipv6Data.cbegin(), ipv6Data.cend());
1512         for (nlohmann::json &thisJson : input)
1513         {
1514             std::string pathString =
1515                 "IPv6StaticAddresses/" + std::to_string(entryIdx);
1516 
1517             if (!thisJson.is_null() && !thisJson.empty())
1518             {
1519                 std::optional<std::string> address;
1520                 std::optional<uint8_t> prefixLength;
1521 
1522                 if (!json_util::readJson(thisJson, asyncResp->res, "Address",
1523                                          address, "PrefixLength", prefixLength))
1524                 {
1525                     messages::propertyValueFormatError(
1526                         asyncResp->res, thisJson.dump(), pathString);
1527                     return;
1528                 }
1529 
1530                 const std::string *addr;
1531                 uint8_t prefix;
1532 
1533                 // Find the address and prefixLength values. Any values that are
1534                 // not explicitly provided are assumed to be unmodified from the
1535                 // current state of the interface. Merge existing state into the
1536                 // current request.
1537                 if (address)
1538                 {
1539                     addr = &(*address);
1540                 }
1541                 else if (NICIPentry != ipv6Data.end())
1542                 {
1543                     addr = &(NICIPentry->address);
1544                 }
1545                 else
1546                 {
1547                     messages::propertyMissing(asyncResp->res,
1548                                               pathString + "/Address");
1549                     return;
1550                 }
1551 
1552                 if (prefixLength)
1553                 {
1554                     prefix = *prefixLength;
1555                 }
1556                 else if (NICIPentry != ipv6Data.end())
1557                 {
1558                     prefix = NICIPentry->prefixLength;
1559                 }
1560                 else
1561                 {
1562                     messages::propertyMissing(asyncResp->res,
1563                                               pathString + "/PrefixLength");
1564                     return;
1565                 }
1566 
1567                 if (NICIPentry != ipv6Data.end())
1568                 {
1569                     deleteAndCreateIPv6(ifaceId, NICIPentry->id, prefix, *addr,
1570                                         asyncResp);
1571                     NICIPentry =
1572                         GetNextStaticIPEntry(++NICIPentry, ipv6Data.cend());
1573                 }
1574                 else
1575                 {
1576                     createIPv6(ifaceId, *prefixLength, *addr, asyncResp);
1577                 }
1578                 entryIdx++;
1579             }
1580             else
1581             {
1582                 if (NICIPentry == ipv6Data.end())
1583                 {
1584                     // Requesting a DELETE/DO NOT MODIFY action for an item
1585                     // that isn't present on the eth(n) interface. Input JSON is
1586                     // in error, so bail out.
1587                     if (thisJson.is_null())
1588                     {
1589                         messages::resourceCannotBeDeleted(asyncResp->res);
1590                         return;
1591                     }
1592                     else
1593                     {
1594                         messages::propertyValueFormatError(
1595                             asyncResp->res, thisJson.dump(), pathString);
1596                         return;
1597                     }
1598                 }
1599 
1600                 if (thisJson.is_null())
1601                 {
1602                     deleteIPv6(ifaceId, NICIPentry->id, asyncResp);
1603                 }
1604                 if (NICIPentry != ipv6Data.cend())
1605                 {
1606                     NICIPentry =
1607                         GetNextStaticIPEntry(++NICIPentry, ipv6Data.cend());
1608                 }
1609                 entryIdx++;
1610             }
1611         }
1612     }
1613 
1614     void parseInterfaceData(
1615         nlohmann::json &json_response, const std::string &iface_id,
1616         const EthernetInterfaceData &ethData,
1617         const boost::container::flat_set<IPv4AddressData> &ipv4Data,
1618         const boost::container::flat_set<IPv6AddressData> &ipv6Data)
1619     {
1620         json_response["Id"] = iface_id;
1621         json_response["@odata.id"] =
1622             "/redfish/v1/Managers/bmc/EthernetInterfaces/" + iface_id;
1623         json_response["InterfaceEnabled"] = true;
1624         if (ethData.speed == 0)
1625         {
1626             json_response["Status"] = {
1627                 {"Health", "OK"},
1628                 {"State", "Disabled"},
1629             };
1630         }
1631         else
1632         {
1633             json_response["Status"] = {
1634                 {"Health", "OK"},
1635                 {"State", "Enabled"},
1636             };
1637         }
1638 
1639         json_response["LinkStatus"] = ethData.linkUp ? "LinkUp" : "LinkDown";
1640         json_response["SpeedMbps"] = ethData.speed;
1641         json_response["MACAddress"] = ethData.mac_address;
1642         json_response["DHCPv4"]["DHCPEnabled"] =
1643             translateDHCPEnabledToBool(ethData.DHCPEnabled, true);
1644         json_response["DHCPv4"]["UseNTPServers"] = ethData.NTPEnabled;
1645         json_response["DHCPv4"]["UseDNSServers"] = ethData.DNSEnabled;
1646         json_response["DHCPv4"]["UseDomainName"] = ethData.HostNameEnabled;
1647 
1648         json_response["DHCPv6"]["OperatingMode"] =
1649             translateDHCPEnabledToBool(ethData.DHCPEnabled, false) ? "Stateful"
1650                                                                    : "Disabled";
1651         json_response["DHCPv6"]["UseNTPServers"] = ethData.NTPEnabled;
1652         json_response["DHCPv6"]["UseDNSServers"] = ethData.DNSEnabled;
1653         json_response["DHCPv6"]["UseDomainName"] = ethData.HostNameEnabled;
1654 
1655         if (!ethData.hostname.empty())
1656         {
1657             json_response["HostName"] = ethData.hostname;
1658             if (!ethData.domainnames.empty())
1659             {
1660                 json_response["FQDN"] =
1661                     ethData.hostname + "." + ethData.domainnames[0];
1662             }
1663         }
1664 
1665         json_response["VLANs"] = {
1666             {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1667                               iface_id + "/VLANs"}};
1668 
1669         if (translateDHCPEnabledToBool(ethData.DHCPEnabled, true) &&
1670             ethData.DNSEnabled)
1671         {
1672             json_response["StaticNameServers"] = nlohmann::json::array();
1673         }
1674         else
1675         {
1676             json_response["StaticNameServers"] = ethData.nameservers;
1677         }
1678 
1679         nlohmann::json &ipv4_array = json_response["IPv4Addresses"];
1680         nlohmann::json &ipv4_static_array =
1681             json_response["IPv4StaticAddresses"];
1682         ipv4_array = nlohmann::json::array();
1683         ipv4_static_array = nlohmann::json::array();
1684         for (auto &ipv4_config : ipv4Data)
1685         {
1686 
1687             std::string gatewayStr = ipv4_config.gateway;
1688             if (gatewayStr.empty())
1689             {
1690                 gatewayStr = "0.0.0.0";
1691             }
1692 
1693             ipv4_array.push_back({{"AddressOrigin", ipv4_config.origin},
1694                                   {"SubnetMask", ipv4_config.netmask},
1695                                   {"Address", ipv4_config.address},
1696                                   {"Gateway", gatewayStr}});
1697             if (ipv4_config.origin == "Static")
1698             {
1699                 ipv4_static_array.push_back(
1700                     {{"AddressOrigin", ipv4_config.origin},
1701                      {"SubnetMask", ipv4_config.netmask},
1702                      {"Address", ipv4_config.address},
1703                      {"Gateway", gatewayStr}});
1704             }
1705         }
1706 
1707         json_response["IPv6DefaultGateway"] = ethData.ipv6_default_gateway;
1708 
1709         nlohmann::json &ipv6_array = json_response["IPv6Addresses"];
1710         nlohmann::json &ipv6_static_array =
1711             json_response["IPv6StaticAddresses"];
1712         ipv6_array = nlohmann::json::array();
1713         ipv6_static_array = nlohmann::json::array();
1714         for (auto &ipv6_config : ipv6Data)
1715         {
1716             ipv6_array.push_back({{"Address", ipv6_config.address},
1717                                   {"PrefixLength", ipv6_config.prefixLength},
1718                                   {"AddressOrigin", ipv6_config.origin}});
1719             if (ipv6_config.origin == "Static")
1720             {
1721                 ipv6_static_array.push_back(
1722                     {{"Address", ipv6_config.address},
1723                      {"PrefixLength", ipv6_config.prefixLength},
1724                      {"AddressOrigin", ipv6_config.origin}});
1725             }
1726         }
1727     }
1728 
1729     /**
1730      * Functions triggers appropriate requests on DBus
1731      */
1732     void doGet(crow::Response &res, const crow::Request &req,
1733                const std::vector<std::string> &params) override
1734     {
1735         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1736         if (params.size() != 1)
1737         {
1738             messages::internalError(asyncResp->res);
1739             return;
1740         }
1741 
1742         getEthernetIfaceData(
1743             params[0],
1744             [this, asyncResp, iface_id{std::string(params[0])}](
1745                 const bool &success, const EthernetInterfaceData &ethData,
1746                 const boost::container::flat_set<IPv4AddressData> &ipv4Data,
1747                 const boost::container::flat_set<IPv6AddressData> &ipv6Data) {
1748                 if (!success)
1749                 {
1750                     // TODO(Pawel)consider distinguish between non existing
1751                     // object, and other errors
1752                     messages::resourceNotFound(asyncResp->res,
1753                                                "EthernetInterface", iface_id);
1754                     return;
1755                 }
1756 
1757                 asyncResp->res.jsonValue["@odata.type"] =
1758                     "#EthernetInterface.v1_4_1.EthernetInterface";
1759                 asyncResp->res.jsonValue["Name"] = "Manager Ethernet Interface";
1760                 asyncResp->res.jsonValue["Description"] =
1761                     "Management Network Interface";
1762 
1763                 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
1764                                    ipv4Data, ipv6Data);
1765             });
1766     }
1767 
1768     void doPatch(crow::Response &res, const crow::Request &req,
1769                  const std::vector<std::string> &params) override
1770     {
1771         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1772         if (params.size() != 1)
1773         {
1774             messages::internalError(asyncResp->res);
1775             return;
1776         }
1777 
1778         const std::string &iface_id = params[0];
1779 
1780         std::optional<std::string> hostname;
1781         std::optional<std::string> macAddress;
1782         std::optional<std::string> ipv6DefaultGateway;
1783         std::optional<nlohmann::json> ipv4StaticAddresses;
1784         std::optional<nlohmann::json> ipv6StaticAddresses;
1785         std::optional<std::vector<std::string>> staticNameServers;
1786         std::optional<nlohmann::json> dhcpv4;
1787         std::optional<nlohmann::json> dhcpv6;
1788         DHCPParameters v4dhcpParms;
1789         DHCPParameters v6dhcpParms;
1790 
1791         if (!json_util::readJson(
1792                 req, res, "HostName", hostname, "IPv4StaticAddresses",
1793                 ipv4StaticAddresses, "MACAddress", macAddress,
1794                 "StaticNameServers", staticNameServers, "IPv6DefaultGateway",
1795                 ipv6DefaultGateway, "IPv6StaticAddresses", ipv6StaticAddresses,
1796                 "DHCPv4", dhcpv4, "DHCPv6", dhcpv6))
1797         {
1798             return;
1799         }
1800         if (dhcpv4)
1801         {
1802             if (!json_util::readJson(*dhcpv4, res, "DHCPEnabled",
1803                                      v4dhcpParms.dhcpv4Enabled, "UseDNSServers",
1804                                      v4dhcpParms.useDNSServers, "UseNTPServers",
1805                                      v4dhcpParms.useNTPServers, "UseDomainName",
1806                                      v4dhcpParms.useUseDomainName))
1807             {
1808                 return;
1809             }
1810         }
1811 
1812         if (dhcpv6)
1813         {
1814             if (!json_util::readJson(*dhcpv6, res, "OperatingMode",
1815                                      v6dhcpParms.dhcpv6OperatingMode,
1816                                      "UseDNSServers", v6dhcpParms.useDNSServers,
1817                                      "UseNTPServers", v6dhcpParms.useNTPServers,
1818                                      "UseDomainName",
1819                                      v6dhcpParms.useUseDomainName))
1820             {
1821                 return;
1822             }
1823         }
1824 
1825         // Get single eth interface data, and call the below callback for
1826         // JSON preparation
1827         getEthernetIfaceData(
1828             iface_id,
1829             [this, asyncResp, iface_id, hostname = std::move(hostname),
1830              macAddress = std::move(macAddress),
1831              ipv4StaticAddresses = std::move(ipv4StaticAddresses),
1832              ipv6DefaultGateway = std::move(ipv6DefaultGateway),
1833              ipv6StaticAddresses = std::move(ipv6StaticAddresses),
1834              staticNameServers = std::move(staticNameServers),
1835              dhcpv4 = std::move(dhcpv4), dhcpv6 = std::move(dhcpv6),
1836              v4dhcpParms = std::move(v4dhcpParms),
1837              v6dhcpParms = std::move(v6dhcpParms)](
1838                 const bool &success, const EthernetInterfaceData &ethData,
1839                 const boost::container::flat_set<IPv4AddressData> &ipv4Data,
1840                 const boost::container::flat_set<IPv6AddressData> &ipv6Data) {
1841                 if (!success)
1842                 {
1843                     // ... otherwise return error
1844                     // TODO(Pawel)consider distinguish between non existing
1845                     // object, and other errors
1846                     messages::resourceNotFound(asyncResp->res,
1847                                                "Ethernet Interface", iface_id);
1848                     return;
1849                 }
1850 
1851                 if (dhcpv4 || dhcpv6)
1852                 {
1853                     handleDHCPPatch(iface_id, ethData, std::move(v4dhcpParms),
1854                                     std::move(v6dhcpParms), asyncResp);
1855                 }
1856 
1857                 if (hostname)
1858                 {
1859                     handleHostnamePatch(*hostname, asyncResp);
1860                 }
1861 
1862                 if (macAddress)
1863                 {
1864                     handleMACAddressPatch(iface_id, *macAddress, asyncResp);
1865                 }
1866 
1867                 if (ipv4StaticAddresses)
1868                 {
1869                     // TODO(ed) for some reason the capture of ipv4Addresses
1870                     // above is returning a const value, not a non-const
1871                     // value. This doesn't really work for us, as we need to
1872                     // be able to efficiently move out the intermedia
1873                     // nlohmann::json objects. This makes a copy of the
1874                     // structure, and operates on that, but could be done
1875                     // more efficiently
1876                     nlohmann::json ipv4Static = std::move(*ipv4StaticAddresses);
1877                     handleIPv4StaticPatch(iface_id, ipv4Static, ipv4Data,
1878                                           asyncResp);
1879                 }
1880 
1881                 if (staticNameServers)
1882                 {
1883                     handleStaticNameServersPatch(iface_id, *staticNameServers,
1884                                                  asyncResp);
1885                 }
1886 
1887                 if (ipv6DefaultGateway)
1888                 {
1889                     messages::propertyNotWritable(asyncResp->res,
1890                                                   "IPv6DefaultGateway");
1891                 }
1892 
1893                 if (ipv6StaticAddresses)
1894                 {
1895                     nlohmann::json ipv6Static = std::move(*ipv6StaticAddresses);
1896                     handleIPv6StaticAddressesPatch(iface_id, ipv6Static,
1897                                                    ipv6Data, asyncResp);
1898                 }
1899             });
1900     }
1901 };
1902 
1903 /**
1904  * VlanNetworkInterface derived class for delivering VLANNetworkInterface
1905  * Schema
1906  */
1907 class VlanNetworkInterface : public Node
1908 {
1909   public:
1910     /*
1911      * Default Constructor
1912      */
1913     template <typename CrowApp>
1914     VlanNetworkInterface(CrowApp &app) :
1915         Node(app,
1916              "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/<str>",
1917              std::string(), std::string())
1918     {
1919         entityPrivileges = {
1920             {boost::beast::http::verb::get, {{"Login"}}},
1921             {boost::beast::http::verb::head, {{"Login"}}},
1922             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1923             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1924             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1925             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1926     }
1927 
1928   private:
1929     void parseInterfaceData(
1930         nlohmann::json &json_response, const std::string &parent_iface_id,
1931         const std::string &iface_id, const EthernetInterfaceData &ethData,
1932         const boost::container::flat_set<IPv4AddressData> &ipv4Data,
1933         const boost::container::flat_set<IPv6AddressData> &ipv6Data)
1934     {
1935         // Fill out obvious data...
1936         json_response["Id"] = iface_id;
1937         json_response["@odata.id"] =
1938             "/redfish/v1/Managers/bmc/EthernetInterfaces/" + parent_iface_id +
1939             "/VLANs/" + iface_id;
1940 
1941         json_response["VLANEnable"] = true;
1942         if (!ethData.vlan_id.empty())
1943         {
1944             json_response["VLANId"] = ethData.vlan_id.back();
1945         }
1946     }
1947 
1948     bool verifyNames(const std::string &parent, const std::string &iface)
1949     {
1950         if (!boost::starts_with(iface, parent + "_"))
1951         {
1952             return false;
1953         }
1954         else
1955         {
1956             return true;
1957         }
1958     }
1959 
1960     /**
1961      * Functions triggers appropriate requests on DBus
1962      */
1963     void doGet(crow::Response &res, const crow::Request &req,
1964                const std::vector<std::string> &params) override
1965     {
1966         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1967         // TODO(Pawel) this shall be parameterized call (two params) to get
1968         // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1969         // Check if there is required param, truly entering this shall be
1970         // impossible.
1971         if (params.size() != 2)
1972         {
1973             messages::internalError(res);
1974             res.end();
1975             return;
1976         }
1977 
1978         const std::string &parent_iface_id = params[0];
1979         const std::string &iface_id = params[1];
1980         res.jsonValue["@odata.type"] =
1981             "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1982         res.jsonValue["Name"] = "VLAN Network Interface";
1983 
1984         if (!verifyNames(parent_iface_id, iface_id))
1985         {
1986             return;
1987         }
1988 
1989         // Get single eth interface data, and call the below callback for
1990         // JSON preparation
1991         getEthernetIfaceData(
1992             params[1],
1993             [this, asyncResp, parent_iface_id{std::string(params[0])},
1994              iface_id{std::string(params[1])}](
1995                 const bool &success, const EthernetInterfaceData &ethData,
1996                 const boost::container::flat_set<IPv4AddressData> &ipv4Data,
1997                 const boost::container::flat_set<IPv6AddressData> &ipv6Data) {
1998                 if (success && ethData.vlan_id.size() != 0)
1999                 {
2000                     parseInterfaceData(asyncResp->res.jsonValue,
2001                                        parent_iface_id, iface_id, ethData,
2002                                        ipv4Data, ipv6Data);
2003                 }
2004                 else
2005                 {
2006                     // ... otherwise return error
2007                     // TODO(Pawel)consider distinguish between non existing
2008                     // object, and other errors
2009                     messages::resourceNotFound(
2010                         asyncResp->res, "VLAN Network Interface", iface_id);
2011                 }
2012             });
2013     }
2014 
2015     void doPatch(crow::Response &res, const crow::Request &req,
2016                  const std::vector<std::string> &params) override
2017     {
2018         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2019         if (params.size() != 2)
2020         {
2021             messages::internalError(asyncResp->res);
2022             return;
2023         }
2024 
2025         const std::string &parentIfaceId = params[0];
2026         const std::string &ifaceId = params[1];
2027 
2028         if (!verifyNames(parentIfaceId, ifaceId))
2029         {
2030             messages::resourceNotFound(asyncResp->res, "VLAN Network Interface",
2031                                        ifaceId);
2032             return;
2033         }
2034 
2035         bool vlanEnable = false;
2036         uint64_t vlanId = 0;
2037 
2038         if (!json_util::readJson(req, res, "VLANEnable", vlanEnable, "VLANId",
2039                                  vlanId))
2040         {
2041             return;
2042         }
2043 
2044         // Get single eth interface data, and call the below callback for
2045         // JSON preparation
2046         getEthernetIfaceData(
2047             params[1],
2048             [asyncResp, parentIfaceId{std::string(params[0])},
2049              ifaceId{std::string(params[1])}, &vlanEnable, &vlanId](
2050                 const bool &success, const EthernetInterfaceData &ethData,
2051                 const boost::container::flat_set<IPv4AddressData> &ipv4Data,
2052                 const boost::container::flat_set<IPv6AddressData> &ipv6Data) {
2053                 if (success && !ethData.vlan_id.empty())
2054                 {
2055                     auto callback =
2056                         [asyncResp](const boost::system::error_code ec) {
2057                             if (ec)
2058                             {
2059                                 messages::internalError(asyncResp->res);
2060                             }
2061                         };
2062 
2063                     if (vlanEnable == true)
2064                     {
2065                         crow::connections::systemBus->async_method_call(
2066                             std::move(callback), "xyz.openbmc_project.Network",
2067                             "/xyz/openbmc_project/network/" + ifaceId,
2068                             "org.freedesktop.DBus.Properties", "Set",
2069                             "xyz.openbmc_project.Network.VLAN", "Id",
2070                             std::variant<uint32_t>(vlanId));
2071                     }
2072                     else
2073                     {
2074                         BMCWEB_LOG_DEBUG << "vlanEnable is false. Deleting the "
2075                                             "vlan interface";
2076                         crow::connections::systemBus->async_method_call(
2077                             std::move(callback), "xyz.openbmc_project.Network",
2078                             std::string("/xyz/openbmc_project/network/") +
2079                                 ifaceId,
2080                             "xyz.openbmc_project.Object.Delete", "Delete");
2081                     }
2082                 }
2083                 else
2084                 {
2085                     // TODO(Pawel)consider distinguish between non existing
2086                     // object, and other errors
2087                     messages::resourceNotFound(
2088                         asyncResp->res, "VLAN Network Interface", ifaceId);
2089                     return;
2090                 }
2091             });
2092     }
2093 
2094     void doDelete(crow::Response &res, const crow::Request &req,
2095                   const std::vector<std::string> &params) override
2096     {
2097         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2098         if (params.size() != 2)
2099         {
2100             messages::internalError(asyncResp->res);
2101             return;
2102         }
2103 
2104         const std::string &parentIfaceId = params[0];
2105         const std::string &ifaceId = params[1];
2106 
2107         if (!verifyNames(parentIfaceId, ifaceId))
2108         {
2109             messages::resourceNotFound(asyncResp->res, "VLAN Network Interface",
2110                                        ifaceId);
2111             return;
2112         }
2113 
2114         // Get single eth interface data, and call the below callback for
2115         // JSON preparation
2116         getEthernetIfaceData(
2117             params[1],
2118             [asyncResp, parentIfaceId{std::string(params[0])},
2119              ifaceId{std::string(params[1])}](
2120                 const bool &success, const EthernetInterfaceData &ethData,
2121                 const boost::container::flat_set<IPv4AddressData> &ipv4Data,
2122                 const boost::container::flat_set<IPv6AddressData> &ipv6Data) {
2123                 if (success && !ethData.vlan_id.empty())
2124                 {
2125                     auto callback =
2126                         [asyncResp](const boost::system::error_code ec) {
2127                             if (ec)
2128                             {
2129                                 messages::internalError(asyncResp->res);
2130                             }
2131                         };
2132                     crow::connections::systemBus->async_method_call(
2133                         std::move(callback), "xyz.openbmc_project.Network",
2134                         std::string("/xyz/openbmc_project/network/") + ifaceId,
2135                         "xyz.openbmc_project.Object.Delete", "Delete");
2136                 }
2137                 else
2138                 {
2139                     // ... otherwise return error
2140                     // TODO(Pawel)consider distinguish between non existing
2141                     // object, and other errors
2142                     messages::resourceNotFound(
2143                         asyncResp->res, "VLAN Network Interface", ifaceId);
2144                 }
2145             });
2146     }
2147 };
2148 
2149 /**
2150  * VlanNetworkInterfaceCollection derived class for delivering
2151  * VLANNetworkInterface Collection Schema
2152  */
2153 class VlanNetworkInterfaceCollection : public Node
2154 {
2155   public:
2156     template <typename CrowApp>
2157     VlanNetworkInterfaceCollection(CrowApp &app) :
2158         Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/",
2159              std::string())
2160     {
2161         entityPrivileges = {
2162             {boost::beast::http::verb::get, {{"Login"}}},
2163             {boost::beast::http::verb::head, {{"Login"}}},
2164             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2165             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2166             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2167             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2168     }
2169 
2170   private:
2171     /**
2172      * Functions triggers appropriate requests on DBus
2173      */
2174     void doGet(crow::Response &res, const crow::Request &req,
2175                const std::vector<std::string> &params) override
2176     {
2177         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2178         if (params.size() != 1)
2179         {
2180             // This means there is a problem with the router
2181             messages::internalError(asyncResp->res);
2182             return;
2183         }
2184 
2185         const std::string &rootInterfaceName = params[0];
2186 
2187         // Get eth interface list, and call the below callback for JSON
2188         // preparation
2189         getEthernetIfaceList(
2190             [asyncResp, rootInterfaceName{std::string(rootInterfaceName)}](
2191                 const bool &success,
2192                 const boost::container::flat_set<std::string> &iface_list) {
2193                 if (!success)
2194                 {
2195                     messages::internalError(asyncResp->res);
2196                     return;
2197                 }
2198 
2199                 if (iface_list.find(rootInterfaceName) == iface_list.end())
2200                 {
2201                     messages::resourceNotFound(asyncResp->res,
2202                                                "VLanNetworkInterfaceCollection",
2203                                                rootInterfaceName);
2204                     return;
2205                 }
2206 
2207                 asyncResp->res.jsonValue["@odata.type"] =
2208                     "#VLanNetworkInterfaceCollection."
2209                     "VLanNetworkInterfaceCollection";
2210                 asyncResp->res.jsonValue["Name"] =
2211                     "VLAN Network Interface Collection";
2212 
2213                 nlohmann::json iface_array = nlohmann::json::array();
2214 
2215                 for (const std::string &iface_item : iface_list)
2216                 {
2217                     if (boost::starts_with(iface_item, rootInterfaceName + "_"))
2218                     {
2219                         iface_array.push_back(
2220                             {{"@odata.id",
2221                               "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
2222                                   rootInterfaceName + "/VLANs/" + iface_item}});
2223                     }
2224                 }
2225 
2226                 asyncResp->res.jsonValue["Members@odata.count"] =
2227                     iface_array.size();
2228                 asyncResp->res.jsonValue["Members"] = std::move(iface_array);
2229                 asyncResp->res.jsonValue["@odata.id"] =
2230                     "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
2231                     rootInterfaceName + "/VLANs";
2232             });
2233     }
2234 
2235     void doPost(crow::Response &res, const crow::Request &req,
2236                 const std::vector<std::string> &params) override
2237     {
2238         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2239         if (params.size() != 1)
2240         {
2241             messages::internalError(asyncResp->res);
2242             return;
2243         }
2244         bool vlanEnable = false;
2245         uint32_t vlanId = 0;
2246         if (!json_util::readJson(req, res, "VLANId", vlanId, "VLANEnable",
2247                                  vlanEnable))
2248         {
2249             return;
2250         }
2251         // Need both vlanId and vlanEnable to service this request
2252         if (!vlanId)
2253         {
2254             messages::propertyMissing(asyncResp->res, "VLANId");
2255         }
2256         if (!vlanEnable)
2257         {
2258             messages::propertyMissing(asyncResp->res, "VLANEnable");
2259         }
2260         if (static_cast<bool>(vlanId) ^ vlanEnable)
2261         {
2262             return;
2263         }
2264 
2265         const std::string &rootInterfaceName = params[0];
2266         auto callback = [asyncResp](const boost::system::error_code ec) {
2267             if (ec)
2268             {
2269                 // TODO(ed) make more consistent error messages based on
2270                 // phosphor-network responses
2271                 messages::internalError(asyncResp->res);
2272                 return;
2273             }
2274             messages::created(asyncResp->res);
2275         };
2276         crow::connections::systemBus->async_method_call(
2277             std::move(callback), "xyz.openbmc_project.Network",
2278             "/xyz/openbmc_project/network",
2279             "xyz.openbmc_project.Network.VLAN.Create", "VLAN",
2280             rootInterfaceName, vlanId);
2281     }
2282 };
2283 } // namespace redfish
2284