xref: /openbmc/bmcweb/features/redfish/lib/ethernet.hpp (revision 9474b3788017bddd70e493e2b9b7674be30abc87)
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 basic single Ethernet Interface information
75  * available from DBus
76  */
77 struct EthernetInterfaceData
78 {
79     uint32_t speed;
80     bool auto_neg;
81     std::string hostname;
82     std::string default_gateway;
83     std::string mac_address;
84     std::optional<uint32_t> vlan_id;
85     std::vector<std::string> nameservers;
86 };
87 
88 // Helper function that changes bits netmask notation (i.e. /24)
89 // into full dot notation
90 inline std::string getNetmask(unsigned int bits)
91 {
92     uint32_t value = 0xffffffff << (32 - bits);
93     std::string netmask = std::to_string((value >> 24) & 0xff) + "." +
94                           std::to_string((value >> 16) & 0xff) + "." +
95                           std::to_string((value >> 8) & 0xff) + "." +
96                           std::to_string(value & 0xff);
97     return netmask;
98 }
99 
100 inline std::string
101     translateAddressOriginDbusToRedfish(const std::string &inputOrigin,
102                                         bool isIPv4)
103 {
104     if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.Static")
105     {
106         return "Static";
107     }
108     if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal")
109     {
110         if (isIPv4)
111         {
112             return "IPv4LinkLocal";
113         }
114         else
115         {
116             return "LinkLocal";
117         }
118     }
119     if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
120     {
121         if (isIPv4)
122         {
123             return "DHCP";
124         }
125         else
126         {
127             return "DHCPv6";
128         }
129     }
130     if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC")
131     {
132         return "SLAAC";
133     }
134     return "";
135 }
136 
137 inline std::string
138     translateAddressOriginRedfishToDbus(const std::string &inputOrigin)
139 {
140     if (inputOrigin == "Static")
141     {
142         return "xyz.openbmc_project.Network.IP.AddressOrigin.Static";
143     }
144     if (inputOrigin == "DHCP" || inputOrigin == "DHCPv6")
145     {
146         return "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP";
147     }
148     if (inputOrigin == "IPv4LinkLocal" || inputOrigin == "LinkLocal")
149     {
150         return "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal";
151     }
152     if (inputOrigin == "SLAAC")
153     {
154         return "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC";
155     }
156     return "";
157 }
158 
159 inline void extractEthernetInterfaceData(const std::string &ethiface_id,
160                                          const GetManagedObjects &dbus_data,
161                                          EthernetInterfaceData &ethData)
162 {
163     for (const auto &objpath : dbus_data)
164     {
165         for (const auto &ifacePair : objpath.second)
166         {
167             if (objpath.first == "/xyz/openbmc_project/network/" + ethiface_id)
168             {
169                 if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress")
170                 {
171                     for (const auto &propertyPair : ifacePair.second)
172                     {
173                         if (propertyPair.first == "MACAddress")
174                         {
175                             const std::string *mac =
176                                 std::get_if<std::string>(&propertyPair.second);
177                             if (mac != nullptr)
178                             {
179                                 ethData.mac_address = *mac;
180                             }
181                         }
182                     }
183                 }
184                 else if (ifacePair.first == "xyz.openbmc_project.Network.VLAN")
185                 {
186                     for (const auto &propertyPair : ifacePair.second)
187                     {
188                         if (propertyPair.first == "Id")
189                         {
190                             const uint32_t *id =
191                                 std::get_if<uint32_t>(&propertyPair.second);
192                             if (id != nullptr)
193                             {
194                                 ethData.vlan_id = *id;
195                             }
196                         }
197                     }
198                 }
199                 else if (ifacePair.first ==
200                          "xyz.openbmc_project.Network.EthernetInterface")
201                 {
202                     for (const auto &propertyPair : ifacePair.second)
203                     {
204                         if (propertyPair.first == "AutoNeg")
205                         {
206                             const bool *auto_neg =
207                                 std::get_if<bool>(&propertyPair.second);
208                             if (auto_neg != nullptr)
209                             {
210                                 ethData.auto_neg = *auto_neg;
211                             }
212                         }
213                         else if (propertyPair.first == "Speed")
214                         {
215                             const uint32_t *speed =
216                                 std::get_if<uint32_t>(&propertyPair.second);
217                             if (speed != nullptr)
218                             {
219                                 ethData.speed = *speed;
220                             }
221                         }
222                         else if (propertyPair.first == "NameServers")
223                         {
224                             const std::vector<std::string> *nameservers =
225                                 sdbusplus::message::variant_ns::get_if<
226                                     std::vector<std::string>>(
227                                     &propertyPair.second);
228                             if (nameservers != nullptr)
229                             {
230                                 ethData.nameservers = std::move(*nameservers);
231                             }
232                         }
233                     }
234                 }
235             }
236             // System configuration shows up in the global namespace, so no need
237             // to check eth number
238             if (ifacePair.first ==
239                 "xyz.openbmc_project.Network.SystemConfiguration")
240             {
241                 for (const auto &propertyPair : ifacePair.second)
242                 {
243                     if (propertyPair.first == "HostName")
244                     {
245                         const std::string *hostname =
246                             sdbusplus::message::variant_ns::get_if<std::string>(
247                                 &propertyPair.second);
248                         if (hostname != nullptr)
249                         {
250                             ethData.hostname = *hostname;
251                         }
252                     }
253                     else if (propertyPair.first == "DefaultGateway")
254                     {
255                         const std::string *defaultGateway =
256                             sdbusplus::message::variant_ns::get_if<std::string>(
257                                 &propertyPair.second);
258                         if (defaultGateway != nullptr)
259                         {
260                             ethData.default_gateway = *defaultGateway;
261                         }
262                     }
263                 }
264             }
265         }
266     }
267 }
268 
269 // Helper function that extracts data for single ethernet ipv4 address
270 inline void
271     extractIPData(const std::string &ethiface_id,
272                   const GetManagedObjects &dbus_data,
273                   boost::container::flat_set<IPv4AddressData> &ipv4_config)
274 {
275     const std::string ipv4PathStart =
276         "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/";
277 
278     // Since there might be several IPv4 configurations aligned with
279     // single ethernet interface, loop over all of them
280     for (const auto &objpath : dbus_data)
281     {
282         // Check if proper pattern for object path appears
283         if (boost::starts_with(objpath.first.str, ipv4PathStart))
284         {
285             for (auto &interface : objpath.second)
286             {
287                 if (interface.first == "xyz.openbmc_project.Network.IP")
288                 {
289                     // Instance IPv4AddressData structure, and set as
290                     // appropriate
291                     std::pair<
292                         boost::container::flat_set<IPv4AddressData>::iterator,
293                         bool>
294                         it = ipv4_config.insert(
295                             {objpath.first.str.substr(ipv4PathStart.size())});
296                     IPv4AddressData &ipv4_address = *it.first;
297                     for (auto &property : interface.second)
298                     {
299                         if (property.first == "Address")
300                         {
301                             const std::string *address =
302                                 std::get_if<std::string>(&property.second);
303                             if (address != nullptr)
304                             {
305                                 ipv4_address.address = *address;
306                             }
307                         }
308                         else if (property.first == "Gateway")
309                         {
310                             const std::string *gateway =
311                                 std::get_if<std::string>(&property.second);
312                             if (gateway != nullptr)
313                             {
314                                 ipv4_address.gateway = *gateway;
315                             }
316                         }
317                         else if (property.first == "Origin")
318                         {
319                             const std::string *origin =
320                                 std::get_if<std::string>(&property.second);
321                             if (origin != nullptr)
322                             {
323                                 ipv4_address.origin =
324                                     translateAddressOriginDbusToRedfish(*origin,
325                                                                         true);
326                             }
327                         }
328                         else if (property.first == "PrefixLength")
329                         {
330                             const uint8_t *mask =
331                                 std::get_if<uint8_t>(&property.second);
332                             if (mask != nullptr)
333                             {
334                                 // convert it to the string
335                                 ipv4_address.netmask = getNetmask(*mask);
336                             }
337                         }
338                         else
339                         {
340                             BMCWEB_LOG_ERROR
341                                 << "Got extra property: " << property.first
342                                 << " on the " << objpath.first.str << " object";
343                         }
344                     }
345                     // Check if given address is local, or global
346                     ipv4_address.linktype =
347                         boost::starts_with(ipv4_address.address, "169.254.")
348                             ? LinkType::Global
349                             : LinkType::Local;
350                 }
351             }
352         }
353     }
354 }
355 
356 /**
357  * @brief Sets given Id on the given VLAN interface through D-Bus
358  *
359  * @param[in] ifaceId       Id of VLAN interface that should be modified
360  * @param[in] inputVlanId   New ID of the VLAN
361  * @param[in] callback      Function that will be called after the operation
362  *
363  * @return None.
364  */
365 template <typename CallbackFunc>
366 void changeVlanId(const std::string &ifaceId, const uint32_t &inputVlanId,
367                   CallbackFunc &&callback)
368 {
369     crow::connections::systemBus->async_method_call(
370         callback, "xyz.openbmc_project.Network",
371         std::string("/xyz/openbmc_project/network/") + ifaceId,
372         "org.freedesktop.DBus.Properties", "Set",
373         "xyz.openbmc_project.Network.VLAN", "Id",
374         std::variant<uint32_t>(inputVlanId));
375 }
376 
377 /**
378  * @brief Helper function that verifies IP address to check if it is in
379  *        proper format. If bits pointer is provided, also calculates active
380  *        bit count for Subnet Mask.
381  *
382  * @param[in]  ip     IP that will be verified
383  * @param[out] bits   Calculated mask in bits notation
384  *
385  * @return true in case of success, false otherwise
386  */
387 inline bool ipv4VerifyIpAndGetBitcount(const std::string &ip,
388                                        uint8_t *bits = nullptr)
389 {
390     std::vector<std::string> bytesInMask;
391 
392     boost::split(bytesInMask, ip, boost::is_any_of("."));
393 
394     static const constexpr int ipV4AddressSectionsCount = 4;
395     if (bytesInMask.size() != ipV4AddressSectionsCount)
396     {
397         return false;
398     }
399 
400     if (bits != nullptr)
401     {
402         *bits = 0;
403     }
404 
405     char *endPtr;
406     long previousValue = 255;
407     bool firstZeroInByteHit;
408     for (const std::string &byte : bytesInMask)
409     {
410         if (byte.empty())
411         {
412             return false;
413         }
414 
415         // Use strtol instead of stroi to avoid exceptions
416         long value = std::strtol(byte.c_str(), &endPtr, 10);
417 
418         // endPtr should point to the end of the string, otherwise given string
419         // is not 100% number
420         if (*endPtr != '\0')
421         {
422             return false;
423         }
424 
425         // Value should be contained in byte
426         if (value < 0 || value > 255)
427         {
428             return false;
429         }
430 
431         if (bits != nullptr)
432         {
433             // Mask has to be continuous between bytes
434             if (previousValue != 255 && value != 0)
435             {
436                 return false;
437             }
438 
439             // Mask has to be continuous inside bytes
440             firstZeroInByteHit = false;
441 
442             // Count bits
443             for (int bitIdx = 7; bitIdx >= 0; bitIdx--)
444             {
445                 if (value & (1 << bitIdx))
446                 {
447                     if (firstZeroInByteHit)
448                     {
449                         // Continuity not preserved
450                         return false;
451                     }
452                     else
453                     {
454                         (*bits)++;
455                     }
456                 }
457                 else
458                 {
459                     firstZeroInByteHit = true;
460                 }
461             }
462         }
463 
464         previousValue = value;
465     }
466 
467     return true;
468 }
469 
470 /**
471  * @brief Changes IPv4 address type property (Address, Gateway)
472  *
473  * @param[in] ifaceId     Id of interface whose IP should be modified
474  * @param[in] ipIdx       Index of IP in input array that should be modified
475  * @param[in] ipHash      DBus Hash id of modified IP
476  * @param[in] name        Name of field in JSON representation
477  * @param[in] newValue    New value that should be written
478  * @param[io] asyncResp   Response object that will be returned to client
479  *
480  * @return true if give IP is valid and has been sent do D-Bus, false
481  * otherwise
482  */
483 inline void changeIPv4AddressProperty(
484     const std::string &ifaceId, int ipIdx, const std::string &ipHash,
485     const std::string &name, const std::string &newValue,
486     const std::shared_ptr<AsyncResp> asyncResp)
487 {
488     auto callback = [asyncResp, ipIdx, name{std::string(name)},
489                      newValue{std::move(newValue)}](
490                         const boost::system::error_code ec) {
491         if (ec)
492         {
493             messages::internalError(asyncResp->res);
494         }
495         else
496         {
497             asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] = newValue;
498         }
499     };
500 
501     crow::connections::systemBus->async_method_call(
502         std::move(callback), "xyz.openbmc_project.Network",
503         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
504         "org.freedesktop.DBus.Properties", "Set",
505         "xyz.openbmc_project.Network.IP", name,
506         std::variant<std::string>(newValue));
507 }
508 
509 /**
510  * @brief Changes IPv4 address origin property
511  *
512  * @param[in] ifaceId       Id of interface whose IP should be modified
513  * @param[in] ipIdx         Index of IP in input array that should be
514  * modified
515  * @param[in] ipHash        DBus Hash id of modified IP
516  * @param[in] newValue      New value in Redfish format
517  * @param[in] newValueDbus  New value in D-Bus format
518  * @param[io] asyncResp     Response object that will be returned to client
519  *
520  * @return true if give IP is valid and has been sent do D-Bus, false
521  * otherwise
522  */
523 inline void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
524                              const std::string &ipHash,
525                              const std::string &newValue,
526                              const std::string &newValueDbus,
527                              const std::shared_ptr<AsyncResp> asyncResp)
528 {
529     auto callback = [asyncResp, ipIdx, newValue{std::move(newValue)}](
530                         const boost::system::error_code ec) {
531         if (ec)
532         {
533             messages::internalError(asyncResp->res);
534         }
535         else
536         {
537             asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] =
538                 newValue;
539         }
540     };
541 
542     crow::connections::systemBus->async_method_call(
543         std::move(callback), "xyz.openbmc_project.Network",
544         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
545         "org.freedesktop.DBus.Properties", "Set",
546         "xyz.openbmc_project.Network.IP", "Origin",
547         std::variant<std::string>(newValueDbus));
548 }
549 
550 /**
551  * @brief Modifies SubnetMask for given IP
552  *
553  * @param[in] ifaceId      Id of interface whose IP should be modified
554  * @param[in] ipIdx        Index of IP in input array that should be
555  * modified
556  * @param[in] ipHash       DBus Hash id of modified IP
557  * @param[in] newValueStr  Mask in dot notation as string
558  * @param[in] newValue     Mask as PrefixLength in bitcount
559  * @param[io] asyncResp   Response object that will be returned to client
560  *
561  * @return None
562  */
563 inline void changeIPv4SubnetMaskProperty(const std::string &ifaceId, int ipIdx,
564                                          const std::string &ipHash,
565                                          const std::string &newValueStr,
566                                          uint8_t &newValue,
567                                          std::shared_ptr<AsyncResp> asyncResp)
568 {
569     auto callback = [asyncResp, ipIdx, newValueStr{std::move(newValueStr)}](
570                         const boost::system::error_code ec) {
571         if (ec)
572         {
573             messages::internalError(asyncResp->res);
574         }
575         else
576         {
577             asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] =
578                 newValueStr;
579         }
580     };
581 
582     crow::connections::systemBus->async_method_call(
583         std::move(callback), "xyz.openbmc_project.Network",
584         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
585         "org.freedesktop.DBus.Properties", "Set",
586         "xyz.openbmc_project.Network.IP", "PrefixLength",
587         std::variant<uint8_t>(newValue));
588 }
589 
590 /**
591  * @brief Deletes given IPv4
592  *
593  * @param[in] ifaceId     Id of interface whose IP should be deleted
594  * @param[in] ipIdx       Index of IP in input array that should be deleted
595  * @param[in] ipHash      DBus Hash id of IP that should be deleted
596  * @param[io] asyncResp   Response object that will be returned to client
597  *
598  * @return None
599  */
600 inline void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
601                        unsigned int ipIdx,
602                        const std::shared_ptr<AsyncResp> asyncResp)
603 {
604     crow::connections::systemBus->async_method_call(
605         [ipIdx, asyncResp](const boost::system::error_code ec) {
606             if (ec)
607             {
608                 messages::internalError(asyncResp->res);
609             }
610             else
611             {
612                 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr;
613             }
614         },
615         "xyz.openbmc_project.Network",
616         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
617         "xyz.openbmc_project.Object.Delete", "Delete");
618 }
619 
620 /**
621  * @brief Creates IPv4 with given data
622  *
623  * @param[in] ifaceId     Id of interface whose IP should be deleted
624  * @param[in] ipIdx       Index of IP in input array that should be deleted
625  * @param[in] ipHash      DBus Hash id of IP that should be deleted
626  * @param[io] asyncResp   Response object that will be returned to client
627  *
628  * @return None
629  */
630 inline void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
631                        uint8_t subnetMask, const std::string &gateway,
632                        const std::string &address,
633                        std::shared_ptr<AsyncResp> asyncResp)
634 {
635     auto createIpHandler = [asyncResp](const boost::system::error_code ec) {
636         if (ec)
637         {
638             messages::internalError(asyncResp->res);
639         }
640     };
641 
642     crow::connections::systemBus->async_method_call(
643         std::move(createIpHandler), "xyz.openbmc_project.Network",
644         "/xyz/openbmc_project/network/" + ifaceId,
645         "xyz.openbmc_project.Network.IP.Create", "IP",
646         "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
647         gateway);
648 }
649 
650 /**
651  * Function that retrieves all properties for given Ethernet Interface
652  * Object
653  * from EntityManager Network Manager
654  * @param ethiface_id a eth interface id to query on DBus
655  * @param callback a function that shall be called to convert Dbus output
656  * into JSON
657  */
658 template <typename CallbackFunc>
659 void getEthernetIfaceData(const std::string &ethiface_id,
660                           CallbackFunc &&callback)
661 {
662     crow::connections::systemBus->async_method_call(
663         [ethiface_id{std::string{ethiface_id}}, callback{std::move(callback)}](
664             const boost::system::error_code error_code,
665             const GetManagedObjects &resp) {
666             EthernetInterfaceData ethData{};
667             boost::container::flat_set<IPv4AddressData> ipv4Data;
668 
669             if (error_code)
670             {
671                 callback(false, ethData, ipv4Data);
672                 return;
673             }
674 
675             extractEthernetInterfaceData(ethiface_id, resp, ethData);
676             extractIPData(ethiface_id, resp, ipv4Data);
677 
678             // Fix global GW
679             for (IPv4AddressData &ipv4 : ipv4Data)
680             {
681                 if ((ipv4.linktype == LinkType::Global) &&
682                     (ipv4.gateway == "0.0.0.0"))
683                 {
684                     ipv4.gateway = ethData.default_gateway;
685                 }
686             }
687 
688             // Finally make a callback with usefull data
689             callback(true, ethData, ipv4Data);
690         },
691         "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
692         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
693 };
694 
695 /**
696  * Function that retrieves all Ethernet Interfaces available through Network
697  * Manager
698  * @param callback a function that shall be called to convert Dbus output
699  * into JSON.
700  */
701 template <typename CallbackFunc>
702 void getEthernetIfaceList(CallbackFunc &&callback)
703 {
704     crow::connections::systemBus->async_method_call(
705         [callback{std::move(callback)}](
706             const boost::system::error_code error_code,
707             GetManagedObjects &resp) {
708             // Callback requires vector<string> to retrieve all available
709             // ethernet interfaces
710             std::vector<std::string> iface_list;
711             iface_list.reserve(resp.size());
712             if (error_code)
713             {
714                 callback(false, iface_list);
715                 return;
716             }
717 
718             // Iterate over all retrieved ObjectPaths.
719             for (const auto &objpath : resp)
720             {
721                 // And all interfaces available for certain ObjectPath.
722                 for (const auto &interface : objpath.second)
723                 {
724                     // If interface is
725                     // xyz.openbmc_project.Network.EthernetInterface, this is
726                     // what we're looking for.
727                     if (interface.first ==
728                         "xyz.openbmc_project.Network.EthernetInterface")
729                     {
730                         // Cut out everyting until last "/", ...
731                         const std::string &iface_id = objpath.first.str;
732                         std::size_t last_pos = iface_id.rfind("/");
733                         if (last_pos != std::string::npos)
734                         {
735                             // and put it into output vector.
736                             iface_list.emplace_back(
737                                 iface_id.substr(last_pos + 1));
738                         }
739                     }
740                 }
741             }
742             // Finally make a callback with useful data
743             callback(true, iface_list);
744         },
745         "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
746         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
747 };
748 
749 /**
750  * EthernetCollection derived class for delivering Ethernet Collection Schema
751  */
752 class EthernetCollection : public Node
753 {
754   public:
755     template <typename CrowApp>
756     EthernetCollection(CrowApp &app) :
757         Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/")
758     {
759         entityPrivileges = {
760             {boost::beast::http::verb::get, {{"Login"}}},
761             {boost::beast::http::verb::head, {{"Login"}}},
762             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
763             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
764             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
765             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
766     }
767 
768   private:
769     /**
770      * Functions triggers appropriate requests on DBus
771      */
772     void doGet(crow::Response &res, const crow::Request &req,
773                const std::vector<std::string> &params) override
774     {
775         res.jsonValue["@odata.type"] =
776             "#EthernetInterfaceCollection.EthernetInterfaceCollection";
777         res.jsonValue["@odata.context"] =
778             "/redfish/v1/"
779             "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
780         res.jsonValue["@odata.id"] =
781             "/redfish/v1/Managers/bmc/EthernetInterfaces";
782         res.jsonValue["Name"] = "Ethernet Network Interface Collection";
783         res.jsonValue["Description"] =
784             "Collection of EthernetInterfaces for this Manager";
785 
786         // Get eth interface list, and call the below callback for JSON
787         // preparation
788         getEthernetIfaceList(
789             [&res](const bool &success,
790                    const std::vector<std::string> &iface_list) {
791                 if (!success)
792                 {
793                     messages::internalError(res);
794                     res.end();
795                     return;
796                 }
797 
798                 nlohmann::json &iface_array = res.jsonValue["Members"];
799                 iface_array = nlohmann::json::array();
800                 for (const std::string &iface_item : iface_list)
801                 {
802                     iface_array.push_back(
803                         {{"@odata.id",
804                           "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
805                               iface_item}});
806                 }
807 
808                 res.jsonValue["Members@odata.count"] = iface_array.size();
809                 res.jsonValue["@odata.id"] =
810                     "/redfish/v1/Managers/bmc/EthernetInterfaces";
811                 res.end();
812             });
813     }
814 };
815 
816 /**
817  * EthernetInterface derived class for delivering Ethernet Schema
818  */
819 class EthernetInterface : public Node
820 {
821   public:
822     /*
823      * Default Constructor
824      */
825     template <typename CrowApp>
826     EthernetInterface(CrowApp &app) :
827         Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/",
828              std::string())
829     {
830         entityPrivileges = {
831             {boost::beast::http::verb::get, {{"Login"}}},
832             {boost::beast::http::verb::head, {{"Login"}}},
833             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
834             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
835             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
836             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
837     }
838 
839     // TODO(kkowalsk) Find a suitable class/namespace for this
840     static void handleVlanPatch(const std::string &ifaceId, bool vlanEnable,
841                                 uint64_t vlanId,
842                                 const EthernetInterfaceData &ethData,
843                                 const std::shared_ptr<AsyncResp> asyncResp)
844     {
845         if (!ethData.vlan_id)
846         {
847             // This interface is not a VLAN. Cannot do anything with it
848             // TODO(kkowalsk) Change this message
849             messages::propertyNotWritable(asyncResp->res, "VLANEnable");
850 
851             return;
852         }
853 
854         // VLAN is configured on the interface
855         if (vlanEnable == true)
856         {
857             // Change VLAN Id
858             asyncResp->res.jsonValue["VLANId"] = vlanId;
859             auto callback = [asyncResp](const boost::system::error_code ec) {
860                 if (ec)
861                 {
862                     messages::internalError(asyncResp->res);
863                 }
864                 else
865                 {
866                     asyncResp->res.jsonValue["VLANEnable"] = true;
867                 }
868             };
869             crow::connections::systemBus->async_method_call(
870                 std::move(callback), "xyz.openbmc_project.Network",
871                 "/xyz/openbmc_project/network/" + ifaceId,
872                 "org.freedesktop.DBus.Properties", "Set",
873                 "xyz.openbmc_project.Network.VLAN", "Id",
874                 std::variant<uint32_t>(vlanId));
875         }
876         else
877         {
878             auto callback = [asyncResp](const boost::system::error_code ec) {
879                 if (ec)
880                 {
881                     messages::internalError(asyncResp->res);
882                     return;
883                 }
884                 asyncResp->res.jsonValue["VLANEnable"] = false;
885             };
886 
887             crow::connections::systemBus->async_method_call(
888                 std::move(callback), "xyz.openbmc_project.Network",
889                 "/xyz/openbmc_project/network/" + ifaceId,
890                 "xyz.openbmc_project.Object.Delete", "Delete");
891         }
892     }
893 
894   private:
895     void handleHostnamePatch(const std::string &hostname,
896                              const std::shared_ptr<AsyncResp> asyncResp)
897     {
898         asyncResp->res.jsonValue["HostName"] = hostname;
899         crow::connections::systemBus->async_method_call(
900             [asyncResp](const boost::system::error_code ec) {
901                 if (ec)
902                 {
903                     messages::internalError(asyncResp->res);
904                 }
905             },
906             "xyz.openbmc_project.Network",
907             "/xyz/openbmc_project/network/config",
908             "org.freedesktop.DBus.Properties", "Set",
909             "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
910             std::variant<std::string>(hostname));
911     }
912 
913     void handleIPv4Patch(
914         const std::string &ifaceId, std::vector<nlohmann::json> &input,
915         const boost::container::flat_set<IPv4AddressData> &ipv4Data,
916         const std::shared_ptr<AsyncResp> asyncResp)
917     {
918         int entryIdx = 0;
919         boost::container::flat_set<IPv4AddressData>::const_iterator thisData =
920             ipv4Data.begin();
921         for (nlohmann::json &thisJson : input)
922         {
923             std::string pathString =
924                 "IPv4Addresses/" + std::to_string(entryIdx);
925 
926             if (thisJson.empty())
927             {
928                 if (thisData != ipv4Data.end())
929                 {
930                     thisData++;
931                 }
932                 else
933                 {
934                     messages::propertyMissing(asyncResp->res,
935                                               pathString + "/Address");
936                     return;
937                     // TODO Not sure about the property where value is
938                     // list and if unable to update one of the
939                     // list value then should we proceed further or
940                     // break there, would ask in the redfish forum
941                     // till then we stop processing the next list item.
942                 }
943                 entryIdx++;
944                 continue; // not an error as per the redfish spec.
945             }
946 
947             std::optional<std::string> address;
948             std::optional<std::string> addressOrigin;
949             std::optional<std::string> subnetMask;
950             std::optional<std::string> gateway;
951 
952             if (!json_util::readJson(thisJson, asyncResp->res, "Address",
953                                      address, "AddressOrigin", addressOrigin,
954                                      "SubnetMask", subnetMask, "Gateway",
955                                      gateway))
956             {
957                 return;
958             }
959 
960             if (address)
961             {
962                 if (!ipv4VerifyIpAndGetBitcount(*address))
963                 {
964                     messages::propertyValueFormatError(asyncResp->res, *address,
965                                                        pathString + "/Address");
966                     return;
967                 }
968             }
969 
970             uint8_t prefixLength = 0;
971             if (subnetMask)
972             {
973                 if (!ipv4VerifyIpAndGetBitcount(*subnetMask, &prefixLength))
974                 {
975                     messages::propertyValueFormatError(
976                         asyncResp->res, *subnetMask,
977                         pathString + "/SubnetMask");
978                     return;
979                 }
980             }
981             std::string addressOriginInDBusFormat;
982             if (addressOrigin)
983             {
984                 // Get Address origin in proper format
985                 addressOriginInDBusFormat =
986                     translateAddressOriginRedfishToDbus(*addressOrigin);
987                 if (addressOriginInDBusFormat.empty())
988                 {
989                     messages::propertyValueNotInList(
990                         asyncResp->res, *addressOrigin,
991                         pathString + "/AddressOrigin");
992                     return;
993                 }
994             }
995 
996             if (gateway)
997             {
998                 if (!ipv4VerifyIpAndGetBitcount(*gateway))
999                 {
1000                     messages::propertyValueFormatError(asyncResp->res, *gateway,
1001                                                        pathString + "/Gateway");
1002                     return;
1003                 }
1004             }
1005 
1006             // if a vlan already exists, modify the existing
1007             if (thisData != ipv4Data.end())
1008             {
1009                 // Existing object that should be modified/deleted/remain
1010                 // unchanged
1011                 if (thisJson.is_null())
1012                 {
1013                     auto callback = [entryIdx{std::to_string(entryIdx)},
1014                                      asyncResp](
1015                                         const boost::system::error_code ec) {
1016                         if (ec)
1017                         {
1018                             messages::internalError(asyncResp->res);
1019                             return;
1020                         }
1021                         asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] =
1022                             nullptr;
1023                     };
1024                     crow::connections::systemBus->async_method_call(
1025                         std::move(callback), "xyz.openbmc_project.Network",
1026                         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" +
1027                             thisData->id,
1028                         "xyz.openbmc_project.Object.Delete", "Delete");
1029                 }
1030                 else if (thisJson.is_object())
1031                 {
1032                     // Apply changes
1033                     if (address)
1034                     {
1035                         auto callback =
1036                             [asyncResp, entryIdx, address = *address](
1037                                 const boost::system::error_code ec) {
1038                                 if (ec)
1039                                 {
1040                                     messages::internalError(asyncResp->res);
1041                                     return;
1042                                 }
1043                                 asyncResp->res.jsonValue["IPv4Addresses"]
1044                                                         [entryIdx]["Address"] =
1045                                     address;
1046                             };
1047 
1048                         crow::connections::systemBus->async_method_call(
1049                             std::move(callback), "xyz.openbmc_project.Network",
1050                             "/xyz/openbmc_project/network/" + ifaceId +
1051                                 "/ipv4/" + thisData->id,
1052                             "org.freedesktop.DBus.Properties", "Set",
1053                             "xyz.openbmc_project.Network.IP", "Address",
1054                             std::variant<std::string>(*address));
1055                     }
1056 
1057                     if (subnetMask)
1058                     {
1059                         changeIPv4SubnetMaskProperty(ifaceId, entryIdx,
1060                                                      thisData->id, *subnetMask,
1061                                                      prefixLength, asyncResp);
1062                     }
1063 
1064                     if (addressOrigin)
1065                     {
1066                         changeIPv4Origin(ifaceId, entryIdx, thisData->id,
1067                                          *addressOrigin,
1068                                          addressOriginInDBusFormat, asyncResp);
1069                     }
1070 
1071                     if (gateway)
1072                     {
1073                         auto callback =
1074                             [asyncResp, entryIdx,
1075                              gateway{std::string(*gateway)}](
1076                                 const boost::system::error_code ec) {
1077                                 if (ec)
1078                                 {
1079                                     messages::internalError(asyncResp->res);
1080                                     return;
1081                                 }
1082                                 asyncResp->res.jsonValue["IPv4Addresses"]
1083                                                         [entryIdx]["Gateway"] =
1084                                     std::move(gateway);
1085                             };
1086 
1087                         crow::connections::systemBus->async_method_call(
1088                             std::move(callback), "xyz.openbmc_project.Network",
1089                             "/xyz/openbmc_project/network/" + ifaceId +
1090                                 "/ipv4/" + thisData->id,
1091                             "org.freedesktop.DBus.Properties", "Set",
1092                             "xyz.openbmc_project.Network.IP", "Gateway",
1093                             std::variant<std::string>(*gateway));
1094                     }
1095                 }
1096                 thisData++;
1097             }
1098             else
1099             {
1100                 // Create IPv4 with provided data
1101                 if (!gateway)
1102                 {
1103                     messages::propertyMissing(asyncResp->res,
1104                                               pathString + "/Gateway");
1105                     continue;
1106                 }
1107 
1108                 if (!address)
1109                 {
1110                     messages::propertyMissing(asyncResp->res,
1111                                               pathString + "/Address");
1112                     continue;
1113                 }
1114 
1115                 if (!subnetMask)
1116                 {
1117                     messages::propertyMissing(asyncResp->res,
1118                                               pathString + "/SubnetMask");
1119                     continue;
1120                 }
1121 
1122                 createIPv4(ifaceId, entryIdx, prefixLength, *gateway, *address,
1123                            asyncResp);
1124                 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] = thisJson;
1125             }
1126             entryIdx++;
1127         }
1128     }
1129 
1130     void parseInterfaceData(
1131         nlohmann::json &json_response, const std::string &iface_id,
1132         const EthernetInterfaceData &ethData,
1133         const boost::container::flat_set<IPv4AddressData> &ipv4Data)
1134     {
1135         json_response["Id"] = iface_id;
1136         json_response["@odata.id"] =
1137             "/redfish/v1/Managers/bmc/EthernetInterfaces/" + iface_id;
1138         json_response["InterfaceEnabled"] = true;
1139         if (ethData.speed == 0)
1140         {
1141             json_response["LinkStatus"] = "NoLink";
1142             json_response["Status"] = {
1143                 {"Health", "OK"},
1144                 {"State", "Disabled"},
1145             };
1146         }
1147         else
1148         {
1149             json_response["LinkStatus"] = "LinkUp";
1150             json_response["Status"] = {
1151                 {"Health", "OK"},
1152                 {"State", "Enabled"},
1153             };
1154         }
1155         json_response["SpeedMbps"] = ethData.speed;
1156         json_response["MACAddress"] = ethData.mac_address;
1157         if (!ethData.hostname.empty())
1158         {
1159             json_response["HostName"] = ethData.hostname;
1160         }
1161 
1162         nlohmann::json &vlanObj = json_response["VLAN"];
1163         if (ethData.vlan_id)
1164         {
1165             vlanObj["VLANEnable"] = true;
1166             vlanObj["VLANId"] = *ethData.vlan_id;
1167         }
1168         else
1169         {
1170             vlanObj["VLANEnable"] = false;
1171             vlanObj["VLANId"] = 0;
1172         }
1173         json_response["NameServers"] = ethData.nameservers;
1174 
1175         if (ipv4Data.size() > 0)
1176         {
1177             nlohmann::json &ipv4_array = json_response["IPv4Addresses"];
1178             ipv4_array = nlohmann::json::array();
1179             for (auto &ipv4_config : ipv4Data)
1180             {
1181                 ipv4_array.push_back({{"AddressOrigin", ipv4_config.origin},
1182                                       {"SubnetMask", ipv4_config.netmask},
1183                                       {"Address", ipv4_config.address},
1184                                       {"Gateway", ipv4_config.gateway}});
1185             }
1186         }
1187     }
1188 
1189     /**
1190      * Functions triggers appropriate requests on DBus
1191      */
1192     void doGet(crow::Response &res, const crow::Request &req,
1193                const std::vector<std::string> &params) override
1194     {
1195         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1196         if (params.size() != 1)
1197         {
1198             messages::internalError(asyncResp->res);
1199             return;
1200         }
1201 
1202         getEthernetIfaceData(
1203             params[0],
1204             [this, asyncResp, iface_id{std::string(params[0])}](
1205                 const bool &success, const EthernetInterfaceData &ethData,
1206                 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1207                 if (!success)
1208                 {
1209                     // TODO(Pawel)consider distinguish between non existing
1210                     // object, and other errors
1211                     messages::resourceNotFound(asyncResp->res,
1212                                                "EthernetInterface", iface_id);
1213                     return;
1214                 }
1215                 asyncResp->res.jsonValue["@odata.type"] =
1216                     "#EthernetInterface.v1_2_0.EthernetInterface";
1217                 asyncResp->res.jsonValue["@odata.context"] =
1218                     "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
1219                 asyncResp->res.jsonValue["Name"] = "Manager Ethernet Interface";
1220                 asyncResp->res.jsonValue["Description"] =
1221                     "Management Network Interface";
1222 
1223                 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
1224                                    ipv4Data);
1225             });
1226     }
1227 
1228     void doPatch(crow::Response &res, const crow::Request &req,
1229                  const std::vector<std::string> &params) override
1230     {
1231         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1232         if (params.size() != 1)
1233         {
1234             messages::internalError(asyncResp->res);
1235             return;
1236         }
1237 
1238         const std::string &iface_id = params[0];
1239 
1240         std::optional<nlohmann::json> vlan;
1241         std::optional<std::string> hostname;
1242         std::optional<std::vector<nlohmann::json>> ipv4Addresses;
1243         std::optional<std::vector<nlohmann::json>> ipv6Addresses;
1244 
1245         if (!json_util::readJson(req, res, "VLAN", vlan, "HostName", hostname,
1246                                  "IPv4Addresses", ipv4Addresses,
1247                                  "IPv6Addresses", ipv6Addresses))
1248         {
1249             return;
1250         }
1251 
1252         std::optional<uint64_t> vlanId;
1253         std::optional<bool> vlanEnable;
1254 
1255         if (vlan)
1256         {
1257             if (!json_util::readJson(*vlan, res, "VLANEnable", vlanEnable,
1258                                      "VLANId", vlanId))
1259             {
1260                 return;
1261             }
1262             // Need both vlanId and vlanEnable to service this request
1263             if (static_cast<bool>(vlanId) ^ static_cast<bool>(vlanEnable))
1264             {
1265                 if (vlanId)
1266                 {
1267                     messages::propertyMissing(asyncResp->res, "VLANEnable");
1268                 }
1269                 else
1270                 {
1271                     messages::propertyMissing(asyncResp->res, "VLANId");
1272                 }
1273 
1274                 return;
1275             }
1276         }
1277 
1278         // Get single eth interface data, and call the below callback for JSON
1279         // preparation
1280         getEthernetIfaceData(
1281             iface_id,
1282             [this, asyncResp, iface_id, vlanId, vlanEnable,
1283              hostname = std::move(hostname),
1284              ipv4Addresses = std::move(ipv4Addresses),
1285              ipv6Addresses = std::move(ipv6Addresses)](
1286                 const bool &success, const EthernetInterfaceData &ethData,
1287                 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1288                 if (!success)
1289                 {
1290                     // ... otherwise return error
1291                     // TODO(Pawel)consider distinguish between non existing
1292                     // object, and other errors
1293                     messages::resourceNotFound(
1294                         asyncResp->res, "VLAN Network Interface", iface_id);
1295                     return;
1296                 }
1297 
1298                 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
1299                                    ipv4Data);
1300 
1301                 if (vlanId && vlanEnable)
1302                 {
1303                     handleVlanPatch(iface_id, *vlanId, *vlanEnable, ethData,
1304                                     asyncResp);
1305                 }
1306 
1307                 if (hostname)
1308                 {
1309                     handleHostnamePatch(*hostname, asyncResp);
1310                 }
1311 
1312                 if (ipv4Addresses)
1313                 {
1314                     // TODO(ed) for some reason the capture of ipv4Addresses
1315                     // above is returning a const value, not a non-const value.
1316                     // This doesn't really work for us, as we need to be able to
1317                     // efficiently move out the intermedia nlohmann::json
1318                     // objects. This makes a copy of the structure, and operates
1319                     // on that, but could be done more efficiently
1320                     std::vector<nlohmann::json> ipv4 =
1321                         std::move(*ipv4Addresses);
1322                     handleIPv4Patch(iface_id, ipv4, ipv4Data, asyncResp);
1323                 }
1324 
1325                 if (ipv6Addresses)
1326                 {
1327                     // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
1328                     messages::propertyNotWritable(asyncResp->res,
1329                                                   "IPv6Addresses");
1330                 }
1331             });
1332     }
1333 };
1334 
1335 /**
1336  * VlanNetworkInterface derived class for delivering VLANNetworkInterface
1337  * Schema
1338  */
1339 class VlanNetworkInterface : public Node
1340 {
1341   public:
1342     /*
1343      * Default Constructor
1344      */
1345     template <typename CrowApp>
1346     VlanNetworkInterface(CrowApp &app) :
1347         Node(app,
1348              "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/<str>",
1349              std::string(), std::string())
1350     {
1351         entityPrivileges = {
1352             {boost::beast::http::verb::get, {{"Login"}}},
1353             {boost::beast::http::verb::head, {{"Login"}}},
1354             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1355             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1356             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1357             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1358     }
1359 
1360   private:
1361     void parseInterfaceData(
1362         nlohmann::json &json_response, const std::string &parent_iface_id,
1363         const std::string &iface_id, const EthernetInterfaceData &ethData,
1364         const boost::container::flat_set<IPv4AddressData> &ipv4Data)
1365     {
1366         // Fill out obvious data...
1367         json_response["Id"] = iface_id;
1368         json_response["@odata.id"] =
1369             "/redfish/v1/Managers/bmc/EthernetInterfaces/" + parent_iface_id +
1370             "/VLANs/" + iface_id;
1371 
1372         json_response["VLANEnable"] = true;
1373         if (ethData.vlan_id)
1374         {
1375             json_response["VLANId"] = *ethData.vlan_id;
1376         }
1377     }
1378 
1379     bool verifyNames(crow::Response &res, const std::string &parent,
1380                      const std::string &iface)
1381     {
1382         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1383         if (!boost::starts_with(iface, parent + "_"))
1384         {
1385             messages::resourceNotFound(asyncResp->res, "VLAN Network Interface",
1386                                        iface);
1387             return false;
1388         }
1389         else
1390         {
1391             return true;
1392         }
1393     }
1394 
1395     /**
1396      * Functions triggers appropriate requests on DBus
1397      */
1398     void doGet(crow::Response &res, const crow::Request &req,
1399                const std::vector<std::string> &params) override
1400     {
1401         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1402         // TODO(Pawel) this shall be parameterized call (two params) to get
1403         // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1404         // Check if there is required param, truly entering this shall be
1405         // impossible.
1406         if (params.size() != 2)
1407         {
1408             messages::internalError(res);
1409             res.end();
1410             return;
1411         }
1412 
1413         const std::string &parent_iface_id = params[0];
1414         const std::string &iface_id = params[1];
1415         res.jsonValue["@odata.type"] =
1416             "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1417         res.jsonValue["@odata.context"] =
1418             "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
1419         res.jsonValue["Name"] = "VLAN Network Interface";
1420 
1421         if (!verifyNames(res, parent_iface_id, iface_id))
1422         {
1423             return;
1424         }
1425 
1426         // Get single eth interface data, and call the below callback for JSON
1427         // preparation
1428         getEthernetIfaceData(
1429             iface_id,
1430             [this, asyncResp, parent_iface_id, iface_id](
1431                 const bool &success, const EthernetInterfaceData &ethData,
1432                 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1433                 if (success && ethData.vlan_id)
1434                 {
1435                     parseInterfaceData(asyncResp->res.jsonValue,
1436                                        parent_iface_id, iface_id, ethData,
1437                                        ipv4Data);
1438                 }
1439                 else
1440                 {
1441                     // ... otherwise return error
1442                     // TODO(Pawel)consider distinguish between non existing
1443                     // object, and other errors
1444                     messages::resourceNotFound(
1445                         asyncResp->res, "VLAN Network Interface", iface_id);
1446                 }
1447             });
1448     }
1449 
1450     void doPatch(crow::Response &res, const crow::Request &req,
1451                  const std::vector<std::string> &params) override
1452     {
1453         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1454         if (params.size() != 2)
1455         {
1456             messages::internalError(asyncResp->res);
1457             return;
1458         }
1459 
1460         const std::string &parentIfaceId = params[0];
1461         const std::string &ifaceId = params[1];
1462 
1463         if (!verifyNames(res, parentIfaceId, ifaceId))
1464         {
1465             return;
1466         }
1467 
1468         bool vlanEnable = false;
1469         uint64_t vlanId = 0;
1470 
1471         if (!json_util::readJson(req, res, "VLANEnable", vlanEnable, "VLANId",
1472                                  vlanId))
1473         {
1474             return;
1475         }
1476 
1477         // Get single eth interface data, and call the below callback for JSON
1478         // preparation
1479         getEthernetIfaceData(
1480             ifaceId,
1481             [this, asyncResp, parentIfaceId, ifaceId, vlanEnable, vlanId](
1482                 const bool &success, const EthernetInterfaceData &ethData,
1483                 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1484                 if (!success)
1485                 {
1486                     // TODO(Pawel)consider distinguish between non existing
1487                     // object, and other errors
1488                     messages::resourceNotFound(
1489                         asyncResp->res, "VLAN Network Interface", ifaceId);
1490 
1491                     return;
1492                 }
1493 
1494                 parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId,
1495                                    ifaceId, ethData, ipv4Data);
1496 
1497                 EthernetInterface::handleVlanPatch(ifaceId, vlanId, vlanEnable,
1498                                                    ethData, asyncResp);
1499             });
1500     }
1501 
1502     void doDelete(crow::Response &res, const crow::Request &req,
1503                   const std::vector<std::string> &params) override
1504     {
1505         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1506         if (params.size() != 2)
1507         {
1508             messages::internalError(asyncResp->res);
1509             return;
1510         }
1511 
1512         const std::string &parentIfaceId = params[0];
1513         const std::string &ifaceId = params[1];
1514 
1515         if (!verifyNames(asyncResp->res, parentIfaceId, ifaceId))
1516         {
1517             return;
1518         }
1519 
1520         // Get single eth interface data, and call the below callback for JSON
1521         // preparation
1522         getEthernetIfaceData(
1523             ifaceId,
1524             [this, asyncResp, parentIfaceId{std::string(parentIfaceId)},
1525              ifaceId{std::string(ifaceId)}](
1526                 const bool &success, const EthernetInterfaceData &ethData,
1527                 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1528                 if (success && ethData.vlan_id)
1529                 {
1530                     parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId,
1531                                        ifaceId, ethData, ipv4Data);
1532 
1533                     auto callback =
1534                         [asyncResp](const boost::system::error_code ec) {
1535                             if (ec)
1536                             {
1537                                 messages::internalError(asyncResp->res);
1538                             }
1539                         };
1540                     crow::connections::systemBus->async_method_call(
1541                         std::move(callback), "xyz.openbmc_project.Network",
1542                         std::string("/xyz/openbmc_project/network/") + ifaceId,
1543                         "xyz.openbmc_project.Object.Delete", "Delete");
1544                 }
1545                 else
1546                 {
1547                     // ... otherwise return error
1548                     // TODO(Pawel)consider distinguish between non existing
1549                     // object, and other errors
1550                     messages::resourceNotFound(
1551                         asyncResp->res, "VLAN Network Interface", ifaceId);
1552                 }
1553             });
1554     }
1555 };
1556 
1557 /**
1558  * VlanNetworkInterfaceCollection derived class for delivering
1559  * VLANNetworkInterface Collection Schema
1560  */
1561 class VlanNetworkInterfaceCollection : public Node
1562 {
1563   public:
1564     template <typename CrowApp>
1565     VlanNetworkInterfaceCollection(CrowApp &app) :
1566         Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/",
1567              std::string())
1568     {
1569         entityPrivileges = {
1570             {boost::beast::http::verb::get, {{"Login"}}},
1571             {boost::beast::http::verb::head, {{"Login"}}},
1572             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1573             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1574             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1575             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1576     }
1577 
1578   private:
1579     /**
1580      * Functions triggers appropriate requests on DBus
1581      */
1582     void doGet(crow::Response &res, const crow::Request &req,
1583                const std::vector<std::string> &params) override
1584     {
1585         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1586         if (params.size() != 1)
1587         {
1588             // This means there is a problem with the router
1589             messages::internalError(asyncResp->res);
1590             return;
1591         }
1592 
1593         const std::string &rootInterfaceName = params[0];
1594 
1595         // Get eth interface list, and call the below callback for JSON
1596         // preparation
1597         getEthernetIfaceList(
1598             [asyncResp, rootInterfaceName{std::string(rootInterfaceName)}](
1599                 const bool &success,
1600                 const std::vector<std::string> &iface_list) {
1601                 if (!success)
1602                 {
1603                     messages::internalError(asyncResp->res);
1604                     return;
1605                 }
1606                 asyncResp->res.jsonValue["@odata.type"] =
1607                     "#VLanNetworkInterfaceCollection."
1608                     "VLanNetworkInterfaceCollection";
1609                 asyncResp->res.jsonValue["@odata.context"] =
1610                     "/redfish/v1/$metadata"
1611                     "#VLanNetworkInterfaceCollection."
1612                     "VLanNetworkInterfaceCollection";
1613                 asyncResp->res.jsonValue["Name"] =
1614                     "VLAN Network Interface Collection";
1615 
1616                 nlohmann::json iface_array = nlohmann::json::array();
1617 
1618                 for (const std::string &iface_item : iface_list)
1619                 {
1620                     if (boost::starts_with(iface_item, rootInterfaceName + "_"))
1621                     {
1622                         iface_array.push_back(
1623                             {{"@odata.id",
1624                               "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1625                                   rootInterfaceName + "/VLANs/" + iface_item}});
1626                     }
1627                 }
1628 
1629                 if (iface_array.empty())
1630                 {
1631                     messages::resourceNotFound(
1632                         asyncResp->res, "EthernetInterface", rootInterfaceName);
1633                     return;
1634                 }
1635                 asyncResp->res.jsonValue["Members@odata.count"] =
1636                     iface_array.size();
1637                 asyncResp->res.jsonValue["Members"] = std::move(iface_array);
1638                 asyncResp->res.jsonValue["@odata.id"] =
1639                     "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1640                     rootInterfaceName + "/VLANs";
1641             });
1642     }
1643 
1644     void doPost(crow::Response &res, const crow::Request &req,
1645                 const std::vector<std::string> &params) override
1646     {
1647         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1648         if (params.size() != 1)
1649         {
1650             messages::internalError(asyncResp->res);
1651             return;
1652         }
1653 
1654         uint32_t vlanId = 0;
1655         if (!json_util::readJson(req, res, "VLANId", vlanId))
1656         {
1657             return;
1658         }
1659         const std::string &rootInterfaceName = params[0];
1660         auto callback = [asyncResp](const boost::system::error_code ec) {
1661             if (ec)
1662             {
1663                 // TODO(ed) make more consistent error messages based on
1664                 // phosphor-network responses
1665                 messages::internalError(asyncResp->res);
1666                 return;
1667             }
1668             messages::created(asyncResp->res);
1669         };
1670         crow::connections::systemBus->async_method_call(
1671             std::move(callback), "xyz.openbmc_project.Network",
1672             "/xyz/openbmc_project/network",
1673             "xyz.openbmc_project.Network.VLAN.Create", "VLAN",
1674             rootInterfaceName, vlanId);
1675     }
1676 };
1677 } // namespace redfish
1678