xref: /openbmc/bmcweb/features/redfish/lib/ethernet.hpp (revision aa05fb27ebd2429c29040708ff05eb27407bd963)
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.context"] =
1009             "/redfish/v1/"
1010             "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
1011         res.jsonValue["@odata.id"] =
1012             "/redfish/v1/Managers/bmc/EthernetInterfaces";
1013         res.jsonValue["Name"] = "Ethernet Network Interface Collection";
1014         res.jsonValue["Description"] =
1015             "Collection of EthernetInterfaces for this Manager";
1016         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1017         // Get eth interface list, and call the below callback for JSON
1018         // preparation
1019         getEthernetIfaceList(
1020             [asyncResp](
1021                 const bool &success,
1022                 const boost::container::flat_set<std::string> &iface_list) {
1023                 if (!success)
1024                 {
1025                     messages::internalError(asyncResp->res);
1026                     return;
1027                 }
1028 
1029                 nlohmann::json &iface_array =
1030                     asyncResp->res.jsonValue["Members"];
1031                 iface_array = nlohmann::json::array();
1032                 std::string tag = "_";
1033                 for (const std::string &iface_item : iface_list)
1034                 {
1035                     std::size_t found = iface_item.find(tag);
1036                     if (found == std::string::npos)
1037                     {
1038                         iface_array.push_back(
1039                             {{"@odata.id",
1040                               "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1041                                   iface_item}});
1042                     }
1043                 }
1044 
1045                 asyncResp->res.jsonValue["Members@odata.count"] =
1046                     iface_array.size();
1047                 asyncResp->res.jsonValue["@odata.id"] =
1048                     "/redfish/v1/Managers/bmc/EthernetInterfaces";
1049             });
1050     }
1051 };
1052 
1053 /**
1054  * EthernetInterface derived class for delivering Ethernet Schema
1055  */
1056 class EthernetInterface : public Node
1057 {
1058   public:
1059     /*
1060      * Default Constructor
1061      */
1062     template <typename CrowApp>
1063     EthernetInterface(CrowApp &app) :
1064         Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/",
1065              std::string())
1066     {
1067         entityPrivileges = {
1068             {boost::beast::http::verb::get, {{"Login"}}},
1069             {boost::beast::http::verb::head, {{"Login"}}},
1070             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1071             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1072             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1073             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1074     }
1075 
1076   private:
1077     void handleHostnamePatch(const std::string &hostname,
1078                              const std::shared_ptr<AsyncResp> asyncResp)
1079     {
1080         asyncResp->res.jsonValue["HostName"] = hostname;
1081         crow::connections::systemBus->async_method_call(
1082             [asyncResp](const boost::system::error_code ec) {
1083                 if (ec)
1084                 {
1085                     messages::internalError(asyncResp->res);
1086                 }
1087             },
1088             "xyz.openbmc_project.Network",
1089             "/xyz/openbmc_project/network/config",
1090             "org.freedesktop.DBus.Properties", "Set",
1091             "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
1092             std::variant<std::string>(hostname));
1093     }
1094 
1095     void handleMACAddressPatch(const std::string &ifaceId,
1096                                const std::string &macAddress,
1097                                const std::shared_ptr<AsyncResp> &asyncResp)
1098     {
1099         crow::connections::systemBus->async_method_call(
1100             [asyncResp, macAddress](const boost::system::error_code ec) {
1101                 if (ec)
1102                 {
1103                     messages::internalError(asyncResp->res);
1104                     return;
1105                 }
1106             },
1107             "xyz.openbmc_project.Network",
1108             "/xyz/openbmc_project/network/" + ifaceId,
1109             "org.freedesktop.DBus.Properties", "Set",
1110             "xyz.openbmc_project.Network.MACAddress", "MACAddress",
1111             std::variant<std::string>(macAddress));
1112     }
1113 
1114     void setDHCPEnabled(const std::string &ifaceId,
1115                         const std::string &propertyName, const bool v4Value,
1116                         const bool v6Value,
1117                         const std::shared_ptr<AsyncResp> asyncResp)
1118     {
1119         const std::string dhcp = GetDHCPEnabledEnumeration(v4Value, v6Value);
1120         crow::connections::systemBus->async_method_call(
1121             [asyncResp](const boost::system::error_code ec) {
1122                 if (ec)
1123                 {
1124                     BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1125                     messages::internalError(asyncResp->res);
1126                     return;
1127                 }
1128             },
1129             "xyz.openbmc_project.Network",
1130             "/xyz/openbmc_project/network/" + ifaceId,
1131             "org.freedesktop.DBus.Properties", "Set",
1132             "xyz.openbmc_project.Network.EthernetInterface", propertyName,
1133             std::variant<std::string>{dhcp});
1134     }
1135 
1136     void setDHCPv4Config(const std::string &propertyName, const bool &value,
1137                          const std::shared_ptr<AsyncResp> asyncResp)
1138     {
1139         BMCWEB_LOG_DEBUG << propertyName << " = " << value;
1140         crow::connections::systemBus->async_method_call(
1141             [asyncResp](const boost::system::error_code ec) {
1142                 if (ec)
1143                 {
1144                     BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1145                     messages::internalError(asyncResp->res);
1146                     return;
1147                 }
1148             },
1149             "xyz.openbmc_project.Network",
1150             "/xyz/openbmc_project/network/config/dhcp",
1151             "org.freedesktop.DBus.Properties", "Set",
1152             "xyz.openbmc_project.Network.DHCPConfiguration", propertyName,
1153             std::variant<bool>{value});
1154     }
1155 
1156     void handleDHCPPatch(const std::string &ifaceId,
1157                          const EthernetInterfaceData &ethData,
1158                          DHCPParameters v4dhcpParms, DHCPParameters v6dhcpParms,
1159                          const std::shared_ptr<AsyncResp> asyncResp)
1160     {
1161         bool ipv4Active = translateDHCPEnabledToBool(ethData.DHCPEnabled, true);
1162         bool ipv6Active =
1163             translateDHCPEnabledToBool(ethData.DHCPEnabled, false);
1164 
1165         bool nextv4DHCPState =
1166             v4dhcpParms.dhcpv4Enabled ? *v4dhcpParms.dhcpv4Enabled : ipv4Active;
1167 
1168         bool nextv6DHCPState{};
1169         if (v6dhcpParms.dhcpv6OperatingMode)
1170         {
1171             if ((*v6dhcpParms.dhcpv6OperatingMode != "Stateful") &&
1172                 (*v6dhcpParms.dhcpv6OperatingMode != "Stateless") &&
1173                 (*v6dhcpParms.dhcpv6OperatingMode != "Disabled"))
1174             {
1175                 messages::propertyValueFormatError(
1176                     asyncResp->res, *v6dhcpParms.dhcpv6OperatingMode,
1177                     "OperatingMode");
1178                 return;
1179             }
1180             nextv6DHCPState = (*v6dhcpParms.dhcpv6OperatingMode == "Stateful");
1181         }
1182         else
1183         {
1184             nextv6DHCPState = ipv6Active;
1185         }
1186 
1187         bool nextDNS{};
1188         if (v4dhcpParms.useDNSServers && v6dhcpParms.useDNSServers)
1189         {
1190             if (*v4dhcpParms.useDNSServers != *v6dhcpParms.useDNSServers)
1191             {
1192                 messages::generalError(asyncResp->res);
1193                 return;
1194             }
1195             nextDNS = *v4dhcpParms.useDNSServers;
1196         }
1197         else if (v4dhcpParms.useDNSServers)
1198         {
1199             nextDNS = *v4dhcpParms.useDNSServers;
1200         }
1201         else if (v6dhcpParms.useDNSServers)
1202         {
1203             nextDNS = *v6dhcpParms.useDNSServers;
1204         }
1205         else
1206         {
1207             nextDNS = ethData.DNSEnabled;
1208         }
1209 
1210         bool nextNTP{};
1211         if (v4dhcpParms.useNTPServers && v6dhcpParms.useNTPServers)
1212         {
1213             if (*v4dhcpParms.useNTPServers != *v6dhcpParms.useNTPServers)
1214             {
1215                 messages::generalError(asyncResp->res);
1216                 return;
1217             }
1218             nextNTP = *v4dhcpParms.useNTPServers;
1219         }
1220         else if (v4dhcpParms.useNTPServers)
1221         {
1222             nextNTP = *v4dhcpParms.useNTPServers;
1223         }
1224         else if (v6dhcpParms.useNTPServers)
1225         {
1226             nextNTP = *v6dhcpParms.useNTPServers;
1227         }
1228         else
1229         {
1230             nextNTP = ethData.NTPEnabled;
1231         }
1232 
1233         bool nextUseDomain{};
1234         if (v4dhcpParms.useUseDomainName && v6dhcpParms.useUseDomainName)
1235         {
1236             if (*v4dhcpParms.useUseDomainName != *v6dhcpParms.useUseDomainName)
1237             {
1238                 messages::generalError(asyncResp->res);
1239                 return;
1240             }
1241             nextUseDomain = *v4dhcpParms.useUseDomainName;
1242         }
1243         else if (v4dhcpParms.useUseDomainName)
1244         {
1245             nextUseDomain = *v4dhcpParms.useUseDomainName;
1246         }
1247         else if (v6dhcpParms.useUseDomainName)
1248         {
1249             nextUseDomain = *v6dhcpParms.useUseDomainName;
1250         }
1251         else
1252         {
1253             nextUseDomain = ethData.HostNameEnabled;
1254         }
1255 
1256         BMCWEB_LOG_DEBUG << "set DHCPEnabled...";
1257         setDHCPEnabled(ifaceId, "DHCPEnabled", nextv4DHCPState, nextv6DHCPState,
1258                        asyncResp);
1259         BMCWEB_LOG_DEBUG << "set DNSEnabled...";
1260         setDHCPv4Config("DNSEnabled", nextDNS, asyncResp);
1261         BMCWEB_LOG_DEBUG << "set NTPEnabled...";
1262         setDHCPv4Config("NTPEnabled", nextNTP, asyncResp);
1263         BMCWEB_LOG_DEBUG << "set HostNameEnabled...";
1264         setDHCPv4Config("HostNameEnabled", nextUseDomain, asyncResp);
1265     }
1266 
1267     boost::container::flat_set<IPv4AddressData>::const_iterator
1268         GetNextStaticIPEntry(
1269             boost::container::flat_set<IPv4AddressData>::const_iterator head,
1270             boost::container::flat_set<IPv4AddressData>::const_iterator end)
1271     {
1272         for (; head != end; head++)
1273         {
1274             if (head->origin == "Static")
1275             {
1276                 return head;
1277             }
1278         }
1279         return end;
1280     }
1281 
1282     boost::container::flat_set<IPv6AddressData>::const_iterator
1283         GetNextStaticIPEntry(
1284             boost::container::flat_set<IPv6AddressData>::const_iterator head,
1285             boost::container::flat_set<IPv6AddressData>::const_iterator end)
1286     {
1287         for (; head != end; head++)
1288         {
1289             if (head->origin == "Static")
1290             {
1291                 return head;
1292             }
1293         }
1294         return end;
1295     }
1296 
1297     void handleIPv4StaticPatch(
1298         const std::string &ifaceId, nlohmann::json &input,
1299         const boost::container::flat_set<IPv4AddressData> &ipv4Data,
1300         const std::shared_ptr<AsyncResp> asyncResp)
1301     {
1302         if ((!input.is_array()) || input.empty())
1303         {
1304             messages::propertyValueTypeError(asyncResp->res, input.dump(),
1305                                              "IPv4StaticAddresses");
1306             return;
1307         }
1308 
1309         unsigned entryIdx = 1;
1310         // Find the first static IP address currently active on the NIC and
1311         // match it to the first JSON element in the IPv4StaticAddresses array.
1312         // Match each subsequent JSON element to the next static IP programmed
1313         // into the NIC.
1314         boost::container::flat_set<IPv4AddressData>::const_iterator NICIPentry =
1315             GetNextStaticIPEntry(ipv4Data.cbegin(), ipv4Data.cend());
1316 
1317         for (nlohmann::json &thisJson : input)
1318         {
1319             std::string pathString =
1320                 "IPv4StaticAddresses/" + std::to_string(entryIdx);
1321 
1322             if (!thisJson.is_null() && !thisJson.empty())
1323             {
1324                 std::optional<std::string> address;
1325                 std::optional<std::string> subnetMask;
1326                 std::optional<std::string> gateway;
1327 
1328                 if (!json_util::readJson(thisJson, asyncResp->res, "Address",
1329                                          address, "SubnetMask", subnetMask,
1330                                          "Gateway", gateway))
1331                 {
1332                     messages::propertyValueFormatError(
1333                         asyncResp->res, thisJson.dump(), pathString);
1334                     return;
1335                 }
1336 
1337                 // Find the address/subnet/gateway values. Any values that are
1338                 // not explicitly provided are assumed to be unmodified from the
1339                 // current state of the interface. Merge existing state into the
1340                 // current request.
1341                 const std::string *addr = nullptr;
1342                 const std::string *gw = nullptr;
1343                 uint8_t prefixLength = 0;
1344                 bool errorInEntry = false;
1345                 if (address)
1346                 {
1347                     if (ipv4VerifyIpAndGetBitcount(*address))
1348                     {
1349                         addr = &(*address);
1350                     }
1351                     else
1352                     {
1353                         messages::propertyValueFormatError(
1354                             asyncResp->res, *address, pathString + "/Address");
1355                         errorInEntry = true;
1356                     }
1357                 }
1358                 else if (NICIPentry != ipv4Data.cend())
1359                 {
1360                     addr = &(NICIPentry->address);
1361                 }
1362                 else
1363                 {
1364                     messages::propertyMissing(asyncResp->res,
1365                                               pathString + "/Address");
1366                     errorInEntry = true;
1367                 }
1368 
1369                 if (subnetMask)
1370                 {
1371                     if (!ipv4VerifyIpAndGetBitcount(*subnetMask, &prefixLength))
1372                     {
1373                         messages::propertyValueFormatError(
1374                             asyncResp->res, *subnetMask,
1375                             pathString + "/SubnetMask");
1376                         errorInEntry = true;
1377                     }
1378                 }
1379                 else if (NICIPentry != ipv4Data.cend())
1380                 {
1381                     if (!ipv4VerifyIpAndGetBitcount(NICIPentry->netmask,
1382                                                     &prefixLength))
1383                     {
1384                         messages::propertyValueFormatError(
1385                             asyncResp->res, NICIPentry->netmask,
1386                             pathString + "/SubnetMask");
1387                         errorInEntry = true;
1388                     }
1389                 }
1390                 else
1391                 {
1392                     messages::propertyMissing(asyncResp->res,
1393                                               pathString + "/SubnetMask");
1394                     errorInEntry = true;
1395                 }
1396 
1397                 if (gateway)
1398                 {
1399                     if (ipv4VerifyIpAndGetBitcount(*gateway))
1400                     {
1401                         gw = &(*gateway);
1402                     }
1403                     else
1404                     {
1405                         messages::propertyValueFormatError(
1406                             asyncResp->res, *gateway, pathString + "/Gateway");
1407                         errorInEntry = true;
1408                     }
1409                 }
1410                 else if (NICIPentry != ipv4Data.cend())
1411                 {
1412                     gw = &NICIPentry->gateway;
1413                 }
1414                 else
1415                 {
1416                     messages::propertyMissing(asyncResp->res,
1417                                               pathString + "/Gateway");
1418                     errorInEntry = true;
1419                 }
1420 
1421                 if (errorInEntry)
1422                 {
1423                     return;
1424                 }
1425 
1426                 if (NICIPentry != ipv4Data.cend())
1427                 {
1428                     if (gw != nullptr || addr != nullptr)
1429                     {
1430                         // Shouldn't be possible based on errorInEntry, but
1431                         // it flags -wmaybe-uninitialized in the compiler,
1432                         // so defend against that
1433                         return;
1434                     }
1435                     deleteAndCreateIPv4(ifaceId, NICIPentry->id, prefixLength,
1436                                         *gw, *addr, asyncResp);
1437                     NICIPentry =
1438                         GetNextStaticIPEntry(++NICIPentry, ipv4Data.cend());
1439                 }
1440                 else
1441                 {
1442                     createIPv4(ifaceId, entryIdx, prefixLength, *gateway,
1443                                *address, asyncResp);
1444                 }
1445                 entryIdx++;
1446             }
1447             else
1448             {
1449                 if (NICIPentry == ipv4Data.cend())
1450                 {
1451                     // Requesting a DELETE/DO NOT MODIFY action for an item
1452                     // that isn't present on the eth(n) interface. Input JSON is
1453                     // in error, so bail out.
1454                     if (thisJson.is_null())
1455                     {
1456                         messages::resourceCannotBeDeleted(asyncResp->res);
1457                         return;
1458                     }
1459                     else
1460                     {
1461                         messages::propertyValueFormatError(
1462                             asyncResp->res, thisJson.dump(), pathString);
1463                         return;
1464                     }
1465                 }
1466 
1467                 if (thisJson.is_null())
1468                 {
1469                     deleteIPv4(ifaceId, NICIPentry->id, asyncResp);
1470                 }
1471                 if (NICIPentry != ipv4Data.cend())
1472                 {
1473                     NICIPentry =
1474                         GetNextStaticIPEntry(++NICIPentry, ipv4Data.cend());
1475                 }
1476                 entryIdx++;
1477             }
1478         }
1479     }
1480 
1481     void handleStaticNameServersPatch(
1482         const std::string &ifaceId,
1483         const std::vector<std::string> &updatedStaticNameServers,
1484         const std::shared_ptr<AsyncResp> &asyncResp)
1485     {
1486         crow::connections::systemBus->async_method_call(
1487             [asyncResp](const boost::system::error_code ec) {
1488                 if (ec)
1489                 {
1490                     messages::internalError(asyncResp->res);
1491                     return;
1492                 }
1493             },
1494             "xyz.openbmc_project.Network",
1495             "/xyz/openbmc_project/network/" + ifaceId,
1496             "org.freedesktop.DBus.Properties", "Set",
1497             "xyz.openbmc_project.Network.EthernetInterface", "Nameservers",
1498             std::variant<std::vector<std::string>>{updatedStaticNameServers});
1499     }
1500 
1501     void handleIPv6StaticAddressesPatch(
1502         const std::string &ifaceId, nlohmann::json &input,
1503         const boost::container::flat_set<IPv6AddressData> &ipv6Data,
1504         const std::shared_ptr<AsyncResp> asyncResp)
1505     {
1506         if (!input.is_array() || input.empty())
1507         {
1508             messages::propertyValueTypeError(asyncResp->res, input.dump(),
1509                                              "IPv6StaticAddresses");
1510             return;
1511         }
1512         size_t entryIdx = 1;
1513         boost::container::flat_set<IPv6AddressData>::const_iterator NICIPentry =
1514             GetNextStaticIPEntry(ipv6Data.cbegin(), ipv6Data.cend());
1515         for (nlohmann::json &thisJson : input)
1516         {
1517             std::string pathString =
1518                 "IPv6StaticAddresses/" + std::to_string(entryIdx);
1519 
1520             if (!thisJson.is_null() && !thisJson.empty())
1521             {
1522                 std::optional<std::string> address;
1523                 std::optional<uint8_t> prefixLength;
1524 
1525                 if (!json_util::readJson(thisJson, asyncResp->res, "Address",
1526                                          address, "PrefixLength", prefixLength))
1527                 {
1528                     messages::propertyValueFormatError(
1529                         asyncResp->res, thisJson.dump(), pathString);
1530                     return;
1531                 }
1532 
1533                 const std::string *addr;
1534                 uint8_t prefix;
1535 
1536                 // Find the address and prefixLength values. Any values that are
1537                 // not explicitly provided are assumed to be unmodified from the
1538                 // current state of the interface. Merge existing state into the
1539                 // current request.
1540                 if (address)
1541                 {
1542                     addr = &(*address);
1543                 }
1544                 else if (NICIPentry != ipv6Data.end())
1545                 {
1546                     addr = &(NICIPentry->address);
1547                 }
1548                 else
1549                 {
1550                     messages::propertyMissing(asyncResp->res,
1551                                               pathString + "/Address");
1552                     return;
1553                 }
1554 
1555                 if (prefixLength)
1556                 {
1557                     prefix = *prefixLength;
1558                 }
1559                 else if (NICIPentry != ipv6Data.end())
1560                 {
1561                     prefix = NICIPentry->prefixLength;
1562                 }
1563                 else
1564                 {
1565                     messages::propertyMissing(asyncResp->res,
1566                                               pathString + "/PrefixLength");
1567                     return;
1568                 }
1569 
1570                 if (NICIPentry != ipv6Data.end())
1571                 {
1572                     deleteAndCreateIPv6(ifaceId, NICIPentry->id, prefix, *addr,
1573                                         asyncResp);
1574                     NICIPentry =
1575                         GetNextStaticIPEntry(++NICIPentry, ipv6Data.cend());
1576                 }
1577                 else
1578                 {
1579                     createIPv6(ifaceId, *prefixLength, *addr, asyncResp);
1580                 }
1581                 entryIdx++;
1582             }
1583             else
1584             {
1585                 if (NICIPentry == ipv6Data.end())
1586                 {
1587                     // Requesting a DELETE/DO NOT MODIFY action for an item
1588                     // that isn't present on the eth(n) interface. Input JSON is
1589                     // in error, so bail out.
1590                     if (thisJson.is_null())
1591                     {
1592                         messages::resourceCannotBeDeleted(asyncResp->res);
1593                         return;
1594                     }
1595                     else
1596                     {
1597                         messages::propertyValueFormatError(
1598                             asyncResp->res, thisJson.dump(), pathString);
1599                         return;
1600                     }
1601                 }
1602 
1603                 if (thisJson.is_null())
1604                 {
1605                     deleteIPv6(ifaceId, NICIPentry->id, asyncResp);
1606                 }
1607                 if (NICIPentry != ipv6Data.cend())
1608                 {
1609                     NICIPentry =
1610                         GetNextStaticIPEntry(++NICIPentry, ipv6Data.cend());
1611                 }
1612                 entryIdx++;
1613             }
1614         }
1615     }
1616 
1617     void parseInterfaceData(
1618         nlohmann::json &json_response, const std::string &iface_id,
1619         const EthernetInterfaceData &ethData,
1620         const boost::container::flat_set<IPv4AddressData> &ipv4Data,
1621         const boost::container::flat_set<IPv6AddressData> &ipv6Data)
1622     {
1623         json_response["Id"] = iface_id;
1624         json_response["@odata.id"] =
1625             "/redfish/v1/Managers/bmc/EthernetInterfaces/" + iface_id;
1626         json_response["InterfaceEnabled"] = true;
1627         if (ethData.speed == 0)
1628         {
1629             json_response["Status"] = {
1630                 {"Health", "OK"},
1631                 {"State", "Disabled"},
1632             };
1633         }
1634         else
1635         {
1636             json_response["Status"] = {
1637                 {"Health", "OK"},
1638                 {"State", "Enabled"},
1639             };
1640         }
1641 
1642         json_response["LinkStatus"] = ethData.linkUp ? "LinkUp" : "LinkDown";
1643         json_response["SpeedMbps"] = ethData.speed;
1644         json_response["MACAddress"] = ethData.mac_address;
1645         json_response["DHCPv4"]["DHCPEnabled"] =
1646             translateDHCPEnabledToBool(ethData.DHCPEnabled, true);
1647         json_response["DHCPv4"]["UseNTPServers"] = ethData.NTPEnabled;
1648         json_response["DHCPv4"]["UseDNSServers"] = ethData.DNSEnabled;
1649         json_response["DHCPv4"]["UseDomainName"] = ethData.HostNameEnabled;
1650 
1651         json_response["DHCPv6"]["OperatingMode"] =
1652             translateDHCPEnabledToBool(ethData.DHCPEnabled, false) ? "Stateful"
1653                                                                    : "Disabled";
1654         json_response["DHCPv6"]["UseNTPServers"] = ethData.NTPEnabled;
1655         json_response["DHCPv6"]["UseDNSServers"] = ethData.DNSEnabled;
1656         json_response["DHCPv6"]["UseDomainName"] = ethData.HostNameEnabled;
1657 
1658         if (!ethData.hostname.empty())
1659         {
1660             json_response["HostName"] = ethData.hostname;
1661             if (!ethData.domainnames.empty())
1662             {
1663                 json_response["FQDN"] =
1664                     ethData.hostname + "." + ethData.domainnames[0];
1665             }
1666         }
1667 
1668         json_response["VLANs"] = {
1669             {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1670                               iface_id + "/VLANs"}};
1671 
1672         if (translateDHCPEnabledToBool(ethData.DHCPEnabled, true) &&
1673             ethData.DNSEnabled)
1674         {
1675             json_response["StaticNameServers"] = nlohmann::json::array();
1676         }
1677         else
1678         {
1679             json_response["StaticNameServers"] = ethData.nameservers;
1680         }
1681 
1682         nlohmann::json &ipv4_array = json_response["IPv4Addresses"];
1683         nlohmann::json &ipv4_static_array =
1684             json_response["IPv4StaticAddresses"];
1685         ipv4_array = nlohmann::json::array();
1686         ipv4_static_array = nlohmann::json::array();
1687         for (auto &ipv4_config : ipv4Data)
1688         {
1689 
1690             std::string gatewayStr = ipv4_config.gateway;
1691             if (gatewayStr.empty())
1692             {
1693                 gatewayStr = "0.0.0.0";
1694             }
1695 
1696             ipv4_array.push_back({{"AddressOrigin", ipv4_config.origin},
1697                                   {"SubnetMask", ipv4_config.netmask},
1698                                   {"Address", ipv4_config.address},
1699                                   {"Gateway", gatewayStr}});
1700             if (ipv4_config.origin == "Static")
1701             {
1702                 ipv4_static_array.push_back(
1703                     {{"AddressOrigin", ipv4_config.origin},
1704                      {"SubnetMask", ipv4_config.netmask},
1705                      {"Address", ipv4_config.address},
1706                      {"Gateway", gatewayStr}});
1707             }
1708         }
1709 
1710         json_response["IPv6DefaultGateway"] = ethData.ipv6_default_gateway;
1711 
1712         nlohmann::json &ipv6_array = json_response["IPv6Addresses"];
1713         nlohmann::json &ipv6_static_array =
1714             json_response["IPv6StaticAddresses"];
1715         ipv6_array = nlohmann::json::array();
1716         ipv6_static_array = nlohmann::json::array();
1717         for (auto &ipv6_config : ipv6Data)
1718         {
1719             ipv6_array.push_back({{"Address", ipv6_config.address},
1720                                   {"PrefixLength", ipv6_config.prefixLength},
1721                                   {"AddressOrigin", ipv6_config.origin}});
1722             if (ipv6_config.origin == "Static")
1723             {
1724                 ipv6_static_array.push_back(
1725                     {{"Address", ipv6_config.address},
1726                      {"PrefixLength", ipv6_config.prefixLength},
1727                      {"AddressOrigin", ipv6_config.origin}});
1728             }
1729         }
1730     }
1731 
1732     /**
1733      * Functions triggers appropriate requests on DBus
1734      */
1735     void doGet(crow::Response &res, const crow::Request &req,
1736                const std::vector<std::string> &params) override
1737     {
1738         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1739         if (params.size() != 1)
1740         {
1741             messages::internalError(asyncResp->res);
1742             return;
1743         }
1744 
1745         getEthernetIfaceData(
1746             params[0],
1747             [this, asyncResp, iface_id{std::string(params[0])}](
1748                 const bool &success, const EthernetInterfaceData &ethData,
1749                 const boost::container::flat_set<IPv4AddressData> &ipv4Data,
1750                 const boost::container::flat_set<IPv6AddressData> &ipv6Data) {
1751                 if (!success)
1752                 {
1753                     // TODO(Pawel)consider distinguish between non existing
1754                     // object, and other errors
1755                     messages::resourceNotFound(asyncResp->res,
1756                                                "EthernetInterface", iface_id);
1757                     return;
1758                 }
1759 
1760                 asyncResp->res.jsonValue["@odata.type"] =
1761                     "#EthernetInterface.v1_4_1.EthernetInterface";
1762                 asyncResp->res.jsonValue["@odata.context"] =
1763                     "/redfish/v1/"
1764                     "$metadata#EthernetInterface.EthernetInterface";
1765                 asyncResp->res.jsonValue["Name"] = "Manager Ethernet Interface";
1766                 asyncResp->res.jsonValue["Description"] =
1767                     "Management Network Interface";
1768 
1769                 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
1770                                    ipv4Data, ipv6Data);
1771             });
1772     }
1773 
1774     void doPatch(crow::Response &res, const crow::Request &req,
1775                  const std::vector<std::string> &params) override
1776     {
1777         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1778         if (params.size() != 1)
1779         {
1780             messages::internalError(asyncResp->res);
1781             return;
1782         }
1783 
1784         const std::string &iface_id = params[0];
1785 
1786         std::optional<std::string> hostname;
1787         std::optional<std::string> macAddress;
1788         std::optional<std::string> ipv6DefaultGateway;
1789         std::optional<nlohmann::json> ipv4StaticAddresses;
1790         std::optional<nlohmann::json> ipv6StaticAddresses;
1791         std::optional<std::vector<std::string>> staticNameServers;
1792         std::optional<nlohmann::json> dhcpv4;
1793         std::optional<nlohmann::json> dhcpv6;
1794         DHCPParameters v4dhcpParms;
1795         DHCPParameters v6dhcpParms;
1796 
1797         if (!json_util::readJson(
1798                 req, res, "HostName", hostname, "IPv4StaticAddresses",
1799                 ipv4StaticAddresses, "MACAddress", macAddress,
1800                 "StaticNameServers", staticNameServers, "IPv6DefaultGateway",
1801                 ipv6DefaultGateway, "IPv6StaticAddresses", ipv6StaticAddresses,
1802                 "DHCPv4", dhcpv4, "DHCPv6", dhcpv6))
1803         {
1804             return;
1805         }
1806         if (dhcpv4)
1807         {
1808             if (!json_util::readJson(*dhcpv4, res, "DHCPEnabled",
1809                                      v4dhcpParms.dhcpv4Enabled, "UseDNSServers",
1810                                      v4dhcpParms.useDNSServers, "UseNTPServers",
1811                                      v4dhcpParms.useNTPServers, "UseDomainName",
1812                                      v4dhcpParms.useUseDomainName))
1813             {
1814                 return;
1815             }
1816         }
1817 
1818         if (dhcpv6)
1819         {
1820             if (!json_util::readJson(*dhcpv6, res, "OperatingMode",
1821                                      v6dhcpParms.dhcpv6OperatingMode,
1822                                      "UseDNSServers", v6dhcpParms.useDNSServers,
1823                                      "UseNTPServers", v6dhcpParms.useNTPServers,
1824                                      "UseDomainName",
1825                                      v6dhcpParms.useUseDomainName))
1826             {
1827                 return;
1828             }
1829         }
1830 
1831         // Get single eth interface data, and call the below callback for
1832         // JSON preparation
1833         getEthernetIfaceData(
1834             iface_id,
1835             [this, asyncResp, iface_id, hostname = std::move(hostname),
1836              macAddress = std::move(macAddress),
1837              ipv4StaticAddresses = std::move(ipv4StaticAddresses),
1838              ipv6DefaultGateway = std::move(ipv6DefaultGateway),
1839              ipv6StaticAddresses = std::move(ipv6StaticAddresses),
1840              staticNameServers = std::move(staticNameServers),
1841              dhcpv4 = std::move(dhcpv4), dhcpv6 = std::move(dhcpv6),
1842              v4dhcpParms = std::move(v4dhcpParms),
1843              v6dhcpParms = std::move(v6dhcpParms)](
1844                 const bool &success, const EthernetInterfaceData &ethData,
1845                 const boost::container::flat_set<IPv4AddressData> &ipv4Data,
1846                 const boost::container::flat_set<IPv6AddressData> &ipv6Data) {
1847                 if (!success)
1848                 {
1849                     // ... otherwise return error
1850                     // TODO(Pawel)consider distinguish between non existing
1851                     // object, and other errors
1852                     messages::resourceNotFound(asyncResp->res,
1853                                                "Ethernet Interface", iface_id);
1854                     return;
1855                 }
1856 
1857                 if (dhcpv4 || dhcpv6)
1858                 {
1859                     handleDHCPPatch(iface_id, ethData, std::move(v4dhcpParms),
1860                                     std::move(v6dhcpParms), asyncResp);
1861                 }
1862 
1863                 if (hostname)
1864                 {
1865                     handleHostnamePatch(*hostname, asyncResp);
1866                 }
1867 
1868                 if (macAddress)
1869                 {
1870                     handleMACAddressPatch(iface_id, *macAddress, asyncResp);
1871                 }
1872 
1873                 if (ipv4StaticAddresses)
1874                 {
1875                     // TODO(ed) for some reason the capture of ipv4Addresses
1876                     // above is returning a const value, not a non-const
1877                     // value. This doesn't really work for us, as we need to
1878                     // be able to efficiently move out the intermedia
1879                     // nlohmann::json objects. This makes a copy of the
1880                     // structure, and operates on that, but could be done
1881                     // more efficiently
1882                     nlohmann::json ipv4Static = std::move(*ipv4StaticAddresses);
1883                     handleIPv4StaticPatch(iface_id, ipv4Static, ipv4Data,
1884                                           asyncResp);
1885                 }
1886 
1887                 if (staticNameServers)
1888                 {
1889                     handleStaticNameServersPatch(iface_id, *staticNameServers,
1890                                                  asyncResp);
1891                 }
1892 
1893                 if (ipv6DefaultGateway)
1894                 {
1895                     messages::propertyNotWritable(asyncResp->res,
1896                                                   "IPv6DefaultGateway");
1897                 }
1898 
1899                 if (ipv6StaticAddresses)
1900                 {
1901                     nlohmann::json ipv6Static = std::move(*ipv6StaticAddresses);
1902                     handleIPv6StaticAddressesPatch(iface_id, ipv6Static,
1903                                                    ipv6Data, asyncResp);
1904                 }
1905             });
1906     }
1907 };
1908 
1909 /**
1910  * VlanNetworkInterface derived class for delivering VLANNetworkInterface
1911  * Schema
1912  */
1913 class VlanNetworkInterface : public Node
1914 {
1915   public:
1916     /*
1917      * Default Constructor
1918      */
1919     template <typename CrowApp>
1920     VlanNetworkInterface(CrowApp &app) :
1921         Node(app,
1922              "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/<str>",
1923              std::string(), std::string())
1924     {
1925         entityPrivileges = {
1926             {boost::beast::http::verb::get, {{"Login"}}},
1927             {boost::beast::http::verb::head, {{"Login"}}},
1928             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1929             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1930             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1931             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1932     }
1933 
1934   private:
1935     void parseInterfaceData(
1936         nlohmann::json &json_response, const std::string &parent_iface_id,
1937         const std::string &iface_id, const EthernetInterfaceData &ethData,
1938         const boost::container::flat_set<IPv4AddressData> &ipv4Data,
1939         const boost::container::flat_set<IPv6AddressData> &ipv6Data)
1940     {
1941         // Fill out obvious data...
1942         json_response["Id"] = iface_id;
1943         json_response["@odata.id"] =
1944             "/redfish/v1/Managers/bmc/EthernetInterfaces/" + parent_iface_id +
1945             "/VLANs/" + iface_id;
1946 
1947         json_response["VLANEnable"] = true;
1948         if (!ethData.vlan_id.empty())
1949         {
1950             json_response["VLANId"] = ethData.vlan_id.back();
1951         }
1952     }
1953 
1954     bool verifyNames(const std::string &parent, const std::string &iface)
1955     {
1956         if (!boost::starts_with(iface, parent + "_"))
1957         {
1958             return false;
1959         }
1960         else
1961         {
1962             return true;
1963         }
1964     }
1965 
1966     /**
1967      * Functions triggers appropriate requests on DBus
1968      */
1969     void doGet(crow::Response &res, const crow::Request &req,
1970                const std::vector<std::string> &params) override
1971     {
1972         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1973         // TODO(Pawel) this shall be parameterized call (two params) to get
1974         // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1975         // Check if there is required param, truly entering this shall be
1976         // impossible.
1977         if (params.size() != 2)
1978         {
1979             messages::internalError(res);
1980             res.end();
1981             return;
1982         }
1983 
1984         const std::string &parent_iface_id = params[0];
1985         const std::string &iface_id = params[1];
1986         res.jsonValue["@odata.type"] =
1987             "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1988         res.jsonValue["@odata.context"] =
1989             "/redfish/v1/"
1990             "$metadata#VLanNetworkInterface.VLanNetworkInterface";
1991         res.jsonValue["Name"] = "VLAN Network Interface";
1992 
1993         if (!verifyNames(parent_iface_id, iface_id))
1994         {
1995             return;
1996         }
1997 
1998         // Get single eth interface data, and call the below callback for
1999         // JSON preparation
2000         getEthernetIfaceData(
2001             params[1],
2002             [this, asyncResp, parent_iface_id{std::string(params[0])},
2003              iface_id{std::string(params[1])}](
2004                 const bool &success, const EthernetInterfaceData &ethData,
2005                 const boost::container::flat_set<IPv4AddressData> &ipv4Data,
2006                 const boost::container::flat_set<IPv6AddressData> &ipv6Data) {
2007                 if (success && ethData.vlan_id.size() != 0)
2008                 {
2009                     parseInterfaceData(asyncResp->res.jsonValue,
2010                                        parent_iface_id, iface_id, ethData,
2011                                        ipv4Data, ipv6Data);
2012                 }
2013                 else
2014                 {
2015                     // ... otherwise return error
2016                     // TODO(Pawel)consider distinguish between non existing
2017                     // object, and other errors
2018                     messages::resourceNotFound(
2019                         asyncResp->res, "VLAN Network Interface", iface_id);
2020                 }
2021             });
2022     }
2023 
2024     void doPatch(crow::Response &res, const crow::Request &req,
2025                  const std::vector<std::string> &params) override
2026     {
2027         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2028         if (params.size() != 2)
2029         {
2030             messages::internalError(asyncResp->res);
2031             return;
2032         }
2033 
2034         const std::string &parentIfaceId = params[0];
2035         const std::string &ifaceId = params[1];
2036 
2037         if (!verifyNames(parentIfaceId, ifaceId))
2038         {
2039             messages::resourceNotFound(asyncResp->res, "VLAN Network Interface",
2040                                        ifaceId);
2041             return;
2042         }
2043 
2044         bool vlanEnable = false;
2045         uint64_t vlanId = 0;
2046 
2047         if (!json_util::readJson(req, res, "VLANEnable", vlanEnable, "VLANId",
2048                                  vlanId))
2049         {
2050             return;
2051         }
2052 
2053         // Get single eth interface data, and call the below callback for
2054         // JSON preparation
2055         getEthernetIfaceData(
2056             params[1],
2057             [asyncResp, parentIfaceId{std::string(params[0])},
2058              ifaceId{std::string(params[1])}, &vlanEnable, &vlanId](
2059                 const bool &success, const EthernetInterfaceData &ethData,
2060                 const boost::container::flat_set<IPv4AddressData> &ipv4Data,
2061                 const boost::container::flat_set<IPv6AddressData> &ipv6Data) {
2062                 if (success && !ethData.vlan_id.empty())
2063                 {
2064                     auto callback =
2065                         [asyncResp](const boost::system::error_code ec) {
2066                             if (ec)
2067                             {
2068                                 messages::internalError(asyncResp->res);
2069                             }
2070                         };
2071 
2072                     if (vlanEnable == true)
2073                     {
2074                         crow::connections::systemBus->async_method_call(
2075                             std::move(callback), "xyz.openbmc_project.Network",
2076                             "/xyz/openbmc_project/network/" + ifaceId,
2077                             "org.freedesktop.DBus.Properties", "Set",
2078                             "xyz.openbmc_project.Network.VLAN", "Id",
2079                             std::variant<uint32_t>(vlanId));
2080                     }
2081                     else
2082                     {
2083                         BMCWEB_LOG_DEBUG << "vlanEnable is false. Deleting the "
2084                                             "vlan interface";
2085                         crow::connections::systemBus->async_method_call(
2086                             std::move(callback), "xyz.openbmc_project.Network",
2087                             std::string("/xyz/openbmc_project/network/") +
2088                                 ifaceId,
2089                             "xyz.openbmc_project.Object.Delete", "Delete");
2090                     }
2091                 }
2092                 else
2093                 {
2094                     // TODO(Pawel)consider distinguish between non existing
2095                     // object, and other errors
2096                     messages::resourceNotFound(
2097                         asyncResp->res, "VLAN Network Interface", ifaceId);
2098                     return;
2099                 }
2100             });
2101     }
2102 
2103     void doDelete(crow::Response &res, const crow::Request &req,
2104                   const std::vector<std::string> &params) override
2105     {
2106         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2107         if (params.size() != 2)
2108         {
2109             messages::internalError(asyncResp->res);
2110             return;
2111         }
2112 
2113         const std::string &parentIfaceId = params[0];
2114         const std::string &ifaceId = params[1];
2115 
2116         if (!verifyNames(parentIfaceId, ifaceId))
2117         {
2118             messages::resourceNotFound(asyncResp->res, "VLAN Network Interface",
2119                                        ifaceId);
2120             return;
2121         }
2122 
2123         // Get single eth interface data, and call the below callback for
2124         // JSON preparation
2125         getEthernetIfaceData(
2126             params[1],
2127             [asyncResp, parentIfaceId{std::string(params[0])},
2128              ifaceId{std::string(params[1])}](
2129                 const bool &success, const EthernetInterfaceData &ethData,
2130                 const boost::container::flat_set<IPv4AddressData> &ipv4Data,
2131                 const boost::container::flat_set<IPv6AddressData> &ipv6Data) {
2132                 if (success && !ethData.vlan_id.empty())
2133                 {
2134                     auto callback =
2135                         [asyncResp](const boost::system::error_code ec) {
2136                             if (ec)
2137                             {
2138                                 messages::internalError(asyncResp->res);
2139                             }
2140                         };
2141                     crow::connections::systemBus->async_method_call(
2142                         std::move(callback), "xyz.openbmc_project.Network",
2143                         std::string("/xyz/openbmc_project/network/") + ifaceId,
2144                         "xyz.openbmc_project.Object.Delete", "Delete");
2145                 }
2146                 else
2147                 {
2148                     // ... otherwise return error
2149                     // TODO(Pawel)consider distinguish between non existing
2150                     // object, and other errors
2151                     messages::resourceNotFound(
2152                         asyncResp->res, "VLAN Network Interface", ifaceId);
2153                 }
2154             });
2155     }
2156 };
2157 
2158 /**
2159  * VlanNetworkInterfaceCollection derived class for delivering
2160  * VLANNetworkInterface Collection Schema
2161  */
2162 class VlanNetworkInterfaceCollection : public Node
2163 {
2164   public:
2165     template <typename CrowApp>
2166     VlanNetworkInterfaceCollection(CrowApp &app) :
2167         Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/",
2168              std::string())
2169     {
2170         entityPrivileges = {
2171             {boost::beast::http::verb::get, {{"Login"}}},
2172             {boost::beast::http::verb::head, {{"Login"}}},
2173             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2174             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2175             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2176             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2177     }
2178 
2179   private:
2180     /**
2181      * Functions triggers appropriate requests on DBus
2182      */
2183     void doGet(crow::Response &res, const crow::Request &req,
2184                const std::vector<std::string> &params) override
2185     {
2186         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2187         if (params.size() != 1)
2188         {
2189             // This means there is a problem with the router
2190             messages::internalError(asyncResp->res);
2191             return;
2192         }
2193 
2194         const std::string &rootInterfaceName = params[0];
2195 
2196         // Get eth interface list, and call the below callback for JSON
2197         // preparation
2198         getEthernetIfaceList(
2199             [asyncResp, rootInterfaceName{std::string(rootInterfaceName)}](
2200                 const bool &success,
2201                 const boost::container::flat_set<std::string> &iface_list) {
2202                 if (!success)
2203                 {
2204                     messages::internalError(asyncResp->res);
2205                     return;
2206                 }
2207 
2208                 if (iface_list.find(rootInterfaceName) == iface_list.end())
2209                 {
2210                     messages::resourceNotFound(asyncResp->res,
2211                                                "VLanNetworkInterfaceCollection",
2212                                                rootInterfaceName);
2213                     return;
2214                 }
2215 
2216                 asyncResp->res.jsonValue["@odata.type"] =
2217                     "#VLanNetworkInterfaceCollection."
2218                     "VLanNetworkInterfaceCollection";
2219                 asyncResp->res.jsonValue["@odata.context"] =
2220                     "/redfish/v1/$metadata"
2221                     "#VLanNetworkInterfaceCollection."
2222                     "VLanNetworkInterfaceCollection";
2223                 asyncResp->res.jsonValue["Name"] =
2224                     "VLAN Network Interface Collection";
2225 
2226                 nlohmann::json iface_array = nlohmann::json::array();
2227 
2228                 for (const std::string &iface_item : iface_list)
2229                 {
2230                     if (boost::starts_with(iface_item, rootInterfaceName + "_"))
2231                     {
2232                         iface_array.push_back(
2233                             {{"@odata.id",
2234                               "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
2235                                   rootInterfaceName + "/VLANs/" + iface_item}});
2236                     }
2237                 }
2238 
2239                 asyncResp->res.jsonValue["Members@odata.count"] =
2240                     iface_array.size();
2241                 asyncResp->res.jsonValue["Members"] = std::move(iface_array);
2242                 asyncResp->res.jsonValue["@odata.id"] =
2243                     "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
2244                     rootInterfaceName + "/VLANs";
2245             });
2246     }
2247 
2248     void doPost(crow::Response &res, const crow::Request &req,
2249                 const std::vector<std::string> &params) override
2250     {
2251         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2252         if (params.size() != 1)
2253         {
2254             messages::internalError(asyncResp->res);
2255             return;
2256         }
2257         bool vlanEnable = false;
2258         uint32_t vlanId = 0;
2259         if (!json_util::readJson(req, res, "VLANId", vlanId, "VLANEnable",
2260                                  vlanEnable))
2261         {
2262             return;
2263         }
2264         // Need both vlanId and vlanEnable to service this request
2265         if (!vlanId)
2266         {
2267             messages::propertyMissing(asyncResp->res, "VLANId");
2268         }
2269         if (!vlanEnable)
2270         {
2271             messages::propertyMissing(asyncResp->res, "VLANEnable");
2272         }
2273         if (static_cast<bool>(vlanId) ^ vlanEnable)
2274         {
2275             return;
2276         }
2277 
2278         const std::string &rootInterfaceName = params[0];
2279         auto callback = [asyncResp](const boost::system::error_code ec) {
2280             if (ec)
2281             {
2282                 // TODO(ed) make more consistent error messages based on
2283                 // phosphor-network responses
2284                 messages::internalError(asyncResp->res);
2285                 return;
2286             }
2287             messages::created(asyncResp->res);
2288         };
2289         crow::connections::systemBus->async_method_call(
2290             std::move(callback), "xyz.openbmc_project.Network",
2291             "/xyz/openbmc_project/network",
2292             "xyz.openbmc_project.Network.VLAN.Create", "VLAN",
2293             rootInterfaceName, vlanId);
2294     }
2295 };
2296 } // namespace redfish
2297