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