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