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