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