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