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