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