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