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